src.nth.io/

summaryrefslogtreecommitdiff
path: root/test/timesync.test.ts
blob: f51369f73fdb2e2c4a3188a055b401bb0540ba53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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();
  });
});