215 lines
10 KiB
Go
215 lines
10 KiB
Go
package runner
|
||
|
||
import (
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
|
||
"gopkg.in/yaml.v3"
|
||
|
||
"textmachine/platform/internal/ingest"
|
||
)
|
||
|
||
// ⛔ THE SEAM AGAINST THE REAL ENGINE, and it exists because every other test of the price
|
||
// projection reads a document THIS SIDE WROTE.
|
||
//
|
||
// The allowlist in `ingest` is a transcription of the engine's own JSON, and a transcription is
|
||
// exactly the artefact that goes stale in silence: a key the engine renames leaves a zero behind,
|
||
// `json.Unmarshal` says nothing, and the platform then sells a book with no floor under its hold —
|
||
// which is the PD-440 wall coming back invisibly. A hand-written fixture cannot catch that, because
|
||
// it agrees with the reader by construction.
|
||
//
|
||
// So this one asks the ENGINE. It runs `tmctl manifest --json` against a probe book, decodes the
|
||
// bytes with the platform's own reader, and asserts that the projection ARRIVED — not merely that
|
||
// the document parsed.
|
||
//
|
||
// Gated like every live-engine test: a bare clone stays green and says why.
|
||
func TestTheRealEnginesPriceProjectionIsReadByThisBuild(t *testing.T) {
|
||
bin := os.Getenv("TM_PLATFORM_TEST_ENGINE_BIN")
|
||
tpl := os.Getenv("TM_PLATFORM_TEST_BOOK_TEMPLATE")
|
||
if bin == "" || tpl == "" {
|
||
t.Skip("TM_PLATFORM_TEST_ENGINE_BIN and TM_PLATFORM_TEST_BOOK_TEMPLATE not set: " +
|
||
"the price projection is not checked against a real engine")
|
||
}
|
||
// Two chapters the engine's own heading rule will find, so the cut is `detected` and the tree has
|
||
// something to price per chapter.
|
||
const src = "第1章 测试\n这是一个测试文本。天空是蓝色的,云朵飘过。\n\n第2章 又一章\n他走进了那座古老的图书馆,书架高耸入云。\n"
|
||
dir := writeReadProbeBook(t, tpl, "bk_PRICEPROBE", src)
|
||
m, err := New(nil).Manifest(t.Context(), bin, dir)
|
||
if err != nil {
|
||
t.Fatalf("the live manifest did not run: %v", err)
|
||
}
|
||
if err := m.Whole(); err != nil {
|
||
t.Fatalf("this build did not read the live manifest whole: %v", err)
|
||
}
|
||
|
||
// THE PROJECTION, read by the platform's own reader out of the engine's own bytes.
|
||
p, ok := m.Priced()
|
||
if !ok {
|
||
t.Fatalf("this build read no projection out of a live manifest of %d chapters: the allowlist "+
|
||
"and the engine have drifted apart, and a book priced by nobody cannot be sold "+
|
||
"(ingest.Priced refuses it, which is the honest half — but the SEAM is what broke)",
|
||
m.ChaptersTotal)
|
||
}
|
||
// ⚠ EVERY LOAD-BEARING FIGURE, each named for what depends on it rather than checked for being
|
||
// non-zero in a loop: what a reader needs from a failure here is WHICH number stopped arriving.
|
||
if p.StepMaxUSD <= 0 {
|
||
t.Error("step_max_usd did not arrive: it is the floor of every hold, and without it a run is " +
|
||
"admitted with a ceiling no single call can clear (PD-440)")
|
||
}
|
||
if p.ExpectedUSD <= 0 {
|
||
t.Error("expected_usd did not arrive: the order has nothing to be priced from")
|
||
}
|
||
if p.SourceChars <= 0 {
|
||
t.Error("source_chars did not arrive: the screen falls back to the intake's approximation")
|
||
}
|
||
// The per-chapter roll-up is what an ORDER is priced from, and its absence is the failure the
|
||
// book-level figure alone cannot show.
|
||
for _, c := range m.Chapters {
|
||
if c.Price == nil || c.Price.ExpectedUSD <= 0 {
|
||
t.Errorf("chapter %d arrived with no price, so no partial order over this book can be quoted", c.Number)
|
||
}
|
||
}
|
||
// The per-UNIT figure, which is what a CHARACTER order resolves against — the only partial order
|
||
// a book with no chapter structure can carry.
|
||
for _, c := range m.Chapters {
|
||
for _, u := range c.Units {
|
||
if u.Price == nil || u.Price.SourceChars <= 0 {
|
||
t.Errorf("unit %q arrived with no size, so a character order cannot be resolved", u.ID)
|
||
}
|
||
}
|
||
}
|
||
// The cut's PROVENANCE, and the vocabulary it comes in. This build reads three words and treats
|
||
// anything else as untrusted; a live engine emitting a fourth is not a failure — it is the day
|
||
// the platform stops offering chapter orders for this book, which is what should happen.
|
||
switch m.Structure {
|
||
case ingest.StructureDetected:
|
||
if !m.ChapterOrdersOffered() {
|
||
t.Error("a detected cut is not offered in chapters")
|
||
}
|
||
case ingest.StructureDeclared, ingest.StructureNone:
|
||
t.Logf("the live engine cut this probe as %q rather than `detected`", m.Structure)
|
||
case "":
|
||
t.Error("the live engine published no `structure` at all: this build then refuses to sell " +
|
||
"chapter orders for every book, which is safe and wrong")
|
||
default:
|
||
t.Logf("the live engine published a structure word this build does not know (%q); "+
|
||
"chapter orders are withheld, which is the intended degradation", m.Structure)
|
||
}
|
||
|
||
// ⛔ AND THE ONE ASSERTION A FIXTURE COULD NEVER MAKE: the book-level `expected_usd` INCLUDES a
|
||
// flat `book_once_usd`, and on a short book it DOMINATES. Measured on this very probe: two
|
||
// chapters whose own bills come to fractions of a cent against a book-level bound of whole
|
||
// dollars. It is pinned because the platform's whole order arithmetic rests on NOT using the
|
||
// book-level figure as the base of a hold — doing so would put a flat two-dollar threshold under
|
||
// every purchase and make a short book unbuyable again.
|
||
if p.BookOnceUSD > 0 {
|
||
units := p.ExpectedUSD - p.BookOnceUSD
|
||
if units <= 0 {
|
||
t.Fatalf("book_once_usd (%s) is not less than expected_usd (%s): the book-level bound is "+
|
||
"not a PART of the expected bill, and every order priced from the difference is wrong",
|
||
p.BookOnceUSD.USD(), p.ExpectedUSD.USD())
|
||
}
|
||
t.Logf("live projection: expected %s of which book-level %s; the units' own share is %s",
|
||
p.ExpectedUSD.USD(), p.BookOnceUSD.USD(), units.USD())
|
||
}
|
||
// The chapters' own bills and the book figure describe ONE book. Two numbers that must agree are
|
||
// where a silent drift lives, and here they come from two different keys of one document.
|
||
sum := int64(0)
|
||
for _, c := range m.Chapters {
|
||
sum += int64(c.Price.ExpectedUSD)
|
||
}
|
||
if want := int64(p.ExpectedUSD - p.BookOnceUSD); sum < want-int64(len(m.Chapters))-1 ||
|
||
sum > want+int64(len(m.Chapters))+1 {
|
||
t.Errorf("the live chapters sum to %d micro-USD and the book figure leaves %d for them", sum, want)
|
||
}
|
||
}
|
||
|
||
// The engine's manifest is the ONE document the order form is built on, so this build's idea of its
|
||
// shape has to be checkable without a database and without a run. It is the same probe as above with
|
||
// the assertions inverted: what the engine publishes that this build does NOT read is listed, so a
|
||
// field arriving for the first time is a decision somebody takes rather than a value dropped.
|
||
func TestWhatTheLiveManifestCarriesAndThisBuildDeclinesToRead(t *testing.T) {
|
||
bin := os.Getenv("TM_PLATFORM_TEST_ENGINE_BIN")
|
||
tpl := os.Getenv("TM_PLATFORM_TEST_BOOK_TEMPLATE")
|
||
if bin == "" || tpl == "" {
|
||
t.Skip("TM_PLATFORM_TEST_ENGINE_BIN and TM_PLATFORM_TEST_BOOK_TEMPLATE not set")
|
||
}
|
||
dir := writeReadProbeBook(t, tpl, "bk_SHAPEPROBE", "第1章 测试\n这是一个测试文本。\n")
|
||
doc, _, err := readEngine(t.Context(), bin, dir, ManifestArgs(dir), maxManifest, "manifest")
|
||
if err != nil {
|
||
t.Fatalf("the live manifest did not run: %v", err)
|
||
}
|
||
// The version this build was written against. It is a SHAPE gate and not a value pin — see
|
||
// ingest.KnownManifestVersion — and the point of asserting it against a live engine is that the
|
||
// constant stops being a copy of a copy.
|
||
m, err := ingest.DecodeManifest(doc)
|
||
if err != nil {
|
||
t.Fatalf("the live manifest did not decode: %v", err)
|
||
}
|
||
if m.Version != ingest.KnownManifestVersion {
|
||
t.Errorf("the live engine publishes manifest %q and this build reads %s: the allowlist is a "+
|
||
"transcription of a document that has moved", m.Version, ingest.KnownManifestVersion)
|
||
}
|
||
// ⚠ ASKED OF THE DECODED BOOK FIGURE, not grepped out of the bytes. A raw `strings.Contains` for
|
||
// `"expected_usd"` is satisfied by any CHAPTER's price object, so a rename of the BOOK-level key —
|
||
// the one the whole order form is built on — would leave this green. The keys that have no twin
|
||
// elsewhere in the document are still worth a grep, and they are the ones grepped.
|
||
if m.Price == nil {
|
||
t.Fatal("the live manifest carries no book-level `price` object at all")
|
||
}
|
||
if m.Price.ExpectedUSD <= 0 || m.Price.StepMaxUSD <= 0 || m.Price.SourceChars <= 0 {
|
||
t.Errorf("a book-level price key did not arrive: %+v", *m.Price)
|
||
}
|
||
for _, key := range []string{`"book_once_usd"`, `"step_max_usd"`, `"structure"`} {
|
||
if !strings.Contains(string(doc), key) {
|
||
t.Errorf("the live manifest carries no %s: the platform's order form is built on it", key)
|
||
}
|
||
}
|
||
}
|
||
|
||
// writeReadProbeBook is writeProbeBook's READ-PATH twin: the deployment's own template, with this
|
||
// book's identity on it and nothing else changed.
|
||
//
|
||
// ⛔ IT DELIBERATELY DOES NOT REWRITE THE PIPELINE, and both halves of that matter.
|
||
//
|
||
// It does not swap the stage models for a local one, because `manifest` reaches no provider: it is a
|
||
// $0, key-less verb (D20.4) that ingests, cuts and prints. The swap exists so a WRITE-path probe does
|
||
// not buy its calls, and buying is not on this path.
|
||
//
|
||
// And it does not inherit the contrast-artefact SKIP. That condition is the write path's — its own
|
||
// message says «a live write-path probe cannot run» — because the bank contour mines during a
|
||
// translate. A manifest mines nothing. Inheriting it cost this probe its whole existence: the seam
|
||
// test that is supposed to be the one independent witness of the allowlist SKIPPED on every host
|
||
// without a multi-megabyte word list nobody needs to read a manifest, and skipped silently, which is
|
||
// the failure shape this project keeps paying for.
|
||
//
|
||
// ⚠ AND IT WANTS THE SHIPPING PIPELINE'S OWN NUMBERS, which is the positive reason rather than the
|
||
// absence of a negative one: the projection this test reads is made of that pipeline's stages, its
|
||
// prices and its gate budgets. Rewritten, the probe would measure a book nobody sells.
|
||
func writeReadProbeBook(t *testing.T, tpl, bookID, source string) string {
|
||
t.Helper()
|
||
dir := t.TempDir()
|
||
raw, err := os.ReadFile(tpl)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
var cfg map[string]any
|
||
if err := yaml.Unmarshal(raw, &cfg); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
cfg["book_id"], cfg["source_lang"], cfg["target_lang"] = bookID, "zh", "ru"
|
||
cfg["source_file"] = "source.txt"
|
||
out, err := yaml.Marshal(cfg)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := os.WriteFile(filepath.Join(dir, ConfigFile), out, 0o600); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := os.WriteFile(filepath.Join(dir, "source.txt"), []byte(source), 0o600); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
return dir
|
||
}
|