import Foundation import Testing import SstCore @testable import SstData @MainActor struct DictionaryStoreTests { func tempFile() -> URL { FileManager.default.temporaryDirectory.appending(path: "sst-tests-\(UUID().uuidString)/dictionary.txt") } @Test func loadCreatesAFileWithAHeaderWhenMissing() throws { let url = tempFile() let store = DictionaryStore(fileURL: url) try store.load() #expect(FileManager.default.fileExists(atPath: url.path)) #expect(store.entries.isEmpty) #expect(try String(contentsOf: url, encoding: .utf8).hasPrefix("# Sst woordenboek")) } @Test func applyWritesAtomicallyAndKeepsComments() throws { let url = tempFile() let store = DictionaryStore(fileURL: url) try store.load() try store.apply { $0.append(.correction(heard: "cloud code", wrote: "Claude Code")) } let text = try String(contentsOf: url, encoding: .utf8) #expect(text.hasPrefix("# Sst woordenboek")) #expect(text.hasSuffix("cloud code => Claude Code\n")) #expect(store.entries == [pair("cloud code", "Claude Code")]) } @Test func externalEditIsPickedUp() throws { let url = tempFile() let store = DictionaryStore(fileURL: url) try store.load() var changes = 0 store.onChange = { changes += 1 } try "Vercel\n".write(to: url, atomically: true, encoding: .utf8) store.reloadIfChanged() #expect(store.entries == [term("Vercel")]) #expect(changes == 1) store.reloadIfChanged() // unchanged text: no second notification #expect(changes == 1) } @Test func externalChangeFiresBothCallbacksAndKeepsIDs() throws { let url = tempFile() let store = DictionaryStore(fileURL: url) try store.load() try store.apply { $0.append(.term("Vercel")) } let id = store.entries[0].id var changes = 0, external = 0 store.onChange = { changes += 1 } store.onExternalChange = { external += 1 } try "Nieuw\nVercel\n".write(to: url, atomically: true, encoding: .utf8) store.reloadIfChanged() #expect(changes == 1 && external == 1) #expect(store.entries[1].id == id) try store.apply { $0.append(.term("Eigen")) } #expect(changes == 2 && external == 1) } @Test func ourOwnWriteDoesNotTriggerAReload() throws { let store = DictionaryStore(fileURL: tempFile()) try store.load() try store.apply { $0.append(.term("Supabase")) } var changes = 0 store.onChange = { changes += 1 } store.reloadIfChanged() #expect(changes == 0) } @Test func watcherNoticesAnEditorSavingTheFile() async throws { let url = tempFile() let store = DictionaryStore(fileURL: url) try store.load() var changed = false store.onChange = { changed = true } store.startWatching() try "Anthropic\n".write(to: url, atomically: true, encoding: .utf8) for _ in 0..<40 where !changed { try await Task.sleep(for: .milliseconds(100)) } store.stopWatching() #expect(changed) #expect(store.entries == [term("Anthropic")]) } } struct DictionaryCorrectorTests { @Test func correctsWhenEnabledAndPassesThroughWhenDisabled() { let corrector = DictionaryCorrector(knowledge: FakeKnowledge()) corrector.update(entries: [pair("cloud code", "Claude Code")]) #expect(corrector.correct("cloud code").text == "Claude Code") corrector.setEnabled(false) #expect(corrector.correct("cloud code").text == "cloud code") corrector.setEnabled(true) corrector.update(entries: []) #expect(corrector.correct("cloud code").text == "cloud code") } }