import Foundation import Testing import SstCore @testable import SstData struct CorrectionEngineTests { let knowledge = FakeKnowledge() func engine(_ entries: [DictionaryEntry]) -> CorrectionEngine { CorrectionEngine(entries: entries, knowledge: knowledge) } @Test func replacesAPairAndReportsIt() { let result = engine([pair("cloud code", "Claude Code")]).correct("ik gebruik cloud code vandaag") #expect(result.text == "ik gebruik Claude Code vandaag") #expect(result.corrections.count == 1) let c = result.corrections[0] #expect(c.heard == "cloud code" && c.wrote == "Claude Code" && c.rule == "cloud code") #expect((result.text as NSString).substring(with: NSRange(location: c.start, length: c.length)) == "Claude Code") } @Test func findsGluedHyphenatedAndSpacedVariants() { let e = engine([pair("cloud code", "Claude Code")]) for variant in ["CloudCode", "Cloud-Code", "CLOUD CODE", "cloud\u{2011}code", "cloud code"] { #expect(e.correct(variant).text == "Claude Code", "variant \(variant)") } } @Test func neverTouchesRealWordsThatMerelyStartTheSame() { let e = engine([pair("cloud code", "Claude Code")]) #expect(e.correct("Cloudflare is handig").text == "Cloudflare is handig") #expect(e.correct("cloud codex").text == "cloud codex") #expect(e.correct("de cloud en de code").text == "de cloud en de code") #expect(e.correct("the cloud").corrections.isEmpty) } @Test func respectsPunctuationBoundaries() { let e = engine([pair("cloud code", "Claude Code")]) #expect(e.correct("(cloud code)").text == "(Claude Code)") #expect(e.correct("cloud code.").text == "Claude Code.") #expect(e.correct("Cloud Code, Cloud Code").text == "Claude Code, Claude Code") } @Test func longestMatchWins() { let e = engine([pair("cloud code", "Claude Code"), pair("cloud code review", "Claude Code Review")]) #expect(e.correct("cloud code review nu").text == "Claude Code Review nu") #expect(e.correct("cloud-code-review").text == "Claude Code Review") } @Test func replacedTextIsNeverRescanned() { let e = engine([pair("a", "b"), pair("b", "c")]) #expect(e.correct("a b").text == "b c") } @Test func termsFixCasingAndSpacing() { let e = engine([term("Anthropic"), term("Claude Code")]) #expect(e.correct("anthropic ANTHROPIC").text == "Anthropic Anthropic") #expect(e.correct("claudecode en claude-code").text == "Claude Code en Claude Code") } @Test func termAlreadyRightIsNotReportedAsACorrection() { let result = engine([term("Anthropic")]).correct("Anthropic bouwt Claude") #expect(result.text == "Anthropic bouwt Claude") #expect(result.corrections.isEmpty) } @Test func singleWordTermThatIsARealWordGetsNoCasingRule() { let e = engine([term("Reef"), term("Slack")]) #expect(e.correct("het reef en slack").text == "het reef en slack") } @Test func firstEntryWinsOnDuplicateSources() { let e = engine([pair("cloud code", "Claude Code"), pair("Cloud-Code", "Other")]) #expect(e.correct("cloudcode").text == "Claude Code") } @Test func replacementIsLiteral() { let e = engine([pair("price", "US$1 \\0 $1")]) #expect(e.correct("the price").text == "the US$1 \\0 $1") } @Test func handlesDiacritics() { let e = engine([term("Café")]) #expect(e.correct("een CAFÉ om de hoek").text == "een Café om de hoek") } @Test func offsetsAreRelativeToTheCorrectedText() { let result = engine([pair("a", "xxx"), pair("b", "y")]).correct("a b a") #expect(result.text == "xxx y xxx") #expect(result.corrections.map(\.start) == [0, 4, 6]) #expect(result.corrections.map(\.length) == [3, 1, 3]) } @Test func emptyDictionaryAndEmptyTextAreNoOps() { #expect(engine([]).correct("hallo").text == "hallo") #expect(engine([pair("a", "b")]).correct("").text == "") } }