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"], nil) 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, 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"], nil) 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)) } }