(t *testing.T)
| 99 | } |
| 100 | |
| 101 | func TestBuildAPIError_ExitCodeMatrix(t *testing.T) { |
| 102 | cases := []struct { |
| 103 | name string |
| 104 | code int |
| 105 | wantCat errs.Category |
| 106 | wantSubtype errs.Subtype |
| 107 | wantExit int |
| 108 | wantTyped string |
| 109 | }{ |
| 110 | {"99991672 app_missing_scope", 99991672, errs.CategoryAuthorization, errs.SubtypeAppScopeNotApplied, 3, "PermissionError"}, |
| 111 | {"99991676 token_no_permission", 99991676, errs.CategoryAuthorization, errs.SubtypeTokenScopeInsufficient, 3, "PermissionError"}, |
| 112 | {"99991679 missing_scope", 99991679, errs.CategoryAuthorization, errs.SubtypeMissingScope, 3, "PermissionError"}, |
| 113 | {"230027 user_not_authorized", 230027, errs.CategoryAuthorization, errs.SubtypeUserUnauthorized, 3, "PermissionError"}, |
| 114 | {"1470403 task_permission_denied", 1470403, errs.CategoryAuthorization, errs.SubtypePermissionDenied, 3, "PermissionError"}, |
| 115 | {"1470400 task_invalid_params", 1470400, errs.CategoryAPI, errs.SubtypeInvalidParameters, 1, "APIError"}, |
| 116 | {"99991400 rate_limit", 99991400, errs.CategoryAPI, errs.SubtypeRateLimit, 1, "APIError"}, |
| 117 | {"99991661 token_missing", 99991661, errs.CategoryAuthentication, errs.SubtypeTokenMissing, 3, "AuthenticationError"}, |
| 118 | {"21000 challenge_required", 21000, errs.CategoryPolicy, errs.Subtype("challenge_required"), 6, "SecurityPolicyError"}, |
| 119 | {"unknown code 999999", 999999, errs.CategoryAPI, errs.SubtypeUnknown, 1, "APIError"}, |
| 120 | } |
| 121 | for _, tc := range cases { |
| 122 | t.Run(tc.name, func(t *testing.T) { |
| 123 | resp := map[string]any{"code": tc.code, "msg": "x"} |
| 124 | err := errclass.BuildAPIError(resp, errclass.ClassifyContext{Brand: "feishu", AppID: "cli_test", Identity: "user"}) |
| 125 | if err == nil { |
| 126 | t.Fatalf("expected error for code %d, got nil", tc.code) |
| 127 | } |
| 128 | p, ok := errs.ProblemOf(err) |
| 129 | if !ok { |
| 130 | t.Fatalf("ProblemOf returned !ok for code %d (err = %T)", tc.code, err) |
| 131 | } |
| 132 | if p.Category != tc.wantCat { |
| 133 | t.Errorf("Category = %q, want %q", p.Category, tc.wantCat) |
| 134 | } |
| 135 | if p.Subtype != tc.wantSubtype { |
| 136 | t.Errorf("Subtype = %q, want %q", p.Subtype, tc.wantSubtype) |
| 137 | } |
| 138 | if got := output.ExitCodeOf(err); got != tc.wantExit { |
| 139 | t.Errorf("ExitCodeOf = %d, want %d (typed = %s)", got, tc.wantExit, tc.wantTyped) |
| 140 | } |
| 141 | if !matchesTypedError(err, tc.wantTyped) { |
| 142 | t.Errorf("typed-error mismatch: got %T, want %s", err, tc.wantTyped) |
| 143 | } |
| 144 | }) |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | // TestBuildAPIError_TaskInvalidParamsRoutesToAPIError pins that code 1470400 |
| 149 | // (Lark API-side parameter rejection) routes to *errs.APIError + CategoryAPI |
nothing calls this directly
no test coverage detected