// // SavedJobsPersistenceTests.swift // indeed_ai_job_searchTests // import XCTest @testable import indeed_ai_job_search @MainActor final class SwiftDataSavedJobsStoreTests: XCTestCase { private var controller: PersistenceController! private var store: SwiftDataSavedJobsStore! override func setUp() { super.setUp() controller = PersistenceController.makeInMemory() store = SwiftDataSavedJobsStore(controller: controller) } override func tearDown() { store = nil controller = nil super.tearDown() } func testSaveUnsaveSurvivesReload() { let listing = makeListing(key: "abc123def4567890", title: "iOS Developer") store.toggleSave(listing) XCTAssertTrue(store.isSaved(listing)) XCTAssertEqual(store.savedJobs.count, 1) let reloaded = SwiftDataSavedJobsStore(controller: controller) XCTAssertTrue(reloaded.isSaved(listing)) XCTAssertEqual(reloaded.savedJobs.first?.listing.title, "iOS Developer") reloaded.toggleSave(listing) XCTAssertFalse(reloaded.isSaved(listing)) XCTAssertTrue(reloaded.savedJobs.isEmpty) } func testDedupeOnSaveKeepsSingleRow() { let first = makeListing(key: "samekey000000001", title: "Old Title") var second = makeListing(key: "samekey000000001", title: "New Title") second.company = "Acme" store.toggleSave(first) let savedAt = store.savedJobs.first?.savedAt store.save(second) XCTAssertEqual(store.savedJobs.count, 1) XCTAssertEqual(store.savedJobs.first?.listing.title, "New Title") XCTAssertEqual(store.savedJobs.first?.listing.company, "Acme") XCTAssertEqual(store.savedJobs.first?.savedAt, savedAt) } func testUpdateListingPersistsAvailability() { var listing = makeListing(key: "verifykey0000001", title: "Swift Engineer") store.toggleSave(listing) listing.availability = .unavailable let verifiedAt = Date(timeIntervalSince1970: 1_700_000_000) store.updateListing(listing, lastVerifiedAt: verifiedAt) XCTAssertEqual(store.savedJobs.first?.availability, .unavailable) XCTAssertEqual(store.savedJobs.first?.lastVerifiedAt, verifiedAt) XCTAssertFalse(store.savedJobs.isEmpty) } func testClearAllRemovesPersistedJobs() { store.toggleSave(makeListing(key: "clear00000000001", title: "A")) store.toggleSave(makeListing(key: "clear00000000002", title: "B")) XCTAssertEqual(store.savedJobs.count, 2) store.clearAll() XCTAssertTrue(store.savedJobs.isEmpty) let reloaded = SwiftDataSavedJobsStore(controller: controller) XCTAssertTrue(reloaded.savedJobs.isEmpty) } func testExportJSONContainsKeys() throws { store.toggleSave(makeListing(key: "exportkey0000001", title: "Export Me")) let data = try store.exportJSON() let text = String(data: data, encoding: .utf8) ?? "" XCTAssertTrue(text.contains("exportkey0000001")) XCTAssertTrue(text.contains("Export Me")) } } @MainActor final class SavedJobsRevalidatorTests: XCTestCase { func testRevalidatesOnlyStaleJobs() async { let store = MockSavedJobsStore() var fresh = makeListing(key: "fresh00000000001", title: "Fresh") fresh.availability = .open var stale = makeListing(key: "stale00000000001", title: "Stale") stale.availability = .open store.toggleSave(fresh) store.toggleSave(stale) store.updateListing(fresh, lastVerifiedAt: Date()) store.updateListing(stale, lastVerifiedAt: Date().addingTimeInterval(-86_400 * 2)) let validation = StubValidationService(result: .unavailable) let revalidator = SavedJobsRevalidator( store: store, validationService: validation, staleInterval: 86_400 ) let checked = await revalidator.runPass() XCTAssertEqual(checked, 1) XCTAssertEqual( store.savedJobs.first(where: { $0.listing.indeedJobKey == "stale00000000001" })?.availability, .unavailable ) XCTAssertEqual( store.savedJobs.first(where: { $0.listing.indeedJobKey == "fresh00000000001" })?.availability, .open ) } func testNeverVerifiedJobsAreChecked() async { let store = MockSavedJobsStore() store.toggleSave(makeListing(key: "never00000000001", title: "Never")) let validation = StubValidationService(result: .unknown) let revalidator = SavedJobsRevalidator( store: store, validationService: validation, staleInterval: 86_400 ) let checked = await revalidator.runPass() XCTAssertEqual(checked, 1) XCTAssertNotNil(store.savedJobs.first?.lastVerifiedAt) XCTAssertEqual(store.savedJobs.first?.availability, .unknown) } } private struct StubValidationService: JobValidationService { let result: JobAvailability func validate(listing: JobListing) async -> JobAvailability { result } } private func makeListing( key: String, title: String, company: String = "Test Co", postedAt: Date = Date() ) -> JobListing { JobListing( indeedJobKey: key, title: title, company: company, location: "Remote", remoteType: .remote, postedAt: postedAt, snippet: "Test snippet", indeedURL: URL(string: "https://www.indeed.com/viewjob?jk=\(key)")! ) }