Skip to content

Commit f7926f4

Browse files
test: make credential writer race deterministic
1 parent 74f345b commit f7926f4

2 files changed

Lines changed: 39 additions & 5 deletions

File tree

test/credentials-cross-process.test.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ function runCredentialWriter(env: Record<string, string>): Promise<ChildResult>
4949
});
5050
}
5151

52+
async function waitForFiles(paths: string[], timeoutMs = 5_000): Promise<void> {
53+
const deadline = Date.now() + timeoutMs;
54+
55+
while (true) {
56+
const missing = paths.filter(path => !existsSync(path));
57+
if (missing.length === 0) return;
58+
if (Date.now() >= deadline) {
59+
throw new Error(`Timed out waiting for files: ${missing.join(', ')}`);
60+
}
61+
await new Promise(resolveDelay => setTimeout(resolveDelay, 5));
62+
}
63+
}
64+
5265
describe('credentials cross-process writes', () => {
5366
beforeAll(() => {
5467
execNpm(['run', 'build'], { cwd: REPO_ROOT, stdio: 'pipe' });
@@ -81,7 +94,9 @@ describe('credentials cross-process writes', () => {
8194
CRED_API_KEY: 'sk-child-timeout',
8295
CRED_PATH: join(tmpRoot, 'credentials'),
8396
CRED_PROFILE: 'child-timeout',
97+
CRED_POLL_MS: '1',
8498
CRED_START_PATH: startPath,
99+
CRED_START_TIMEOUT_MS: '100',
85100
});
86101

87102
expect(result).toMatchObject({
@@ -130,16 +145,18 @@ describe('credentials cross-process writes', () => {
130145
apiKey: `sk-child-${index}`,
131146
profile: `child-${index}`,
132147
}));
133-
const children = profiles.map(({ apiKey, profile }) =>
148+
const readyPaths = profiles.map(({ profile }) => join(tmpRoot, `${profile}.ready`));
149+
const children = profiles.map(({ apiKey, profile }, index) =>
134150
runCredentialWriter({
135151
CRED_API_KEY: apiKey,
136152
CRED_PATH: credentialsPath,
137153
CRED_PROFILE: profile,
154+
CRED_READY_PATH: readyPaths[index]!,
138155
CRED_START_PATH: startPath,
139156
}),
140157
);
141158

142-
await new Promise(resolveDelay => setTimeout(resolveDelay, 50));
159+
await waitForFiles(readyPaths);
143160
writeFileSync(startPath, 'go');
144161

145162
const results = await Promise.all(children);

test/helpers/credentials-write-child.mjs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
1-
import { existsSync } from 'node:fs';
1+
import { existsSync, writeFileSync } from 'node:fs';
22
import { writeProfile } from '../../dist/lib/credentials.js';
33

44
const profile = process.env.CRED_PROFILE;
55
const credentialsPath = process.env.CRED_PATH;
66
const apiKey = process.env.CRED_API_KEY;
77
const startPath = process.env.CRED_START_PATH;
8+
const readyPath = process.env.CRED_READY_PATH;
9+
const startTimeoutMs = Number(process.env.CRED_START_TIMEOUT_MS ?? '5000');
10+
const pollMs = Number(process.env.CRED_POLL_MS ?? '5');
811

912
if (!profile || !credentialsPath || !apiKey || !startPath) {
1013
console.error('CRED_PROFILE, CRED_PATH, CRED_API_KEY, and CRED_START_PATH are required');
1114
process.exit(1);
1215
}
1316

14-
const deadline = Date.now() + 5_000;
17+
if (
18+
!Number.isInteger(startTimeoutMs) ||
19+
startTimeoutMs < 1 ||
20+
!Number.isInteger(pollMs) ||
21+
pollMs < 1
22+
) {
23+
console.error('CRED_START_TIMEOUT_MS and CRED_POLL_MS must be positive integers');
24+
process.exit(1);
25+
}
26+
27+
if (readyPath) {
28+
writeFileSync(readyPath, 'ready');
29+
}
30+
31+
const deadline = Date.now() + startTimeoutMs;
1532
while (!existsSync(startPath)) {
1633
if (Date.now() >= deadline) {
1734
console.error(`Timed out waiting for start marker: ${startPath}`);
1835
process.exit(2);
1936
}
20-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5);
37+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, pollMs);
2138
}
2239

2340
try {

0 commit comments

Comments
 (0)