textmachine/backend/internal/terminology/script_test.go

105 lines
5.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package terminology
import (
"strings"
"testing"
"unicode"
)
// offLanguageReplies are renderings a live model actually returned when it lost the target language, and
// signedRenderings are renderings from a real signed bank plus the shapes most likely to be false
// positives (two letters, digits only, quoted, doubled space). Both lists are the acceptance material for
// the screen: every line of the first must be refused, every line of the second must pass.
var (
offLanguageReplies = []string{"cultivation", "Primordial Sea", "Rank 1", "Grade C", "talent",
"Wine Bug", "realm", "Qingmao Mountain", "Gu Yue", "is"}
signedRenderings = []string{"культивация", "море истинной ци", "гу-мастер", "Фан Юань", "гу",
"талант", "винный червь", "старейшина", "Гуюэ", "апертура", "третья гу", "гу Силы",
"Река времени", "9", "«Ода сливе»", "Фан Юань"}
)
func TestOffLanguageSeparatesForeignRenderingsFromTargetOnes(t *testing.T) {
cyr := unicode.Scripts["Cyrillic"]
for _, s := range offLanguageReplies {
if !OffLanguage(s, cyr) {
t.Errorf("%q must be refused: it carries letters and none of them are in the target script", s)
}
}
for _, s := range signedRenderings {
if OffLanguage(s, cyr) {
t.Errorf("%q is a legitimate rendering and must pass", s)
}
}
// A mixed rendering passes deliberately: one target-script letter is enough, so the screen never
// touches a rendering that carries a foreign name beside a target word.
if OffLanguage("Фан Юань (Fang Yuan)", cyr) {
t.Error("a mixed rendering must pass — the screen judges language, not spelling")
}
if OffLanguage("anything", nil) {
t.Error("with no declared script the screen must be inert")
}
}
// TestOffLanguageIsTargetBlind is the generality pin: nothing about a language lives in this package, so
// swapping the declared script swaps every verdict and no Go edit is involved.
func TestOffLanguageIsTargetBlind(t *testing.T) {
lat := unicode.Scripts["Latin"]
if !OffLanguage("культивация", lat) || OffLanguage("cultivation", lat) {
t.Fatal("under a Latin target the verdicts must be exactly reversed")
}
// A pair with neither script: Han as a target refuses both of the above and accepts a Han rendering.
han := unicode.Scripts["Han"]
if !OffLanguage("культивация", han) || !OffLanguage("cultivation", han) || OffLanguage("元海", han) {
t.Fatal("a third target must work on the same code path")
}
if _, ok := ScriptByName("Cyrillic"); !ok {
t.Fatal("a valid script name must resolve")
}
if _, ok := ScriptByName("cyrillic"); ok {
t.Fatal("resolution must be exact, so a typo fails loudly at config load instead of disabling the screen")
}
}
// TestParseReplyRefusesForeignLanguageRenderings pins the screen where it actually protects the bank: a
// reply mixing correct and foreign-language lines banks only the correct ones, counts the rest, and names
// them. Without the screen every line here is accepted and reaches the editor as this book's canon.
func TestParseReplyRefusesForeignLanguageRenderings(t *testing.T) {
id := func(s string) string { return s }
keys := []string{"修行", "元海", "转", "是为", "资质"}
reply := "修行\tcultivation\n元海\tPrimordial Sea\n转\trealm\n是为\tis\n资质\tталант"
got, _, st := ParseReply(reply, keys, id, unicode.Scripts["Cyrillic"])
if len(got) != 1 || got["资质"] != "талант" {
t.Fatalf("only the target-language line may be banked, got %#v", got)
}
if st.OffLanguage != 4 || st.Bad != 4 {
t.Fatalf("all four foreign lines must be refused AND counted, got off_language=%d bad=%d", st.OffLanguage, st.Bad)
}
if len(st.OffLanguageSamples) != 4 || !strings.Contains(st.OffLanguageSamples[0], "cultivation") {
t.Fatalf("the refused lines must be named for the operator, got %q", st.OffLanguageSamples)
}
// Without a declared script the same reply is banked whole — the state the engine was in before, kept
// visible so the cost of an undeclared script is a test, not a surprise.
if inert, _, st := ParseReply(reply, keys, id, nil); len(inert) != 5 || st.OffLanguage != 0 {
t.Fatalf("with no script the screen must be inert (that is what config refuses for an enabled gate), got %#v", inert)
}
}
// TestOffLanguageSamplesAreBounded keeps a whole batch answered in the wrong language from turning one
// warning into the reply itself.
func TestOffLanguageSamplesAreBounded(t *testing.T) {
id := func(s string) string { return s }
var lines []string
var keys []string
for _, k := range []string{"a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "b1", "b2", "b3"} {
keys = append(keys, k)
lines = append(lines, k+"\tforeign")
}
_, _, st := ParseReply(strings.Join(lines, "\n"), keys, id, unicode.Scripts["Cyrillic"])
if st.OffLanguage != len(keys) {
t.Fatalf("every foreign line must be counted, got %d of %d", st.OffLanguage, len(keys))
}
if len(st.OffLanguageSamples) != offLanguageSampleCap {
t.Fatalf("the samples must stop at %d, got %d", offLanguageSampleCap, len(st.OffLanguageSamples))
}
}