textmachine/backend/internal/pipeline/snapshot_wave_test.go

73 lines
3 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/membank"
"textmachine/backend/internal/store"
)
// snapshot_wave_test.go: WS1 §1в per-wave snapshot mechanics — the folds the wave driver pins every job to.
// ⚠ The book-global snapshotID() is TEST-ONLY (no production caller) and is NOT exercised here; it is
// called from four other files, five call sites between them, and each pins one payload input. The golden pins the two per-wave ids and
// their payloads, so nothing anywhere should be read as "the book-global fold is covered".
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 = membank.Materialize(seed, false)
w1id, w1p, err := r.snapshotIDForWave(waveDraft)
if err != nil {
t.Fatal(err)
}
w2id, w2p, err := r.snapshotIDForWave(waveEdit)
if err != nil {
t.Fatal(err)
}
// the draft wave and the edit wave differ (different stage subset AND different bank version).
if w1id == w2id {
t.Fatalf("draft-wave snapshot must differ from snapshot_W2 (stage subset + bank version)")
}
// The stage PARTITION: the draft wave folds the draft (translator) stage, the edit wave folds the edit (editor) stage.
if !strings.Contains(w1p, `"name":"draft"`) || strings.Contains(w1p, `"name":"edit"`) {
t.Fatalf("draft-wave snapshot 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)
}
// «Re-pay ONCE» at the snapshot level (§1в): a mined-approved addition at the bank-mining stop moves ONLY
// the edit wave.
enriched := append(append([]store.GlossaryEntry{}, seed...), store.GlossaryEntry{
Src: "蛊", Dst: "гу", Status: "approved", Source: "mined",
})
r.memory = membank.Materialize(enriched, false)
w1id2, _, _ := r.snapshotIDForWave(waveDraft)
w2id2, _, _ := r.snapshotIDForWave(waveEdit)
if w1id2 != w1id {
t.Fatalf("draft-wave snapshot moved on a mined-approved addition — the draft wave checkpoints would re-bill (pay-once invariant 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()
draftStages := waveStages(r.Pipeline.Stages, waveDraft)
editStages := waveStages(r.Pipeline.Stages, waveEdit)
if len(draftStages) != 1 || draftStages[0].Role != roleTranslator {
t.Fatalf("the draft wave must be the translator stage(s), got %+v", draftStages)
}
if len(editStages) != 1 || editStages[0].Role != roleEditor {
t.Fatalf("the edit wave must be the editor stage(s), got %+v", editStages)
}
}