Skip to content

Commit a0aaec7

Browse files
bwp91claude
andcommitted
test: add unit tests for bug fix commits
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4824154 commit a0aaec7

4 files changed

Lines changed: 145 additions & 0 deletions

File tree

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { DataStreamParser, DataStreamReader, DataStreamWriter, Float64 } from "./DataStreamParser";
2+
3+
describe("DataStreamParser", () => {
4+
describe("writeNumber Int32 range check", () => {
5+
test("should encode numbers in Int32 range as Int32, not Int64", () => {
6+
const writer = new DataStreamWriter();
7+
// 100000 is within Int32 range but was previously encoded as Int64
8+
writer.writeNumber(100000);
9+
const data = writer.getData();
10+
const reader = new DataStreamReader(data);
11+
12+
// Tag byte (1) + 4 bytes for Int32 = 5 bytes total
13+
// If incorrectly encoded as Int64, it would be 1 + 8 = 9 bytes
14+
expect(data.length).toBe(5);
15+
16+
// Verify it decodes back correctly
17+
const decoded = DataStreamParser.decode(reader);
18+
expect(decoded).toBe(100000);
19+
});
20+
21+
test("should encode max Int32 value as Int32", () => {
22+
const writer = new DataStreamWriter();
23+
writer.writeNumber(2147483647);
24+
const data = writer.getData();
25+
26+
// 1 tag byte + 4 data bytes = 5
27+
expect(data.length).toBe(5);
28+
29+
const reader = new DataStreamReader(data);
30+
expect(DataStreamParser.decode(reader)).toBe(2147483647);
31+
});
32+
33+
test("should encode min Int32 value as Int32", () => {
34+
const writer = new DataStreamWriter();
35+
writer.writeNumber(-2147483648);
36+
const data = writer.getData();
37+
38+
// 1 tag byte + 4 data bytes = 5
39+
expect(data.length).toBe(5);
40+
41+
const reader = new DataStreamReader(data);
42+
expect(DataStreamParser.decode(reader)).toBe(-2147483648);
43+
});
44+
45+
test("should encode numbers beyond Int32 range as Int64", () => {
46+
const writer = new DataStreamWriter();
47+
writer.writeNumber(2147483648); // one above Int32 max
48+
const data = writer.getData();
49+
50+
// 1 tag byte + 8 data bytes = 9
51+
expect(data.length).toBe(9);
52+
53+
const reader = new DataStreamReader(data);
54+
expect(DataStreamParser.decode(reader)).toBe(2147483648);
55+
});
56+
});
57+
58+
describe("readFloat64LE reader index advance", () => {
59+
test("should advance reader index after reading Float64", () => {
60+
const writer = new DataStreamWriter();
61+
writer.writeFloat64LE(new Float64(3.14));
62+
writer.writeFloat64LE(new Float64(2.71));
63+
const data = writer.getData();
64+
65+
const reader = new DataStreamReader(data);
66+
const first = DataStreamParser.decode(reader);
67+
const second = DataStreamParser.decode(reader);
68+
69+
expect(first).toBeCloseTo(3.14);
70+
expect(second).toBeCloseTo(2.71);
71+
// If the reader index wasn't advancing, both would be 3.14
72+
expect(second).not.toBeCloseTo(3.14);
73+
});
74+
});
75+
76+
describe("UTF-8 tag byte length", () => {
77+
test("should correctly encode multi-byte UTF-8 strings", () => {
78+
const writer = new DataStreamWriter();
79+
// "é" is 2 bytes in UTF-8 but 1 character in JS
80+
const testString = "é";
81+
writer.writeUTF8(testString);
82+
const data = writer.getData();
83+
84+
const reader = new DataStreamReader(data);
85+
const decoded = DataStreamParser.decode(reader);
86+
expect(decoded).toBe(testString);
87+
});
88+
89+
test("should round-trip short strings with multi-byte characters", () => {
90+
const writer = new DataStreamWriter();
91+
// Mix of ASCII and multi-byte: 5 chars, 7 bytes in UTF-8
92+
const testString = "hëllo";
93+
writer.writeUTF8(testString);
94+
const data = writer.getData();
95+
96+
const reader = new DataStreamReader(data);
97+
const decoded = DataStreamParser.decode(reader);
98+
expect(decoded).toBe(testString);
99+
});
100+
});
101+
});

src/lib/model/AccessoryInfo.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
11
import { AccessoryInfo } from "./AccessoryInfo";
2+
import { Categories } from "../Accessory";
3+
import { HAPStorage } from "./HAPStorage";
24
import { AssertionError } from "assert";
35

46
describe("AccessoryInfo", () => {
7+
describe("#load()", () => {
8+
it("should default category to Categories.OTHER when missing", () => {
9+
const mockStorage = HAPStorage.storage();
10+
(mockStorage.getItem as jest.Mock).mockReturnValueOnce({
11+
displayName: "Test",
12+
pincode: "123-45-678",
13+
signSk: "aa".repeat(64),
14+
signPk: "bb".repeat(32),
15+
pairedClients: {},
16+
// category intentionally omitted
17+
});
18+
19+
const info = AccessoryInfo.load("0E:AE:FC:45:7B:91");
20+
expect(info).not.toBeNull();
21+
expect(info!.category).toBe(Categories.OTHER);
22+
expect(typeof info!.category).toBe("number");
23+
});
24+
});
25+
526
describe("#assertValidUsername()", () => {
627
it("should verify correct device id", () => {
728
const VALUE = "0E:AE:FC:45:7B:91";

src/lib/util/hapCrypto.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,14 @@ describe("hapCrypto", () => {
126126
expect(C_decrypted).toEqual(Buffer.concat([message1, message3]));
127127
});
128128

129+
test("large message encryption and decryption (multi-chunk)", () => {
130+
// Message larger than 0x400 (1024) bytes forces multiple chunks
131+
const largeMessage = crypto.randomBytes(3000);
132+
const encrypted = hapCrypto.layerEncrypt(largeMessage, serverEncryption);
133+
const decrypted = hapCrypto.layerDecrypt(encrypted, clientEncryption);
134+
expect(decrypted).toEqual(largeMessage);
135+
});
136+
129137
test("incomplete frames decryption; first", () => {
130138
const S_encrypted0 = hapCrypto.layerEncrypt(message0, serverEncryption);
131139
const S_encrypted1 = hapCrypto.layerEncrypt(message1, serverEncryption);

src/lib/util/tlv.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,21 @@ describe("tlv", () => {
9595
1: Buffer.from("010203", "hex"),
9696
});
9797
});
98+
99+
test("should throw on truncated buffer (incomplete header)", () => {
100+
// Only 1 byte — not enough for type + length
101+
expect(() => decode(Buffer.from("01", "hex"))).toThrow("incomplete type/length header");
102+
});
103+
104+
test("should throw when declared length exceeds remaining buffer", () => {
105+
// Type 0x01, length 0x05, but only 2 bytes of data follow
106+
expect(() => decode(Buffer.from("01050102", "hex"))).toThrow("declared length");
107+
});
108+
109+
test("should not throw on valid zero-length entry", () => {
110+
// Type 0x01, length 0x00
111+
expect(() => decode(Buffer.from("0100", "hex"))).not.toThrow();
112+
});
98113
});
99114

100115
describe("decodeWithLists", () => {

0 commit comments

Comments
 (0)