diff options
Diffstat (limited to 'test/timesync.test.ts')
| -rw-r--r-- | test/timesync.test.ts | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/test/timesync.test.ts b/test/timesync.test.ts new file mode 100644 index 0000000..f51369f --- /dev/null +++ b/test/timesync.test.ts @@ -0,0 +1,79 @@ +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import type { TimeSyncSummary } from "../src/inspection.js"; +import { compareDeviceClock } from "../src/sync.js"; +import { capabilitiesFromTimeSync, planTimeSync } from "../src/timesync.js"; + +/** + * Tests driven by the captured `inspect --json` output of the real device + * (IKEA ALPSTUGA air quality monitor, firmware 1.0.26), per the plan: the + * sync behavior is derived from inspected capabilities, not assumptions. + */ + +const fixture = JSON.parse(readFileSync(join(__dirname, "fixtures", "alpstuga-inspect.json"), "utf8")) as { + basicInformation: { vendorName: string; productName: string }; + timeSynchronization: TimeSyncSummary; +}; + +describe("alpstuga fixture", () => { + it("is the expected device", () => { + expect(fixture.basicInformation.vendorName).toBe("IKEA of Sweden"); + expect(fixture.timeSynchronization).not.toBeNull(); + }); + + it("derives capabilities from the inspection", () => { + const capabilities = capabilitiesFromTimeSync(fixture.timeSynchronization); + expect(capabilities).toEqual({ + timeZoneFeature: true, + supportsSetUtcTime: true, + supportsSetTimeZone: true, + supportsSetDstOffset: true, + timeZoneListMaxSize: 2, + dstOffsetListMaxSize: 2, + }); + }); + + it("plans UTC + time zone + a 2-entry DST list for this device", () => { + const capabilities = capabilitiesFromTimeSync(fixture.timeSynchronization); + const plan = planTimeSync(capabilities, "America/Chicago", new Date("2026-07-26T00:00:00Z")); + expect(plan.setUtcTime).toBe(true); + expect(plan.timeZone).toEqual([ + { offset: -21600, validAt: 946_684_800_000_000n, name: "America/Chicago" }, + ]); + expect(plan.dstOffsets).toHaveLength(2); + expect(plan.dstOffsets![0]!.offset).toBe(3600); + expect(plan.dstOffsets![1]!.offset).toBe(0); + }); + + it("reads the fixture's utcTime as healthy Unix-epoch microseconds", () => { + // matter.js's TlvEpochUs decodes wire values to Unix-epoch microseconds, + // so the fixture's utcTime (read at commissioning, 2026-07-27T00:18:15Z + // wall clock) compares cleanly against that instant with no epoch shift. + const attribute = fixture.timeSynchronization.attributes.find(a => a.name === "utcTime"); + const deviceMicros = BigInt(attribute!.value as string); + const captureInstant = BigInt(Date.parse("2026-07-27T00:18:20Z")) * 1_000n; + const comparison = compareDeviceClock(deviceMicros, captureInstant); + expect(comparison.epochShifted).toBe(false); + expect(comparison.deviceTime).toBe("2026-07-27T00:18:15.000Z"); + const effective = comparison.effectiveDeltaMicroseconds!; + const magnitude = effective < 0n ? -effective : effective; + expect(magnitude < 60n * 1_000_000n).toBe(true); + }); + + it("degrades to UTC-only when the TimeZone feature is absent", () => { + const utcOnly: TimeSyncSummary = { + ...fixture.timeSynchronization, + supportedFeatures: { ...fixture.timeSynchronization.supportedFeatures, timeZone: false }, + supportedCommands: { + ...fixture.timeSynchronization.supportedCommands, + setTimeZone: false, + setDstOffset: false, + }, + }; + const plan = planTimeSync(capabilitiesFromTimeSync(utcOnly), "America/Chicago"); + expect(plan.setUtcTime).toBe(true); + expect(plan.timeZone).toBeNull(); + expect(plan.dstOffsets).toBeNull(); + }); +}); |
