package release import ( "errors" "os" "os/exec" "strings" "testing" "time" ) type recoveryCrashStore struct { *healthyRetryStore boundary string } func (s *recoveryCrashStore) Write(name string, raw []byte) error { if !strings.HasSuffix(name, ".outcome") { return s.healthyRetryStore.Write(name, raw) } if s.boundary == "visible" { s.fail = 1 } err := s.healthyRetryStore.Write(name, raw) if err != nil && s.boundary != "visible" { return err } os.Exit(74) return nil } func TestPlan14HistoryRecoveryProcessCrash(t *testing.T) { b := HistoryBinding{Host: proofHost, Account: "bab", App: "console"} now := time.Date(2026, 10, 13, 0, 0, 0, 0, time.UTC) r := HistoryRecovery{Binding: b, Attempt: HistoryAttempt{ID: "crashed-recovery", Source: "canonical", Version: "v2", SHA256: "failed-digest", Platform: AdoptPlatform(), Started: now, PromotedAt: now}, Settled: true, Failed: true} if boundary := os.Getenv("PLAN14_OUTCOME_CRASH"); boundary != "" { h := &RootHistory{Store: &recoveryCrashStore{healthyRetryStore: &healthyRetryStore{dir: os.Getenv("PLAN14_OUTCOME_DIR"), fault: errors.New("directory sync failed")}, boundary: boundary}} if err := h.RetainRecovery(r); err != nil { t.Fatal(err) } t.Fatal("crash boundary not reached") } for _, boundary := range []string{"durable", "visible"} { t.Run(boundary, func(t *testing.T) { _, s, b := rootHistoryFixture(t) cmd := exec.CommandContext(t.Context(), os.Args[0], "-test.run=^TestPlan14HistoryRecoveryProcessCrash$", "-test.count=1") cmd.Dir = t.TempDir() cmd.Env = append(os.Environ(), "PLAN14_OUTCOME_CRASH="+boundary, "PLAN14_OUTCOME_DIR="+s.dir) out, err := cmd.CombinedOutput() var exit *exec.ExitError if !errors.As(err, &exit) || exit.ExitCode() != 74 { t.Fatalf("child: %v\n%s", err, out) } h := &RootHistory{Store: s} if refs, err := h.Refusals(b); err != nil || len(refs) != 1 || refs[0].SHA256 != "failed-digest" { t.Fatalf("crash erased refusal: %+v %v", refs, err) } }) } }