From d564cdcae34ccd76b63188c79efe2025fb0c46dd Mon Sep 17 00:00:00 2001 From: Mathias Bergqvist Date: Mon, 4 May 2026 21:26:29 +0200 Subject: [PATCH] feat(gitea): typed error mapping from http status --- internal/gitea/errors.go | 34 ++++++++++++++++++++++++++++++++++ internal/gitea/errors_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 internal/gitea/errors.go create mode 100644 internal/gitea/errors_test.go diff --git a/internal/gitea/errors.go b/internal/gitea/errors.go new file mode 100644 index 0000000..f1ed180 --- /dev/null +++ b/internal/gitea/errors.go @@ -0,0 +1,34 @@ +package gitea + +import ( + "errors" + "fmt" +) + +var ( + ErrPermissionDenied = errors.New("permission denied") + ErrNotFound = errors.New("not found") + ErrConflict = errors.New("conflict") + ErrValidation = errors.New("validation failed") + ErrUpstream = errors.New("upstream gitea error") +) + +// MapStatus returns nil for 2xx, otherwise a typed error wrapping the response body. +func MapStatus(status int, body []byte) error { + if status >= 200 && status < 300 { + return nil + } + switch { + case status == 401, status == 403: + return fmt.Errorf("%w: %s", ErrPermissionDenied, body) + case status == 404: + return fmt.Errorf("%w: %s", ErrNotFound, body) + case status == 409: + return fmt.Errorf("%w: %s", ErrConflict, body) + case status == 422: + return fmt.Errorf("%w: %s", ErrValidation, body) + case status >= 500: + return fmt.Errorf("%w (status %d)", ErrUpstream, status) + } + return fmt.Errorf("unexpected status %d: %s", status, body) +} diff --git a/internal/gitea/errors_test.go b/internal/gitea/errors_test.go new file mode 100644 index 0000000..9b8a221 --- /dev/null +++ b/internal/gitea/errors_test.go @@ -0,0 +1,34 @@ +package gitea_test + +import ( + "errors" + "testing" + + "gitea.d-ma.be/mathias/gitea-mcp/internal/gitea" + "github.com/stretchr/testify/assert" +) + +func TestMapStatus(t *testing.T) { + cases := []struct { + status int + want error + }{ + {401, gitea.ErrPermissionDenied}, + {403, gitea.ErrPermissionDenied}, + {404, gitea.ErrNotFound}, + {409, gitea.ErrConflict}, + {422, gitea.ErrValidation}, + {500, gitea.ErrUpstream}, + {502, gitea.ErrUpstream}, + {200, nil}, + {299, nil}, + } + for _, tc := range cases { + got := gitea.MapStatus(tc.status, []byte(`{"message":"x"}`)) + if tc.want == nil { + assert.NoError(t, got) + } else { + assert.True(t, errors.Is(got, tc.want), "status %d", tc.status) + } + } +}