delivery: keep a merged member in its group's candidate, marked merged, so the heads an Approve covers hold until every member merged (issue 1921, issue 1791, try speed S9)

This commit is contained in:
2026-10-25 01:17:16 +02:00
parent 4a12ccb78d
commit 571ac521f0
2 changed files with 38 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
package delivery
import (
"strings"
"testing"
)
// 1791/1921 S9: a member of a group that merged stays in the candidate, marked merged, with its head, so the heads hash
// the Approve covers holds until every member merged; a candidate whose every member merged is refused.
func TestACandidateKeepsAMergedMemberAndRefusesOneWhoseEveryMemberMerged(t *testing.T) {
a := Head{Repository: "novox/a", Number: 1, Head: strings.Repeat("1", 40)}
b := Head{Repository: "novox/b", Number: 2, Head: strings.Repeat("2", 40)}
hash, _ := HeadsHash([]Head{a, b})
c := Candidate{Group: "feat/x", HeadsHash: hash, Pulls: []CandidatePull{{Head: a, Modules: []string{}, Merged: true}, {Head: b, Modules: []string{}}}}
raw, err := EncodeCandidate(c)
if err != nil {
t.Fatal(err)
}
back, err := DecodeCandidate(raw)
if err != nil || !back.Pulls[0].Merged || back.Pulls[1].Merged || back.HeadsHash != hash {
t.Fatalf("read back %+v, %v", back, err)
}
c.Pulls[1].Merged = true
if _, err := EncodeCandidate(c); err == nil {
t.Error("a candidate whose every member merged was written")
}
}
+11
View File
@@ -269,6 +269,10 @@ type CandidatePull struct {
Head
Modules []string `json:"modules"`
Fps map[string]CandidateModule `json:"fps,omitempty"`
// Merged says the pull request merged while another member of its group is still open: it stays in the candidate
// with the head it merged at, so the heads hash an Approve covers holds until every member merged, and the checker
// judges the members still open by the verdict the group had (issue 1791). A reader of the older shape ignores it.
Merged bool `json:"merged,omitempty"`
}
// CandidateNoneNoModule is the None2 of a candidate's module whose directory the merge preview names no manifest for:
@@ -346,6 +350,13 @@ func (c Candidate) check() error {
if len(c.Pulls) == 0 {
return fmt.Errorf("delivery: a candidate names no pull request")
}
open := false
for _, p := range c.Pulls {
open = open || !p.Merged
}
if !open {
return fmt.Errorf("delivery: every pull request of the candidate merged: the group is no candidate any more")
}
heads := make([]Head, len(c.Pulls))
for i, p := range c.Pulls {
heads[i] = p.Head