95 lines
3.6 KiB
Go
95 lines
3.6 KiB
Go
package main
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
|
||
"textmachine/backend/internal/pipeline"
|
||
"textmachine/backend/internal/store"
|
||
)
|
||
|
||
// render_observability_test.go pins the two counters the engine had always computed but no surface
|
||
// printed (found by the coldrun-b readiness pass): the pack-19 voice flagger and the pack-20 banknote
|
||
// channel. Both are pure observability — no gate, no snapshot fold, no verdict — so each test also
|
||
// pins the SILENCE case: a run without the channel keeps its byte-identical output.
|
||
|
||
func TestRenderQualityPrintsVoiceAxesWithDenominators(t *testing.T) {
|
||
var b strings.Builder
|
||
q := &pipeline.QualityReport{
|
||
VoiceFlags: 2, VoiceReplies: 40, VoiceAttributed: 31, VoicePairRegister: 5,
|
||
SpoilerLeaks: 1, VoiceCheckVersion: "voice-v1",
|
||
}
|
||
if err := renderQuality(&b, q); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
out := b.String()
|
||
// The count alone is unreadable — 2 of 31 attributed replies is a different fact from 2 of 3.
|
||
for _, want := range []string{"VOICE (axes A–C", "flags=2", "attributed=31", "replies=40",
|
||
"axis-D pair-register=5", "spoiler-leaks=1", "rules=voice-v1"} {
|
||
if !strings.Contains(out, want) {
|
||
t.Fatalf("the voice line must carry %q, got:\n%s", want, out)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestRenderQualityVoiceLinePrintsZeroWhenTheGateRan(t *testing.T) {
|
||
// «Not measured» and «measured, clean» are different answers (coldrun-a §7 reported the first as
|
||
// the second). The version string is the discriminator: it is set iff gates.voice was on.
|
||
var b strings.Builder
|
||
if err := renderQuality(&b, &pipeline.QualityReport{VoiceCheckVersion: "voice-v1"}); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if !strings.Contains(b.String(), "flags=0 over attributed=0 of replies=0") {
|
||
t.Fatalf("a gate that ran and found nothing must say so with its denominators:\n%s", b.String())
|
||
}
|
||
}
|
||
|
||
func TestRenderQualityStaysSilentWithoutTheVoiceGate(t *testing.T) {
|
||
var b strings.Builder
|
||
if err := renderQuality(&b, &pipeline.QualityReport{TotalUnits: 3}); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if strings.Contains(b.String(), "VOICE") {
|
||
t.Fatalf("a book whose voice gate never ran must not grow a voice line:\n%s", b.String())
|
||
}
|
||
}
|
||
|
||
func TestRenderReportPrintsBanknoteCoverageNotJustTheTotal(t *testing.T) {
|
||
// Chunk coverage is the signal a grand total hides: 12 lines can be 12 chunks × 1 or 1 chunk × 12,
|
||
// and only the second is a broken channel.
|
||
var b strings.Builder
|
||
states := []store.RetrievalState{
|
||
{Chapter: 1, ChunkIdx: 0, NBanknoteLines: 7},
|
||
{Chapter: 1, ChunkIdx: 1, NBanknoteLines: 0, BanknoteParseFail: 1},
|
||
{Chapter: 2, ChunkIdx: 0, NBanknoteLines: 5, BanknoteTruncated: 1},
|
||
}
|
||
if err := renderReport(&b,
|
||
func() ([]store.RequestLogView, error) { return nil, nil },
|
||
func() ([]store.ChunkStatus, error) { return nil, nil },
|
||
func() ([]store.RetrievalState, error) { return states, nil },
|
||
okLedger); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
out := b.String()
|
||
for _, want := range []string{"=== BANKNOTE", "lines=12", "over 2/3 chunk(s)",
|
||
"parse-fail chunks=1", "truncated chunks=1"} {
|
||
if !strings.Contains(out, want) {
|
||
t.Fatalf("the banknote line must carry %q, got:\n%s", want, out)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestRenderReportStaysSilentWithoutTheBanknoteChannel(t *testing.T) {
|
||
var b strings.Builder
|
||
states := []store.RetrievalState{{Chapter: 1, ChunkIdx: 0, NExactHits: 2}}
|
||
if err := renderReport(&b,
|
||
func() ([]store.RequestLogView, error) { return nil, nil },
|
||
func() ([]store.ChunkStatus, error) { return nil, nil },
|
||
func() ([]store.RetrievalState, error) { return states, nil },
|
||
okLedger); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if strings.Contains(b.String(), "BANKNOTE") {
|
||
t.Fatalf("a banknote-off book must not grow a banknote section:\n%s", b.String())
|
||
}
|
||
}
|