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