Skip to content

Commit 7207986

Browse files
vn7n24fzkqclaude
andcommitted
fix: bound App token mint with a timeout; per-test axios mock lifecycle
Review feedback: appApi now carries an explicit 10s axios timeout so a hanging GitHub API call degrades to the PAT slots within the request instead of riding the invocation to the platform kill. The handle-card App-slot tests recreate the MockAdapter per test — restore() detaches the adapter, so a suite-level instance left later tests unmocked. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent bbda368 commit 7207986

2 files changed

Lines changed: 10 additions & 3 deletions

File tree

api/utils/github-app-token.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import axios from 'axios';
1717
// per hour. They are never logged and never written to Redis.
1818

1919
const REFRESH_MARGIN_MS = 5 * 60 * 1000; // re-mint when under 5 minutes left
20+
// A hanging mint would otherwise ride the whole function invocation while the
21+
// rotation can't move on — bound it so a slow GitHub API degrades to the PAT
22+
// slots within a request, not at the platform timeout.
23+
const MINT_TIMEOUT_MS = 10 * 1000;
2024

2125
const cachedTokens = new Map<number, {token: string; expiresAtMs: number}>();
2226
const inflightMints = new Map<number, Promise<string>>();
@@ -76,7 +80,8 @@ async function appApi(method: 'get' | 'post', path: string, jwt: string): Promis
7680
'User-Agent': 'github-profile-summary-cards',
7781
Authorization: `Bearer ${jwt}`,
7882
Accept: 'application/vnd.github+json'
79-
}
83+
},
84+
timeout: MINT_TIMEOUT_MS
8085
});
8186
}
8287

tests/utils/handle-card.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ describe('handleCard token rotation', () => {
143143
// The App slot participates in the same rotation: a working App serves cards,
144144
// a broken App (mint failure) rotates to the PATs instead of failing the card.
145145
describe('handleCard with a GitHub App slot', () => {
146-
const mock = new MockAdapter(axios);
146+
// Created per test: restore() detaches the adapter from axios, so a
147+
// suite-level instance would leave later tests running unmocked.
148+
let mock: InstanceType<typeof MockAdapter>;
147149
const originalEnv = {...process.env};
148150

149151
// Pool: [GITHUB_APP, GITHUB_TOKEN, GITHUB_TOKEN_1]. Pick a username whose
@@ -154,7 +156,7 @@ describe('handleCard with a GitHub App slot', () => {
154156

155157
beforeEach(() => {
156158
jest.clearAllMocks();
157-
mock.reset();
159+
mock = new MockAdapter(axios);
158160
__resetGitHubAppTokenCacheForTests();
159161
process.env.GITHUB_TOKEN = 'tok0';
160162
process.env.GITHUB_TOKEN_1 = 'tok1';

0 commit comments

Comments
 (0)