fix: pairing admin satisfies write (#23125) (thanks @vignesh07)

This commit is contained in:
Vignesh Natarajan
2026-02-21 18:24:58 -08:00
committed by Vignesh
parent 426d97797d
commit 5b4409d5d0
4 changed files with 58 additions and 3 deletions

View File

@@ -196,6 +196,55 @@ describe("gateway lock", () => {
staleSpy.mockRestore();
});
it("keeps lock when fs.stat fails until payload is stale", async () => {
vi.useRealTimers();
const env = await makeEnv();
const { lockPath, configPath } = resolveLockPath(env);
const payload = createLockPayload({ configPath, startTime: 111 });
await fs.writeFile(lockPath, JSON.stringify(payload), "utf8");
const procSpy = mockProcStatRead({
onProcRead: () => {
throw new Error("EACCES");
},
});
const statSpy = vi
.spyOn(fs, "stat")
.mockRejectedValue(Object.assign(new Error("EPERM"), { code: "EPERM" }));
const pending = acquireForTest(env, {
timeoutMs: 20,
staleMs: 10_000,
platform: "linux",
});
await expect(pending).rejects.toBeInstanceOf(GatewayLockError);
procSpy.mockRestore();
const stalePayload = createLockPayload({
configPath,
startTime: 111,
createdAt: new Date(0).toISOString(),
});
await fs.writeFile(lockPath, JSON.stringify(stalePayload), "utf8");
const staleProcSpy = mockProcStatRead({
onProcRead: () => {
throw new Error("EACCES");
},
});
const lock = await acquireForTest(env, {
staleMs: 1,
platform: "linux",
});
expect(lock).not.toBeNull();
await lock?.release();
staleProcSpy.mockRestore();
statSpy.mockRestore();
});
it("returns null when multi-gateway override is enabled", async () => {
const env = await makeEnv();
const lock = await acquireGatewayLock({

View File

@@ -231,7 +231,11 @@ export async function acquireGatewayLock(
const st = await fs.stat(lockPath);
stale = Date.now() - st.mtimeMs > staleMs;
} catch {
stale = true;
// On Windows or locked filesystems we may be unable to stat the
// lock file even though the existing gateway is still healthy.
// Treat the lock as non-stale so we keep waiting instead of
// forcefully removing another gateway's lock.
stale = false;
}
}
if (stale) {

View File

@@ -985,8 +985,9 @@ describe("QmdMemoryManager", () => {
);
expect(mcporterCall).toBeDefined();
const spawnOpts = mcporterCall?.[2] as { env?: NodeJS.ProcessEnv } | undefined;
expect(spawnOpts?.env?.XDG_CONFIG_HOME).toContain("/agents/main/qmd/xdg-config");
expect(spawnOpts?.env?.XDG_CACHE_HOME).toContain("/agents/main/qmd/xdg-cache");
const normalizePath = (value?: string) => value?.replace(/\\/g, "/");
expect(normalizePath(spawnOpts?.env?.XDG_CONFIG_HOME)).toContain("/agents/main/qmd/xdg-config");
expect(normalizePath(spawnOpts?.env?.XDG_CACHE_HOME)).toContain("/agents/main/qmd/xdg-cache");
await manager.close();
});