TestHandleRootError_SecurityPolicyCanonicalEnvelope verifies that *errs.SecurityPolicyError flows through the canonical typed envelope (output.WriteTypedErrorEnvelope) — type=policy, numeric code, subtype, top-level identity, exit code 6 — after the dispatcher carve-out is removed.
(t *testing.T)
| 144 | // (output.WriteTypedErrorEnvelope) — type=policy, numeric code, subtype, |
| 145 | // top-level identity, exit code 6 — after the dispatcher carve-out is removed. |
| 146 | func TestHandleRootError_SecurityPolicyCanonicalEnvelope(t *testing.T) { |
| 147 | t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) |
| 148 | |
| 149 | t.Run("21000 challenge_required", func(t *testing.T) { |
| 150 | f, _, _, _ := cmdutil.TestFactory(t, nil) |
| 151 | errOut := &bytes.Buffer{} |
| 152 | f.IOStreams.ErrOut = errOut |
| 153 | |
| 154 | spErr := &errs.SecurityPolicyError{ |
| 155 | Problem: errs.Problem{ |
| 156 | Category: errs.CategoryPolicy, |
| 157 | Subtype: errs.SubtypeChallengeRequired, |
| 158 | Code: 21000, |
| 159 | Message: "blocked by access policy", |
| 160 | Hint: "complete challenge in your browser", |
| 161 | }, |
| 162 | ChallengeURL: "https://example.com/challenge", |
| 163 | } |
| 164 | |
| 165 | gotExit := handleRootError(f, spErr) |
| 166 | if gotExit != int(output.ExitContentSafety) { |
| 167 | t.Errorf("exit code = %d, want %d (ExitContentSafety)", gotExit, output.ExitContentSafety) |
| 168 | } |
| 169 | |
| 170 | var env map[string]any |
| 171 | if err := json.Unmarshal(errOut.Bytes(), &env); err != nil { |
| 172 | t.Fatalf("envelope is not valid JSON: %v\n%s", err, errOut.String()) |
| 173 | } |
| 174 | errObj, ok := env["error"].(map[string]any) |
| 175 | if !ok { |
| 176 | t.Fatalf("envelope missing top-level error object: %s", errOut.String()) |
| 177 | } |
| 178 | if got := errObj["type"]; got != "policy" { |
| 179 | t.Errorf("error.type = %v, want %q", got, "policy") |
| 180 | } |
| 181 | if got := errObj["subtype"]; got != "challenge_required" { |
| 182 | t.Errorf("error.subtype = %v, want %q", got, "challenge_required") |
| 183 | } |
| 184 | if got, ok := errObj["code"].(float64); !ok || int(got) != 21000 { |
| 185 | t.Errorf("error.code = %v (%T), want 21000 (number)", errObj["code"], errObj["code"]) |
| 186 | } |
| 187 | if got := errObj["challenge_url"]; got != "https://example.com/challenge" { |
| 188 | t.Errorf("error.challenge_url = %v, want challenge url", got) |
| 189 | } |
| 190 | if got := errObj["hint"]; got != "complete challenge in your browser" { |
| 191 | t.Errorf("error.hint = %v, want hint message", got) |
| 192 | } |
| 193 | if _, exists := errObj["retryable"]; exists { |
| 194 | t.Errorf("error.retryable leaked into canonical envelope: %v", errObj["retryable"]) |
| 195 | } |
| 196 | }) |
| 197 | |
| 198 | t.Run("21001 access_denied", func(t *testing.T) { |
| 199 | f, _, _, _ := cmdutil.TestFactory(t, nil) |
| 200 | errOut := &bytes.Buffer{} |
| 201 | f.IOStreams.ErrOut = errOut |
| 202 | |
| 203 | spErr := &errs.SecurityPolicyError{ |
nothing calls this directly
no test coverage detected