src.nth.io/

summaryrefslogtreecommitdiff
path: root/test/timesync.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'test/timesync.test.ts')
-rw-r--r--test/timesync.test.ts79
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();
+ });
+});