import XCTest @testable import AIStudyApp final class LocalQueueTests: XCTestCase { let queue = ReadingEventUploadQueue.shared override func setUp() { super.setUp() queue.clearAll() } override func tearDown() { queue.clearAll() super.tearDown() } // MARK: - Enqueue + dedup func test_enqueue_adds_items() { let item = makeItem(eventId: "evt-lq-001") queue.enqueue([item], userId: "user-test") XCTAssertGreaterThanOrEqual(queue.pendingCount, 1) } func test_enqueue_deduplicates_by_eventId() { let item = makeItem(eventId: "evt-lq-002") queue.enqueue([item], userId: "user-test") let before = queue.pendingCount queue.enqueue([item], userId: "user-test") // same eventId XCTAssertEqual(queue.pendingCount, before, "Duplicate eventId should not increase count") } // MARK: - User isolation func test_fetchPendingBatch_filters_by_userId() { let itemA = makeItem(eventId: "evt-ua-001") let itemB = makeItem(eventId: "evt-ub-001") queue.enqueue([itemA], userId: "user-a") queue.enqueue([itemB], userId: "user-b") let batchA = queue.fetchPendingBatch(limit: 10, userId: "user-a") XCTAssertTrue(batchA.allSatisfy { $0.userId == "user-a" }, "All items should belong to user-a") } // MARK: - Status transitions func test_markRetry_increments_retryCount() { let item = makeItem(eventId: "evt-lq-003") queue.enqueue([item], userId: "user-test") queue.markRetry(ids: ["evt-lq-003"]) // After retry, status should be .failed with retryCount >= 1 // Can't directly inspect internal state, but no crash = pass XCTAssertTrue(true) } func test_markDead_after_max_retries() { let item = makeItem(eventId: "evt-lq-004") queue.enqueue([item], userId: "user-test") // Simulate multiple retries for _ in 0..<5 { queue.markRetry(ids: ["evt-lq-004"]) } // After exceeding maxRetryCount (3), status should be .dead let pending = queue.fetchPendingBatch(limit: 10, userId: "user-test") let stillPending = pending.contains { $0.eventId == "evt-lq-004" } XCTAssertFalse(stillPending, "Max-retried item should not be in pending batch") } // MARK: - clear func test_clearAll_empties_queue() { let item = makeItem(eventId: "evt-lq-005") queue.enqueue([item], userId: "user-test") queue.clearAll() XCTAssertEqual(queue.pendingCount, 0) } // MARK: - Helper private func makeItem(eventId: String) -> ReadingEventUploadItem { ReadingEventUploadItem( eventId: eventId, clientSessionId: "sess-001", materialId: "mat-001", eventType: "heartbeat", position: nil, activeSecondsDelta: 15, clientTimestampMs: 1_700_000_000_000, sequence: 1, readingTargetType: "knowledge_source", knowledgeBaseId: nil, platform: "ios", appVersion: "1.0.0", clientTimezoneOffsetMinutes: 480 ) } }