diff --git a/AIStudyApp/AIStudyAppTests/MarkedReadStoreTests.swift b/AIStudyApp/AIStudyAppTests/MarkedReadStoreTests.swift new file mode 100644 index 0000000..ab23bfc --- /dev/null +++ b/AIStudyApp/AIStudyAppTests/MarkedReadStoreTests.swift @@ -0,0 +1,104 @@ +import XCTest +@testable import AIStudyApp + +final class MarkedReadStoreTests: XCTestCase { + + private let store = MarkedReadStore.shared + private let defaults = UserDefaults.standard + private let key = "zx_marked_read_material_ids" + + override func setUp() { + super.setUp() + // Clean up persisted state before each test + defaults.removeObject(forKey: key) + } + + override func tearDown() { + defaults.removeObject(forKey: key) + super.tearDown() + } + + // MARK: - contains + + func test_contains_returnsFalse_for_unmarkedId() { + XCTAssertFalse(store.contains("mat-unknown")) + } + + func test_contains_returnsTrue_after_mark() { + store.mark("mat-001") + XCTAssertTrue(store.contains("mat-001")) + } + + func test_contains_returnsFalse_after_unmark() { + store.mark("mat-001") + store.unmark("mat-001") + XCTAssertFalse(store.contains("mat-001")) + } + + // MARK: - mark + + func test_mark_persists_to_userDefaults() { + store.mark("mat-persist") + let arr = defaults.stringArray(forKey: key) ?? [] + XCTAssertTrue(arr.contains("mat-persist")) + } + + func test_mark_is_idempotent() { + store.mark("mat-001") + store.mark("mat-001") + store.mark("mat-001") + let arr = defaults.stringArray(forKey: key) ?? [] + // Set semantics: no duplicates + XCTAssertEqual(arr.count, 1) + } + + func test_mark_multiple_ids() { + store.mark("mat-a") + store.mark("mat-b") + store.mark("mat-c") + let arr = defaults.stringArray(forKey: key) ?? [] + XCTAssertEqual(arr.count, 3) + XCTAssertTrue(arr.contains("mat-a")) + XCTAssertTrue(arr.contains("mat-b")) + XCTAssertTrue(arr.contains("mat-c")) + } + + // MARK: - unmark + + func test_unmark_removes_from_persisted_array() { + store.mark("mat-a") + store.mark("mat-b") + store.unmark("mat-a") + let arr = defaults.stringArray(forKey: key) ?? [] + XCTAssertFalse(arr.contains("mat-a")) + XCTAssertTrue(arr.contains("mat-b")) + } + + func test_unmark_nonexistent_is_safe() { + store.unmark("mat-nope") + // Should not crash or corrupt state + let arr = defaults.stringArray(forKey: key) ?? [] + XCTAssertEqual(arr.count, 0) + } + + // MARK: - Persistence across instances + + func test_state_survives_reinit() { + store.mark("mat-survive") + + // Simulate app restart by creating a new instance (singleton in real app, + // but we can verify the persisted data is loadable) + let newStore = MarkedReadStore.shared + // Singleton — same instance. But the key test is: data survived UserDefaults write. + XCTAssertTrue(newStore.contains("mat-survive")) + } + + // MARK: - Empty string edge cases + + func test_empty_string_as_id() { + store.mark("") + XCTAssertTrue(store.contains("")) + store.unmark("") + XCTAssertFalse(store.contains("")) + } +}