Files
factorio-clusterio/test/lib/LockFile.test.js
T
Hornwitser 2e48b728dd Remove fs-extra and klaw (#882)
These packages are obsoleted by the promises API to the built-in Node fs
module.  The fs-extra package is also causing problems with broken APIs
that don't behave the same as Node's fs module, and throwing errors
without code properties on them.
2026-04-09 22:53:13 +02:00

134 lines
4.0 KiB
JavaScript

/* eslint-disable node/no-sync */
"use strict";
const assert = require("assert").strict;
const fs = require("node:fs/promises");
const path = require("path");
const { LockFile } = require("@clusterio/lib");
const baseDir = path.join("temp", "test", "lock_file");
describe("class LockFile", function () {
/** @type {string} */
let filePath;
/** @type {LockFile} */
let lock;
before(async function () {
await fs.mkdir(baseDir, { recursive: true });
});
beforeEach(function() {
filePath = path.join(baseDir, `${Date.now()}-${Math.random()}.lock`);
lock = new LockFile(filePath);
});
afterEach(async function () {
await fs.unlink(filePath).catch(() => {});
});
describe("acquire()", function () {
it("should create the lock file when acquiring", async function () {
await lock.acquire();
await fs.access(filePath);
});
it("should throw LockFileBusyError if called when not idle", async function () {
await lock.acquire();
await assert.rejects(lock.acquire(), { name: "LockFileBusyError" });
});
it("should remove stale lock file and acquire it", async function () {
await fs.writeFile(filePath, "999999\n"); // stale lock
await lock.acquire();
await fs.access(filePath);
});
it("should throw if existing lock file is not stale", async function () {
await fs.writeFile(filePath, `${process.pid}\n`);
await assert.rejects(lock.acquire(), { name: "LockFileExistsError" });
});
});
describe("release()", function () {
it("should remove the lock file when releasing", async function () {
await lock.acquire();
await lock.release();
await assert.rejects(fs.access(filePath));
});
it("should throw LockFileBusyError if called when not acquired", async function () {
await assert.rejects(lock.release(), { name: "LockFileBusyError" });
});
});
describe("isHeld()", function () {
it("should return true only when lock file contains current PID", async function () {
await lock.acquire();
const held = await lock.isHeld();
assert.equal(held, true);
});
it("should return false when lock file does not exist", async function () {
const held = await lock.isHeld();
assert.equal(held, false);
});
it("should return false if lock file contains different PID", async function () {
await fs.writeFile(filePath, "999999\n"); // simulate another process
const held = await lock.isHeld();
assert.equal(held, false);
});
it("should return false if lock file was deleted while held", async function() {
await lock.acquire();
await fs.unlink(filePath);
const held = await lock.isHeld();
assert.equal(held, false);
});
});
describe("isStale()", function () {
it("should return true if lock file does not exist", async function () {
const stale = await lock.isStale();
assert.equal(stale, true);
});
it("should return true for invalid PID in lock file", async function () {
await fs.writeFile(filePath, "abc\n"); // non-numeric PID
const stale = await lock.isStale();
assert.equal(stale, true);
});
it("should return true for non-existent process PID", async function () {
await fs.writeFile(filePath, "999999\n"); // assume PID does not exist
const stale = await lock.isStale();
assert.equal(stale, true);
});
it("should return false for current process PID", async function () {
await fs.writeFile(filePath, `${process.pid}\n`);
const stale = await lock.isStale();
assert.equal(stale, false);
});
});
describe("attach/detach exit handlers", function () {
it("should attach exit handler on acquire and detach on release", async function () {
await lock.acquire();
const listenersBefore = process.listeners("exit").slice();
assert.ok(listenersBefore.includes(lock.handleExitSync));
await lock.release();
const listenersAfter = process.listeners("exit").slice();
assert.ok(!listenersAfter.includes(lock.handleExitSync));
});
});
describe("handleExitSync()", function () {
it("should not throw if file already removed", function () {
assert.doesNotThrow(() => lock.handleExitSync()); // simulate non-existent file
});
});
});