diff --git a/internal/error/error_test.go b/internal/error/error_test.go index 6966760..ad3aca7 100644 --- a/internal/error/error_test.go +++ b/internal/error/error_test.go @@ -18,11 +18,11 @@ func TestBaseWithSubject(t *testing.T) { withSubjectf := err.Subjectf("%s %s", "foo", "bar") ExpectError(t, err, withSubject) - ExpectStrEqual(t, withSubject.Error(), "foo: error") + ExpectEqual(t, withSubject.Error(), "foo: error") ExpectTrue(t, withSubject.Is(err)) ExpectError(t, err, withSubjectf) - ExpectStrEqual(t, withSubjectf.Error(), "foo bar: error") + ExpectEqual(t, withSubjectf.Error(), "foo bar: error") ExpectTrue(t, withSubjectf.Is(err)) } @@ -114,9 +114,9 @@ func TestErrorWith(t *testing.T) { func TestErrorStringSimple(t *testing.T) { errFailure := New("generic failure") ne := errFailure.Subject("foo bar") - ExpectStrEqual(t, ne.Error(), "foo bar: generic failure") + ExpectEqual(t, ne.Error(), "foo bar: generic failure") ne = ne.Subject("baz") - ExpectStrEqual(t, ne.Error(), "baz > foo bar: generic failure") + ExpectEqual(t, ne.Error(), "baz > foo bar: generic failure") } func TestErrorStringNested(t *testing.T) { @@ -153,5 +153,5 @@ func TestErrorStringNested(t *testing.T) { • action 3 > inner3: generic failure • 3 • 3` - ExpectStrEqual(t, ne.Error(), want) + ExpectEqual(t, ne.Error(), want) } diff --git a/internal/error/utils.go b/internal/error/utils.go index 4af9300..05179c6 100644 --- a/internal/error/utils.go +++ b/internal/error/utils.go @@ -1,7 +1,6 @@ package err import ( - "errors" "fmt" ) @@ -20,12 +19,16 @@ func Errorf(format string, args ...any) Error { return &baseError{fmt.Errorf(format, args...)} } -func From(err error) (e Error) { +func From(err error) Error { if err == nil { return nil } - if errors.As(err, &e) { - return e + //nolint:errorlint + switch err := err.(type) { + case *baseError: + return err + case *nestedError: + return err } return &baseError{err} } diff --git a/internal/route/provider/all_fields.yaml b/internal/route/provider/all_fields.yaml new file mode 100644 index 0000000..1d812b4 --- /dev/null +++ b/internal/route/provider/all_fields.yaml @@ -0,0 +1,65 @@ +example: # matching `example.y.z` + scheme: http + host: 10.0.0.254 + port: 80 + path_patterns: # Check https://pkg.go.dev/net/http#hdr-Patterns-ServeMux for syntax + - GET / # accept any GET request + - POST /auth # for /auth and /auth/* accept only POST + - GET /home/{$} # for exactly /home + healthcheck: + disabled: false + path: / + interval: 5s + load_balance: + link: app + mode: ip_hash + options: + header: X-Forwarded-For + middlewares: + cidr_whitelist: + allow: + - 127.0.0.1 + - 10.0.0.0/8 + status_code: 403 + message: IP not allowed + hideXForwarded: + homepage: + name: Example App + icon: png/example.png + description: An example app + category: example + access_log: + buffer_size: 100 + path: /var/log/example.log + filters: + status_codes: + values: + - 200-299 + - 101 + method: + values: + - GET + host: + values: + - example.y.z + headers: + negative: true + values: + - foo=bar + - baz + cidr: + values: + - 192.168.10.0/24 + fields: + headers: + default: keep + config: + foo: redact + query: + default: drop + config: + foo: keep + cookies: + default: redact + config: + foo: keep diff --git a/internal/route/provider/file.go b/internal/route/provider/file.go index 12c7257..573f347 100644 --- a/internal/route/provider/file.go +++ b/internal/route/provider/file.go @@ -31,8 +31,12 @@ func FileProviderImpl(filename string) (ProviderImpl, error) { return impl, nil } +func validate(data []byte) (route.RawEntries, E.Error) { + return utils.DeserializeYAMLMap[*route.RawEntry](data) +} + func Validate(data []byte) (err E.Error) { - _, err = utils.DeserializeYAMLMap[*route.RawEntry](data) + _, err = validate(data) return } @@ -52,7 +56,7 @@ func (p *FileProvider) loadRoutesImpl() (route.Routes, E.Error) { return routes, E.From(err) } - entries, err := utils.DeserializeYAMLMap[*route.RawEntry](data) + entries, err := validate(data) if err == nil { return route.FromEntries(entries) } diff --git a/internal/route/provider/file_test.go b/internal/route/provider/file_test.go new file mode 100644 index 0000000..afe2782 --- /dev/null +++ b/internal/route/provider/file_test.go @@ -0,0 +1,17 @@ +package provider + +import ( + "testing" + + _ "embed" + + . "github.com/yusing/go-proxy/internal/utils/testing" +) + +//go:embed all_fields.yaml +var yaml []byte + +func TestFile(t *testing.T) { + _, err := validate(yaml) + ExpectNoError(t, err) +} diff --git a/internal/utils/serialization.go b/internal/utils/serialization.go index a78f905..737f4c1 100644 --- a/internal/utils/serialization.go +++ b/internal/utils/serialization.go @@ -277,6 +277,18 @@ func isIntFloat(t reflect.Kind) bool { // Returns: // - error: the error occurred during conversion, or nil if no error occurred. func Convert(src reflect.Value, dst reflect.Value) E.Error { + if !dst.IsValid() { + return E.Errorf("convert: dst is %w", ErrNilValue) + } + + if !src.IsValid() { + if dst.CanSet() { + dst.Set(reflect.Zero(dst.Type())) + return nil + } + return E.Errorf("convert: src is %w", ErrNilValue) + } + srcT := src.Type() dstT := dst.Type() @@ -339,7 +351,7 @@ func Convert(src reflect.Value, dst reflect.Value) E.Error { return nil } if dstT.Kind() != reflect.Slice { - return ErrUnsupportedConversion.Subject(dstT.String() + " to slice") + return ErrUnsupportedConversion.Subject(dstT.String() + " to " + srcT.String()) } newSlice := reflect.MakeSlice(dstT, 0, src.Len()) i := 0 @@ -469,11 +481,10 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr E.E return true, errs.Error() } tmp = m + default: + return false, nil } - if tmp != nil { - return true, Convert(reflect.ValueOf(tmp), dst) - } - return false, nil + return true, Convert(reflect.ValueOf(tmp), dst) } func DeserializeYAML[T any](data []byte, target T) E.Error { diff --git a/internal/utils/testing/testing.go b/internal/utils/testing/testing.go index e8cecc7..a443c97 100644 --- a/internal/utils/testing/testing.go +++ b/internal/utils/testing/testing.go @@ -23,7 +23,7 @@ func IgnoreError[Result any](r Result, _ error) Result { func ExpectNoError(t *testing.T, err error) { t.Helper() if err != nil && !reflect.ValueOf(err).IsNil() { - t.Errorf("expected err=nil, got %v", err) + t.Errorf("expected err=nil, got %s", ansi.StripANSI(err.Error())) t.FailNow() } } @@ -31,7 +31,7 @@ func ExpectNoError(t *testing.T, err error) { func ExpectError(t *testing.T, expected error, err error) { t.Helper() if !errors.Is(err, expected) { - t.Errorf("expected err %s, got %v", expected, err) + t.Errorf("expected err %s, got %s", expected, ansi.StripANSI(err.Error())) t.FailNow() } } @@ -39,7 +39,7 @@ func ExpectError(t *testing.T, expected error, err error) { func ExpectError2(t *testing.T, input any, expected error, err error) { t.Helper() if !errors.Is(err, expected) { - t.Errorf("%v: expected err %s, got %v", input, expected, err) + t.Errorf("%v: expected err %s, got %s", input, expected, ansi.StripANSI(err.Error())) t.FailNow() } } @@ -48,22 +48,17 @@ func ExpectErrorT[T error](t *testing.T, err error) { t.Helper() var errAs T if !errors.As(err, &errAs) { - t.Errorf("expected err %T, got %v", errAs, err) + t.Errorf("expected err %T, got %s", errAs, ansi.StripANSI(err.Error())) t.FailNow() } } func ExpectEqual[T comparable](t *testing.T, got T, want T) { t.Helper() - if got != want { - t.Errorf("expected:\n%v, got\n%v", want, got) - t.FailNow() + if gotStr, ok := any(got).(string); ok { + ExpectDeepEqual(t, ansi.StripANSI(gotStr), any(want).(string)) + return } -} - -func ExpectStrEqual(t *testing.T, got string, want string) { - t.Helper() - got = ansi.StripANSI(got) if got != want { t.Errorf("expected:\n%v, got\n%v", want, got) t.FailNow()