Merge pull request 'Integration tests that R4g never ran now pass: the assigner's fixture, the builder's restart test, four checker tests (issues 1927, 1928, 1929)' (#28) from fix/1927-assigner-fpset into main
This commit was merged in pull request #28.
This commit is contained in:
@@ -181,8 +181,10 @@ func TestAChangeIsComposedOnTheBus(t *testing.T) {
|
||||
b.putJSON(t, provider.AssignmentsBucket, "ace.other", []byte(`{"node":"ace","module":"other","build":"build-2"}`))
|
||||
heads := []delivery.Head{{Repository: "novox/web", Number: 7, Head: strings.Repeat("2", 40)}}
|
||||
hash, _ := delivery.HeadsHash(heads)
|
||||
cand, err := delivery.EncodeCandidate(delivery.Candidate{Group: "feat/x", HeadsHash: hash, Pulls: []delivery.CandidatePull{{Head: heads[0],
|
||||
Modules: []string{"web"}, Fps: map[string]delivery.CandidateModule{"web": {FP2: delivery.SourcePrefix2 + strings.Repeat("b", 64), Manifest: changed}}}}})
|
||||
// 1927/AC1: the candidate carries the fingerprint set the SDK computes, as the indexer writes it.
|
||||
pulls := []delivery.CandidatePull{{Head: heads[0],
|
||||
Modules: []string{"web"}, Fps: map[string]delivery.CandidateModule{"web": {FP2: delivery.SourcePrefix2 + strings.Repeat("b", 64), Manifest: changed}}}}
|
||||
cand, err := delivery.EncodeCandidate(delivery.Candidate{Group: "feat/x", HeadsHash: hash, Pulls: pulls, FPSet: delivery.FingerprintSet(pulls)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -179,7 +179,11 @@ type bed struct {
|
||||
// startBed starts the bus, the buckets the controller makes from the builder's and the build modules' manifests, the
|
||||
// build modules' streams and the builder's durable consumers on them, two nodes' toolchains, the indexer's manifests, and
|
||||
// the fake build modules. It runs no builder.
|
||||
func startBed(t *testing.T) *bed {
|
||||
func startBed(t *testing.T) *bed { return startBedWith(t, false) }
|
||||
|
||||
// startBedWith is startBed with the nodes' capacity 0 from the start when closed: a puller that reads its capacity before a
|
||||
// test could close the nodes may be in a pull already and takes the first ask put (issue 1928).
|
||||
func startBedWith(t *testing.T, closed bool) *bed {
|
||||
dir, err := os.MkdirTemp(os.Getenv("TMPDIR"), "b")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -243,6 +247,7 @@ func startBed(t *testing.T) *bed {
|
||||
b.builds = mk(delivery.BuilderBuildsBucket, delivery.BuilderBuildsHistory)
|
||||
b.instances = mk(instancesBucket, 1)
|
||||
b.fake = &fakeBuildModules{js: js, ctx: ctx, calls: map[string]int{}}
|
||||
b.fake.closed.Store(closed)
|
||||
b.fake.serve(t, "mesh-build-go", "n1", "n2")
|
||||
b.fake.serve(t, "mesh-build-node", "n1", "n2")
|
||||
b.fake.serve(t, "mesh-build-image", "n1") // the image build module runs on n1 only
|
||||
@@ -781,8 +786,7 @@ func (b *bed) asksPut(t *testing.T, nc *nats.Conn) func() []delivery.StepAsk {
|
||||
// 1887/AC3: a builder that started again while its asks waited in the streams (no node had a free slot) puts no second
|
||||
// ask: it waits for the asks its building key names, and the key is done once a node took them.
|
||||
func TestARestartedBuilderPutsNoSecondAsk(t *testing.T) {
|
||||
b := startBed(t)
|
||||
b.fake.closed.Store(true)
|
||||
b := startBedWith(t, true) // 1928/AC1: no node can take an ask from the start, so no pull in hand takes the first one
|
||||
b.module(t, "netcheck", "catalogue-a67f0196-netcheck.module.json", "modules/netcheck")
|
||||
sub, err := nats.Connect(b.srvURL, nats.UserInfo("ctl", "pw"))
|
||||
if err != nil {
|
||||
@@ -792,11 +796,13 @@ func TestARestartedBuilderPutsNoSecondAsk(t *testing.T) {
|
||||
asked := b.asksPut(t, sub)
|
||||
b.start(t, "n1", nil)
|
||||
v := b.await(t, "netcheck", func(v delivery.BuilderBuild) bool { return v.State == delivery.BuildBuilding && len(v.Asks) == 2 })
|
||||
for deadline := time.Now().Add(5 * time.Second); len(asked()) < 2 && time.Now().Before(deadline); time.Sleep(20 * time.Millisecond) {
|
||||
for deadline := time.Now().Add(20 * time.Second); len(asked()) < 2; time.Sleep(20 * time.Millisecond) {
|
||||
if time.Now().After(deadline) {
|
||||
t.Fatalf("asks put = %+v within 20 s, want the two the key names", asked())
|
||||
}
|
||||
}
|
||||
b.stop()
|
||||
b.start(t, "n1", nil) // the same builder, started again, while both asks wait
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
b.fake.open()
|
||||
done := b.await(t, "netcheck", isState(delivery.BuildDone))
|
||||
if got := asked(); len(got) != 2 {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -81,6 +82,9 @@ type fakeBuildModules struct {
|
||||
mu sync.Mutex
|
||||
calls map[string]int // by `<module>.<tool>`
|
||||
testTool *testing.T
|
||||
// beside is, by workspace, the siblings the fetch placed in it as `<owner/repo> <commit>`, sorted: a digest echoes them
|
||||
// (1929/AC2; mesh-sdk buildstep.besideOf), or the level runs whole and keeps nothing.
|
||||
beside map[string][]string
|
||||
}
|
||||
|
||||
func (f *fakeBuildModules) count(tool string) int {
|
||||
@@ -100,10 +104,30 @@ func (f *fakeBuildModules) result(t *testing.T, tool string, args map[string]any
|
||||
}
|
||||
fetched := readCaptured(t, name)
|
||||
fetched["commit"], fetched["base"] = args["commit"], args["base"]
|
||||
var refs []struct {
|
||||
Repository string `json:"repository"`
|
||||
Commit string `json:"commit"`
|
||||
}
|
||||
raw, _ := json.Marshal(args["beside"])
|
||||
_ = json.Unmarshal(raw, &refs)
|
||||
var lines []string
|
||||
for _, r := range refs {
|
||||
lines = append(lines, r.Repository+" "+r.Commit)
|
||||
}
|
||||
sort.Strings(lines)
|
||||
f.mu.Lock()
|
||||
if f.beside == nil {
|
||||
f.beside = map[string][]string{}
|
||||
}
|
||||
f.beside[fmt.Sprint(fetched["workspace"])] = lines
|
||||
f.mu.Unlock()
|
||||
v = fetched
|
||||
case strings.HasSuffix(tool, "_digest"):
|
||||
d := map[string]string{"unit": digU, "integration": digI}[level]
|
||||
v = map[string]any{"level": level, "packages": map[string]string{"fixture/slow": d}}
|
||||
f.mu.Lock()
|
||||
beside := f.beside[fmt.Sprint(args["workspace"])]
|
||||
f.mu.Unlock()
|
||||
v = map[string]any{"level": level, "packages": map[string]string{"fixture/slow": d}, "beside": beside}
|
||||
case strings.HasSuffix(tool, "_test"):
|
||||
r := readCaptured(t, "go-test-slow")
|
||||
r["level"] = level
|
||||
@@ -288,6 +312,15 @@ func startBed(t *testing.T) *bed {
|
||||
}
|
||||
b.buckets[name] = kv
|
||||
}
|
||||
// 1929/AC1: the indexer's value of the repository's module, with no TypeScript manifest, so the Go build module alone
|
||||
// judges the repository (an unknown repository is judged by both, issue 1887 A12).
|
||||
known, err := delivery.EncodeModule(delivery.Module{Repository: repo, Commit: trunk, Path: "modules/m", None2: "the fingerprint is computed with its chain"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := b.buckets[delivery.ModulesBucket].Put(ctx, "m", known); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b.setOnline(t, "n1", true)
|
||||
for _, module := range []string{"mesh-build-go", "mesh-build-node"} {
|
||||
b.setHealth(t, "n1", module)
|
||||
@@ -436,7 +469,7 @@ func (b *bed) owed(t *testing.T, number int, head, state string) delivery.Owed {
|
||||
// TestCheckerJudgesACandidateAndOwesItsStatus: a candidate the indexer wrote, one pull request of mesh-delivery, is fetched
|
||||
// merged onto the trunk the gate names, digested and tested at both levels on the node, kept, previewed; the verdict passes
|
||||
// with the counts of both levels and the composition not run, and the status mesh/check is owed on the head as success.
|
||||
func TestCheckerJudgesACandidateAndOwesItsStatus(t *testing.T) { // N6/AC5, 1300/AC5, 1445/AC1
|
||||
func TestCheckerJudgesACandidateAndOwesItsStatus(t *testing.T) { // N6/AC5, 1300/AC5, 1445/AC1, 1929/AC1
|
||||
b := startBed(t)
|
||||
key := b.candidate(t, "feat/x", pull(repo, 7, head1))
|
||||
b.start(t, nil)
|
||||
@@ -476,7 +509,7 @@ func TestCheckerJudgesACandidateAndOwesItsStatus(t *testing.T) { // N6/AC5, 1300
|
||||
|
||||
// TestACheckOfEqualInputsRunsNothing: a second candidate (another head, the same digests) is checked with every package
|
||||
// taken from the first's kept verdicts: no test step runs and no second kept key appears.
|
||||
func TestACheckOfEqualInputsRunsNothing(t *testing.T) { // 1287/AC5, 1288/AC6
|
||||
func TestACheckOfEqualInputsRunsNothing(t *testing.T) { // 1287/AC5, 1288/AC6, 1929/AC2
|
||||
b := startBed(t)
|
||||
first := b.candidate(t, "feat/x", pull(repo, 7, head1))
|
||||
b.start(t, nil)
|
||||
@@ -574,7 +607,7 @@ func TestACheckLeftRunningIsTakenOverWhenTheCheckerStarts(t *testing.T) { // N6/
|
||||
|
||||
// TestAMemberNamingAMeshSDKCommitOffTheTrunkFailsNamingTheCommit: the go.mod the forge answers names a commit that main does not
|
||||
// hold; the verdict fails naming the member and the commit and no test runs (ADR 0484 clause 9).
|
||||
func TestAMemberNamingAMeshSDKCommitOffTheTrunkFailsNamingTheCommit(t *testing.T) { // 1624/AC2
|
||||
func TestAMemberNamingAMeshSDKCommitOffTheTrunkFailsNamingTheCommit(t *testing.T) { // 1624/AC2, 1929/AC3
|
||||
b := startBed(t)
|
||||
b.stalePin.Store(true)
|
||||
key := b.candidate(t, "feat/x", pull(repo, 7, head1))
|
||||
@@ -583,9 +616,7 @@ func TestAMemberNamingAMeshSDKCommitOffTheTrunkFailsNamingTheCommit(t *testing.T
|
||||
if v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, "9aaa5c80ac5c") || !strings.Contains(v.Failed.Words, repo+"#7") {
|
||||
t.Fatalf("verdict = %+v", v)
|
||||
}
|
||||
if n := b.fake.count("mesh-build-go.build_go_test"); n != 0 {
|
||||
t.Errorf("%d test steps ran after a stale pin", n)
|
||||
}
|
||||
// 1929/AC3: no assertion that no test ran: one ask runs fetch, digest and tests (ADR 0525 §3).
|
||||
}
|
||||
|
||||
// triesStore is the tries bucket as the tools write it.
|
||||
@@ -671,7 +702,7 @@ func TestTheCheckerSaysOwedReadableOnTheBus(t *testing.T) { // R2-1
|
||||
break
|
||||
}
|
||||
if said.Module != "mesh-delivery-checker" || said.Node != "n1" || !said.Keeps("owed") || !said.Readable("owed") ||
|
||||
!said.Readable("previews") || !said.Readable("verdicts") || said.Readable("kept") || said.Readable("tries") || len(said.States) != 5 {
|
||||
t.Fatalf("the checker said %+v, want its five states with owed, previews and verdicts readable", said)
|
||||
!said.Readable("previews") || !said.Readable("verdicts") || !said.Readable("kept") || said.Readable("tries") || len(said.States) != 5 {
|
||||
t.Fatalf("the checker said %+v, want its five states with kept, owed, previews and verdicts readable (1929/AC4; 1887 A5b)", said)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user