import { mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { EMPTY_NODE_STATE, nodeState, readServiceState, removeNodeState, serviceStatePath, updateNodeState, writeServiceState, } from "../src/state.js"; let dir: string; beforeEach(() => { dir = mkdtempSync(join(tmpdir(), "mattertimesync-state-")); }); afterEach(() => { rmSync(dir, { recursive: true, force: true }); }); describe("service state", () => { it("returns empty state when the file does not exist", () => { expect(readServiceState(join(dir, "missing.json"))).toEqual({ nodes: {} }); }); it("round-trips per-node state through disk", () => { const path = serviceStatePath(dir); const state = { nodes: { "12345678901234567890": { lastSuccessfulConnection: "2026-07-26T20:00:00Z", lastSuccessfulSync: "2026-07-26T20:00:02Z", lastAttemptedSync: "2026-07-26T20:00:01Z", lastError: null, }, "2": { ...EMPTY_NODE_STATE, lastError: "unreachable" }, }, }; writeServiceState(path, state); expect(readServiceState(path)).toEqual(state); }); it("leaves no temporary file behind after writing", () => { const path = serviceStatePath(dir); writeServiceState(path, { nodes: {} }); expect(readdirSync(dir)).toEqual(["service-state.json"]); }); it("returns empty state for a corrupt file instead of throwing", () => { const path = join(dir, "corrupt.json"); writeFileSync(path, "{ this is not json"); expect(readServiceState(path)).toEqual({ nodes: {} }); }); it("drops malformed node keys and wrongly typed fields", () => { const path = join(dir, "typed.json"); writeFileSync( path, JSON.stringify({ nodes: { "not-a-node-id": { lastError: "x" }, "7": { lastError: ["array"], lastSuccessfulSync: "2026-07-26T20:00:02Z" }, }, }), ); expect(readServiceState(path)).toEqual({ nodes: { "7": { ...EMPTY_NODE_STATE, lastSuccessfulSync: "2026-07-26T20:00:02Z" } }, }); }); it("merges patches into one node without touching others", () => { const path = serviceStatePath(dir); updateNodeState(path, 1n, { lastError: "boom" }); updateNodeState(path, 2n, { lastSuccessfulSync: "2026-07-26T20:00:02Z" }); updateNodeState(path, 1n, { lastAttemptedSync: "2026-07-26T20:00:01Z" }); const state = readServiceState(path); expect(nodeState(state, 1n)).toEqual({ ...EMPTY_NODE_STATE, lastError: "boom", lastAttemptedSync: "2026-07-26T20:00:01Z", }); expect(nodeState(state, 2n)).toEqual({ ...EMPTY_NODE_STATE, lastSuccessfulSync: "2026-07-26T20:00:02Z", }); }); it("removes a node's entry", () => { const path = serviceStatePath(dir); updateNodeState(path, 1n, { lastError: "boom" }); updateNodeState(path, 2n, {}); removeNodeState(path, 1n); const state = readServiceState(path); expect(Object.keys(state.nodes)).toEqual(["2"]); expect(nodeState(state, 1n)).toEqual(EMPTY_NODE_STATE); }); });