fix(token-refresh): discover projectId during token refresh (#8860)

* fix(token-refresh): discover projectId during token refresh

The token refresh path (tokenRefresh.ts) did not discover projectId
for antigravity/agy accounts. Dashboard and health check refresh use
this path, not the executor path.

Add ensureAntigravityProjectAssigned call after refreshGoogleToken
for antigravity/agy providers when projectId is empty.

Signed-off-by: Minxi Hou <houminxi@gmail.com>

* chore(quality): rebaseline the token-refresh test file + changelog fragment

tests/unit/token-refresh-service.test.ts 1311->1378 (+67) — the four cases
covering projectId discovery on the tokenRefresh.ts path. Growth is the tests
this PR adds, nothing else.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
Bob.Hou
2026-07-28 11:40:08 -04:00
committed by GitHub
parent 26a7783521
commit ec0edef499
4 changed files with 114 additions and 4 deletions

View File

@@ -795,6 +795,73 @@ test("supportsTokenRefresh, isUnrecoverableRefreshError and formatProviderCreden
assert.equal(formatProviderCredentials("missing-provider", {}, log), null);
});
test("getAccessToken discovers projectId for antigravity when stored value is empty", async () => {
const log = createLog();
const { clearAntigravityProjectCache } = await import("../../open-sse/services/antigravityProjectBootstrap.ts");
clearAntigravityProjectCache();
let fetchCalls: string[] = [];
await withMockedFetch(async (url, init) => {
const urlStr = String(url);
fetchCalls.push(urlStr);
if (urlStr.includes("oauth2.googleapis.com/token")) {
return jsonResponse({
access_token: "new-token-1",
refresh_token: "new-refresh",
expires_in: 3600,
});
}
if (urlStr.includes("loadCodeAssist")) {
return jsonResponse({ cloudaicompanionProject: "discovered-project" });
}
return new Response("not found", { status: 404 });
}, async () => {
const result = await getAccessToken("antigravity", {
refreshToken: "refresh",
projectId: "",
connectionId: "conn-1",
providerSpecificData: {},
});
assert.equal(result.projectId, "discovered-project");
assert.ok(fetchCalls.some((u) => u.includes("loadCodeAssist")), "should call loadCodeAssist");
});
clearAntigravityProjectCache();
});
test("getAccessToken handles projectId discovery failure gracefully for antigravity", async () => {
const log = createLog();
const { clearAntigravityProjectCache } = await import("../../open-sse/services/antigravityProjectBootstrap.ts");
clearAntigravityProjectCache();
tokenRefresh._clearTokenRotationMap();
await withMockedFetch(async (url) => {
const urlStr = String(url);
if (urlStr.includes("oauth2.googleapis.com/token")) {
return jsonResponse({
access_token: "new-token-3",
refresh_token: "new-refresh",
expires_in: 3600,
});
}
if (urlStr.includes("loadCodeAssist")) {
return new Response("server error", { status: 500 });
}
return new Response("not found", { status: 404 });
}, async () => {
const result = await getAccessToken("antigravity", {
refreshToken: "refresh",
projectId: "",
connectionId: "conn-1",
providerSpecificData: {},
});
assert.equal(result.accessToken, "new-token-3");
assert.ok(result, "should return a result without throwing");
});
clearAntigravityProjectCache();
});
test("getAccessToken deduplicates concurrent refreshes for the same provider and token", async () => {
const log = createLog();
let fetchCount = 0;