package runs import ( "bytes" "encoding/json" "log/slog" "os" "path/filepath" "strings" "testing" "time" "textmachine/platform/internal/ingest" "textmachine/platform/internal/runner" ) // ⛔ «THE COLUMN IS FILLED» AND «A PERSON SEES IT» ARE DIFFERENT CLAIMS, and this test makes the // second one. // // A run this platform killed by its own grace is OUR POLICY, not a fault of the deployment, and the // pack that asked for it to be «told apart» is answered on three surfaces at once — which is why they // are asserted together: // // - the WIRE keeps its retry axis (`interrupted`: retrying IS the remedy, and it is, because nobody // is stopping the next spawn) — a fourth value there would answer the same question in a fourth // phrase, which the canon's own constraint comment forbids; // - the ATTEMPT keeps the machine's word `timeout`, which is what tells our deadline from a kill // nobody chose (`oom-kill` and friends); // - and a PERSON is told at the moment it happens, because a column no surface reads is not a // signal (this zone's own rule, written at the parked attempt's line). func TestAKillByOurOwnGraceIsRecordedWhereAPersonCanSeeIt(t *testing.T) { f := newFixture(t, "10", 500) var buf bytes.Buffer f.svc.Log = slog.New(slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})) run, err := f.svc.Start(f.ctx, StartRequest{UserID: "u1", BookID: f.bookID(t), Chapters: order(10)}) if err != nil { t.Fatal(err) } if err := f.svc.Spawn(f.ctx, run.ID); err != nil { t.Fatal(err) } live := f.live(t) // What systemd writes when TimeoutStopSec — the grace THIS platform sets — expires: measured on a // unit of this shape, and told from a stop the engine caught (`exit-code/exited/5`) by what the // machine saw rather than by inference. if err := runner.WriteMarker(f.svc.markerPath(live.RunID, live.AttemptNo), runner.Marker{Unit: live.UnitName, Result: "timeout", Code: "killed", Status: "KILL", At: f.now.Add(time.Second)}); err != nil { t.Fatal(err) } f.engine.set(ingest.StatusReport{TotalUnits: 10, Done: 2, Spend: usd(0), Reserved: usd(0)}, nil) if err := f.svc.Sweep(f.ctx); err != nil { t.Fatal(err) } got, err := f.store.ReadRun(f.ctx, "u1", run.ID) if err != nil { t.Fatal(err) } if got.Status != "failed" || got.FailureReason != "interrupted" { t.Errorf("a run we killed by our own deadline reads %s/%s, want failed/interrupted: "+ "`service_error` would tell the user their deployment is broken, and a fourth word would "+ "answer the retry question a fourth way", got.Status, got.FailureReason) } var result string if err := f.store.Pool().QueryRow(f.ctx, `select coalesce(exit_result, '') from run_attempts where run_id = $1`, run.ID).Scan(&result); err != nil { t.Fatal(err) } if result != "timeout" { t.Errorf("the attempt's exit_result is %q, want the machine's own %q: without it our policy is "+ "byte-identical to a kill nobody chose", result, "timeout") } // The human-facing half: the ending is ANNOUNCED with that word, so an operator does not have to // query the database to tell a policy from a fault. var said bool for _, l := range strings.Split(buf.String(), "\n") { if !strings.Contains(l, `"run finished"`) { continue } var line map[string]any if err := json.Unmarshal([]byte(l), &line); err != nil { t.Fatal(err) } if line["result"] == "timeout" { said = true } } if !said { t.Errorf("no line told anybody HOW this run ended; the fact would live in a column only: %s", buf.String()) } } // ⛔ THE FIXTURE THE PACK ASKED FOR: the «stranger» in the journal IS THIS VERY RUN. // // The platform names a stream before the unit starts and keys the name on (run, attempt), and the // engine mints a FRESH one when the given id has already written for this book — so a respawn of this // same run appears in its own journal under an id the cursor will never match // (reconcile.drainJournal, `may be this very run, alive and writing`). Everything the buyer is told // has to be true in THAT case, which is the one where a wording built on «somebody else touched your // book» lies. // // What is asserted is therefore not only the word but the ACTION: the run stays live and translating, // nothing is quarantined, and the only thing said is that the figures are behind — which is true. func TestWhenTheStrangerInTheJournalIsThisVeryRunNothingWeSayIsFalse(t *testing.T) { f := newFixture(t, "10", 500) run, err := f.svc.Start(f.ctx, StartRequest{UserID: "u1", BookID: f.bookID(t), Chapters: order(100)}) if err != nil { t.Fatal(err) } if err := f.svc.Spawn(f.ctx, run.ID); err != nil { t.Fatal(err) } f.runner.alive = true // Our own stream, some progress, and then a handshake under an id the ENGINE minted for this same // book because ours had already written. It is indistinguishable from a foreign writer here, which // is exactly the point. respawned := `{"seq":1,"type":"hello","data":{"stream_version":"1.1","engine_run_id":"` + f.live(t).EngineRunID + `-minted-again","book_id":"b"}}` + "\n" journal := filepath.Join(f.workdir, ingest.JournalFile) if err := os.WriteFile(journal, []byte(hello(t, f)+ `{"seq":2,"type":"progress","data":{"draft":{"done":3,"total":20},"eta_seconds":99}}`+"\n"+ respawned), 0o600); err != nil { t.Fatal(err) } f.engine.set(ingest.StatusReport{TotalUnits: 20, Done: 7, ETASeconds: 42, Spend: usd(0), Reserved: usd(0)}, nil) if err := f.svc.Sweep(f.ctx); err != nil { t.Fatal(err) } got, err := f.store.ReadRun(f.ctx, "u1", run.ID) if err != nil { t.Fatal(err) } // TRUE, and the only claim made: the figures are behind. The run is not accused of anything and // nobody is named. if !got.ProgressLagging { t.Error("the buyer is told nothing while the projection has stopped at a handshake: the bar " + "stands, the estimate keeps moving, and the run goes on spending") } if got.Status != "translating" { t.Errorf("the run reads %q: a stream this platform cannot follow is not a run that ended, and "+ "treating it as one would end a paying run over its own respawn", got.Status) } if got.FailureReason != "" || got.PausedReason != "" { t.Errorf("the run carries reason failure=%q paused=%q: a park is neither", got.FailureReason, got.PausedReason) } row := stalledRow(t, f, run.ID) if row.QuarantineReason != "" { t.Errorf("the park was recorded as a quarantine (%q), which offers an operator a lift that would "+ "refuse — and says the stream is unreadable when it is merely somebody else's", row.QuarantineReason) } acct := f.account(t) if acct.Reserved != fixtureHold(100) { t.Errorf("the hold moved to %s while the projection was parked: the run is still going and its "+ "money must not be touched by our inability to follow it", acct.Reserved.USD()) } }