Files
oh-my-pi/packages/ai/test/github-copilot-error.test.ts
T
roboomp 1bcf08c27f fix(ai): preserved Copilot integrator entitlement errors
- Classified model_not_available_for_integrator as a permanent entitlement denial instead of transient fleet skew.

- Preserved the provider response and Available models list while retaining model_not_supported fleet retries.

Fixes #7819
2026-08-06 09:50:32 +00:00

74 lines
2.9 KiB
TypeScript

import { describe, expect, it } from "bun:test";
import { rewriteCopilotError } from "@oh-my-pi/pi-ai/utils/http-inspector";
function errorWithStatus(
status: number,
options: { message?: string; code?: string } = {},
): Error & { status: number; code?: string } {
return Object.assign(new Error(options.message ?? `${status} Unauthorized`), {
status,
...(options.code === undefined ? {} : { code: options.code }),
});
}
describe("rewriteCopilotError", () => {
it("returns original message for non-copilot providers", () => {
const err = errorWithStatus(401);
expect(rewriteCopilotError("some error", err, "openai")).toBe("some error");
});
it("returns original message for non-401/403 errors", () => {
const err = errorWithStatus(500);
expect(rewriteCopilotError("server error", err, "github-copilot")).toBe("server error");
});
it("rewrites message for 401 with github-copilot provider", () => {
const err = errorWithStatus(401);
const result = rewriteCopilotError("401 Unauthorized: ...", err, "github-copilot");
expect(result).toContain("GitHub Copilot authentication failed (HTTP 401)");
expect(result).toContain("/login github-copilot");
});
it("rewrites 403 with access-denied message (not auth-failed, to avoid credential removal)", () => {
const err = errorWithStatus(403);
const result = rewriteCopilotError("403 Forbidden", err, "github-copilot");
expect(result).toContain("GitHub Copilot access denied (HTTP 403)");
expect(result).not.toContain("GitHub Copilot authentication failed");
expect(result).not.toContain("/login github-copilot");
});
it("rewrites 400 model_not_supported with fleet-skew guidance", () => {
const err = errorWithStatus(400, {
message: "400 The requested model is not supported.",
code: "model_not_supported",
});
const result = rewriteCopilotError("original", err, "github-copilot");
expect(result).toContain("HTTP 400");
expect(result).toContain("only part of its fleet");
expect(result).not.toContain("authentication failed");
});
it("preserves per-integrator entitlement details and available models", () => {
const message =
'400 The requested model is not available for integrator "opencode". Available models: [gpt-4.1 claude-opus-4.7 gpt-5.5]';
const err = errorWithStatus(400, { message, code: "model_not_available_for_integrator" });
expect(rewriteCopilotError(message, err, "github-copilot")).toBe(message);
});
it("leaves non-copilot 400 model_not_supported untouched", () => {
const err = errorWithStatus(400, {
message: "400 model_not_supported",
code: "model_not_supported",
});
expect(rewriteCopilotError("orig", err, "openai")).toBe("orig");
});
it("leaves 400 without model_not_supported code untouched", () => {
const err = errorWithStatus(400, {
message: "400 invalid request",
code: "invalid_request_body",
});
expect(rewriteCopilotError("orig", err, "github-copilot")).toBe("orig");
});
});