diff options
Diffstat (limited to 'test/state.test.ts')
| -rw-r--r-- | test/state.test.ts | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/test/state.test.ts b/test/state.test.ts new file mode 100644 index 0000000..a80fcfd --- /dev/null +++ b/test/state.test.ts @@ -0,0 +1,103 @@ +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); + }); +}); |
