textmachine/backend/internal/membank/render_test.go

80 lines
3.7 KiB
Go
Raw 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 membank
import (
"strings"
"testing"
"textmachine/backend/internal/lang"
)
// render_test.go: the injection RENDERER fixtures — the exact bytes the bank puts on the wire for
// each role. ruTX is the ru target wire-text (pair-14 §2: the strings live in lang/data/injection.txt).
func ruTX() lang.InjectionTexts { return lang.InjectionTextsFor("ru") }
func TestRenderGlossaryBlock(t *testing.T) {
confirmed := PickedEntry{entry: &entry{src: "阿Q", dst: "А-кью", status: "approved"}, Disp: Confirmed}
ambiguous := PickedEntry{entry: &entry{src: "小D", dst: "Малыш Дэ", status: "auto"}, Disp: Ambiguous}
emptyDst := PickedEntry{entry: &entry{src: "鈴木", dst: "", status: "auto"}, Disp: Ambiguous}
block := RenderGlossaryBlock([]PickedEntry{confirmed, ambiguous, emptyDst}, ruTX())
if !strings.Contains(block, "阿Q → А-кью") {
t.Errorf("confirmed line missing: %q", block)
}
if !strings.Contains(block, "小D → Малыш Дэ ⟨проверить⟩") {
t.Errorf("ambiguous line missing its unverified tag: %q", block)
}
if strings.Contains(block, "鈴木") {
t.Errorf("empty-dst candidate should be skipped (nothing to inject): %q", block)
}
// Nothing to inject → empty block (better nothing).
if RenderGlossaryBlock(nil, ruTX()) != "" {
t.Error("empty selection must render an empty block")
}
if RenderGlossaryBlock([]PickedEntry{emptyDst}, ruTX()) != "" {
t.Error("only-empty-dst selection must render an empty block")
}
}
// TestDC3GenderInjection pins the DC3 gender constraint annotation on the editor block (WS5 §5(б)): a
// gendered CONFIRMED term carries a gender directive; a genderless term does not (byte-identical line).
func TestDC3GenderInjection(t *testing.T) {
mk := func(gender string) []PickedEntry {
return []PickedEntry{{entry: &entry{src: "方源", dst: "Фан Юань", status: "approved", gender: gender}, Via: "方源", Disp: Confirmed}}
}
male := RenderEditorConstraintBlock(mk("male"), ruTX())
if !strings.Contains(male, "方源 → «Фан Юань» (муж.") {
t.Fatalf("male term must carry a masculine directive, got:\n%s", male)
}
hidden := RenderEditorConstraintBlock(mk("hidden"), ruTX())
if !strings.Contains(hidden, "пол СКРЫТ") {
t.Fatalf("hidden term must carry the gender-avoidance mandate, got:\n%s", hidden)
}
none := RenderEditorConstraintBlock(mk(""), ruTX())
if !strings.HasSuffix(none, "«Фан Юань»") {
t.Fatalf("a genderless term must have NO gender note (line ends at the dst), got:\n%s", none)
}
}
// TestZeroValuePickedEntryAndBankDoNotPanic pins the pack-16 tail: both types are EXPORTED with exported
// fields, so a caller outside this package can construct a zero value whose private row/matcher is nil.
// Every consumer used to nil-deref on it — a panic in a paid run. The contract is now "a row-less record is
// skipped; a matcher-less bank selects nothing", which cannot change a real bank's behaviour because
// Materialize and Select always populate both.
func TestZeroValuePickedEntryAndBankDoNotPanic(t *testing.T) {
zero := []PickedEntry{{Via: "x", Disp: Confirmed}}
tx := lang.InjectionTextsFor("ru")
if got := RenderGlossaryBlock(zero, tx); got != "" {
t.Fatalf("a row-less record must render nothing, got %q", got)
}
if got := RenderEditorConstraintBlock(zero, tx); got != "" {
t.Fatalf("a row-less record must render no constraint, got %q", got)
}
var b Bank
if misses := b.Postcheck(zero, "любой текст"); !misses.Empty() {
t.Fatalf("a row-less record must produce no post-check miss, got %+v", misses)
}
if sel := b.Select("непустой текст для матчера", 1, nil, 100); len(sel.Injected) != 0 {
t.Fatalf("a zero-value bank must select nothing, got %+v", sel.Injected)
}
}