Files
OmniRoute/tests/unit/antigravity-missing-project-chat.test.ts
Andrew B. 9a99a39b33 fix(antigravity): quota-aware account selection and projectId persistence (#8891)
* fix(antigravity): per-model quota + 30min credits_exhausted reprobe

- accountFallback.ts: hasPerModelQuota() now treats antigravity/agy as
  per-model quota. A single-model 429 no longer cascades to all models
  in the provider.
- connectionRecovery.ts: credits_exhausted removed from terminal set;
  isCreditsExhaustedReprobeCandidate() with 30min default. Loads
  active+inactive rows so inactive credits_exhausted accounts can recover.
- tests/unit/quota-connection-recovery.test.ts: 6 cases covering pure
  helpers + tick wiring.

* fix(antigravity): persist projectId and prefer healthy accounts

Save Cloud Code projectId after runtime discovery, skip accounts missing
projectId when alternatives exist, and mark missing_project_id on 422.

* fix(antigravity): skip quota-exhausted models during account selection

Avoid repeatedly dispatching to Antigravity models that already report
exhausted quota, reducing wasted upstream calls and combo fallback latency.

---------

Co-authored-by: hermes <hermes@nous.local>
2026-08-11 04:25:13 -03:00

87 lines
3.2 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { createChatPipelineHarness } from "../integration/_chatPipelineHarness.ts";
const harness = await createChatPipelineHarness("antigravity-missing-project-chat");
const { BaseExecutor, buildRequest, handleChat, resetStorage, settingsDb } = harness;
const providersDb = await import("../../src/lib/db/providers.ts");
const { clearAntigravityProjectCache } =
await import("../../open-sse/services/antigravityProjectBootstrap.ts");
const { seedAntigravityIdeVersionCache, seedAntigravityCliVersionCache } =
await import("../../open-sse/services/antigravityVersion.ts");
const BOOTSTRAP_URL = "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist";
test.beforeEach(async () => {
BaseExecutor.RETRY_CONFIG.delayMs = 0;
process.env.ANTIGRAVITY_CREDITS = "off";
await resetStorage();
await settingsDb.updateSettings({ requestRetry: 0, maxRetryIntervalSec: 0 });
clearAntigravityProjectCache();
seedAntigravityIdeVersionCache("2026.04.17-missing-project-test");
seedAntigravityCliVersionCache("2026.04.17-missing-project-test");
});
test.afterEach(() => {
clearAntigravityProjectCache();
delete process.env.ANTIGRAVITY_CREDITS;
});
test.after(async () => {
await harness.cleanup();
});
test("Antigravity missing-project 422 stays fail-closed without account cooldown or retry", async () => {
const connection = await providersDb.createProviderConnection({
provider: "antigravity",
authType: "oauth",
name: "antigravity-missing-project",
email: "antigravity-missing-project@example.test",
accessToken: "fake-antigravity-missing-project-token",
refreshToken: "fake-antigravity-missing-project-refresh",
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
providerSpecificData: {},
isActive: true,
testStatus: "active",
});
assert(connection && typeof connection.id === "string");
let bootstrapCalls = 0;
globalThis.fetch = async (input, init) => {
const request = input instanceof Request ? input : new Request(input, init);
if (request.url !== BOOTSTRAP_URL) {
throw new Error(`Unexpected external fetch: ${request.url}`);
}
bootstrapCalls += 1;
return new Response("{}", {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
const response = await handleChat(
buildRequest({
body: {
model: "antigravity/gemini-2.5-flash",
stream: false,
messages: [{ role: "user", content: "Missing project must fail closed" }],
},
})
);
const payload = (await response.json()) as {
error?: { code?: string; type?: string; message?: string };
};
const persisted = await providersDb.getProviderConnectionById(connection.id);
assert.equal(response.status, 422);
assert.equal(payload.error?.code, "missing_project_id");
assert.equal(payload.error?.type, "oauth_missing_project_id");
assert.equal(bootstrapCalls, 1);
assert.equal(persisted?.testStatus, "active");
assert.equal(persisted?.rateLimitedUntil, undefined);
assert.equal(persisted?.errorCode, "missing_project_id");
assert.equal(persisted?.lastErrorType, "oauth_missing_project_id");
assert.match(String(persisted?.lastError), /Missing Google projectId/);
});