46 lines
1.7 KiB
TypeScript
46 lines
1.7 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
import { rm } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { acquireLock, releaseLock, isLocked, lockPathFor } from "../src/foundry/docker.js";
|
|
|
|
const TMP = "/tmp/test-docker-out";
|
|
const LOCK = join(TMP, "foundry-sync.lock");
|
|
|
|
beforeEach(async () => { await rm(TMP, { recursive: true, force: true }); });
|
|
afterEach(async () => { await rm(TMP, { recursive: true, force: true }); });
|
|
|
|
describe("lockPathFor", () => {
|
|
it("places the lock inside the out dir", () => {
|
|
expect(lockPathFor("/foo/out")).toBe(join("/foo/out", "foundry-sync.lock"));
|
|
});
|
|
});
|
|
|
|
describe("acquireLock / releaseLock / isLocked", () => {
|
|
it("acquires a fresh lock and reports it as held", async () => {
|
|
await acquireLock(LOCK, "refresh");
|
|
expect(await isLocked(LOCK)).toBe(true);
|
|
});
|
|
|
|
it("refuses to acquire while a live lock is held", async () => {
|
|
await acquireLock(LOCK, "push");
|
|
await expect(acquireLock(LOCK, "refresh")).rejects.toThrow(/lock held by push/);
|
|
});
|
|
|
|
it("releases the lock", async () => {
|
|
await acquireLock(LOCK, "refresh");
|
|
await releaseLock(LOCK);
|
|
expect(await isLocked(LOCK)).toBe(false);
|
|
});
|
|
|
|
it("reclaims a stale lock", async () => {
|
|
// Write a stale lock (timestamp 1 hour ago, beyond LOCK_STALE_MS=10min).
|
|
const { writeFile, mkdir } = await import("node:fs/promises");
|
|
const { dirname } = await import("node:path");
|
|
await mkdir(dirname(LOCK), { recursive: true });
|
|
const stale = Date.now() - 60 * 60 * 1000;
|
|
await writeFile(LOCK, `push\n${stale}\n`, "utf8");
|
|
// Should succeed (reclaim) rather than throw.
|
|
await acquireLock(LOCK, "refresh");
|
|
expect(await isLocked(LOCK)).toBe(true);
|
|
});
|
|
}); |