textmachine/backend/internal/pipeline/allowshort_wire_test.go

104 lines
3.9 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 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") }