93 lines
4.1 KiB
Go
93 lines
4.1 KiB
Go
package pipeline
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
func TestMessagesInjectionLayout(t *testing.T) {
|
||
tpl := writeTemplate(t, "Стабильный системный префикс.\n---USER---\nПереведи: {{text}}")
|
||
v := RenderVars{Book: testBook(), Text: "исходный чанк"}
|
||
injection := "ГЛОССАРИЙ:\n阿Q → А-кью"
|
||
|
||
msgs, err := MessagesWithInjection(tpl, v, injection)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if len(msgs) != 3 {
|
||
t.Fatalf("want 3 messages (system, injection, user), got %d", len(msgs))
|
||
}
|
||
// [0] stable system prefix carries the cache boundary.
|
||
if msgs[0].Role != "system" || !msgs[0].CacheBoundary {
|
||
t.Errorf("msg[0] must be the system prefix with CacheBoundary=true: %+v", msgs[0])
|
||
}
|
||
// [1] injection sits AFTER the boundary (volatile → CacheBoundary=false).
|
||
if msgs[1].Role != "system" || msgs[1].CacheBoundary || msgs[1].Content != injection {
|
||
t.Errorf("msg[1] must be the injection with CacheBoundary=false: %+v", msgs[1])
|
||
}
|
||
// [2] the volatile user chunk. It must contain ONLY the rendered chunk, never the
|
||
// glossary (ch.Text stays clean — the 3a contract; the coverage gate reads ch.Text).
|
||
if msgs[2].Role != "user" {
|
||
t.Errorf("msg[2] must be user: %+v", msgs[2])
|
||
}
|
||
if strings.Contains(msgs[2].Content, "ГЛОССАРИЙ") || strings.Contains(msgs[2].Content, "阿Q") {
|
||
t.Errorf("glossary leaked into the user (ch.Text) message: %q", msgs[2].Content)
|
||
}
|
||
if !strings.Contains(msgs[2].Content, "исходный чанк") {
|
||
t.Errorf("user message lost the chunk: %q", msgs[2].Content)
|
||
}
|
||
}
|
||
|
||
func TestMessagesEmptyInjectionIdentical(t *testing.T) {
|
||
tpl := writeTemplate(t, "Системный префикс.\n---USER---\nТекст: {{text}}")
|
||
v := RenderVars{Book: testBook(), Text: "чанк"}
|
||
plain, _ := Messages(tpl, v)
|
||
withEmpty, _ := MessagesWithInjection(tpl, v, " ") // whitespace-only → no injection
|
||
if len(plain) != 2 || len(withEmpty) != 2 {
|
||
t.Fatalf("empty injection must yield 2 messages: %d vs %d", len(plain), len(withEmpty))
|
||
}
|
||
for i := range plain {
|
||
if plain[i] != withEmpty[i] {
|
||
t.Errorf("empty-injection message %d differs from plain: %+v vs %+v", i, withEmpty[i], plain[i])
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestInjectionChangesRequestHash: the injection is folded into request_hash via the
|
||
// messages, so a different glossary is a different request (a changed approved term
|
||
// re-derives the chunk, never serves a stale checkpoint).
|
||
func TestInjectionChangesRequestHash(t *testing.T) {
|
||
tpl := writeTemplate(t, "Префикс.\n---USER---\n{{text}}")
|
||
v := RenderVars{Book: testBook(), Text: "чанк"}
|
||
none, _ := Messages(tpl, v)
|
||
inj, _ := MessagesWithInjection(tpl, v, "ГЛОССАРИЙ:\n阿Q → А-кью")
|
||
hNone := RequestHash("b", 1, 0, 0, "draft", "translator", "m", 0.3, "off", false, 1024, "snap", none)
|
||
hInj := RequestHash("b", 1, 0, 0, "draft", "translator", "m", 0.3, "off", false, 1024, "snap", inj)
|
||
if hNone == hInj {
|
||
t.Error("injection did not change request_hash — a glossary change would silently hit a stale checkpoint")
|
||
}
|
||
}
|
||
|
||
func TestRenderGlossaryBlock(t *testing.T) {
|
||
confirmed := pickedEntry{entry: &memoryEntry{src: "阿Q", dst: "А-кью", status: "approved"}, disp: memConfirmed}
|
||
ambiguous := pickedEntry{entry: &memoryEntry{src: "小D", dst: "Малыш Дэ", status: "auto"}, disp: memAmbiguous}
|
||
emptyDst := pickedEntry{entry: &memoryEntry{src: "鈴木", dst: "", status: "auto"}, disp: memAmbiguous}
|
||
|
||
block := renderGlossaryBlock([]pickedEntry{confirmed, ambiguous, emptyDst})
|
||
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 (лучше ничего).
|
||
if renderGlossaryBlock(nil) != "" {
|
||
t.Error("empty selection must render an empty block")
|
||
}
|
||
if renderGlossaryBlock([]pickedEntry{emptyDst}) != "" {
|
||
t.Error("only-empty-dst selection must render an empty block")
|
||
}
|
||
}
|