Files
diegosouzapw--omniroute/tests/unit/antigravity-discovery-bootstrap.test.ts
2026-07-13 13:39:12 +08:00

236 lines
9.5 KiB
TypeScript

/**
* Tests: antigravity loadCodeAssist bootstrap before :models discovery.
*
* The Google Cloud Code Assist /v1internal:models endpoint requires a prior
* /v1internal:loadCodeAssist call to assign a project context to the OAuth
* token. Without this bootstrap, :models returns 404 for all three base URLs.
*
* These tests verify:
* 1. ensureAntigravityProjectAssigned calls loadCodeAssist before returning.
* 2. The call is memoized — repeated calls for the same token do not re-hit
* the network.
* 3. Non-fatal: if loadCodeAssist fails, the function resolves without throwing.
* 4. The loadCodeAssist request uses the correct headers (Authorization, User-Agent).
* 5. Ordering guarantee — in a full discovery flow, loadCodeAssist is called
* BEFORE any :models request.
*/
import { test, describe, beforeEach } from "node:test";
import assert from "node:assert/strict";
import {
ensureAntigravityProjectAssigned,
clearAntigravityProjectCache,
getAntigravityProjectFromCache,
getAntigravityLoadCodeAssistUrls,
} from "../../open-sse/services/antigravityProjectBootstrap.ts";
// Reset the module-level memoization cache between tests.
beforeEach(() => {
clearAntigravityProjectCache();
});
describe("ensureAntigravityProjectAssigned", () => {
test("calls loadCodeAssist and caches the returned project id", async () => {
const calls: string[] = [];
const mockFetch = async (url: string, _init?: RequestInit): Promise<Response> => {
calls.push(url);
if (url.endsWith(":loadCodeAssist")) {
return new Response(JSON.stringify({ cloudaicompanionProject: "proj-from-bootstrap" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
return new Response("Not Found", { status: 404 });
};
const projectId = await ensureAntigravityProjectAssigned("fake-token-1", mockFetch);
const loadCalls = calls.filter((u) => u.endsWith(":loadCodeAssist"));
assert.ok(loadCalls.length >= 1, ":loadCodeAssist must be called at least once");
assert.equal(projectId, "proj-from-bootstrap", "project id must be returned");
assert.equal(
getAntigravityProjectFromCache("fake-token-1"),
"proj-from-bootstrap",
"project id must be memoized after first call"
);
});
test("subsequent calls for the same token skip the network", async () => {
let networkCalls = 0;
const mockFetch = async (url: string, _init?: RequestInit): Promise<Response> => {
networkCalls += 1;
return new Response(JSON.stringify({ cloudaicompanionProject: "proj-cached" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
await ensureAntigravityProjectAssigned("fake-token-2", mockFetch);
await ensureAntigravityProjectAssigned("fake-token-2", mockFetch);
await ensureAntigravityProjectAssigned("fake-token-2", mockFetch);
assert.equal(networkCalls, 1, "network must be called exactly once for the same token");
});
test("different tokens each trigger their own loadCodeAssist call", async () => {
const calledFor: string[] = [];
const mockFetch = async (url: string, init?: RequestInit): Promise<Response> => {
const auth = new Headers(init?.headers).get("Authorization") ?? "";
calledFor.push(auth);
return new Response(JSON.stringify({ cloudaicompanionProject: "proj-x" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
await ensureAntigravityProjectAssigned("token-A", mockFetch);
await ensureAntigravityProjectAssigned("token-B", mockFetch);
assert.equal(calledFor.length, 2, "each unique token should trigger one network call");
});
test("does not throw when loadCodeAssist returns non-200", async () => {
const mockFetch = async (_url: string, _init?: RequestInit): Promise<Response> => {
return new Response("Service Unavailable", { status: 503 });
};
// Must resolve without throwing even if all endpoints fail.
await assert.doesNotReject(ensureAntigravityProjectAssigned("fail-token", mockFetch));
});
test("does not throw when fetch rejects (network error)", async () => {
const mockFetch = async (_url: string, _init?: RequestInit): Promise<Response> => {
throw new Error("ECONNREFUSED");
};
await assert.doesNotReject(ensureAntigravityProjectAssigned("throw-token", mockFetch));
});
test("sets Authorization header with Bearer token", async () => {
let capturedAuth: string | null = null;
const mockFetch = async (_url: string, init?: RequestInit): Promise<Response> => {
capturedAuth = new Headers(init?.headers).get("Authorization") ?? null;
return new Response(JSON.stringify({ cloudaicompanionProject: "proj-auth-check" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
await ensureAntigravityProjectAssigned("my-secret-token", mockFetch);
assert.equal(capturedAuth, "Bearer my-secret-token", "Authorization header must be set");
});
test("uses CLI/SDK harness headers when requested", async () => {
let capturedHeaders: Headers | null = null;
const mockFetch = async (_url: string, init?: RequestInit): Promise<Response> => {
capturedHeaders = new Headers(init?.headers);
return new Response(JSON.stringify({ cloudaicompanionProject: "proj-harness" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
await ensureAntigravityProjectAssigned("harness-token", mockFetch, "harness");
assert.match(
capturedHeaders?.get("User-Agent") || "",
/^antigravity\/4\.2\.0 [^ ]+\/[^ ]+ google-api-nodejs-client\/10\.3\.0$/
);
assert.equal(capturedHeaders?.get("X-Goog-Api-Client"), "gl-node/22.21.1");
assert.equal(capturedHeaders?.get("Client-Metadata"), null);
});
test("falls through to next URL when first loadCodeAssist returns 404", async () => {
const hitUrls: string[] = [];
const mockFetch = async (url: string, _init?: RequestInit): Promise<Response> => {
hitUrls.push(url);
// Exact hostname match (not substring .includes) so the check can't be fooled by a
// look-alike host like daily-cloudcode-pa.googleapis.com.evil.com (CodeQL
// js/incomplete-url-substring-sanitization).
if (new URL(url).hostname === "daily-cloudcode-pa.googleapis.com") {
// First URL fails
return new Response("not found", { status: 404 });
}
// Second URL succeeds
return new Response(JSON.stringify({ cloudaicompanionProject: "proj-fallback" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
const projectId = await ensureAntigravityProjectAssigned("fallback-token", mockFetch);
assert.ok(hitUrls.length >= 2, "should try at least two URLs on the first failure");
assert.equal(projectId, "proj-fallback", "should return the project from the successful URL");
assert.equal(
getAntigravityProjectFromCache("fallback-token"),
"proj-fallback",
"should cache the project from the successful URL"
);
});
test("getAntigravityLoadCodeAssistUrls returns URLs matching ANTIGRAVITY_BASE_URLS", () => {
const urls = getAntigravityLoadCodeAssistUrls();
assert.ok(urls.length >= 1, "must return at least one URL");
for (const url of urls) {
assert.ok(url.endsWith(":loadCodeAssist"), `URL must end with :loadCodeAssist, got: ${url}`);
assert.ok(url.startsWith("https://"), `URL must be HTTPS, got: ${url}`);
}
});
});
// ── Ordering guarantee: loadCodeAssist BEFORE :models ─────────────────────────
//
// This test simulates the full discovery flow: a test-controlled fetch
// that records call order, and verifies that :loadCodeAssist precedes
// any :models request. The integration is verified by calling
// ensureAntigravityProjectAssigned then simulating a :models request.
describe("ordering guarantee: loadCodeAssist before :models", () => {
test("loadCodeAssist is called before :models in a simulated discovery flow", async () => {
const callOrder: string[] = [];
const mockFetch = async (url: string, _init?: RequestInit): Promise<Response> => {
if (url.endsWith(":loadCodeAssist")) {
callOrder.push("loadCodeAssist");
return new Response(JSON.stringify({ cloudaicompanionProject: "proj-order-test" }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
if (url.endsWith(":models")) {
callOrder.push("models");
return new Response(
JSON.stringify({
models: [{ id: "gemini-3-pro-antigravity", displayName: "Gemini 3 Pro" }],
}),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
}
return new Response("not found", { status: 404 });
};
// Step 1: bootstrap project (what route.ts now does before the models loop).
await ensureAntigravityProjectAssigned("order-token", mockFetch);
// Step 2: simulate a :models discovery request (what the loop does).
const modelsUrl = "https://cloudcode-pa.googleapis.com/v1internal:models";
await mockFetch(modelsUrl);
const loadIdx = callOrder.indexOf("loadCodeAssist");
const modelsIdx = callOrder.indexOf("models");
assert.ok(loadIdx >= 0, ":loadCodeAssist must be called");
assert.ok(modelsIdx >= 0, ":models must be called");
assert.ok(loadIdx < modelsIdx, ":loadCodeAssist must be called BEFORE :models");
});
});