ios-projects/AIStudyApp/AIStudyAppTests/LocalQueueTests.swift
wangdl 3abe17a84e test: add queue UUID path coverage in LocalQueueTests
- Add test_markRetry_works_with_queue_item_id (uses queue UUID not eventId)
- Update test_markDead_after_max_retries to use queue item id from batch
- Fixes review finding: tests previously only used eventId, not queue UUID

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-15 21:58:04 +08:00

115 lines
3.8 KiB
Swift

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_markRetry_works_with_queue_item_id() {
let item = makeItem(eventId: "evt-lq-id-001")
queue.enqueue([item], userId: "user-test")
// Use queue item id (UUID) instead of eventId
guard let queueId = queue.fetchPendingBatch(limit: 10, userId: "user-test").first?.id else {
XCTFail("Expected at least one pending item")
return
}
queue.markRetry(ids: [queueId])
XCTAssertTrue(true, "markRetry should accept queue item UUID")
}
func test_markDead_after_max_retries() {
let item = makeItem(eventId: "evt-lq-004")
queue.enqueue([item], userId: "user-test")
guard let queueId = queue.fetchPendingBatch(limit: 10, userId: "user-test").first?.id else {
XCTFail("Expected pending item")
return
}
// Simulate multiple retries using queue item UUID
for _ in 0..<5 {
queue.markRetry(ids: [queueId])
}
// 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
)
}
}