Make runner persistence session-only

This commit is contained in:
2026-09-03 04:09:12 +00:00
parent 6d748d65d3
commit 7997f81f47
17 changed files with 123 additions and 505 deletions

View File

@@ -31,54 +31,30 @@ func (s *terminalSessionStore) Save(_ context.Context, _ string, m *manifest.Man
return s.saveErr
}
type terminalRunStore struct {
events []string
saveErr error
saved *manifest.RunManifest
}
func (s *terminalRunStore) CreateRun(_ context.Context, sessionID, campaign, runID string, force bool, requestedStages []string) (*manifest.RunManifest, error) {
return manifest.NewRun(sessionID, campaign, runID, force, requestedStages, time.Now().UTC()), nil
}
func (s *terminalRunStore) SaveRun(_ context.Context, _ string, m *manifest.RunManifest) error {
s.events = append(s.events, "run")
s.saved = m
return s.saveErr
}
func TestPersistTerminalFailureKeepsSessionAuthorityBeforeRunAudit(t *testing.T) {
func TestPersistSessionTerminalFailurePreservesCauseAndSessionAuthority(t *testing.T) {
cause := errors.New("stage failed")
sessionFailure := errors.New("session unavailable")
runFailure := errors.New("run unavailable")
for _, tc := range []struct {
name string
sessionErr error
runErr error
cause error
alreadyTerminal bool
}{
{name: "both ledgers saved", cause: cause},
{name: "session saved", cause: cause},
{name: "session save fails", sessionErr: sessionFailure, cause: cause},
{name: "run save fails", runErr: runFailure, cause: cause},
{name: "both saves fail", sessionErr: sessionFailure, runErr: runFailure, cause: cause},
{name: "cancellation", cause: context.Canceled},
{name: "already terminal run", cause: cause, alreadyTerminal: true},
{name: "already terminal session", cause: cause, alreadyTerminal: true},
} {
t.Run(tc.name, func(t *testing.T) {
sessionStore := &terminalSessionStore{saveErr: tc.sessionErr}
runStore := &terminalRunStore{saveErr: tc.runErr}
sessionManifest := manifest.New("2026-05-03", time.Now().UTC())
runManifest := manifest.NewRun("2026-05-03", "sample-campaign", "20260503T010203Z-a1b2c3d4", false, nil, time.Now().UTC())
if tc.alreadyTerminal {
sessionManifest.RecordFailure(time.Now().UTC(), "earlier failure")
runManifest.MarkFailed(time.Now().UTC(), "earlier failure")
}
err := persistTerminalFailure(
context.Background(), sessionStore, "session.json", sessionManifest,
runStore, "run.json", runManifest, tc.cause,
err := persistSessionTerminalFailure(
context.Background(), sessionStore, "session.json", sessionManifest, tc.cause,
)
if !errors.Is(err, tc.cause) {
t.Fatalf("error = %v, want original cause %v", err, tc.cause)
@@ -86,35 +62,25 @@ func TestPersistTerminalFailureKeepsSessionAuthorityBeforeRunAudit(t *testing.T)
if tc.sessionErr != nil && !errors.Is(err, tc.sessionErr) {
t.Fatalf("error = %v, want session failure %v", err, tc.sessionErr)
}
if tc.runErr != nil && !errors.Is(err, tc.runErr) {
t.Fatalf("error = %v, want run failure %v", err, tc.runErr)
}
if got := append(sessionStore.events, runStore.events...); len(got) != 2 || got[0] != "session" || got[1] != "run" {
t.Fatalf("save order = %v, want [session run]", got)
if got := sessionStore.events; len(got) != 1 || got[0] != "session" {
t.Fatalf("save calls = %v, want [session]", got)
}
if sessionStore.saved == nil || sessionStore.saved.LastError == nil || sessionStore.saved.LastError.Message != tc.cause.Error() {
t.Fatalf("session terminal error = %#v, want %q", sessionStore.saved, tc.cause)
}
if runStore.saved == nil || runStore.saved.Status != manifest.RunManifestStatusFailed || runStore.saved.LastError == nil || runStore.saved.LastError.Message != tc.cause.Error() {
t.Fatalf("run terminal record = %#v, want failed with %q", runStore.saved, tc.cause)
}
})
}
}
func TestExecuteStagesTerminalizesCancelledStage(t *testing.T) {
cfg := testConfig(t)
runStore := &terminalRunStore{}
_, err := executeStages(context.Background(), cfg, []stage.Stage{
failingStage{name: "prepare", err: context.Canceled},
}, RunOptions{RunManifestStore: runStore})
}, RunOptions{})
if !errors.Is(err, context.Canceled) {
t.Fatalf("executeStages() error = %v, want context cancellation", err)
}
if runStore.saved == nil || runStore.saved.Status != manifest.RunManifestStatusFailed {
t.Fatalf("run terminal record = %#v, want failed", runStore.saved)
}
store := &manifest.LocalStore{}
sessionManifest, loadErr := store.Load(context.Background(), manifestPathFor(cfg))
if loadErr != nil {
@@ -125,7 +91,7 @@ func TestExecuteStagesTerminalizesCancelledStage(t *testing.T) {
}
}
func TestExecuteStagesResumeValidationFailureTerminalizesRunRecord(t *testing.T) {
func TestExecuteStagesResumeValidationFailureRecordsSessionFailure(t *testing.T) {
cfg := testConfig(t)
store := &manifest.LocalStore{}
seed := manifest.New(cfg.Session.SessionID, time.Now().UTC())
@@ -133,16 +99,11 @@ func TestExecuteStagesResumeValidationFailureTerminalizesRunRecord(t *testing.T)
if err := store.Save(context.Background(), manifestPathFor(cfg), seed); err != nil {
t.Fatalf("save session manifest: %v", err)
}
runStore := &terminalRunStore{}
_, err := executeStages(context.Background(), cfg, []stage.Stage{
resumeCheckingStage{name: "checked", validateErr: errors.New("inspection unavailable"), runs: new(int)},
}, RunOptions{RunManifestStore: runStore})
if err == nil || !strings.Contains(err.Error(), "inspection unavailable") || runStore.saved == nil {
t.Fatalf("executeStages() error = %v, want resume validation failure and terminal run", err)
}
if runStore.saved.Status != manifest.RunManifestStatusFailed {
t.Fatalf("run manifest status = %q, want failed", runStore.saved.Status)
}, RunOptions{})
if err == nil || !strings.Contains(err.Error(), "inspection unavailable") {
t.Fatalf("executeStages() error = %v, want resume validation failure", err)
}
persisted, loadErr := store.Load(context.Background(), manifestPathFor(cfg))
if loadErr != nil {