diff --git a/config/type_hostport.go b/config/type_hostport.go index e86149d..4bc58f1 100644 --- a/config/type_hostport.go +++ b/config/type_hostport.go @@ -16,11 +16,17 @@ func (c *TypeHostPort) UnmarshalText(data []byte) error { return nil } - host, port, err := net.SplitHostPort(string(data)) + text := string(data) + + host, port, err := net.SplitHostPort(text) if err != nil { return fmt.Errorf("incorrect host:port syntax: %w", err) } + if port == "" { + return fmt.Errorf("port in %s host:port pair cannot be empty", text) + } + if err := c.port.UnmarshalJSON([]byte(port)); err != nil { return fmt.Errorf("incorrect port in host:port: %w", err) } @@ -52,5 +58,10 @@ func (c TypeHostPort) Value(defaultHostValue net.IP, defaultPortValue uint) stri host := c.HostValue(defaultHostValue) port := c.PortValue(defaultPortValue) - return net.JoinHostPort(host.String(), strconv.Itoa(int(port))) + hostStr := "" + if len(host) > 0 { + hostStr = host.String() + } + + return net.JoinHostPort(hostStr, strconv.Itoa(int(port))) } diff --git a/config/type_hostport_test.go b/config/type_hostport_test.go new file mode 100644 index 0000000..5cf1994 --- /dev/null +++ b/config/type_hostport_test.go @@ -0,0 +1,115 @@ +package config_test + +import ( + "encoding/json" + "net" + "testing" + + "github.com/9seconds/mtg/v2/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeHostPortTestStruct struct { + Value config.TypeHostPort `json:"value"` +} + +type TypeHostPortTestSuite struct { + suite.Suite +} + +func (suite *TypeHostPortTestSuite) TestUnmarshalFail() { + testData := []string{ + "10.0.0.10:aaa", + "10.0.0.10:", + ":", + "xxx", + "xxx:80", + } + + for _, v := range testData { + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeHostPortTestStruct{})) + }) + } +} + +func (suite *TypeHostPortTestSuite) TestUnmarshalOk() { + testData := []string{ + "10.0.0.10:80", + "0.0.0.0:80", + ":8000", + } + + for _, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + testStruct := &typeHostPortTestStruct{} + + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.EqualValues(t, value, testStruct.Value.Value(nil, 0)) + }) + } +} + +func (suite *TypeHostPortTestSuite) TestMarshalOk() { + testData := []string{ + "10.0.0.10:80", + "0.0.0.0:80", + ":8000", + } + + for _, v := range testData { + value := v + + data, err := json.Marshal(map[string]string{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(v, func(t *testing.T) { + testStruct := &typeHostPortTestStruct{} + + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.Equal(t, value, testStruct.Value.String()) + + marshalled, err := testStruct.Value.MarshalText() + assert.NoError(t, err) + assert.Equal(t, value, string(marshalled)) + }) + } +} + +func (suite *TypeHostPortTestSuite) TestValue() { + testStruct := &typeHostPortTestStruct{} + + suite.EqualValues("127.0.0.1:80", + testStruct.Value.Value(net.ParseIP("127.0.0.1"), 80)) + suite.EqualValues("127.1.0.1:80", + testStruct.Value.Value(net.ParseIP("127.1.0.1"), 80)) + + data, err := json.Marshal(map[string]string{ + "value": "127.0.0.1:80", + }) + suite.NoError(err) + suite.NoError(json.Unmarshal(data, testStruct)) + + suite.EqualValues("127.0.0.1:80", testStruct.Value.Value(nil, 0)) + suite.EqualValues("127.0.0.1:80", testStruct.Value.Value(net.ParseIP("10.0.0.10"), 3000)) +} + +func TestTypeHostPort(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeHostPortTestSuite{}) +}