// 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")) } }