Files
pi-gui/test/instance-lock.test.js
T
alex b7fea83ed6 feat(bridge): establish local pi status bridge
Deliver the initial local bridge, Noctalia v4/v5 adapters, desktop client, service unit, tests, and implementation documentation for persistent Pi status and control.
2026-07-27 14:51:45 +02:00

107 lines
2.4 KiB
JavaScript

import assert from "node:assert/strict";
import { mkdtemp, readFile, stat, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import {
BridgeAlreadyRunningError,
BridgeLockError,
acquireBridgeLock,
lockMode,
} from "../src/bridge/instance-lock.js";
async function paths() {
const directory = await mkdtemp(join(tmpdir(), "pi-status-bridge-lock-"));
return {
lockPath: join(directory, "bridge.lock"),
socketPath: join(directory, "bridge.sock"),
};
}
test("acquires an owner-only lock and releases only its own token", async () => {
const { lockPath, socketPath } = await paths();
const lock = await acquireBridgeLock({
lockPath,
socketPath,
pid: 101,
now: () => new Date("2026-01-02T03:04:05.000Z"),
});
const lockContents = await readFile(lockPath, "utf8");
let metadata;
try {
metadata = JSON.parse(lockContents);
} catch (error) {
assert.fail(
error instanceof Error ? error.message : "lock file was not JSON",
);
}
const lockStats = await stat(lockPath);
assert.equal(metadata.pid, 101);
assert.equal(metadata.socketPath, socketPath);
assert.equal(lockStats.mode & 0o777, 0o600);
assert.equal(lockMode, 0o600);
assert.equal(await lock.release(), true);
assert.equal(await lock.release(), false);
});
test("refuses to replace a live owner", async () => {
const { lockPath, socketPath } = await paths();
await writeFile(
lockPath,
JSON.stringify({
pid: 202,
socketPath,
startedAt: "2026-01-02T03:04:05.000Z",
token: "live-owner",
}),
);
await assert.rejects(
acquireBridgeLock({
lockPath,
socketPath,
pid: 303,
processAlive: (pid) => pid === 202,
}),
BridgeAlreadyRunningError,
);
});
test("recovers only a proven-stale lock", async () => {
const { lockPath, socketPath } = await paths();
await writeFile(
lockPath,
JSON.stringify({
pid: 404,
socketPath,
startedAt: "2026-01-02T03:04:05.000Z",
token: "stale-owner",
}),
);
const lock = await acquireBridgeLock({
lockPath,
socketPath,
pid: 505,
processAlive: () => false,
});
assert.equal(lock.metadata.pid, 505);
await lock.release();
});
test("does not reclaim a malformed lock", async () => {
const { lockPath, socketPath } = await paths();
await writeFile(lockPath, "broken");
await assert.rejects(
acquireBridgeLock({
lockPath,
socketPath,
pid: 606,
processAlive: () => false,
}),
BridgeLockError,
);
});