Files
duooomi-ios-sdk/Tests/DuooomiBleSDKTests/PlayModeTests.swift
km2023 97e5b8359a feat(sdk): 重命名 owner→brand 并在硬绑前校验设备品牌
- DuooomiBleConfig.owner → brand(语义:设备品牌名),HTTP header x-owner 不变
- bind() 前置 verifyBrand:缺 deviceInfo 先 getDeviceInfo,与 config.brand 严格匹配,不一致主动断连并抛 .brandMismatch
- README 抹除软绑/对账/UpgradeRecord 等内部实现细节,仅保留厂商面向的公开 API(功能代码完全保留)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:51:17 +08:00

159 lines
7.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import XCTest
@testable import DuooomiBleSDK
final class PlayModeTests: XCTestCase {
func testRawValues() {
XCTAssertEqual(PlayMode.single.rawValue, 0)
XCTAssertEqual(PlayMode.loop.rawValue, 1)
}
func testInitFromRawValue() {
XCTAssertEqual(PlayMode(rawValue: 0), .single)
XCTAssertEqual(PlayMode(rawValue: 1), .loop)
XCTAssertNil(PlayMode(rawValue: 2))
}
func testCodableRoundTrip() throws {
let data = try JSONEncoder().encode(PlayMode.loop)
XCTAssertEqual(String(data: data, encoding: .utf8), "1")
let decoded = try JSONDecoder().decode(PlayMode.self, from: Data("0".utf8))
XCTAssertEqual(decoded, .single)
}
}
final class DeviceInfoLoopTests: XCTestCase {
private func decode(_ json: String) throws -> DeviceInfo {
try JSONDecoder().decode(DeviceInfo.self, from: Data(json.utf8))
}
func testLoopMissingDecodesToNil() throws {
let json = #"{"name":"d1","size":"360","brand":"duomi","powerlevel":80,"allspace":1024,"freespace":512}"#
let info = try decode(json)
XCTAssertNil(info.loop)
}
func testLoopZeroDecodesToSingle() throws {
let json = #"{"name":"d1","size":"360","brand":"duomi","powerlevel":80,"allspace":1024,"freespace":512,"loop":0}"#
let info = try decode(json)
XCTAssertEqual(info.loop, .single)
}
func testLoopOneDecodesToLoop() throws {
let json = #"{"name":"d1","size":"360","brand":"duomi","powerlevel":80,"allspace":1024,"freespace":512,"loop":1}"#
let info = try decode(json)
XCTAssertEqual(info.loop, .loop)
}
func testLoopInvalidValueDecodesToNil() throws {
let json = #"{"name":"d1","size":"360","brand":"duomi","powerlevel":80,"allspace":1024,"freespace":512,"loop":7}"#
let info = try decode(json)
XCTAssertNil(info.loop)
}
}
final class BindPayloadEncodingTests: XCTestCase {
private static let fixedTime = "2026-05-07T16:58:00+08:00"
private func encode(_ payload: BindPayload) throws -> [String: Any] {
let data = try JSONEncoder().encode(payload)
return try JSONSerialization.jsonObject(with: data) as? [String: Any] ?? [:]
}
func testBindPayloadWithoutLoopOmitsKey() throws {
let payload = BindPayload(type: 0x0F, userId: "u1", loop: nil, time: Self.fixedTime)
let dict = try encode(payload)
XCTAssertEqual(dict["type"] as? Int, 0x0F)
XCTAssertEqual(dict["userId"] as? String, "u1")
XCTAssertEqual(dict["time"] as? String, Self.fixedTime)
XCTAssertNil(dict["loop"], "loop key MUST be omitted when nil (not encoded as null)")
XCTAssertEqual(dict.count, 3)
}
func testBindPayloadWithLoopIncludesValue() throws {
let payload = BindPayload(type: 0x0F, userId: "u1", loop: 1, time: Self.fixedTime)
let dict = try encode(payload)
XCTAssertEqual(dict["type"] as? Int, 0x0F)
XCTAssertEqual(dict["userId"] as? String, "u1")
XCTAssertEqual(dict["loop"] as? Int, 1)
XCTAssertEqual(dict["time"] as? String, Self.fixedTime)
XCTAssertEqual(dict.count, 4)
}
func testBindPayloadWithLoopZeroIncludesValue() throws {
let payload = BindPayload(type: 0x0F, userId: "u1", loop: 0, time: Self.fixedTime)
let dict = try encode(payload)
XCTAssertEqual(dict["loop"] as? Int, 0)
}
}
final class SetPlayModeGuardTests: XCTestCase {
private func makeSDK() -> DuooomiBleSDK {
DuooomiBleSDK(config: DuooomiBleConfig(apiKey: "test", brand: "test-brand", scanNamePrefix: "Duooomi-"))
}
func testSetPlayModeFailsWhenNotConnected() {
let sdk = makeSDK()
let exp = expectation(description: "completion")
sdk.setPlayMode(.loop, userId: "u1") { result in
switch result {
case .success:
XCTFail("expected failure when not connected")
case .failure(let error):
XCTAssertTrue("\(error)".lowercased().contains("connect"),
"expected notConnected-style error, got \(error)")
}
exp.fulfill()
}
wait(for: [exp], timeout: 1.0)
}
}
final class BindPayloadByteRegressionTests: XCTestCase {
private static let fixedTime = "2026-05-07T16:58:00+08:00"
/// 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, time: Self.fixedTime)
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 / time key
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["time"] as? String, Self.fixedTime)
XCTAssertNil(dict["loop"])
}
/// setPlayMode loop=1 "1"
func testSetPlayModePayloadIncludesLoopOne() throws {
let payload = BindPayload(type: 0x0F, userId: "abc", loop: 1, time: Self.fixedTime)
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, 4)
XCTAssertEqual(dict["type"] as? Int, 15)
XCTAssertEqual(dict["userId"] as? String, "abc")
XCTAssertEqual(dict["loop"] as? Int, 1)
XCTAssertEqual(dict["time"] as? String, Self.fixedTime)
}
/// setPlayMode loop=0 0 "default"
func testSetPlayModePayloadIncludesLoopZero() throws {
let payload = BindPayload(type: 0x0F, userId: "abc", loop: 0, time: Self.fixedTime)
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)
XCTAssertEqual(dict["time"] as? String, Self.fixedTime)
}
}