Repository: moby/moby
File: daemon/config/config_test.go (Go)

Code before:
			input: DefaultAPIVersion,
		},
	}

	for _, tc := range tests {
		t.Run(tc.doc, func(t *testing.T) {
			err := ValidateMinAPIVersion(tc.input)
			if tc.expectedErr != "" {
				assert.Check(t, is.ErrorContains(err, tc.expectedErr))
			} else {
				assert.Check(t, err)
			}
		})
	}
}

func TestConfigInvalidDNS(t *testing.T) {
	tests := []struct {
		doc         string
		input       string
		expectedErr string
	}{
		{
			doc:         "single DNS, invalid IP-address",
			input:       `{"dns": ["1.1.1.1o"]}`,
			expectedErr: `invalid IP address: 1.1.1.1o`,
		},
		{
			doc:         "multiple DNS, invalid IP-address",
			input:       `{"dns": ["2.2.2.2", "1.1.1.1o"]}`,
			expectedErr: `invalid IP address: 1.1.1.1o`,
		},
	}

	for _, tc := range tests {
		t.Run(tc.doc, func(t *testing.T) {
			var cfg Config
			err := json.Unmarshal([]byte(tc.input), &cfg)
			assert.Check(t, is.Error(err, tc.expectedErr))
		})
	}
}

func field(field string) cmp.Option {
	tmp := reflect.TypeOf(Config{})
	ignoreFields := make([]string, 0, tmp.NumField())
	for i := 0; i < tmp.NumField(); i++ {
		if tmp.Field(i).Name != field {
			ignoreFields = append(ignoreFields, tmp.Field(i).Name)
		}
	}

Review comment (bug):
This is the only downside; I dislike the  errors produced by stdlib. In this case, the error is not exported, so it's not possible to dismantle it (i.e., do an `errors.As` and cherry-pick the bits that are useful to produce a user-friendly error); https://github.com/golang/go/blob/e666972a674eb96f5847c7bbeb5c6e1bcb572af5/src/net/netip/netip.go#L141-L153

```go
type parseAddrError struct {
	in  string // the string given to ParseAddr
	msg string // an explanation of the parse failure
	at  string // optionally, the unparsed portion of in at which the error occurred.
}

func (err parseAddrError) Error() string {
	q := strconv.Quote
	if err.at != "" {
		return "ParseAddr(" + q(err.in) + "): " + err.msg + " (at " + q(err.at) + ")"
	}
	return "ParseAddr(" + q(err.in) + "): " + err.msg
}
```

Code after:
			input: DefaultAPIVersion,
		},
	}

	for _, tc := range tests {
		t.Run(tc.doc, func(t *testing.T) {
			err := ValidateMinAPIVersion(tc.input)
			if tc.expectedErr != "" {
				assert.Check(t, is.ErrorContains(err, tc.expectedErr))
			} else {
				assert.Check(t, err)
			}
		})
	}
}

func TestConfigInvalidDNS(t *testing.T) {
	tests := []struct {
		doc         string
		input       string
		expectedErr string
	}{
		{
			doc:         "single DNS, invalid IP-address",
			input:       `{"dns": ["1.1.1.1o"]}`,
			expectedErr: `ParseAddr("1.1.1.1o"): unexpected character (at "o")`,
		},
		{
			doc:         "multiple DNS, invalid IP-address",
			input:       `{"dns": ["2.2.2.2", "1.1.1.1o"]}`,
			expectedErr: `ParseAddr("1.1.1.1o"): unexpected character (at "o")`,
		},
	}

	for _, tc := range tests {
		t.Run(tc.doc, func(t *testing.T) {
			var cfg Config
			err := json.Unmarshal([]byte(tc.input), &cfg)
			assert.Check(t, is.Error(err, tc.expectedErr), "type: %T", err)
		})
	}
}

func field(field string) cmp.Option {
	tmp := reflect.TypeOf(Config{})
	ignoreFields := make([]string, 0, tmp.NumField())
	for i := 0; i < tmp.NumField(); i++ {
		if tmp.Field(i).Name != field {
			ignoreFields = append(ignoreFields, tmp.Field(i).Name)
		}
	}