textmachine/backend/internal/pipeline/snapshot_wave_test.go

71 lines
2.7 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"
"textmachine/backend/internal/store"
)
// snapshot_wave_test.go: WS1 §1в per-wave snapshot mechanics (R1 ADDITIVE sub-step — the wave executor
// itself is residual; this pins the snapshot machinery it will use, WITHOUT switching the driver). The
// book-global snapshotID() is byte-unchanged (TestGolden proves it); these tests exercise the new
// snapshotIDForWave.
func TestSnapshotIDForWave(t *testing.T) {
r := newRunner(t, setupProject(t, "http://127.0.0.1:1"))
defer r.Close()
seed := []store.GlossaryEntry{
{Src: "方源", Dst: "Фан Юань", Status: "approved", Source: "seed"},
{Src: "古月", Dst: "Гу Юэ", Status: "approved", Source: "ruby"},
}
r.memory = materializeMemory(seed, false)
w1id, w1p, err := r.snapshotIDForWave(waveW1)
if err != nil {
t.Fatal(err)
}
w2id, w2p, err := r.snapshotIDForWave(waveW2)
if err != nil {
t.Fatal(err)
}
// W1 and W2 differ (different stage subset AND different bank version).
if w1id == w2id {
t.Fatalf("snapshot_W1 must differ from snapshot_W2 (stage subset + bank version)")
}
// The stage PARTITION: W1 folds the draft (translator) stage, W2 folds the edit (editor) stage.
if !strings.Contains(w1p, `"name":"draft"`) || strings.Contains(w1p, `"name":"edit"`) {
t.Fatalf("snapshot_W1 must fold ONLY the draft stage, payload: %s", w1p)
}
if !strings.Contains(w2p, `"name":"edit"`) || strings.Contains(w2p, `"name":"draft"`) {
t.Fatalf("snapshot_W2 must fold ONLY the edit stage, payload: %s", w2p)
}
// «Переоплата ОДНА» at the snapshot level (§1в): a W1.5 mined-approved addition moves ONLY W2.
enriched := append(append([]store.GlossaryEntry{}, seed...), store.GlossaryEntry{
Src: "蛊", Dst: "гу", Status: "approved", Source: "mined",
})
r.memory = materializeMemory(enriched, false)
w1id2, _, _ := r.snapshotIDForWave(waveW1)
w2id2, _, _ := r.snapshotIDForWave(waveW2)
if w1id2 != w1id {
t.Fatalf("snapshot_W1 moved on a mined-approved addition — W1 checkpoints would re-bill («переоплата ОДНА» broken)")
}
if w2id2 == w2id {
t.Fatalf("snapshot_W2 did NOT move on a mined-approved addition — the edit wave would miss the mined canon")
}
}
// TestWaveStagesPartition pins the role partition independently of the runner.
func TestWaveStagesPartition(t *testing.T) {
r := newRunner(t, setupProject(t, "http://127.0.0.1:1"))
defer r.Close()
w1 := waveStages(r.Pipeline.Stages, waveW1)
w2 := waveStages(r.Pipeline.Stages, waveW2)
if len(w1) != 1 || w1[0].Role != roleTranslator {
t.Fatalf("W1 must be the translator stage(s), got %+v", w1)
}
if len(w2) != 1 || w2[0].Role != roleEditor {
t.Fatalf("W2 must be the editor stage(s), got %+v", w2)
}
}