Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ require (
github.com/celestiaorg/smt v0.2.1-0.20220414134126-dba215ccb884
github.com/deepmap/oapi-codegen v1.12.4
github.com/dgraph-io/badger/v3 v3.2103.2
github.com/foxcpp/go-mockdns v1.0.0
github.com/getkin/kin-openapi v0.107.0
github.com/jackc/pgconn v1.13.0
github.com/jordanorelli/lexnum v0.0.0-20141216151731-460eeb125754
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ=
github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI=
github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4=
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
Expand Down Expand Up @@ -515,6 +517,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
Expand Down Expand Up @@ -809,6 +812,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
Expand Down Expand Up @@ -880,6 +884,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand Down Expand Up @@ -962,6 +967,8 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down Expand Up @@ -1057,6 +1064,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
Expand Down
6 changes: 6 additions & 0 deletions libp2p/docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.0.3] - 2023-03-15

- Added mockdns as a test dependency
- Mocked DNS resolution in url_conversion_test.go
- Added regression tests to url_conversion_test.go for single- and multi-record DNS responses

## [0.0.0.2] - 2023-03-03

- Added a new `modules.P2PModule` implementation to the `libp2p` module directory
Expand Down
204 changes: 170 additions & 34 deletions libp2p/network/url_conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@ package network

import (
"fmt"
"regexp"
"net"
"testing"

"github.com/foxcpp/go-mockdns"

"github.com/multiformats/go-multiaddr"
"github.com/stretchr/testify/require"
)

func TestPeerMultiAddrFromServiceURL_Success(t *testing.T) {
zones := map[string]mockdns.Zone{
"www.google.com.": {
A: []string{"142.250.181.196"},
},
}
closeDNSMock := prepareDNSResolverMock(t, zones)

testCases := []struct {
name string
serviceURL string
expetedMultiaddrRegex string
name string
serviceURL string
expectedMultiaddr string
}{
{
"fqdn",
"www.google.com:8080",
// These regexes match a superset of valid IPv4/6 address space; simplified for convenience.
`(/ip4/(\d{1,3}\.){3}\d{1,3}/tcp/8080)|(/ip6/([0-9a-f]{1,4}::?){1,7}[0-9a-f]{1,4}/tcp/8080)`,
"/ip4/142.250.181.196/tcp/8080",
},
{
"IPv4",
Expand All @@ -38,9 +46,10 @@ func TestPeerMultiAddrFromServiceURL_Success(t *testing.T) {
actualMultiaddr, err := Libp2pMultiaddrFromServiceURL(testCase.serviceURL)
require.NoError(t, err)
require.NotNil(t, actualMultiaddr)
require.Regexp(t, regexp.MustCompile(testCase.expetedMultiaddrRegex), actualMultiaddr.String())
require.Equal(t, testCase.expectedMultiaddr, actualMultiaddr.String())
})
}
closeDNSMock()
}

const (
Expand Down Expand Up @@ -173,33 +182,134 @@ func TestServiceURLFromLibp2pMultiaddr_Error(t *testing.T) {

// TECHDEBT: add helpers for crating and/or using a "test resolver" which can
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remove this comment?

// be switched with `net.DefaultResolver` and can return mocked responses.
func TestGetPeerIP_Success(t *testing.T) {
t.Skip("TODO: replace `net.DefaultResolver` with one which has a `Dial` function that returns a mocked `net.Conn` (see: https://pkg.go.dev/net#Resolver)")

//nolint:gocritic // commentedOutCode - Outlines the minimum requirements for disproving regression.
// testCases := []struct {
// name string
// hostname string
// // TECHDEBT: seed math/rand for predictable selection within mocked response.
// expectedIP net.IP
// }{
// {
// "single A record",
// "single.A.example",
// },
// {
// "single AAAA record",
// "single.AAAA.example",
// },
// {
// "multiple A records",
// "multi.A.example",
// },
// {
// "multiple AAAA records",
// "multi.AAAA.example",
// },
// }
func TestGetPeerIP_SingleRecord_Success(t *testing.T) {
testCases := []struct {
name string
hostname string
recordType dnsRecordType
expectedIP string
}{
{
"A record",
"single.a.example",
aRecord,
"10.0.0.1",
},
{
"AAAA record",
"single.aaaa.example",
quadARecord,
"fc00::1",
},
}

// Setup mock DNS
zones := make(map[string]mockdns.Zone)
for _, testCase := range testCases {
// Fully qualified domain name
// (see: https://en.wikipedia.org/wiki/Fully_qualified_domain_name)
fqdn := fmt.Sprintf("%s.", testCase.hostname)

switch testCase.recordType {
case aRecord:
zones[fqdn] = mockdns.Zone{
A: []string{testCase.expectedIP},
}
case quadARecord:
zones[fqdn] = mockdns.Zone{
AAAA: []string{testCase.expectedIP},
}
}
}
closeDNSMock := prepareDNSResolverMock(t, zones)
defer closeDNSMock()

// Run tests
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {

actualIP, err := getPeerIP(testCase.hostname)
require.NoError(t, err)
require.Equal(t, testCase.expectedIP, actualIP.String())
})
}
}

func TestGetPeerIP_MultipleRecord_Success(t *testing.T) {
testCases := []struct {
name string
hostname string
recordType dnsRecordType
expectedIPs []string
}{
{
"A records",
"multi.a.example",
aRecord,
[]string{
"10.0.0.2",
"10.0.0.3",
"10.0.0.4",
"10.0.0.5",
},
},
{
"AAAA records",
"multi.aaaa.example",
quadARecord,
[]string{
"fc00::1",
"fc00::2",
"fc00::3",
"fc00::4",
},
},
}

// Setup mock DNS
zones := make(map[string]mockdns.Zone)
for _, testCase := range testCases {
// Fully qualified domain name
// (see: https://en.wikipedia.org/wiki/Fully_qualified_domain_name)
fqdn := fmt.Sprintf("%s.", testCase.hostname)

switch testCase.recordType {
case aRecord:
zones[fqdn] = mockdns.Zone{
A: testCase.expectedIPs,
}
case quadARecord:
zones[fqdn] = mockdns.Zone{
AAAA: testCase.expectedIPs,
}
}
}
closeDNSMock := prepareDNSResolverMock(t, zones)
defer closeDNSMock()

// Run tests
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
seenIPCounts := make(map[string]uint)
maxAttempts := len(testCase.expectedIPs) * 100 // arbitrary scalar
for i := 0; i < maxAttempts; i++ {
// Break if all IPs already seen
if len(seenIPCounts) == len(testCase.expectedIPs) {
break
}

actualIP, err := getPeerIP(testCase.hostname)
require.NoError(t, err)

seenIPCounts[actualIP.String()]++
}
var seenIPs []string
for ip := range seenIPCounts {
seenIPs = append(seenIPs, ip)
}
require.ElementsMatchf(t, seenIPs, testCase.expectedIPs, "expected and seen IPs don't match")
})
}
}

func TestGetPeerIP_Error(t *testing.T) {
Expand All @@ -212,3 +322,29 @@ func TestGetPeerIP_Error(t *testing.T) {
require.ErrorContains(t, err, errResolvePeerIPMsg)
require.ErrorContains(t, err, hostname)
}

type dnsRecordType = string

const (
aRecord dnsRecordType = "A"
quadARecord dnsRecordType = "AAAA"
)

func prepareDNSResolverMock(t *testing.T, zones map[string]mockdns.Zone) (done func()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! 😍

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

srv, err := mockdns.NewServerWithLogger(zones, noopLogger{}, false)
require.NoError(t, err)

srv.PatchNet(net.DefaultResolver)
return func() {
_ = srv.Close()
mockdns.UnpatchNet(net.DefaultResolver)
}
}

// noopLogger implements go-mockdns's `mockdns.Logger` interface.
// The default logging behavior in mockdns is too noisy.
type noopLogger struct{}

func (nl noopLogger) Printf(format string, args ...interface{}) {
// noop
}