diff --git a/Tests/DuooomiBleSDKTests/PlayModeTests.swift b/Tests/DuooomiBleSDKTests/PlayModeTests.swift index ca7463c..0c4fa0d 100644 --- a/Tests/DuooomiBleSDKTests/PlayModeTests.swift +++ b/Tests/DuooomiBleSDKTests/PlayModeTests.swift @@ -103,3 +103,47 @@ final class SetPlayModeGuardTests: XCTestCase { wait(for: [exp], timeout: 1.0) } } + +final class BindPayloadByteRegressionTests: XCTestCase { + /// 关键回归:bind / unbind 在 loop=nil 时报文必须完全不含 "loop" key —— 不能输出 "loop":null。 + /// 不假设 JSONEncoder key 顺序(Swift 不同版本顺序可能不同),用 substring + dict 断言锁语义。 + func testBindPayloadNilLoopOmitsKeyAndNull() throws { + let payload = BindPayload(type: 0x0F, userId: "abc", loop: nil) + let data = try JSONEncoder().encode(payload) + let json = String(data: data, encoding: .utf8) ?? "" + // 字面层:JSON 文本不允许出现 "loop" 或 "null" + XCTAssertFalse(json.contains("loop"), "loop key MUST be absent; got \(json)") + XCTAssertFalse(json.contains("null"), "null literal MUST be absent; got \(json)") + // 结构层:反序列化后只有 type / userId,无第三个 key + let dict = try JSONSerialization.jsonObject(with: data) as? [String: Any] ?? [:] + XCTAssertEqual(dict.count, 2) + XCTAssertEqual(dict["type"] as? Int, 15) + XCTAssertEqual(dict["userId"] as? String, "abc") + XCTAssertNil(dict["loop"]) + } + + /// setPlayMode 报文:loop=1 必须以整数形式出现(不是 "1" 字符串)。 + func testSetPlayModePayloadIncludesLoopOne() throws { + let payload = BindPayload(type: 0x0F, userId: "abc", loop: 1) + let data = try JSONEncoder().encode(payload) + let json = String(data: data, encoding: .utf8) ?? "" + // 字面层子串:必须出现 "loop":1(数字,无引号) + XCTAssertTrue(json.contains(#""loop":1"#), "expected literal substring \"loop\":1 in \(json)") + // 结构层 + let dict = try JSONSerialization.jsonObject(with: data) as? [String: Any] ?? [:] + XCTAssertEqual(dict.count, 3) + XCTAssertEqual(dict["type"] as? Int, 15) + XCTAssertEqual(dict["userId"] as? String, "abc") + XCTAssertEqual(dict["loop"] as? Int, 1) + } + + /// setPlayMode 报文:loop=0 边界情况,必须以整数 0 出现(不是被当作 "default" 省略)。 + func testSetPlayModePayloadIncludesLoopZero() throws { + let payload = BindPayload(type: 0x0F, userId: "abc", loop: 0) + let data = try JSONEncoder().encode(payload) + let json = String(data: data, encoding: .utf8) ?? "" + XCTAssertTrue(json.contains(#""loop":0"#), "expected literal substring \"loop\":0 in \(json)") + let dict = try JSONSerialization.jsonObject(with: data) as? [String: Any] ?? [:] + XCTAssertEqual(dict["loop"] as? Int, 0) + } +}