Skip to content

Commit cbe6846

Browse files
committed
test(auth): cover pid:counter lock claims with keepers
1 parent 175be0b commit cbe6846

1 file changed

Lines changed: 64 additions & 0 deletions

File tree

‎src/auth/store.test.ts‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,34 @@ describe("createAuthStore", () => {
355355
}
356356
});
357357

358+
test("takes over a dead-holder pid:counter claim instead of timing out", async () => {
359+
const home = await mkdtemp(join(tmpdir(), "oauth-store-takeover-claim-"));
360+
try {
361+
const store = createAuthStore<TestTokens>({
362+
filename: "test-auth.json",
363+
settingsDirName: TEST_SETTINGS_DIR,
364+
isTokens: isTestTokens,
365+
});
366+
const lockPath = `${store.authPath(home)}.lock`;
367+
await mkdir(join(home, TEST_SETTINGS_DIR), { recursive: true });
368+
// The NEW pid:counter claim format with a certainly-dead PID: kill(pid, 0)
369+
// answers ESRCH (or EINVAL), both of which read as dead. The counter leg
370+
// must not stop holderPid from reading the pid leg.
371+
await writeFile(lockPath, `${2_147_483_647}:99`, { mode: 0o600 });
372+
373+
const profile = {
374+
name: "work",
375+
tokens: { access: "a", refresh: "r", expiresAt: 1 },
376+
createdAt: 1,
377+
};
378+
await expect(store.saveProfile(profile, home)).resolves.toBeUndefined();
379+
expect(await store.loadProfile("work", home)).toEqual(profile);
380+
await expect(readFile(lockPath, "utf8")).rejects.toThrow("ENOENT");
381+
} finally {
382+
await rm(home, { recursive: true, force: true });
383+
}
384+
});
385+
358386
test("waits on a live-holder lock and times out without touching it", async () => {
359387
const home = await mkdtemp(join(tmpdir(), "oauth-store-live-"));
360388
try {
@@ -387,6 +415,42 @@ describe("createAuthStore", () => {
387415
}
388416
});
389417

418+
test("waits on a live-holder pid:counter claim and times out without touching it", async () => {
419+
const home = await mkdtemp(join(tmpdir(), "oauth-store-live-claim-"));
420+
try {
421+
const store = createAuthStore<TestTokens>({
422+
filename: "test-auth.json",
423+
settingsDirName: TEST_SETTINGS_DIR,
424+
isTokens: isTestTokens,
425+
lockTimeoutMs: 100,
426+
});
427+
const lockPath = `${store.authPath(home)}.lock`;
428+
await mkdir(join(home, TEST_SETTINGS_DIR), { recursive: true });
429+
// The NEW pid:counter claim format held by this live process. Never
430+
// signal it; the waiter must read the pid leg as alive, time out, and
431+
// leave the claim byte-identical.
432+
const claim = `${process.pid}:42`;
433+
await writeFile(lockPath, claim, { mode: 0o600 });
434+
435+
await expect(
436+
store.saveProfile(
437+
{
438+
name: "work",
439+
tokens: { access: "a", refresh: "r", expiresAt: 1 },
440+
createdAt: 1,
441+
},
442+
home,
443+
),
444+
).rejects.toThrow(
445+
`Timed out waiting for OAuth credential lock ${lockPath}. ` +
446+
"If no Corbits process is running, remove this lock file manually and retry.",
447+
);
448+
expect(await readFile(lockPath, "utf8")).toBe(claim);
449+
} finally {
450+
await rm(home, { recursive: true, force: true });
451+
}
452+
});
453+
390454
test("takes over a stale legacy lock but waits on a fresh one", async () => {
391455
const home = await mkdtemp(join(tmpdir(), "oauth-store-legacy-"));
392456
try {

0 commit comments

Comments
 (0)