Sfoglia il codice sorgente

Add tests for access command

tags/v2.0.0-rc1
9seconds 5 anni fa
parent
commit
185baf6bc9
8 ha cambiato i file con 252 aggiunte e 29 eliminazioni
  1. 25
    13
      cli/access.go
  2. 205
    0
      cli/access_test.go
  3. 4
    4
      cli/base.go
  4. 3
    3
      cli/cli.go
  5. 3
    3
      cli/generate_secret.go
  6. 3
    2
      go.mod
  7. 8
    2
      go.sum
  8. 1
    2
      main.go

+ 25
- 13
cli/access.go Vedi File

26
 
26
 
27
 type accessResponseURLs struct {
27
 type accessResponseURLs struct {
28
 	IP        net.IP `json:"ip"`
28
 	IP        net.IP `json:"ip"`
29
+	Port      uint   `json:"port"`
29
 	TgURL     string `json:"tg_url"`
30
 	TgURL     string `json:"tg_url"`
30
 	TgQrCode  string `json:"tg_qrcode"`
31
 	TgQrCode  string `json:"tg_qrcode"`
31
 	TmeURL    string `json:"tme_url"`
32
 	TmeURL    string `json:"tme_url"`
33
 }
34
 }
34
 
35
 
35
 type Access struct {
36
 type Access struct {
36
-	base
37
+	base `kong:"-"`
37
 
38
 
38
-	ConfigPath string `arg required type:"existingfile" help:"Path to the configuration file." name:"config-path"` // nolint: lll, govet
39
-	Hex        bool   `help:"Print secret in hex encoding."`
39
+	ConfigPath string `kong:"arg,required,type='existingfile',help='Path to the configuration file.',name='config-path'"` // nolint: lll
40
+	Port       uint   `kong:"help='Port number. Default port is taken from configuration file, bind-to parameter',type:'uint'"`
41
+	Hex        bool   `kong:"help='Print secret in hex encoding.'"`
40
 }
42
 }
41
 
43
 
42
 func (c *Access) Run(cli *CLI, version string) error {
44
 func (c *Access) Run(cli *CLI, version string) error {
44
 		return fmt.Errorf("cannot init config: %w", err)
46
 		return fmt.Errorf("cannot init config: %w", err)
45
 	}
47
 	}
46
 
48
 
49
+	return c.Execute(cli)
50
+}
51
+
52
+func (c *Access) Execute(cli *CLI) error {
47
 	resp := &accessResponse{}
53
 	resp := &accessResponse{}
48
-	resp.Secret.Base64 = c.conf.Secret.Base64()
49
-	resp.Secret.Hex = c.conf.Secret.Hex()
54
+	resp.Secret.Base64 = c.Config.Secret.Base64()
55
+	resp.Secret.Hex = c.Config.Secret.Hex()
50
 
56
 
51
 	wg := &sync.WaitGroup{}
57
 	wg := &sync.WaitGroup{}
52
 	wg.Add(2) // nolint: gomnd
58
 	wg.Add(2) // nolint: gomnd
54
 	go func() {
60
 	go func() {
55
 		defer wg.Done()
61
 		defer wg.Done()
56
 
62
 
57
-		ip := c.conf.Network.PublicIP.IPv4.Value(nil)
63
+		ip := c.Config.Network.PublicIP.IPv4.Value(nil)
58
 		if ip == nil {
64
 		if ip == nil {
59
 			ip = c.getIP("tcp4")
65
 			ip = c.getIP("tcp4")
60
 		}
66
 		}
69
 	go func() {
75
 	go func() {
70
 		defer wg.Done()
76
 		defer wg.Done()
71
 
77
 
72
-		ip := c.conf.Network.PublicIP.IPv4.Value(nil)
78
+		ip := c.Config.Network.PublicIP.IPv6.Value(nil)
73
 		if ip == nil {
79
 		if ip == nil {
74
 			ip = c.getIP("tcp6")
80
 			ip = c.getIP("tcp6")
75
 		}
81
 		}
95
 }
101
 }
96
 
102
 
97
 func (c *Access) getIP(protocol string) net.IP {
103
 func (c *Access) getIP(protocol string) net.IP {
98
-	client := c.network.MakeHTTPClient(func(ctx context.Context, network, address string) (net.Conn, error) {
99
-		return c.network.DialContext(ctx, protocol, address)
104
+	client := c.Network.MakeHTTPClient(func(ctx context.Context, network, address string) (net.Conn, error) {
105
+		return c.Network.DialContext(ctx, protocol, address)
100
 	})
106
 	})
101
 
107
 
102
 	req, err := http.NewRequest(http.MethodGet, "https://ifconfig.co", nil) // nolint: noctx
108
 	req, err := http.NewRequest(http.MethodGet, "https://ifconfig.co", nil) // nolint: noctx
133
 		return nil
139
 		return nil
134
 	}
140
 	}
135
 
141
 
142
+	portNo := cli.Access.Port
143
+	if portNo == 0 {
144
+		portNo = c.Config.BindTo.PortValue(0)
145
+	}
146
+
136
 	values := url.Values{}
147
 	values := url.Values{}
137
 	values.Set("server", ip.String())
148
 	values.Set("server", ip.String())
138
-	values.Set("port", strconv.Itoa(int(c.conf.BindTo.PortValue(0))))
149
+	values.Set("port", strconv.Itoa(int(portNo)))
139
 
150
 
140
 	if cli.Access.Hex {
151
 	if cli.Access.Hex {
141
-		values.Set("secret", c.conf.Secret.Hex())
152
+		values.Set("secret", c.Config.Secret.Hex())
142
 	} else {
153
 	} else {
143
-		values.Set("secret", c.conf.Secret.Base64())
154
+		values.Set("secret", c.Config.Secret.Base64())
144
 	}
155
 	}
145
 
156
 
146
 	urlQuery := values.Encode()
157
 	urlQuery := values.Encode()
147
 
158
 
148
 	rv := &accessResponseURLs{
159
 	rv := &accessResponseURLs{
149
-		IP: ip,
160
+		IP:   ip,
161
+		Port: portNo,
150
 		TgURL: (&url.URL{
162
 		TgURL: (&url.URL{
151
 			Scheme:   "tg",
163
 			Scheme:   "tg",
152
 			Host:     "proxy",
164
 			Host:     "proxy",

+ 205
- 0
cli/access_test.go Vedi File

1
+package cli_test
2
+
3
+import (
4
+	"net/http"
5
+	"testing"
6
+
7
+	"github.com/9seconds/mtg/v2/config"
8
+	"github.com/9seconds/mtg/v2/mtglib"
9
+	"github.com/jarcoal/httpmock"
10
+	"github.com/stretchr/testify/suite"
11
+	"github.com/xeipuuv/gojsonschema"
12
+)
13
+
14
+var accressResponseJSONSchema = func() *gojsonschema.Schema {
15
+	schema, err := gojsonschema.NewSchema(gojsonschema.NewStringLoader(`
16
+{
17
+    "type": "object",
18
+    "required": ["secret"],
19
+    "additionalProperties": true,
20
+    "properties": {
21
+        "secret": {
22
+            "type": "object",
23
+            "required": [
24
+                "hex",
25
+                "base64"
26
+            ],
27
+            "additionalProperties": false,
28
+            "properties": {
29
+                "hex": {
30
+                    "type": "string",
31
+                    "minLength": 34
32
+                },
33
+                "base64": {
34
+                    "type": "string",
35
+                    "minLength": 10
36
+                }
37
+            }
38
+        },
39
+        "ipv4": {
40
+            "$ref": "#/definitions/ip"
41
+        },
42
+        "ipv6": {
43
+            "$ref": "#/definitions/ip"
44
+        }
45
+    },
46
+    "definitions": {
47
+        "ip": {
48
+            "type": "object",
49
+            "required": [
50
+                "ip",
51
+                "port",
52
+                "tg_url",
53
+                "tg_qrcode",
54
+                "tme_url",
55
+                "tme_qrcode"
56
+            ],
57
+            "additionalProperties": false,
58
+            "properties": {
59
+                "ip": {
60
+                    "type": "string",
61
+                    "minLength": 1,
62
+                    "anyOf": [
63
+                        {
64
+                            "format": "ipv4"
65
+                        },
66
+                        {
67
+                            "format": "ipv6"
68
+                        }
69
+                    ]
70
+                },
71
+                "port": {
72
+                    "type": "integer",
73
+                    "multipleOf": 1.0,
74
+                    "exclusiveMinimum": 0,
75
+                    "exclusiveMaximum": 65536
76
+                },
77
+                "tg_url": {
78
+                    "type": "string",
79
+                    "minLength": 1,
80
+                    "format": "uri"
81
+                },
82
+                "tg_qrcode": {
83
+                    "type": "string",
84
+                    "minLength": 1,
85
+                    "format": "uri"
86
+                },
87
+                "tme_url": {
88
+                    "type": "string",
89
+                    "minLength": 1,
90
+                    "format": "uri"
91
+                },
92
+                "tme_qrcode": {
93
+                    "type": "string",
94
+                    "minLength": 1,
95
+                    "format": "uri"
96
+                }
97
+            }
98
+        }
99
+    }
100
+}
101
+    `))
102
+
103
+	if err != nil {
104
+		panic(err)
105
+	}
106
+
107
+	return schema
108
+}()
109
+
110
+type AccessTestSuite struct {
111
+	CommonTestSuite
112
+}
113
+
114
+func (suite *AccessTestSuite) SetupTest() {
115
+	suite.CommonTestSuite.SetupTest()
116
+
117
+	suite.cli.Access.Config = &config.Config{}
118
+	suite.cli.Access.Config.Secret = mtglib.GenerateSecret("google.com")
119
+	suite.cli.Access.Network = suite.networkMock
120
+
121
+	suite.NoError(
122
+		suite.cli.Access.Config.BindTo.UnmarshalText([]byte("0.0.0.0:80")))
123
+}
124
+
125
+func (suite *AccessTestSuite) TestGenerateNoCalls() {
126
+	suite.NoError(
127
+		suite.cli.Access.Config.Network.PublicIP.IPv4.UnmarshalText(
128
+			[]byte("10.0.0.10")))
129
+	suite.NoError(
130
+		suite.cli.Access.Config.Network.PublicIP.IPv6.UnmarshalText(
131
+			[]byte("2001:0db8:85a3:0000:0000:8a2e:0370:7334")))
132
+
133
+	output := suite.CaptureStdout(func() {
134
+		suite.NoError(suite.cli.Access.Execute(suite.cli))
135
+	})
136
+
137
+	validated, err := accressResponseJSONSchema.Validate(
138
+		gojsonschema.NewStringLoader(output))
139
+	suite.NoError(err)
140
+	suite.Empty(validated.Errors())
141
+	suite.True(validated.Valid())
142
+
143
+	suite.Contains(output, "10.0.0.10")
144
+	suite.Contains(output, "2001:db8:85a3::8a2e:370:7334")
145
+	suite.Contains(output, "ipv4")
146
+	suite.Contains(output, "ipv6")
147
+	suite.Contains(output, suite.cli.Access.Config.Secret.Base64())
148
+	suite.Contains(output, suite.cli.Access.Config.Secret.Hex())
149
+}
150
+
151
+func (suite *AccessTestSuite) TestGenerateIPv4Call() {
152
+	suite.NoError(
153
+		suite.cli.Access.Config.Network.PublicIP.IPv6.UnmarshalText(
154
+			[]byte("2001:0db8:85a3:0000:0000:8a2e:0370:7334")))
155
+
156
+	httpmock.RegisterResponder(http.MethodGet, "https://ifconfig.co",
157
+		httpmock.NewStringResponder(http.StatusOK, "10.11.12.13"))
158
+
159
+	output := suite.CaptureStdout(func() {
160
+		suite.NoError(suite.cli.Access.Execute(suite.cli))
161
+	})
162
+
163
+	validated, err := accressResponseJSONSchema.Validate(
164
+		gojsonschema.NewStringLoader(output))
165
+	suite.NoError(err)
166
+	suite.Empty(validated.Errors())
167
+	suite.True(validated.Valid())
168
+
169
+	suite.Contains(output, "10.11.12.13")
170
+	suite.Contains(output, "2001:db8:85a3::8a2e:370:7334")
171
+	suite.Contains(output, "ipv4")
172
+	suite.Contains(output, "ipv6")
173
+	suite.Contains(output, suite.cli.Access.Config.Secret.Base64())
174
+	suite.Contains(output, suite.cli.Access.Config.Secret.Hex())
175
+}
176
+
177
+func (suite *AccessTestSuite) TestIPv4CallFail() {
178
+	suite.NoError(
179
+		suite.cli.Access.Config.Network.PublicIP.IPv6.UnmarshalText(
180
+			[]byte("2001:0db8:85a3:0000:0000:8a2e:0370:7334")))
181
+
182
+	httpmock.RegisterResponder(http.MethodGet, "https://ifconfig.co",
183
+		httpmock.NewStringResponder(http.StatusForbidden, ""))
184
+
185
+	output := suite.CaptureStdout(func() {
186
+		suite.NoError(suite.cli.Access.Execute(suite.cli))
187
+	})
188
+
189
+	validated, err := accressResponseJSONSchema.Validate(
190
+		gojsonschema.NewStringLoader(output))
191
+	suite.NoError(err)
192
+	suite.Empty(validated.Errors())
193
+	suite.True(validated.Valid())
194
+
195
+	suite.Contains(output, "2001:db8:85a3::8a2e:370:7334")
196
+	suite.NotContains(output, "ipv4")
197
+	suite.Contains(output, "ipv6")
198
+	suite.Contains(output, suite.cli.Access.Config.Secret.Base64())
199
+	suite.Contains(output, suite.cli.Access.Config.Secret.Hex())
200
+}
201
+
202
+func TestAccess(t *testing.T) {
203
+	t.Parallel()
204
+	suite.Run(t, &AccessTestSuite{})
205
+}

+ 4
- 4
cli/base.go Vedi File

11
 )
11
 )
12
 
12
 
13
 type base struct {
13
 type base struct {
14
-	network network.Network
15
-	conf    *config.Config
14
+	Network network.Network
15
+	Config  *config.Config
16
 }
16
 }
17
 
17
 
18
 func (b *base) ReadConfig(path, version string) error {
18
 func (b *base) ReadConfig(path, version string) error {
31
 		return fmt.Errorf("cannot build a network: %w", err)
31
 		return fmt.Errorf("cannot build a network: %w", err)
32
 	}
32
 	}
33
 
33
 
34
-	b.conf = conf
35
-	b.network = ntw
34
+	b.Config = conf
35
+	b.Network = ntw
36
 
36
 
37
 	return nil
37
 	return nil
38
 }
38
 }

+ 3
- 3
cli/cli.go Vedi File

3
 import "github.com/alecthomas/kong"
3
 import "github.com/alecthomas/kong"
4
 
4
 
5
 type CLI struct {
5
 type CLI struct {
6
-	GenerateSecret GenerateSecret   `cmd help:"Generate new proxy secret"` // nolint: govet
7
-	Access         Access           `cmd help:"Print access information."` // nolint: govet
8
-	Version        kong.VersionFlag `help:"Print version."`
6
+	GenerateSecret GenerateSecret   `kong:"cmd,help='Generate new proxy secret'"` // nolint: govet
7
+	Access         Access           `kong:"cmd,help='Print access information.'"` // nolint: govet
8
+	Version        kong.VersionFlag `kong:"help='Print version.'"`
9
 }
9
 }

+ 3
- 3
cli/generate_secret.go Vedi File

7
 )
7
 )
8
 
8
 
9
 type GenerateSecret struct {
9
 type GenerateSecret struct {
10
-	base
10
+    base `kong:"-"`
11
 
11
 
12
-	HostName string `arg optional help:"Hostname to use for domain fronting. Default is '${domain_front}'." name:"hostname" default:"${domain_front}"` // nolint: lll, govet
13
-	Hex      bool   `help:"Print secret in hex encoding."`
12
+	HostName string `kong:"arg,required,help='Hostname to use for domain fronting.',name='hostname'"` // nolint: lll, govet
13
+	Hex      bool   `kong:"help='Print secret in hex encoding.'"`
14
 }
14
 }
15
 
15
 
16
 func (c *GenerateSecret) Run(cli *CLI, _ string) error {
16
 func (c *GenerateSecret) Run(cli *CLI, _ string) error {

+ 3
- 2
go.mod Vedi File

7
 	github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15
7
 	github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15
8
 	github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
8
 	github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
9
 	github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6
9
 	github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6
10
-	github.com/jarcoal/httpmock v1.0.8 // indirect
10
+	github.com/jarcoal/httpmock v1.0.8
11
 	github.com/kr/pretty v0.1.0 // indirect
11
 	github.com/kr/pretty v0.1.0 // indirect
12
 	github.com/libp2p/go-reuseport v0.0.2
12
 	github.com/libp2p/go-reuseport v0.0.2
13
 	github.com/mccutchen/go-httpbin v1.1.1
13
 	github.com/mccutchen/go-httpbin v1.1.1
14
 	github.com/pelletier/go-toml v1.8.1
14
 	github.com/pelletier/go-toml v1.8.1
15
 	github.com/stretchr/objx v0.3.0 // indirect
15
 	github.com/stretchr/objx v0.3.0 // indirect
16
 	github.com/stretchr/testify v1.7.0
16
 	github.com/stretchr/testify v1.7.0
17
+	github.com/xeipuuv/gojsonschema v1.2.0
17
 	golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
18
 	golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
18
-	golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect
19
+	golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 // indirect
19
 	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
20
 	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
20
 )
21
 )

+ 8
- 2
go.sum Vedi File

36
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
36
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
37
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
37
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
38
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
38
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
39
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
40
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
41
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
42
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
43
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
44
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
39
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
45
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
40
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
46
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
41
 golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
47
 golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
42
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
48
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
43
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
44
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
49
+golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
50
+golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
45
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
51
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
46
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
52
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
47
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
53
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

+ 1
- 2
main.go Vedi File

15
 
15
 
16
 	cli := &cli.CLI{}
16
 	cli := &cli.CLI{}
17
 	ctx := kong.Parse(cli, kong.Vars{
17
 	ctx := kong.Parse(cli, kong.Vars{
18
-		"domain_front": "amazonaws.com",
19
-		"version":      version,
18
+		"version": version,
20
 	})
19
 	})
21
 
20
 
22
 	ctx.FatalIfErrorf(ctx.Run(cli, version))
21
 	ctx.FatalIfErrorf(ctx.Run(cli, version))

Loading…
Annulla
Salva