Refuse a retry of a failed plan that a newer done plan of its branch has passed (issue 1328, ADR 0451)

This commit is contained in:
2026-10-19 21:45:48 +00:00
parent 7b83aaa949
commit f1775281e2
4 changed files with 108 additions and 5 deletions
+61
View File
@@ -190,3 +190,64 @@ func TestNoClockRetriesAFailedPlan(t *testing.T) {
t.Fatalf("a plan failed beyond the window is still raised: %+v", got)
}
}
// A later plan of the same repository and branch, in a state, made after the failed one.
func laterPlan(id, branch, state string, after inventory.Plan) inventory.Plan {
q := aFailedPlan(id, func(p *inventory.Plan) {
p.State, p.Branch, p.Created = state, branch, after.Created.Add(time.Hour)
})
return q
}
// 1328/AC1: a later done plan of the same repository and branch makes `plans retry` refuse, naming the later plan.
func TestARetryIsRefusedWhenALaterPlanOfTheBranchIsDone(t *testing.T) {
failed := aFailedPlan("plan-1", func(p *inventory.Plan) { p.Branch, p.Created = "main", time.Date(2026, 10, 19, 12, 0, 0, 0, time.UTC) })
later := laterPlan("plan-2", "main", inventory.PlanDone, failed)
err := retryRefusal(failed, []inventory.Plan{failed, later}, map[string]string{})
if err == nil || !strings.Contains(err.Error(), "plan-2") || !strings.Contains(err.Error(), "passed") {
t.Fatalf("the retry was not refused, naming the later plan: %v", err)
}
}
// 1328/AC2: with the later plan still open the refusal is today's; a later done plan of another branch refuses nothing.
func TestARetryIsRefusedAsBeforeWhileTheLaterPlanIsOpenAndNotForAnotherBranch(t *testing.T) {
failed := aFailedPlan("plan-1", func(p *inventory.Plan) { p.Branch, p.Created = "main", time.Date(2026, 10, 19, 12, 0, 0, 0, time.UTC) })
open := laterPlan("plan-2", "main", inventory.PlanBuilding, failed)
err := retryRefusal(failed, []inventory.Plan{failed, open}, map[string]string{})
if err == nil || !strings.Contains(err.Error(), "supersedes it") {
t.Fatalf("with the later plan open the refusal is not today's: %v", err)
}
other := laterPlan("plan-3", "feature/x", inventory.PlanDone, failed)
if err := retryRefusal(failed, []inventory.Plan{failed, other}, map[string]string{}); err != nil {
t.Fatalf("a later done plan of another branch refused the retry: %v", err)
}
earlier := laterPlan("plan-4", "main", inventory.PlanDone, failed)
earlier.Created = failed.Created.Add(-time.Hour)
if err := retryRefusal(failed, []inventory.Plan{failed, earlier}, map[string]string{}); err != nil {
t.Fatalf("an earlier done plan refused the retry: %v", err)
}
}
// 1328/AC3: such a plan's plan-failed condition has no action and says it was passed by a newer delivery.
func TestAPassedFailedPlanHasNoActionAndSaysItWasPassed(t *testing.T) {
now := time.Date(2026, 10, 19, 18, 30, 0, 0, time.UTC)
failed := aFailedPlan("plan-1", func(p *inventory.Plan) { p.Branch, p.Created = "main", now.Add(-5*time.Hour) })
later := laterPlan("plan-2", "main", inventory.PlanDone, failed)
f := calm(now)
f.failed = []failedPlanFacts{failedFactsFor(failed, later)}
got := watchPlansFailed(f)
if len(got) != 1 || len(got[0].Actions) != 0 {
t.Fatalf("%+v", got)
}
o := got[0]
if !strings.Contains(o.Explanation, "newer delivery") || !strings.Contains(o.Explanation, "passed") {
t.Errorf("the words do not say it was passed by a newer delivery: %q", o.Explanation)
}
if strings.Contains(o.Explanation, "details say why") {
t.Errorf("the generic fallback was said: %q", o.Explanation)
}
words := conditions.Words{Headline: o.Headline, Explanation: o.Explanation, Resolved: o.Resolved, Needs: o.Needs}
if why, ok := conditions.PlainWords(words, "g14", "shanks"); !ok {
t.Errorf("not plain: %s\n%+v", why, words)
}
}