package woodpecker import ( "encoding/json" "strings" "testing" ) func TestAPIErrorToContextStatusMapping(t *testing.T) { cases := []struct { status int wantCode string wantInDesc string }{ {401, "AUTH_ERROR", "Authentication failed"}, {403, "PERMISSION_ERROR", "Access denied"}, {404, "NOT_FOUND", "Resource not found"}, {429, "RATE_LIMIT", "Rate limit exceeded"}, {500, "SERVER_ERROR", "server error"}, {502, "SERVER_ERROR", "server error"}, {503, "SERVER_ERROR", "server error"}, {504, "SERVER_ERROR", "server error"}, {418, "UNKNOWN_ERROR", "Request failed"}, } for _, tc := range cases { t.Run(strings.ToLower(tc.wantCode), func(t *testing.T) { e := &APIError{Status: tc.status, Message: "boom"} got := e.ToContext("sample operation") var ctx errorContext if err := json.Unmarshal([]byte(got), &ctx); err != nil { t.Fatalf("ToContext returned invalid JSON: %v\n%s", err, got) } if ctx.Error != tc.wantCode { t.Errorf("error code = %q, want %q", ctx.Error, tc.wantCode) } if !strings.Contains(ctx.Description, tc.wantInDesc) { t.Errorf("description %q does not contain %q", ctx.Description, tc.wantInDesc) } if !strings.Contains(ctx.Description, "sample operation") { t.Errorf("description %q does not mention the context", ctx.Description) } if len(ctx.Suggestions) == 0 { t.Error("suggestions must not be empty") } }) } } func TestAPIErrorToContextUnknownIncludesMessage(t *testing.T) { e := &APIError{Status: 418, Message: "some detail"} var ctx errorContext if err := json.Unmarshal([]byte(e.ToContext("op")), &ctx); err != nil { t.Fatal(err) } if !strings.Contains(ctx.Description, "some detail") { t.Errorf("description %q should include the underlying message", ctx.Description) } } func TestAPIErrorSuggestionsMatchReference(t *testing.T) { cases := []struct { status int want []string }{ {401, []string{ "Check that your WOODPECKER_TOKEN environment variable is set correctly", "Verify the token has not expired", "Ensure the token has sufficient permissions", }}, {404, []string{ "Double-check the repository name, pipeline number, or step ID", "Verify the resource exists and hasn't been deleted", }}, } for _, tc := range cases { e := &APIError{Status: tc.status} var ctx errorContext if err := json.Unmarshal([]byte(e.ToContext("op")), &ctx); err != nil { t.Fatal(err) } if len(ctx.Suggestions) != len(tc.want) { t.Fatalf("status %d: got %d suggestions, want %d", tc.status, len(ctx.Suggestions), len(tc.want)) } for i := range tc.want { if ctx.Suggestions[i] != tc.want[i] { t.Errorf("status %d suggestion %d = %q, want %q", tc.status, i, ctx.Suggestions[i], tc.want[i]) } } } }