pi-remote-ios/Tests/CoreTests/ResumeCursorTests.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"))
}
}