gate: an approved merge writes its turn on every repository it goes into before it is handed, kept for a group until its last member merged, read by the checker (ADR 0548 §3; issue 568 G2)
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
sdk "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// One check per delivery (novox/hq ADR 0548), the gate's part: nothing merges into a repository while a check of other heads
|
||||
@@ -22,6 +26,49 @@ func approvedLone(w *world) string {
|
||||
return IDOf(oneRepo, oneHead)
|
||||
}
|
||||
|
||||
// memTurns is the bucket turns in memory; it logs each write with the merges the fake bridge had made by then.
|
||||
type memTurns struct {
|
||||
mu sync.Mutex
|
||||
m map[string]sdk.Turn
|
||||
log []string
|
||||
merges func() int
|
||||
}
|
||||
|
||||
func newMemTurns(w *world) *memTurns {
|
||||
return &memTurns{m: map[string]sdk.Turn{}, merges: func() int { return len(w.forge.merges) }}
|
||||
}
|
||||
|
||||
func (f *memTurns) All() (map[string]sdk.Turn, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
out := map[string]sdk.Turn{}
|
||||
for k, v := range f.m {
|
||||
out[k] = v
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (f *memTurns) Put(t sdk.Turn) error {
|
||||
if _, err := sdk.EncodeTurn(t); err != nil {
|
||||
return err
|
||||
}
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
f.m[t.Repository] = t
|
||||
f.log = append(f.log, fmt.Sprintf("put %s after %d merges", t.Repository, f.merges()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *memTurns) Delete(repository string) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
delete(f.m, repository)
|
||||
f.log = append(f.log, fmt.Sprintf("delete %s after %d merges", repository, f.merges()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *memTurns) said() string { f.mu.Lock(); defer f.mu.Unlock(); return strings.Join(f.log, "; ") }
|
||||
|
||||
func TestOneCheckPerDeliveryAtTheGate(t *testing.T) {
|
||||
for name, run := range map[string]func(t *testing.T){
|
||||
// §2: a merge into a repository a running verdict names is not written to merges, and is written on that verdict's pass.
|
||||
@@ -67,6 +114,37 @@ func TestOneCheckPerDeliveryAtTheGate(t *testing.T) {
|
||||
t.Fatalf("merges %v, run %+v: its own check held it", w.forge.merges, d.Ready)
|
||||
}
|
||||
},
|
||||
// §3: the turn is written before the verdicts are read for the merge, and deleted after the last merge.
|
||||
"a lone merge's turn is written before it is handed and deleted once it merged": func(t *testing.T) {
|
||||
w := newWorld(t)
|
||||
turns := newMemTurns(w)
|
||||
w.h.Turns = turns
|
||||
approvedLone(w)
|
||||
w.settleAll()
|
||||
if len(w.forge.merges) != 1 || turns.said() != "put "+oneRepo+" after 0 merges; delete "+oneRepo+" after 1 merges" {
|
||||
t.Fatalf("merges %v; the turns were %q", w.forge.merges, turns.said())
|
||||
}
|
||||
},
|
||||
"a group's turns stand on every member's repository until its last member merged": func(t *testing.T) {
|
||||
w := newWorld(t)
|
||||
turns := newMemTurns(w)
|
||||
w.h.Turns = turns
|
||||
a, b := pr(oneRepo, 1, oneHead, "feat/two"), pr(oneRepo2, 2, oneHead2, "feat/two")
|
||||
requiresCheck(w, a, b)
|
||||
w.h.PullUpdated(a)
|
||||
w.h.PullUpdated(b)
|
||||
w.h.wantForge()
|
||||
putCheckerVerdict(w, "pass", headOf(oneRepo, 1, oneHead), headOf(oneRepo2, 2, oneHead2))
|
||||
w.h.CommitStatus(checkStatus(oneRepo, oneHead, "success", oneTrunk))
|
||||
w.h.CommitStatus(checkStatus(oneRepo2, oneHead2, "success", oneTrunk))
|
||||
for range 3 {
|
||||
w.settleAll()
|
||||
}
|
||||
want := "put " + oneRepo2 + " after 0 merges; put " + oneRepo + " after 0 merges; delete " + oneRepo2 + " after 2 merges; delete " + oneRepo + " after 2 merges"
|
||||
if len(w.forge.merges) != 2 || turns.said() != want {
|
||||
t.Fatalf("merges %v; the turns were %q, want %q", w.forge.merges, turns.said(), want)
|
||||
}
|
||||
},
|
||||
} {
|
||||
t.Run(name, run)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import "git.novox.be/novox/mesh-sdk/go/announce"
|
||||
// issue 1790): the module says it itself, from its code, before it serves, and a reader is granted what it said
|
||||
// (clause 3). A state is readable when another module's manifest reads it (keeps_test.go names the readers). keeps_test.go holds the list to the
|
||||
// manifest's `state`, and keeps_integration_test.go that the module announces it on a bus.
|
||||
var Kept = []announce.State{{Name: "asks", Readable: true}, {Name: "conditions"}, {Name: "deliveries"}, {Name: "groups"}, {Name: "merges", Readable: true}, {Name: "owed", Readable: true}, {Name: "pulls", Readable: true}, {Name: "source", Readable: true}}
|
||||
var Kept = []announce.State{{Name: "asks", Readable: true}, {Name: "conditions"}, {Name: "deliveries"}, {Name: "groups"}, {Name: "merges", Readable: true}, {Name: "owed", Readable: true}, {Name: "pulls", Readable: true}, {Name: "source", Readable: true}, {Name: "turns", Readable: true}}
|
||||
|
||||
// announceKept says Kept through keeps, which is stdio.Keeps.
|
||||
func announceKept(keeps func(...announce.State) error) error { return keeps(Kept...) }
|
||||
|
||||
@@ -17,6 +17,9 @@ var readers = map[string][]string{
|
||||
"pulls": {"build-agent"}, // in mesh-controller
|
||||
"owed": {"mesh-delivery-forge-bridge"},
|
||||
"source": {"mesh-delivery-indexer", "mesh-delivery-checker"},
|
||||
// ADR 0548 §3, §4: the checker reads the merge turns and claims no check of a repository one names; the gate alone writes
|
||||
// them (a state is written by its owner's account alone, ADR 0495 §3: announcing it readable grants a read, never a write).
|
||||
"turns": {"mesh-delivery-checker"},
|
||||
}
|
||||
|
||||
// 1790/S: the states the module announces are the states its manifest declares, no more, no fewer, and the readable
|
||||
|
||||
@@ -65,6 +65,7 @@ func main() {
|
||||
Operator: kvOperator{},
|
||||
Merges: kvMerges{},
|
||||
Source: kvSource{},
|
||||
Turns: kvTurns{},
|
||||
Owed: kvOwed{},
|
||||
Pulls: kvPulls{},
|
||||
// The gate says what stalled to the operator itself (issue 1275, ADR 0437 §9).
|
||||
|
||||
@@ -91,9 +91,10 @@ func TestItClaimsTheDeliverySeatAndFollowsWhatItHandles(t *testing.T) {
|
||||
if !reflect.DeepEqual(m.Emits, []string{"transition", "group", "merged"}) {
|
||||
t.Fatalf("emits %v", m.Emits)
|
||||
}
|
||||
// Eight buckets, and no `asked` (the teller asks, ADR 0437 §6): ADR 0450 §3, and the conditions it raised (issue 1275).
|
||||
// Nine buckets, and no `asked` (the teller asks, ADR 0437 §6): ADR 0450 §3, the conditions it raised (issue 1275) and the
|
||||
// merge turns (ADR 0548 §3).
|
||||
buckets := m.buckets()
|
||||
if !reflect.DeepEqual(buckets, []string{"deliveries", "groups", "pulls", "asks", "merges", "owed", "source", "conditions"}) {
|
||||
if !reflect.DeepEqual(buckets, []string{"deliveries", "groups", "pulls", "asks", "merges", "owed", "source", "conditions", "turns"}) {
|
||||
t.Fatalf("state %v", buckets)
|
||||
}
|
||||
// What it reads: the teller's answers and the checker's verdicts (issue 1302). The carry from today's module's buckets, the
|
||||
|
||||
@@ -277,6 +277,7 @@ func (h *Holder) readyAsks(now time.Time) []func() {
|
||||
asks = append(asks, a)
|
||||
}
|
||||
}
|
||||
asks = append(asks, h.turnWrites()...) // the merge turns, after the holds of this look (ADR 0548 §3)
|
||||
asks = append(asks, h.settleMerges()...)
|
||||
asks = append(asks, h.settleForgeMerges()...)
|
||||
asks = append(asks, h.withdrawnKeys()...)
|
||||
|
||||
@@ -282,7 +282,10 @@ func (h *Holder) turnWrites() []func() {
|
||||
h.Poke()
|
||||
}}
|
||||
}
|
||||
want := h.wantedTurns()
|
||||
want := map[string]sdk.Turn{}
|
||||
if h.checkGates() {
|
||||
want = h.wantedTurns() // while the check gates no merge, no merge waits for one, and no turn holds a check
|
||||
}
|
||||
var out []func()
|
||||
repos := make([]string, 0, len(want)+len(h.turnsHeld))
|
||||
for repo := range want {
|
||||
|
||||
@@ -61,7 +61,11 @@
|
||||
"name": "source",
|
||||
"history": 64
|
||||
},
|
||||
"conditions"
|
||||
"conditions",
|
||||
{
|
||||
"name": "turns",
|
||||
"history": 5
|
||||
}
|
||||
],
|
||||
"reads": [
|
||||
"mesh-delivery-teller.answers",
|
||||
|
||||
Reference in New Issue
Block a user