package pipeline import ( "context" "strings" "testing" "textmachine/backend/internal/lang" ) // allowshort_wire_test.go pins backlog 18 (D39.50 п.5) END TO END: an owner-SIGNED single-character term // (蛊, 转 — the two the polygon found unreachable) must reach the MODEL when the seed says allow_short, // and must stay unreachable without it. The bank-level override was already pinned (membank), but the // finding was «не доезжают ни одним каналом», which is a statement about the whole path — seed file → // materialization → selection → rendered injection → wire. The cold run's live signature relies on this // path, so it is verified at $0 first. const shortSeedAllowed = ` terms: - src: 蛊 dst: гу type: term status: approved allow_short: true gender: female decl: { invariant: true, forms: ["гу"] } ` const shortSeedPlain = ` terms: - src: 蛊 dst: гу type: term status: approved decl: { invariant: true, forms: ["гу"] } ` // shortSource names 蛊 twice — nothing else in the chunk can produce the dst, so the rendering's presence // on the wire is proof the key fired. const shortSource = "方源看着蛊,那是一只月光蛊。" func TestAllowShortReachesTheWire(t *testing.T) { for _, c := range []struct { name string seed string wantHit bool }{ {"allow_short signs the single-char key through", shortSeedAllowed, true}, {"without it the single-key ban still holds", shortSeedPlain, false}, } { t.Run(c.name, func(t *testing.T) { rec := &reqRec{} srv := newJSONProvider(rec, func(body string) (string, string) { if isEditBody(body) { return "Фан Юань смотрел на гу.", "stop" } return "Фан Юань смотрел на гу — лунный гу.", "stop" }) defer srv.Close() bookPath := setupProjectOpts(t, srv.URL, projectOpts{source: shortSource, glossarySeed: c.seed}) r := newRunner(t, bookPath) defer r.Close() if _, err := r.TranslateBook(context.Background()); err != nil { t.Fatal(err) } msgs := bodyMessages(t, translatorBody(t, rec)) injected := false for _, m := range msgs { if m.Role == "system" && strings.Contains(m.Content, "蛊 → гу") { injected = true } } if injected != c.wantHit { t.Fatalf("single-char key on the wire = %v, want %v; messages: %+v", injected, c.wantHit, msgs) } // The editor half of the same claim: a signed row is CANON for the bilingual editor, and the // section it lands in is the signed one (backlog 19 — provenance, not match confidence). for _, b := range rec.all() { if !isEditBody(b) { continue } em := bodyMessages(t, b) got := strings.Contains(em[1].Content, "蛊 → «гу»") if got != c.wantHit { t.Fatalf("editor canon carries the short term = %v, want %v: %q", got, c.wantHit, em[1].Content) } // ⚠ The assertion that used to stand here — «a signed row must not carry ⟨проверить⟩» — // cannot fail any more: D39.104 п.2 took the marker off the wire for EVERY row, so it is // absent on the signed and the unsigned alike, and a test that cannot fail is worse than // none. What the fixture can still hold is that `allow_short` delivers the WHOLE row and // not a stripped one: the row carries a gender, and the directive has to arrive with it. // (It arrives because the key is Han and therefore trusted, not because of allow_short — // what allow_short decides is whether the row is on this wire at all.) if c.wantHit && !strings.Contains(em[1].Content, ruInjection().GenderFemale) { t.Fatalf("the row allow_short delivered must carry its gender directive too: %q", em[1].Content) } } }) } } // ruInjection is the ru target's wire text, for assertions about what a rendered block contains. func ruInjection() lang.InjectionTexts { return lang.InjectionTextsFor("ru") }