mesh/delivery merged: source.main moved to novox/mesh-catalog@9cdac09ad962
mesh/check being carried to dfe490d9e245 on 9cdac09ad962 | from 25f615456423
92 lines
3.1 KiB
Go
92 lines
3.1 KiB
Go
//go:build integration
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
// Issue 1781 (ADR 0493 §1, §2, §4), the module whole over a real bus: a milestone made by its verb is kept in the
|
|
// state the manifest declares, and two instances linking one issue to two milestones at the same moment leave it in
|
|
// exactly one; the other is refused naming the first, and the milestones and the list say the same.
|
|
func TestAnIssueRaceIntoTwoMilestonesOverTheBusEndsInOne(t *testing.T) {
|
|
b := newBus(t)
|
|
var events []event
|
|
var evMu sync.Mutex
|
|
settings := writeSettings(t, 365)
|
|
one, two := b.start(t, settings, &events, &evMu), b.start(t, settings, &events, &evMu)
|
|
must := func(raw json.RawMessage, err error) json.RawMessage {
|
|
t.Helper()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return raw
|
|
}
|
|
must(one.call("open", map[string]any{"title": "a milestone's issue", "report": "seen"}))
|
|
must(one.call("milestone-set", map[string]any{"design": 47, "id": "R6", "outcome": "nodes apply their wants", "order": 1}))
|
|
must(two.call("milestone-set", map[string]any{"design": 47, "id": "3 fix", "outcome": "a group merges in one pass", "order": 2}))
|
|
errs := make([]error, 2)
|
|
var wg sync.WaitGroup
|
|
for k, c := range []struct {
|
|
i *instance
|
|
target string
|
|
}{{one, "47/R6"}, {two, "47/3-fix"}} {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
_, errs[k] = c.i.call("link", map[string]any{"number": 365, "kind": "milestone", "target": c.target})
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
if (errs[0] == nil) == (errs[1] == nil) {
|
|
t.Fatalf("both or neither linked: %v", errs)
|
|
}
|
|
refused := errs[0]
|
|
if refused == nil {
|
|
refused = errs[1]
|
|
}
|
|
if !strings.Contains(refused.Error(), "is in milestone") {
|
|
t.Fatalf("refused with %v", refused)
|
|
}
|
|
var read MilestonesRead
|
|
_ = json.Unmarshal(must(two.call("milestones", map[string]any{"design": 47})), &read)
|
|
if len(read.Milestones) != 2 || read.Milestones[0].ID != "R6" || read.Milestones[1].ID != "3-fix" ||
|
|
len(read.Milestones[0].Issues)+len(read.Milestones[1].Issues) != 1 {
|
|
t.Fatalf("%+v", read.Milestones)
|
|
}
|
|
in := "47/R6"
|
|
if len(read.Milestones[1].Issues) == 1 {
|
|
in = "47/3-fix"
|
|
}
|
|
var l Listed
|
|
_ = json.Unmarshal(must(one.call("list", map[string]any{"linked": "milestone:" + in})), &l)
|
|
if len(l.Rows) != 1 || l.Rows[0].Number != 365 {
|
|
t.Fatalf("listed %+v", l)
|
|
}
|
|
var hist MilestonesRead
|
|
_ = json.Unmarshal(must(one.call("milestones", map[string]any{"one": "47/R6"})), &hist)
|
|
if len(hist.History) != 1 {
|
|
t.Fatalf("history %d values", len(hist.History))
|
|
}
|
|
// The moves history over the real log: the open, from nothing to open, read by the other instance.
|
|
var ch ChangesRead
|
|
_ = json.Unmarshal(must(two.call("changes", map[string]any{"since": "60m"})), &ch)
|
|
if len(ch.Changes) != 1 || ch.Changes[0].Number != 365 || ch.Changes[0].Kind != OpOpened || ch.Changes[0].To != StatusOpen ||
|
|
ch.Changes[0].Title != "a milestone's issue" {
|
|
t.Fatalf("changes %+v", ch)
|
|
}
|
|
evMu.Lock()
|
|
defer evMu.Unlock()
|
|
var kinds []string
|
|
for _, e := range events {
|
|
kinds = append(kinds, e.Key)
|
|
}
|
|
if fmt.Sprint(kinds) != "[opened milestone-set milestone-set linked]" {
|
|
t.Fatalf("events %v", kinds)
|
|
}
|
|
}
|