import { describe, expect, it } from "vitest"; import { ConfigError, parseNodeId, validateConfig } from "../src/config.js"; const minimal = { storagePath: "/var/lib/mattertimesync" }; describe("validateConfig", () => { it("accepts a minimal configuration and applies defaults", () => { const config = validateConfig(minimal); expect(config.storagePath).toBe(minimal.storagePath); expect(config.timezone).toBe("America/Chicago"); expect(config.logLevel).toBe("info"); }); it("accepts a full configuration", () => { const config = validateConfig({ ...minimal, timezone: "Europe/Berlin", logLevel: "debug", }); expect(config.timezone).toBe("Europe/Berlin"); expect(config.logLevel).toBe("debug"); }); it("rejects non-object documents", () => { expect(() => validateConfig([])).toThrow(ConfigError); expect(() => validateConfig("x")).toThrow(ConfigError); expect(() => validateConfig(null)).toThrow(ConfigError); }); it("requires storagePath", () => { expect(() => validateConfig({})).toThrow(/storagePath/); expect(() => validateConfig({ storagePath: "" })).toThrow(/storagePath/); }); it("rejects unknown fields", () => { expect(() => validateConfig({ ...minimal, unexpected: 1 })).toThrow(/unknown field "unexpected"/); // Fields from abandoned designs are rejected, not ignored. expect(() => validateConfig({ ...minimal, syncIntervalHours: 24 })).toThrow(/unknown field/); expect(() => validateConfig({ ...minimal, syncOnReconnect: true })).toThrow(/unknown field/); expect(() => validateConfig({ ...minimal, nodeId: "1" })).toThrow(/unknown field/); }); it("rejects invalid time zones", () => { expect(() => validateConfig({ ...minimal, timezone: "Central Time" })).toThrow(/timezone/); expect(() => validateConfig({ ...minimal, timezone: "" })).toThrow(/timezone/); expect(() => validateConfig({ ...minimal, timezone: 5 })).toThrow(/timezone/); }); it("rejects invalid log levels", () => { expect(() => validateConfig({ ...minimal, logLevel: "verbose" })).toThrow(/logLevel/); }); }); describe("parseNodeId", () => { it("parses decimal strings without precision loss", () => { expect(parseNodeId("1")).toBe(1n); expect(parseNodeId("9007199254740993")).toBe(9007199254740993n); expect(parseNodeId("18446744073709551615")).toBe(18446744073709551615n); }); it("accepts small safe integers", () => { expect(parseNodeId(42)).toBe(42n); }); it("rejects unsafe or invalid values", () => { expect(() => parseNodeId("18446744073709551616")).toThrow(ConfigError); // 2^64 expect(() => parseNodeId("-1")).toThrow(ConfigError); expect(() => parseNodeId(1.5)).toThrow(ConfigError); expect(() => parseNodeId("0x10")).toThrow(ConfigError); expect(() => parseNodeId(Number.MAX_SAFE_INTEGER + 2)).toThrow(ConfigError); expect(() => parseNodeId(null)).toThrow(ConfigError); }); });