diff --git a/internal/orchestrator/crash_test.go b/internal/orchestrator/crash_test.go index ab1de72..fc4ab62 100644 --- a/internal/orchestrator/crash_test.go +++ b/internal/orchestrator/crash_test.go @@ -1,10 +1,15 @@ package orchestrator import ( + "bytes" "context" + "encoding/json" + "flag" + "fmt" "os" "os/exec" "path/filepath" + "regexp" "strings" "syscall" "testing" @@ -17,6 +22,28 @@ import ( "github.com/akynte/boundedcode/internal/telemetry" ) +var updateReplay = flag.Bool("update-replay", false, "rewrite testdata/crash-replay.json from this run of TestSIGKILLResume") + +// crashReplay is what TestSIGKILLResume observes, in order. The test compares +// it with testdata/crash-replay.json and fails on any difference, so a passing +// run proves the file is what a real kill and resume produced. Readers (such as +// a site that replays it) can rely on that; -update-replay rewrites the file +// when the behaviour changes on purpose. +type crashReplay struct { + Test string `json:"test"` + About string `json:"about"` + Steps []replayStep `json:"steps"` +} + +// rfc3339 matches the wall-clock times in observed messages, which differ on every run. +var rfc3339 = regexp.MustCompile(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z`) + +type replayStep struct { + Actor string `json:"actor"` + Action string `json:"action"` + Observed string `json:"observed"` +} + // TestKilledRunnerHelper is the child process of TestSIGKILLResume: it runs // the task with an agent that edits the repository and then hangs, until the // parent SIGKILLs it. Not a test on its own. @@ -55,6 +82,16 @@ func TestSIGKILLResume(t *testing.T) { if err != nil { t.Fatal(err) } + l := task.Ledger{DB: s.DB} + rec := crashReplay{Test: "TestSIGKILLResume", About: "A runner process is killed with SIGKILL in the middle of an agent turn; " + + "a second runner is refused, a third takes over the stale lease and finishes the task. Each step is what the test observed."} + see := func(actor, action, observed string) { + observed = rfc3339.ReplaceAllString(strings.ReplaceAll(observed, tk.ID, ""), "