agent/runner: ship repo.stats before job.finished in RunCheck/RunUnlock

RunCheck and RunUnlock were calling sendFinished before reportStats,
inverting the required job.started → log.stream → repo.stats →
job.finished envelope order. Move reportStats ahead of sendFinished in
both functions to match the pattern already correct in RunPrune.

Strengthen TestRunCheckShipsCheckStatus, TestRunCheckErrorsFoundShipsErrorsStatus,
and TestRunUnlockClearsLock with the same position-index ordering
assertions used by TestRunPruneShipsExpectedEnvelopes; these assertions
would have failed against the pre-fix code.
This commit is contained in:
2026-05-03 22:43:57 +01:00
parent 57bf9690f2
commit 22adde36b3
2 changed files with 83 additions and 2 deletions
+79
View File
@@ -143,6 +143,32 @@ esac
t.Fatalf("RunCheck: %v", err)
}
// Assert envelope ordering: job.started → log.stream → repo.stats → job.finished.
order := envelopeOrder(tx.envs)
wantTypes := []api.MessageType{api.MsgJobStarted, api.MsgLogStream, api.MsgRepoStats, api.MsgJobFinished}
positions := map[api.MessageType]int{}
for i, mt := range order {
if _, seen := positions[mt]; !seen {
positions[mt] = i
}
}
for i := 0; i < len(wantTypes)-1; i++ {
a, b := wantTypes[i], wantTypes[i+1]
pa, aOK := positions[a]
pb, bOK := positions[b]
if !aOK {
t.Errorf("envelope type %q not found in output %v", a, order)
continue
}
if !bOK {
t.Errorf("envelope type %q not found in output %v", b, order)
continue
}
if pa >= pb {
t.Errorf("expected %q before %q but positions are %d >= %d (order: %v)", a, b, pa, pb, order)
}
}
statsEnv := firstEnvOfType(t, tx.envs, api.MsgRepoStats)
var p api.RepoStatsPayload
if err := statsEnv.UnmarshalPayload(&p); err != nil {
@@ -180,6 +206,33 @@ esac
t.Fatalf("RunCheck: %v", err)
}
// Assert envelope ordering: job.started → repo.stats → job.finished.
// (No log.stream expected here because check exits immediately.)
order := envelopeOrder(tx.envs)
wantTypes := []api.MessageType{api.MsgJobStarted, api.MsgRepoStats, api.MsgJobFinished}
positions := map[api.MessageType]int{}
for i, mt := range order {
if _, seen := positions[mt]; !seen {
positions[mt] = i
}
}
for i := 0; i < len(wantTypes)-1; i++ {
a, b := wantTypes[i], wantTypes[i+1]
pa, aOK := positions[a]
pb, bOK := positions[b]
if !aOK {
t.Errorf("envelope type %q not found in output %v", a, order)
continue
}
if !bOK {
t.Errorf("envelope type %q not found in output %v", b, order)
continue
}
if pa >= pb {
t.Errorf("expected %q before %q but positions are %d >= %d (order: %v)", a, b, pa, pb, order)
}
}
statsEnv := firstEnvOfType(t, tx.envs, api.MsgRepoStats)
var p api.RepoStatsPayload
if err := statsEnv.UnmarshalPayload(&p); err != nil {
@@ -210,6 +263,32 @@ esac
t.Fatalf("RunUnlock: %v", err)
}
// Assert envelope ordering: job.started → log.stream → repo.stats → job.finished.
order := envelopeOrder(tx.envs)
wantTypes := []api.MessageType{api.MsgJobStarted, api.MsgLogStream, api.MsgRepoStats, api.MsgJobFinished}
positions := map[api.MessageType]int{}
for i, mt := range order {
if _, seen := positions[mt]; !seen {
positions[mt] = i
}
}
for i := 0; i < len(wantTypes)-1; i++ {
a, b := wantTypes[i], wantTypes[i+1]
pa, aOK := positions[a]
pb, bOK := positions[b]
if !aOK {
t.Errorf("envelope type %q not found in output %v", a, order)
continue
}
if !bOK {
t.Errorf("envelope type %q not found in output %v", b, order)
continue
}
if pa >= pb {
t.Errorf("expected %q before %q but positions are %d >= %d (order: %v)", a, b, pa, pb, order)
}
}
statsEnv := firstEnvOfType(t, tx.envs, api.MsgRepoStats)
var p api.RepoStatsPayload
if err := statsEnv.UnmarshalPayload(&p); err != nil {