Coordinate a2amcp credential refresh
All checks were successful
build / test (push) Successful in 19s
publish / npm (push) Successful in 22s

This commit is contained in:
2026-06-01 09:51:45 -03:00
parent f0b2d9ead8
commit 80261e5224
4 changed files with 193 additions and 8 deletions

View File

@@ -93,3 +93,89 @@ test("refreshCredentialsIfNeeded refreshes expired Keycloak access token", async
assert.equal((await loadCredentials())!.token, "fresh-token");
assert.ok(calls.some((call) => call.body?.includes("grant_type=refresh_token")));
});
test("refreshCredentialsIfNeeded serializes concurrent refreshes", async () => {
let tokenCalls = 0;
globalThis.fetch = (async (input: RequestInfo | URL) => {
const url = String(input);
if (url.endsWith("/.well-known/openid-configuration")) {
return new Response(
JSON.stringify({
authorization_endpoint: "https://auth.example.test/auth",
token_endpoint: "https://auth.example.test/token",
}),
{ status: 200, headers: { "content-type": "application/json" } },
);
}
tokenCalls += 1;
await new Promise((resolve) => setTimeout(resolve, 25));
return new Response(
JSON.stringify({
access_token: `fresh-token-${tokenCalls}`,
refresh_token: `fresh-refresh-${tokenCalls}`,
expires_in: 3600,
scope: "openid email mcp:invoke",
}),
{ status: 200, headers: { "content-type": "application/json" } },
);
}) as typeof fetch;
await saveCredentials({
apiUrl: "https://api.example.test",
token: "old-token",
refreshToken: "refresh-token",
expiresAt: 1,
email: "dev@example.test",
issuer: "https://auth.example.test",
clientId: "a2acloud-cli",
});
const stale = (await loadCredentials())!;
const [first, second] = await Promise.all([
refreshCredentialsIfNeeded(stale),
refreshCredentialsIfNeeded({ ...stale }),
]);
assert.equal(tokenCalls, 1);
assert.equal(first.token, "fresh-token-1");
assert.equal(second.token, "fresh-token-1");
assert.equal((await loadCredentials())!.refreshToken, "fresh-refresh-1");
});
test("refreshCredentialsIfNeeded reports stale Keycloak sessions clearly", async () => {
globalThis.fetch = (async (input: RequestInfo | URL) => {
const url = String(input);
if (url.endsWith("/.well-known/openid-configuration")) {
return new Response(
JSON.stringify({
authorization_endpoint: "https://auth.example.test/auth",
token_endpoint: "https://auth.example.test/token",
}),
{ status: 200, headers: { "content-type": "application/json" } },
);
}
return new Response(
JSON.stringify({
error: "invalid_grant",
error_description: "Session doesn't have required client",
}),
{ status: 400, headers: { "content-type": "application/json" } },
);
}) as typeof fetch;
await saveCredentials({
apiUrl: "https://api.example.test",
token: "old-token",
refreshToken: "refresh-token",
expiresAt: 1,
email: "dev@example.test",
issuer: "https://auth.example.test",
clientId: "a2acloud-cli",
});
await assert.rejects(
refreshCredentialsIfNeeded((await loadCredentials())!),
/Login expired or revoked\. Run `a2amcp login` and retry\. .*Session doesn't have required client/,
);
assert.equal((await loadCredentials())!.token, "old-token");
});