src.nth.io/

summaryrefslogtreecommitdiff
path: root/test/sync.test.ts
blob: 0bc5dc9e9caca5cfd5d291e30779b2a677a9e665 (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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { describe, expect, it } from "vitest";
import {
  MATTER_EPOCH_UNIX_SECONDS,
  compareDeviceClock,
  currentMatterUtcMicroseconds,
  formatDurationMicros,
  formatMatterMicros,
  matterMicrosToUnixMillis,
  unixMillisToMatterMicros,
} from "../src/sync.js";

describe("Matter epoch conversion", () => {
  it("maps 2000-01-01T00:00:00Z to 0 microseconds", () => {
    const unixMs = BigInt(Date.parse("2000-01-01T00:00:00Z"));
    expect(unixMillisToMatterMicros(unixMs)).toBe(0n);
  });

  it("maps 2000-01-01T00:00:01Z to 1,000,000 microseconds", () => {
    const unixMs = BigInt(Date.parse("2000-01-01T00:00:01Z"));
    expect(unixMillisToMatterMicros(unixMs)).toBe(1_000_000n);
  });

  it("keeps the Unix epoch conversion exact", () => {
    expect(unixMillisToMatterMicros(0n)).toBe(-MATTER_EPOCH_UNIX_SECONDS * 1_000_000n);
    expect(unixMillisToMatterMicros(0n)).toBe(-946_684_800_000_000n);
  });

  it("keeps current dates exact through bigint round-trips", () => {
    const unixMs = BigInt(Date.parse("2026-07-26T20:00:00.123Z"));
    const micros = unixMillisToMatterMicros(unixMs);
    expect(micros).toBe((unixMs - 946_684_800_000n) * 1_000n);
    expect(matterMicrosToUnixMillis(micros)).toBe(unixMs);
  });

  it("stays exact beyond JavaScript number precision", () => {
    // Microsecond timestamps pass Number.MAX_SAFE_INTEGER around year 2285;
    // conversion must not degrade there, proving no number arithmetic occurs.
    const unixMs = BigInt(Date.parse("2300-01-01T00:00:00.001Z"));
    const micros = unixMillisToMatterMicros(unixMs);
    expect(micros > BigInt(Number.MAX_SAFE_INTEGER)).toBe(true);
    expect(matterMicrosToUnixMillis(micros)).toBe(unixMs);
  });

  it("computes the current time via the injected clock", () => {
    const nowMs = Date.parse("2026-07-26T20:00:00Z");
    const micros = currentMatterUtcMicroseconds(() => nowMs);
    expect(micros).toBe(unixMillisToMatterMicros(BigInt(nowMs)));
  });

  it("formats Matter microseconds as an ISO instant", () => {
    expect(formatMatterMicros(0n)).toBe("2000-01-01T00:00:00.000Z");
    expect(formatMatterMicros(1_000_000n)).toBe("2000-01-01T00:00:01.000Z");
  });
});

describe("formatDurationMicros", () => {
  it("scales units with magnitude", () => {
    expect(formatDurationMicros(0n)).toBe("0us");
    expect(formatDurationMicros(999n)).toBe("999us");
    expect(formatDurationMicros(412_000n)).toBe("412ms");
    expect(formatDurationMicros(3_200_000n)).toBe("3.2s");
    expect(formatDurationMicros(59_000_000n)).toBe("59s");
    expect(formatDurationMicros(125_000_000n)).toBe("2m 5s");
    expect(formatDurationMicros(3_840_000_000n)).toBe("1h 4m");
    // 90,061 seconds = 1d 1h 1m 1s; seconds are dropped once days appear.
    expect(formatDurationMicros(90_061_000_000n)).toBe("1d 1h 1m");
  });

  it("rejects negative durations", () => {
    expect(() => formatDurationMicros(-1n)).toThrow();
  });
});

describe("compareDeviceClock", () => {
  const host = unixMillisToMatterMicros(BigInt(Date.parse("2026-07-26T20:00:00.000Z")));

  it("reports an unset device clock", () => {
    const comparison = compareDeviceClock(null, host);
    expect(comparison.deviceTime).toBeNull();
    expect(comparison.deltaMicroseconds).toBeNull();
    expect(comparison.hostTime).toBe("2026-07-26T20:00:00.000Z");
    expect(comparison.description).toBe("device clock was unset");
  });

  it("reports a device clock behind the host", () => {
    const comparison = compareDeviceClock(host - 83_000_000n, host);
    expect(comparison.deviceTime).toBe("2026-07-26T19:58:37.000Z");
    expect(comparison.deltaMicroseconds).toBe(-83_000_000n);
    expect(comparison.description).toBe("device clock was 1m 23s behind");
  });

  it("reports a device clock ahead of the host", () => {
    const comparison = compareDeviceClock(host + 5_500_000n, host);
    expect(comparison.deltaMicroseconds).toBe(5_500_000n);
    expect(comparison.description).toBe("device clock was 5.5s ahead");
  });

  it("treats sub-second deltas as in sync but still reports them", () => {
    const comparison = compareDeviceClock(host - 412_000n, host);
    expect(comparison.description).toBe("device clock was within 1s of host time (412ms behind)");
  });

  it("handles a device clock wildly in the future (wrong epoch bug)", () => {
    // A device set with Unix-epoch seconds where Matter-epoch was expected
    // shows up decades ahead; the delta must stay exact.
    const wrong = host + 946_684_800n * 1_000_000n;
    const comparison = compareDeviceClock(wrong, host);
    expect(comparison.deltaMicroseconds).toBe(946_684_800_000_000n);
    expect(comparison.description).toContain("ahead");
  });
});