Refactor network to a top-level module

This commit is contained in:
9seconds
2021-03-14 21:43:30 +03:00
parent 37a78bd1c3
commit f8ad90c845
25 changed files with 231 additions and 192 deletions
+5 -5
View File
@@ -7,6 +7,7 @@ import (
"github.com/9seconds/mtg/v2/config" "github.com/9seconds/mtg/v2/config"
"github.com/9seconds/mtg/v2/mtglib" "github.com/9seconds/mtg/v2/mtglib"
"github.com/9seconds/mtg/v2/testlib"
"github.com/jarcoal/httpmock" "github.com/jarcoal/httpmock"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/xeipuuv/gojsonschema" "github.com/xeipuuv/gojsonschema"
@@ -126,7 +127,7 @@ func (suite *AccessTestSuite) TestGenerateNoCalls() {
suite.cli.Access.PublicIPv4 = net.ParseIP("10.0.0.10") suite.cli.Access.PublicIPv4 = net.ParseIP("10.0.0.10")
suite.cli.Access.PublicIPv6 = net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334") suite.cli.Access.PublicIPv6 = net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334")
output := suite.CaptureStdout(func() { output := testlib.CaptureStdout(func() {
suite.NoError(suite.cli.Access.Execute(suite.cli)) suite.NoError(suite.cli.Access.Execute(suite.cli))
}) })
@@ -150,7 +151,7 @@ func (suite *AccessTestSuite) TestGenerateIPv4Call() {
httpmock.RegisterResponder(http.MethodGet, "https://ifconfig.co", httpmock.RegisterResponder(http.MethodGet, "https://ifconfig.co",
httpmock.NewStringResponder(http.StatusOK, "10.11.12.13")) httpmock.NewStringResponder(http.StatusOK, "10.11.12.13"))
output := suite.CaptureStdout(func() { output := testlib.CaptureStdout(func() {
suite.NoError(suite.cli.Access.Execute(suite.cli)) suite.NoError(suite.cli.Access.Execute(suite.cli))
}) })
@@ -174,7 +175,7 @@ func (suite *AccessTestSuite) TestIPv4CallFail() {
httpmock.RegisterResponder(http.MethodGet, "https://ifconfig.co", httpmock.RegisterResponder(http.MethodGet, "https://ifconfig.co",
httpmock.NewStringResponder(http.StatusForbidden, "")) httpmock.NewStringResponder(http.StatusForbidden, ""))
output := suite.CaptureStdout(func() { output := testlib.CaptureStdout(func() {
suite.NoError(suite.cli.Access.Execute(suite.cli)) suite.NoError(suite.cli.Access.Execute(suite.cli))
}) })
@@ -191,7 +192,6 @@ func (suite *AccessTestSuite) TestIPv4CallFail() {
suite.Contains(output, suite.cli.Access.Config.Secret.Hex()) suite.Contains(output, suite.cli.Access.Config.Secret.Hex())
} }
func TestAccess(t *testing.T) { func TestAccess(t *testing.T) { // nolint: paralleltest
t.Parallel()
suite.Run(t, &AccessTestSuite{}) suite.Run(t, &AccessTestSuite{})
} }
+4 -3
View File
@@ -7,11 +7,12 @@ import (
"net/url" "net/url"
"github.com/9seconds/mtg/v2/config" "github.com/9seconds/mtg/v2/config"
"github.com/9seconds/mtg/v2/mtglib/network" "github.com/9seconds/mtg/v2/mtglib"
"github.com/9seconds/mtg/v2/network"
) )
type base struct { type base struct {
Network network.Network Network mtglib.Network
Config *config.Config Config *config.Config
} }
@@ -37,7 +38,7 @@ func (b *base) ReadConfig(path, version string) error {
return nil return nil
} }
func (b *base) makeNetwork(conf *config.Config, version string) (network.Network, error) { func (b *base) makeNetwork(conf *config.Config, version string) (mtglib.Network, error) {
tcpTimeout := conf.Network.Timeout.TCP.Value(network.DefaultTimeout) tcpTimeout := conf.Network.Timeout.TCP.Value(network.DefaultTimeout)
idleTimeout := conf.Network.Timeout.Idle.Value(network.DefaultIdleTimeout) idleTimeout := conf.Network.Timeout.Idle.Value(network.DefaultIdleTimeout)
httpTimeout := conf.Network.Timeout.HTTP.Value(network.DefaultHTTPTimeout) httpTimeout := conf.Network.Timeout.HTTP.Value(network.DefaultHTTPTimeout)
+3 -2
View File
@@ -5,6 +5,7 @@ import (
"testing" "testing"
"github.com/9seconds/mtg/v2/mtglib" "github.com/9seconds/mtg/v2/mtglib"
"github.com/9seconds/mtg/v2/testlib"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
@@ -19,7 +20,7 @@ func (suite *GenerateSecretTestSuite) SetupTest() {
} }
func (suite *GenerateSecretTestSuite) TestDefault() { func (suite *GenerateSecretTestSuite) TestDefault() {
output := suite.CaptureStdout(func() { output := testlib.CaptureStdout(func() {
suite.NoError(suite.cli.GenerateSecret.Run(suite.cli, "dev")) suite.NoError(suite.cli.GenerateSecret.Run(suite.cli, "dev"))
}) })
suite.True(strings.HasPrefix(output, "7")) suite.True(strings.HasPrefix(output, "7"))
@@ -33,7 +34,7 @@ func (suite *GenerateSecretTestSuite) TestDefault() {
func (suite *GenerateSecretTestSuite) TestHex() { func (suite *GenerateSecretTestSuite) TestHex() {
suite.cli.GenerateSecret.Hex = true suite.cli.GenerateSecret.Hex = true
output := suite.CaptureStdout(func() { output := testlib.CaptureStdout(func() {
suite.NoError(suite.cli.GenerateSecret.Run(suite.cli, "dev")) suite.NoError(suite.cli.GenerateSecret.Run(suite.cli, "dev"))
}) })
suite.True(strings.HasPrefix(output, "ee")) suite.True(strings.HasPrefix(output, "ee"))
+3 -78
View File
@@ -1,66 +1,25 @@
package cli_test package cli_test
import ( import (
"bytes"
"context"
"io"
"net"
"net/http" "net/http"
"os"
"strings"
"time"
"github.com/9seconds/mtg/v2/cli" "github.com/9seconds/mtg/v2/cli"
"github.com/9seconds/mtg/v2/mtglib/network" "github.com/9seconds/mtg/v2/testlib"
"github.com/jarcoal/httpmock" "github.com/jarcoal/httpmock"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
type NetworkMock struct {
mock.Mock
}
func (n *NetworkMock) Dial(network, address string) (net.Conn, error) {
args := n.Called(network, address)
return args.Get(0).(net.Conn), args.Error(1)
}
func (n *NetworkMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
args := n.Called(ctx, network, address)
return args.Get(0).(net.Conn), args.Error(1)
}
func (n *NetworkMock) DNSResolve(network, hostname string) ([]string, error) {
args := n.Called(network, hostname)
return args.Get(0).([]string), args.Error(1)
}
func (n *NetworkMock) MakeHTTPClient(dialFunc network.DialFunc) *http.Client {
return n.Called(dialFunc).Get(0).(*http.Client)
}
func (n *NetworkMock) IdleTimeout() time.Duration {
return n.Called().Get(0).(time.Duration)
}
func (n *NetworkMock) HTTPTimeout() time.Duration {
return n.Called().Get(0).(time.Duration)
}
type CommonTestSuite struct { type CommonTestSuite struct {
suite.Suite suite.Suite
cli *cli.CLI cli *cli.CLI
networkMock *NetworkMock networkMock *testlib.NetworkMock
httpClient *http.Client httpClient *http.Client
} }
func (suite *CommonTestSuite) SetupTest() { func (suite *CommonTestSuite) SetupTest() {
suite.networkMock = &NetworkMock{} suite.networkMock = &testlib.NetworkMock{}
suite.httpClient = &http.Client{} suite.httpClient = &http.Client{}
suite.cli = &cli.CLI{} suite.cli = &cli.CLI{}
@@ -76,37 +35,3 @@ func (suite *CommonTestSuite) TearDownTest() {
suite.networkMock.AssertExpectations(suite.T()) suite.networkMock.AssertExpectations(suite.T())
httpmock.DeactivateAndReset() httpmock.DeactivateAndReset()
} }
func (suite *CommonTestSuite) CaptureStdout(callback func()) string {
return suite.captureOutput(&os.Stdout, callback)
}
func (suite *CommonTestSuite) CaptureStderr(callback func()) string {
return suite.captureOutput(&os.Stderr, callback)
}
func (suite *CommonTestSuite) captureOutput(filefp **os.File, callback func()) string {
oldFp := *filefp
defer func() {
*filefp = oldFp
}()
reader, writer, _ := os.Pipe()
buf := &bytes.Buffer{}
closeChan := make(chan bool)
go func() {
io.Copy(buf, reader) // nolint: errcheck
close(closeChan)
}()
*filefp = writer
callback()
writer.Close()
<-closeChan
return strings.TrimSpace(buf.String())
}
+1
View File
@@ -12,6 +12,7 @@ require (
github.com/libp2p/go-reuseport v0.0.2 github.com/libp2p/go-reuseport v0.0.2
github.com/mccutchen/go-httpbin v1.1.1 github.com/mccutchen/go-httpbin v1.1.1
github.com/pelletier/go-toml v1.8.1 github.com/pelletier/go-toml v1.8.1
github.com/rs/zerolog v1.20.0 // indirect
github.com/stretchr/objx v0.3.0 // indirect github.com/stretchr/objx v0.3.0 // indirect
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/xeipuuv/gojsonschema v1.2.0 github.com/xeipuuv/gojsonschema v1.2.0
+11
View File
@@ -6,6 +6,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0=
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -27,6 +28,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -42,15 +46,22 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+28 -1
View File
@@ -1,5 +1,32 @@
package mtglib package mtglib
import "errors" import (
"context"
"errors"
"net"
"net/http"
"time"
)
var ErrSecretEmpty = errors.New("secret is empty") var ErrSecretEmpty = errors.New("secret is empty")
type Network interface {
Dial(network, address string) (net.Conn, error)
DialContext(ctx context.Context, network, address string) (net.Conn, error)
MakeHTTPClient(func(ctx context.Context, network, address string) (net.Conn, error)) *http.Client
IdleTimeout() time.Duration
}
type Logger interface {
Named(name string) Logger
BindInt(name string, value int) Logger
BindStr(name, value string) Logger
Info(msg string)
InfoError(msg string, err error)
Warning(msg string)
WarningError(msg string, err error)
Debug(msg string)
DebugError(msg string, err error)
}
-65
View File
@@ -1,65 +0,0 @@
package network
import (
"context"
"net"
"time"
"github.com/stretchr/testify/mock"
)
type ConnMock struct {
mock.Mock
}
func (c *ConnMock) Read(b []byte) (int, error) {
args := c.Called(b)
return args.Int(0), args.Error(1)
}
func (c *ConnMock) Write(b []byte) (int, error) {
args := c.Called(b)
return args.Int(0), args.Error(1)
}
func (c *ConnMock) Close() error {
return c.Called().Error(0)
}
func (c *ConnMock) LocalAddr() net.Addr {
return c.Called().Get(0).(net.Addr)
}
func (c *ConnMock) RemoteAddr() net.Addr {
return c.Called().Get(0).(net.Addr)
}
func (c *ConnMock) SetDeadline(t time.Time) error {
return c.Called(t).Error(0)
}
func (c *ConnMock) SetReadDeadline(t time.Time) error {
return c.Called(t).Error(0)
}
func (c *ConnMock) SetWriteDeadline(t time.Time) error {
return c.Called(t).Error(0)
}
type DialerMock struct {
mock.Mock
}
func (d *DialerMock) Dial(network, address string) (net.Conn, error) {
args := d.Called(network, address)
return args.Get(0).(net.Conn), args.Error(1)
}
func (d *DialerMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
args := d.Called(ctx, network, address)
return args.Get(0).(net.Conn), args.Error(1)
}
@@ -9,6 +9,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/9seconds/mtg/v2/testlib"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
@@ -20,7 +21,7 @@ type CircuitBreakerTestSuite struct {
mutex sync.Mutex mutex sync.Mutex
ctx context.Context ctx context.Context
ctxCancel context.CancelFunc ctxCancel context.CancelFunc
connMock *ConnMock connMock *testlib.NetConnMock
baseDialerMock *DialerMock baseDialerMock *DialerMock
} }
@@ -28,7 +29,7 @@ func (suite *CircuitBreakerTestSuite) SetupTest() {
suite.mutex = sync.Mutex{} suite.mutex = sync.Mutex{}
suite.ctx, suite.ctxCancel = context.WithCancel(context.Background()) suite.ctx, suite.ctxCancel = context.WithCancel(context.Background())
suite.baseDialerMock = &DialerMock{} suite.baseDialerMock = &DialerMock{}
suite.connMock = &ConnMock{} suite.connMock = &testlib.NetConnMock{}
suite.d = newCircuitBreakerDialer(suite.baseDialerMock, suite.d = newCircuitBreakerDialer(suite.baseDialerMock,
3, 100*time.Millisecond, 50*time.Millisecond) 3, 100*time.Millisecond, 50*time.Millisecond)
} }
@@ -5,7 +5,7 @@ import (
"net/http" "net/http"
"testing" "testing"
"github.com/9seconds/mtg/v2/mtglib/network" "github.com/9seconds/mtg/v2/network"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
@@ -4,7 +4,6 @@ import (
"context" "context"
"errors" "errors"
"net" "net"
"net/http"
"time" "time"
) )
@@ -27,18 +26,7 @@ var (
ErrCannotDialWithAllProxies = errors.New("cannot dial with all proxies") ErrCannotDialWithAllProxies = errors.New("cannot dial with all proxies")
) )
type DialFunc func(ctx context.Context, protocol, address string) (net.Conn, error)
type Dialer interface { type Dialer interface {
Dial(network, address string) (net.Conn, error) Dial(network, address string) (net.Conn, error)
DialContext(ctx context.Context, network, address string) (net.Conn, error) DialContext(ctx context.Context, network, address string) (net.Conn, error)
} }
type Network interface {
Dialer
DNSResolve(network, hostname string) (ips []string, err error)
MakeHTTPClient(DialFunc) *http.Client
IdleTimeout() time.Duration
HTTPTimeout() time.Duration
}
+24
View File
@@ -0,0 +1,24 @@
package network
import (
"context"
"net"
"github.com/stretchr/testify/mock"
)
type DialerMock struct {
mock.Mock
}
func (d *DialerMock) Dial(network, address string) (net.Conn, error) {
args := d.Called(network, address)
return args.Get(0).(net.Conn), args.Error(1)
}
func (d *DialerMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
args := d.Called(ctx, network, address)
return args.Get(0).(net.Conn), args.Error(1)
}
@@ -8,7 +8,7 @@ import (
"net/url" "net/url"
"strings" "strings"
"github.com/9seconds/mtg/v2/mtglib/network" "github.com/9seconds/mtg/v2/network"
socks5 "github.com/armon/go-socks5" socks5 "github.com/armon/go-socks5"
"github.com/mccutchen/go-httpbin/httpbin" "github.com/mccutchen/go-httpbin/httpbin"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
@@ -8,7 +8,7 @@ import (
"net/url" "net/url"
"testing" "testing"
"github.com/9seconds/mtg/v2/mtglib/network" "github.com/9seconds/mtg/v2/network"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
@@ -9,6 +9,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/9seconds/mtg/v2/mtglib"
doh "github.com/babolivier/go-doh-client" doh "github.com/babolivier/go-doh-client"
) )
@@ -38,7 +39,7 @@ func (n *network) Dial(protocol, address string) (net.Conn, error) {
func (n *network) DialContext(ctx context.Context, protocol, address string) (net.Conn, error) { func (n *network) DialContext(ctx context.Context, protocol, address string) (net.Conn, error) {
host, port, _ := net.SplitHostPort(address) host, port, _ := net.SplitHostPort(address)
ips, err := n.DNSResolve(protocol, host) ips, err := n.dnsResolve(protocol, host)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot resolve dns names: %w", err) return nil, fmt.Errorf("cannot resolve dns names: %w", err)
} }
@@ -61,7 +62,20 @@ func (n *network) DialContext(ctx context.Context, protocol, address string) (ne
return nil, fmt.Errorf("cannot dial to %s:%s: %w", protocol, address, err) return nil, fmt.Errorf("cannot dial to %s:%s: %w", protocol, address, err)
} }
func (n *network) DNSResolve(protocol, address string) ([]string, error) { func (n *network) MakeHTTPClient(dialFunc func(ctx context.Context,
network, address string) (net.Conn, error)) *http.Client {
if dialFunc == nil {
dialFunc = n.DialContext
}
return makeHTTPClient(n.userAgent, n.httpTimeout, dialFunc)
}
func (n *network) IdleTimeout() time.Duration {
return n.idleTimeout
}
func (n *network) dnsResolve(protocol, address string) ([]string, error) {
if net.ParseIP(address) != nil { if net.ParseIP(address) != nil {
return []string{address}, nil return []string{address}, nil
} }
@@ -115,25 +129,9 @@ func (n *network) DNSResolve(protocol, address string) ([]string, error) {
return ips, nil return ips, nil
} }
func (n *network) MakeHTTPClient(dialFunc DialFunc) *http.Client {
if dialFunc == nil {
dialFunc = n.DialContext
}
return makeHTTPClient(n.userAgent, n.httpTimeout, dialFunc)
}
func (n *network) IdleTimeout() time.Duration {
return n.idleTimeout
}
func (n *network) HTTPTimeout() time.Duration {
return n.httpTimeout
}
func NewNetwork(dialer Dialer, func NewNetwork(dialer Dialer,
userAgent, dohHostname string, userAgent, dohHostname string,
httpTimeout, idleTimeout time.Duration) (Network, error) { httpTimeout, idleTimeout time.Duration) (mtglib.Network, error) {
switch { switch {
case idleTimeout < 0: case idleTimeout < 0:
return nil, fmt.Errorf("timeout should be positive number %s", idleTimeout) return nil, fmt.Errorf("timeout should be positive number %s", idleTimeout)
@@ -158,7 +156,9 @@ func NewNetwork(dialer Dialer,
}, nil }, nil
} }
func makeHTTPClient(userAgent string, timeout time.Duration, dialFunc DialFunc) *http.Client { func makeHTTPClient(userAgent string,
timeout time.Duration,
dialFunc func(ctx context.Context, network, address string) (net.Conn, error)) *http.Client {
return &http.Client{ return &http.Client{
Timeout: timeout, Timeout: timeout,
Transport: networkHTTPTransport{ Transport: networkHTTPTransport{
@@ -4,7 +4,7 @@ import (
"net/http" "net/http"
"testing" "testing"
"github.com/9seconds/mtg/v2/mtglib/network" "github.com/9seconds/mtg/v2/network"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
) )
+42
View File
@@ -0,0 +1,42 @@
package testlib
import (
"bytes"
"io"
"os"
"strings"
)
func CaptureStdout(callback func()) string {
return captureOutput(&os.Stdout, callback)
}
func CaptureStderr(callback func()) string {
return captureOutput(&os.Stderr, callback)
}
func captureOutput(filefp **os.File, callback func()) string {
oldFp := *filefp
defer func() {
*filefp = oldFp
}()
reader, writer, _ := os.Pipe()
buf := &bytes.Buffer{}
closeChan := make(chan bool)
go func() {
io.Copy(buf, reader) // nolint: errcheck
close(closeChan)
}()
*filefp = writer
callback()
writer.Close()
<-closeChan
return strings.TrimSpace(buf.String())
}
+48
View File
@@ -0,0 +1,48 @@
package testlib
import (
"net"
"time"
"github.com/stretchr/testify/mock"
)
type NetConnMock struct {
mock.Mock
}
func (n *NetConnMock) Read(b []byte) (int, error) {
args := n.Called(b)
return args.Int(0), args.Error(1)
}
func (n *NetConnMock) Write(b []byte) (int, error) {
args := n.Called(b)
return args.Int(0), args.Error(1)
}
func (n *NetConnMock) Close() error {
return n.Called().Error(0)
}
func (n *NetConnMock) LocalAddr() net.Addr {
return n.Called().Get(0).(net.Addr)
}
func (n *NetConnMock) RemoteAddr() net.Addr {
return n.Called().Get(0).(net.Addr)
}
func (n *NetConnMock) SetDeadline(t time.Time) error {
return n.Called(t).Error(0)
}
func (n *NetConnMock) SetReadDeadline(t time.Time) error {
return n.Called(t).Error(0)
}
func (n *NetConnMock) SetWriteDeadline(t time.Time) error {
return n.Called(t).Error(0)
}
+35
View File
@@ -0,0 +1,35 @@
package testlib
import (
"context"
"net"
"net/http"
"time"
"github.com/stretchr/testify/mock"
)
type NetworkMock struct {
mock.Mock
}
func (n *NetworkMock) Dial(network, address string) (net.Conn, error) {
args := n.Called(network, address)
return args.Get(0).(net.Conn), args.Error(1)
}
func (n *NetworkMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
args := n.Called(ctx, network, address)
return args.Get(0).(net.Conn), args.Error(1)
}
func (n *NetworkMock) MakeHTTPClient(dialFunc func(ctx context.Context,
network, address string) (net.Conn, error)) *http.Client {
return n.Called(dialFunc).Get(0).(*http.Client)
}
func (n *NetworkMock) IdleTimeout() time.Duration {
return n.Called().Get(0).(time.Duration)
}