137 lines
5.0 KiB
Swift
137 lines
5.0 KiB
Swift
// ResumeCursorTests.swift
|
|
// Unit tests for ResumeCursor — persistence of IC-1 sequence numbers.
|
|
//
|
|
// Uses a named UserDefaults suite per test to stay completely isolated from
|
|
// the real app defaults and from other test cases.
|
|
|
|
import XCTest
|
|
@testable import piRemote
|
|
|
|
final class ResumeCursorTests: XCTestCase {
|
|
|
|
// Each test creates its own defaults suite; tearDown removes it.
|
|
private var suiteName: String!
|
|
private var defaults: UserDefaults!
|
|
private var cursor: ResumeCursor!
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
suiteName = "ResumeCursorTests.\(UUID().uuidString)"
|
|
defaults = UserDefaults(suiteName: suiteName)!
|
|
cursor = ResumeCursor(defaults: defaults)
|
|
}
|
|
|
|
override func tearDown() {
|
|
defaults.removePersistentDomain(forName: suiteName)
|
|
super.tearDown()
|
|
}
|
|
|
|
// =========================================================================
|
|
// MARK: 1. Initial state
|
|
// =========================================================================
|
|
|
|
func testLastSeq_freshSession_returnsNil() {
|
|
XCTAssertNil(cursor.lastSeq(for: "session-A"),
|
|
"No stored cursor → must return nil")
|
|
}
|
|
|
|
// =========================================================================
|
|
// MARK: 2. Save + load
|
|
// =========================================================================
|
|
|
|
func testUpdateAndLoad_returnsStoredValue() {
|
|
cursor.update(sessionId: "s1", seq: 42)
|
|
XCTAssertEqual(cursor.lastSeq(for: "s1"), 42)
|
|
}
|
|
|
|
func testUpdate_overwrites_previousValue() {
|
|
cursor.update(sessionId: "s1", seq: 1)
|
|
cursor.update(sessionId: "s1", seq: 999)
|
|
XCTAssertEqual(cursor.lastSeq(for: "s1"), 999,
|
|
"Second update must overwrite the first")
|
|
}
|
|
|
|
func testUpdate_zeroSeq_isStoredAndDistinctFromMissing() {
|
|
// Before update: nil
|
|
XCTAssertNil(cursor.lastSeq(for: "s-zero"))
|
|
// After update with 0: 0 (not nil)
|
|
cursor.update(sessionId: "s-zero", seq: 0)
|
|
XCTAssertEqual(cursor.lastSeq(for: "s-zero"), 0)
|
|
}
|
|
|
|
// =========================================================================
|
|
// MARK: 3. UInt64 boundary values
|
|
// =========================================================================
|
|
|
|
func testUpdate_maxUInt64_roundTrips() {
|
|
cursor.update(sessionId: "s-max", seq: UInt64.max)
|
|
XCTAssertEqual(cursor.lastSeq(for: "s-max"), UInt64.max,
|
|
"UInt64.max must survive the Int64-bit-pattern round-trip")
|
|
}
|
|
|
|
func testUpdate_largeValue_roundTrips() {
|
|
let large: UInt64 = 1_000_000_000_000
|
|
cursor.update(sessionId: "s-large", seq: large)
|
|
XCTAssertEqual(cursor.lastSeq(for: "s-large"), large)
|
|
}
|
|
|
|
// =========================================================================
|
|
// MARK: 4. Clear
|
|
// =========================================================================
|
|
|
|
func testClear_removesEntry() {
|
|
cursor.update(sessionId: "s1", seq: 100)
|
|
cursor.clear(sessionId: "s1")
|
|
XCTAssertNil(cursor.lastSeq(for: "s1"),
|
|
"After clear, lastSeq must return nil")
|
|
}
|
|
|
|
func testClear_missingEntry_isNoOp() {
|
|
// Clearing a key that was never written must not throw or crash.
|
|
cursor.clear(sessionId: "never-written")
|
|
XCTAssertNil(cursor.lastSeq(for: "never-written"))
|
|
}
|
|
|
|
// =========================================================================
|
|
// MARK: 5. Session isolation
|
|
// =========================================================================
|
|
|
|
func testIndependentSessions_doNotInterfere() {
|
|
cursor.update(sessionId: "alpha", seq: 10)
|
|
cursor.update(sessionId: "beta", seq: 20)
|
|
|
|
XCTAssertEqual(cursor.lastSeq(for: "alpha"), 10)
|
|
XCTAssertEqual(cursor.lastSeq(for: "beta"), 20)
|
|
}
|
|
|
|
func testClearOneSession_doesNotAffectOther() {
|
|
cursor.update(sessionId: "alpha", seq: 10)
|
|
cursor.update(sessionId: "beta", seq: 20)
|
|
|
|
cursor.clear(sessionId: "alpha")
|
|
|
|
XCTAssertNil(cursor.lastSeq(for: "alpha"),
|
|
"alpha should be nil after clear")
|
|
XCTAssertEqual(cursor.lastSeq(for: "beta"), 20,
|
|
"beta must be unaffected by clearing alpha")
|
|
}
|
|
|
|
func testThreeIndependentSessions() {
|
|
let ids: [String] = ["s1", "s2", "s3"]
|
|
let seqs: [UInt64] = [1, 2, 3]
|
|
zip(ids, seqs).forEach { cursor.update(sessionId: $0, seq: $1) }
|
|
zip(ids, seqs).forEach {
|
|
XCTAssertEqual(cursor.lastSeq(for: $0), $1)
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// MARK: 6. Missing entry (never stored)
|
|
// =========================================================================
|
|
|
|
func testMissingEntry_returnsNil() {
|
|
// Confirm nil for a key that was never written.
|
|
XCTAssertNil(cursor.lastSeq(for: "completely-absent"))
|
|
}
|
|
}
|