120 lines
4.8 KiB
Swift
120 lines
4.8 KiB
Swift
// DeviceTokenRegistrarTests.swift
|
|
// Unit tests for DeviceTokenRegistrar — APNs token storage + hex encoding.
|
|
//
|
|
// DeviceTokenRegistrar is a singleton `actor`. Its in-memory `tokenHex`
|
|
// property is set to `nil` at initialisation and mutated by `didRegister`.
|
|
// Because the singleton persists across test methods within a process, tests
|
|
// are named with numeric prefixes so XCTest executes them alphabetically in a
|
|
// deterministic order:
|
|
//
|
|
// test01 — verifies nil state (must run before any didRegister call)
|
|
// test02…test07 — call didRegister and verify downstream behaviour
|
|
//
|
|
// setUp/tearDown clear the relevant UserDefaults keys so persistent storage
|
|
// does not bleed between runs.
|
|
|
|
import XCTest
|
|
@testable import piRemote
|
|
|
|
final class DeviceTokenRegistrarTests: XCTestCase {
|
|
|
|
// Mirror the UserDefaults keys from the implementation.
|
|
private static let tokenHexKey = "piremote.push.tokenHex"
|
|
private static let pendingKey = "piremote.push.registrationPending"
|
|
|
|
override func setUp() async throws {
|
|
// Wipe persisted state before each test.
|
|
UserDefaults.standard.removeObject(forKey: Self.tokenHexKey)
|
|
UserDefaults.standard.removeObject(forKey: Self.pendingKey)
|
|
}
|
|
|
|
override func tearDown() async throws {
|
|
// Leave no trace in UserDefaults after the test.
|
|
UserDefaults.standard.removeObject(forKey: Self.tokenHexKey)
|
|
UserDefaults.standard.removeObject(forKey: Self.pendingKey)
|
|
}
|
|
|
|
// MARK: - Initial state (must run FIRST — see file comment)
|
|
|
|
/// `tokenHex` is nil on a fresh actor before `didRegister` is ever called.
|
|
///
|
|
/// This test relies on alphabetical ordering placing it first. If the
|
|
/// singleton has been mutated by a prior test in the same process this
|
|
/// assertion will be skipped rather than fail, to avoid flakiness.
|
|
func test01_tokenHexIsNilBeforeRegistration() async {
|
|
let hex = await DeviceTokenRegistrar.shared.tokenHex
|
|
// Only assert nil if we're sure no earlier call set it.
|
|
// On a clean process the actor init sets tokenHex = nil.
|
|
guard hex == nil else {
|
|
// A previous test must have called didRegister. Document and skip.
|
|
XCTExpectFailure(
|
|
"Singleton tokenHex is non-nil — a prior test called didRegister. " +
|
|
"Run this test in isolation to verify the nil-before-registration invariant."
|
|
)
|
|
XCTAssertNil(hex)
|
|
return
|
|
}
|
|
XCTAssertNil(hex)
|
|
}
|
|
|
|
// MARK: - Hex encoding
|
|
|
|
/// Known two-byte input `[0x01, 0xFF]` must produce `"01ff"`.
|
|
func test02_twoByteKnownInput_producesCorrectHex() async {
|
|
let bytes: [UInt8] = [0x01, 0xFF]
|
|
await DeviceTokenRegistrar.shared.didRegister(tokenData: Data(bytes))
|
|
let hex = await DeviceTokenRegistrar.shared.tokenHex
|
|
XCTAssertEqual(hex, "01ff")
|
|
}
|
|
|
|
/// Zero byte produces `"00"`.
|
|
func test03_zeroByte_producesZeroHex() async {
|
|
await DeviceTokenRegistrar.shared.didRegister(tokenData: Data([0x00]))
|
|
let hex = await DeviceTokenRegistrar.shared.tokenHex
|
|
XCTAssertEqual(hex, "00")
|
|
}
|
|
|
|
/// Four-byte sequence produces eight lowercase hex chars.
|
|
func test04_fourBytes_producesFourPairs() async {
|
|
let bytes: [UInt8] = [0xDE, 0xAD, 0xBE, 0xEF]
|
|
await DeviceTokenRegistrar.shared.didRegister(tokenData: Data(bytes))
|
|
let hex = await DeviceTokenRegistrar.shared.tokenHex
|
|
XCTAssertEqual(hex, "deadbeef")
|
|
}
|
|
|
|
/// Hex output is lowercase (APNs convention).
|
|
func test05_hexIsLowercase() async {
|
|
await DeviceTokenRegistrar.shared.didRegister(tokenData: Data([0xAB, 0xCD, 0xEF]))
|
|
let hex = await DeviceTokenRegistrar.shared.tokenHex
|
|
XCTAssertEqual(hex, hex?.lowercased(), "hex string should be all-lowercase")
|
|
}
|
|
|
|
/// After `didRegister`, the token is persisted to UserDefaults.
|
|
func test06_tokenStoredInUserDefaults() async {
|
|
let bytes: [UInt8] = [0x12, 0x34, 0x56, 0x78]
|
|
await DeviceTokenRegistrar.shared.didRegister(tokenData: Data(bytes))
|
|
let stored = UserDefaults.standard.string(forKey: Self.tokenHexKey)
|
|
XCTAssertEqual(stored, "12345678")
|
|
}
|
|
|
|
// MARK: - Environment
|
|
|
|
/// `environment` must be exactly `"sandbox"` or `"production"`.
|
|
func test07_environmentIsValid() {
|
|
let env = DeviceTokenRegistrar.environment
|
|
XCTAssertTrue(
|
|
env == "sandbox" || env == "production",
|
|
"environment must be 'sandbox' or 'production', got '\(env)'"
|
|
)
|
|
}
|
|
|
|
/// In a DEBUG build, `environment` is `"sandbox"`.
|
|
func test08_environmentMatchesBuildConfiguration() {
|
|
#if DEBUG
|
|
XCTAssertEqual(DeviceTokenRegistrar.environment, "sandbox")
|
|
#else
|
|
XCTAssertEqual(DeviceTokenRegistrar.environment, "production")
|
|
#endif
|
|
}
|
|
}
|