package langscreen import ( "fmt" "strconv" "strings" "testing" "unicode" ) func cyr() []*unicode.RangeTable { return []*unicode.RangeTable{unicode.Cyrillic} } // pad repeats s until it clears the evidence floor, so a case can be about the SHARE without also being // about the length. func pad(s string, letters int) string { var b strings.Builder for n := 0; n < letters; n += len([]rune(s)) { b.WriteString(s) } return b.String() } func TestTheScreenAnswersTheClosedSet(t *testing.T) { for _, tc := range []struct { name string text string want Verdict }{ {"target prose", pad("текстанаязыке", 400), OnTarget}, {"a foreign completion", pad("englishtextinstead", 400), OffTarget}, {"the source echoed back", pad("源文本汉字", 400), OffTarget}, {"too short to judge", "коротко", Abstain}, } { t.Run(tc.name, func(t *testing.T) { if got := Screen(tc.text, cyr()).Verdict; got != tc.want { t.Fatalf("verdict %q, want %q", got, tc.want) } }) } } // TestATargetWithNoDataIsNeverJudged is the layer-7 discipline and the review question of the whole // project in one assertion: a pair the repository does not carry must be INERT, not condemned. The screen // abstains on an empty script set however foreign the text looks, so adding a pair is a data row and never // an edit here. func TestATargetWithNoDataIsNeverJudged(t *testing.T) { r := Screen(pad("englishtextinstead", 400), nil) if r.Verdict != Abstain { t.Fatalf("a target that declares no script must not be judged: %+v", r) } if r.Share != 0 { t.Fatalf("an abstention must carry no score to be misread as one: %+v", r) } } // TestTheEvidenceFloorIsAFloorAndNotAVerdict pins the abstain boundary on both sides. Below it the screen // says nothing — never «off target», which on a chapter heading would be a flag on a healthy unit — and one // letter above it the same text is judged normally. func TestTheEvidenceFloorIsAFloorAndNotAVerdict(t *testing.T) { foreign := strings.Repeat("a", MinLetters-1) if got := Screen(foreign, cyr()); got.Verdict != Abstain || got.Letters != MinLetters-1 { t.Fatalf("under the floor the screen must abstain and say how little it saw: %+v", got) } if got := Screen(strings.Repeat("a", MinLetters), cyr()); got.Verdict != OffTarget { t.Fatalf("at the floor the screen must judge: %+v", got) } // Non-letters carry no evidence and must not lift a text over the floor: a table of numbers is not // 200 letters of anything. padded := strings.Repeat("a", MinLetters-1) + strings.Repeat("1234567890 ", 100) if got := Screen(padded, cyr()); got.Verdict != Abstain { t.Fatalf("digits and spacing must not count as evidence: %+v", got) } } // TestTheThresholdSitsInTheMeasuredGap holds the constant against the corpus it was placed from (backlog // row 113): every off-target completion on the stand scores 0.0000 and the worst healthy one 0.9767. Both // sides are asserted, because a threshold is only a threshold if BOTH are on the right side of it. func TestTheThresholdSitsInTheMeasuredGap(t *testing.T) { const worstHealthyMeasured, bestOffTargetMeasured = 0.9767, 0.0000 if OnTargetFloor >= worstHealthyMeasured { t.Errorf("the floor %.4f is at or above the worst HEALTHY completion measured (%.4f): it would flag clean prose", OnTargetFloor, worstHealthyMeasured) } if OnTargetFloor <= bestOffTargetMeasured { t.Errorf("the floor %.4f is at or below the best OFF-TARGET completion measured (%.4f): it would catch nothing", OnTargetFloor, bestOffTargetMeasured) } } // TestTheScoreIsOverLettersOnly is the measurement rule made explicit. Sharing punctuation, digits and // whitespace across languages is exactly why they are excluded: counted, a heavily formatted reply and a // dense one would be judged on different scales. func TestTheScoreIsOverLettersOnly(t *testing.T) { letters := pad("текст", 400) plain := Screen(letters, cyr()) noisy := Screen(strings.Join(strings.Split(letters, ""), " — 42, "), cyr()) if plain.Share != noisy.Share || plain.Letters != noisy.Letters { t.Fatalf("formatting changed the score: %+v vs %+v", plain, noisy) } } // TestTheSupersetLimitIsRealAndNamed asserts the limitation the package doccomment declares, because a // limit that is only written down is a limit somebody will later be surprised by. A target whose declared // scripts CONTAIN another language's — ja declares han — cannot tell that language's output apart here. func TestTheSupersetLimitIsRealAndNamed(t *testing.T) { ja := []*unicode.RangeTable{unicode.Han, unicode.Hiragana, unicode.Katakana} if got := Screen(pad("源文本汉字", 400), ja).Verdict; got != OnTarget { t.Fatalf("the doccomment says a purely Chinese output scores 1.000 for a ja target; it now says %q — if the rule gained a datum, say so there", got) } } // TestTheVersionActuallyTracksTheCalibration closes three plantings that the interval pins could not see, // and it is the difference between a mechanism and a claim about one. // // ⚠ Version's own doc comment says it is derived «by construction» so that a slid threshold takes a loud // --resnapshot with it. Nothing checked that. Planted: the var rewritten as the LITERAL it currently // prints, then the floor slid 0.50 → 0.45 — the string stayed byte-identical, the golden stayed // byte-identical, both interval pins passed (0.45 is inside the empty band), and the whole pipeline package // was green. A resumed chunk would be re-verdicted under a threshold nobody re-billed for. The property was // documented, believed, and held by nothing. func TestTheVersionActuallyTracksTheCalibration(t *testing.T) { want := fmt.Sprintf("langscreen-v1-target-script-share+floor%.2f+min%d", OnTargetFloor, MinLetters) if Version != want { t.Fatalf("Version has stopped tracking the constants it names:\n is: %s\n want: %s\n"+ "A literal here means a calibration change moves no snapshot, and a resumed chunk is re-judged "+ "by a rule nobody paid to change.", Version, want) } // ⚠ AND THE PRECISION WINDOW, which is the same hole one decimal down. `%.2f` cannot express a floor of // 0.504: planted, that slide rendered the identical `floor0.50` and survived everything above. The fix // is NOT to widen the format — that would change the shipped string and cost a --resnapshot of every // book for a rule that did not change. It is to refuse a floor the version cannot express. A future // calibration to three decimals is then a deliberate act: widen the format AND take the re-snapshot. // The test is a ROUND TRIP, not a string comparison: what matters is whether the printed floor still // denotes the same number, not whether it is spelled the same way (`%.2f` of 0.5 is "0.50", and that is // fine — it reads back as 0.5). A floor of 0.504 also prints "0.50" and reads back as 0.5, which is not. printed := fmt.Sprintf("%.2f", OnTargetFloor) back, err := strconv.ParseFloat(printed, 64) if err != nil { t.Fatalf("the version's floor field %q does not parse: %v", printed, err) } if back != OnTargetFloor { t.Fatalf("OnTargetFloor is %v but the version can only say %s, which reads back as %v — a change "+ "inside the last decimal would move no snapshot at all. Either keep the floor at two decimals, "+ "or widen Version's format and take the --resnapshot deliberately.", OnTargetFloor, printed, back) } } // TestTheFloorIsAFloorAndNotACeiling pins the BOUNDARY comparison, which no constant expresses and which // therefore moves nothing when it changes: planted, `share < OnTargetFloor` → `share <= OnTargetFloor` // survived the whole pipeline package, because no fixture sat exactly on the line. The rule is «below the // floor is off target», so a completion sitting EXACTLY on it is on target. func TestTheFloorIsAFloorAndNotACeiling(t *testing.T) { // Exactly 200 letters — the evidence floor — of which exactly 100 are Cyrillic: share is 0.50 exactly, // with no floating-point slack (100/200 is representable). text := strings.Repeat("а", 100) + strings.Repeat("z", 100) got := Screen(text, cyr()) if got.Letters != 200 { t.Fatalf("premise broken: the fixture must weigh exactly 200 letters, got %d", got.Letters) } if got.Share != OnTargetFloor { t.Fatalf("premise broken: the fixture must sit exactly ON the floor, got %v", got.Share) } if got.Verdict != OnTarget { t.Fatalf("a share exactly AT the floor must be on-target — the rule is «below the floor», and a "+ "comparison that quietly became «at or below» moves neither a constant nor the version: %+v", got) } // One letter less on target and it falls: the assertion above must not be passing because the screen // says OnTarget to everything. below := strings.Repeat("а", 99) + strings.Repeat("z", 101) if b := Screen(below, cyr()); b.Verdict != OffTarget { t.Fatalf("one letter below the floor must be off-target, got %+v", b) } }