textmachine/backend/internal/pipeline/render_memory_test.go

72 lines
3.1 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 (
"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 → А-кью")
req := Request{BookID: "b", Chapter: 1, Stage: "draft", Role: "translator", Model: "m",
Temperature: 0.3, Reasoning: "off", MaxTokens: 1024, SnapshotID: "snap"}
reqNone, reqInj := req, req
reqNone.Messages, reqInj.Messages = none, inj
hNone, hInj := RequestHash(reqNone), RequestHash(reqInj)
if hNone == hInj {
t.Error("injection did not change request_hash — a glossary change would silently hit a stale checkpoint")
}
}