textmachine/backend/internal/lang/reader_test.go

122 lines
4.5 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 lang
import (
"os"
"path/filepath"
"strings"
"testing"
"unicode"
)
// reader_test.go: the reader words are DATA of the target language, read outside the pack loader and
// outside Pack.Version() — a wording edit must never re-bill a book.
const sampleReader = `# ru
hole.pending ⚠ Не переведено ({chapter}.{unit}).
hole.withheld ⚠ Не выпущено.
hole.incomplete ⚠ Недостаёт {dropped}.
hole.stale ⚠ Устарело ({chapter}.{unit}).
hole.ghost ⚠ Глава {chapter}: вне нарезки {ghost}.
notice.holes ⚠ Пропусков {holes} из {total}.
notice.ghost ⚠ Вне нарезки {ghost}.
`
func writeReader(t *testing.T, root, tgt, body string) {
t.Helper()
if err := os.MkdirAll(filepath.Join(root, tgt), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(root, tgt, readerFile), []byte(body), 0o644); err != nil {
t.Fatal(err)
}
}
func TestReaderWordsLoadAndFill(t *testing.T) {
root := t.TempDir()
writeReader(t, root, "ru", sampleReader)
w, present, err := LoadReaderWords(root, "ru")
if err != nil || !present {
t.Fatalf("present=%v err=%v", present, err)
}
if got := FillReaderTemplate(w.HolePending, "chapter", "3", "unit", "0"); got != "⚠ Не переведено (3.0)." {
t.Errorf("filled = %q", got)
}
if got := FillReaderTemplate(w.NoticeHoles, "holes", "2", "total", "37"); got != "⚠ Пропусков 2 из 37." {
t.Errorf("filled = %q", got)
}
// A placeholder the caller did not pass stays visible rather than vanishing.
if got := FillReaderTemplate("{x} {y}", "x", "1"); got != "1 {y}" {
t.Errorf("unfilled placeholder must stay: %q", got)
}
}
func TestReaderWordsAbsentIsTheNonVerbalForm(t *testing.T) {
for _, root := range []string{"", t.TempDir()} {
w, present, err := LoadReaderWords(root, "ru")
if err != nil || present {
t.Fatalf("root %q: present=%v err=%v", root, present, err)
}
if w != DefaultReaderWords() {
t.Fatalf("root %q: absent file must yield the defaults", root)
}
}
// The non-verbal form contains no letter of any language: a symbol and the numbers only.
d := DefaultReaderWords()
for name, tmpl := range map[string]string{"pending": d.HolePending, "withheld": d.HoleWithheld, "incomplete": d.HoleIncomplete,
"stale": d.HoleStale, "ghost": d.HoleGhost, "notice": d.NoticeHoles, "notice-ghost": d.NoticeGhost} {
bare := tmpl
for _, ph := range []string{"{chapter}", "{unit}", "{dropped}", "{ghost}", "{holes}", "{total}"} {
bare = strings.ReplaceAll(bare, ph, "")
}
for _, r := range bare {
if unicode.IsLetter(r) {
t.Errorf("default %s template %q carries a letter — that is a word the engine decided", name, tmpl)
break
}
}
}
}
func TestReaderWordsFailLoud(t *testing.T) {
cases := map[string]string{
"unknown key": sampleReader + "hole.other\tx\n",
"missing key": strings.Replace(sampleReader, "notice.ghost\t⚠ Вне нарезки {ghost}.\n", "", 1),
"duplicate key": sampleReader + "hole.pending\tагаин\n",
"empty value": strings.Replace(sampleReader, "⚠ Не выпущено.", "", 1),
"no tab": strings.Replace(sampleReader, "hole.withheld\t", "hole.withheld ", 1),
}
for name, body := range cases {
root := t.TempDir()
writeReader(t, root, "ru", body)
if _, _, err := LoadReaderWords(root, "ru"); err == nil {
t.Errorf("%s: must fail loud", name)
}
}
}
// TestReaderWordsDoNotMoveThePackVersion is the money pin: reader.txt beside the pack changes NOTHING
// the snapshot folds. (The stand proof is `current_snapshot` of the minirun export before and after.)
func TestReaderWordsDoNotMoveThePackVersion(t *testing.T) {
root := t.TempDir()
writeSyntheticPack(t, root, "zh", "ru")
before, err := Load(root, "zh", "ru")
if err != nil {
t.Fatal(err)
}
writeReader(t, root, "ru", sampleReader)
after, err := Load(root, "zh", "ru")
if err != nil {
t.Fatal(err)
}
if before.Version() != after.Version() {
t.Fatalf("adding <root>/ru/reader.txt moved Pack.Version() %s → %s — a wording edit would re-bill every book of the pair", before.Version(), after.Version())
}
// And the real shipped file loads.
w, present, err := LoadReaderWords(filepath.Join("..", "..", "configs", "langpacks"), "ru")
if err != nil || !present {
t.Fatalf("configs/langpacks/ru/reader.txt: present=%v err=%v", present, err)
}
if !strings.Contains(w.HoleIncomplete, "{dropped}") || !strings.Contains(w.NoticeHoles, "{total}") {
t.Errorf("shipped ru words lost a placeholder: %+v", w)
}
}