From cd303f68edd12892919866169b5806f48ef2fbc6 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 19 Oct 2026 01:02:02 +0200 Subject: [PATCH] The first-build test reads the asks its stub records under a lock: the merge path asks on its own goroutine (issue 1146) --- .../first_builds_integration_test.go | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/cmd/mesh-controller/first_builds_integration_test.go b/cmd/mesh-controller/first_builds_integration_test.go index dcd75159..e6f363ce 100644 --- a/cmd/mesh-controller/first_builds_integration_test.go +++ b/cmd/mesh-controller/first_builds_integration_test.go @@ -5,6 +5,8 @@ package main import ( "context" "encoding/json" + "slices" + "sync" "testing" "time" @@ -41,14 +43,19 @@ func TestAMergeOnTheApproveIntoANewRepositoryAsksTheFirstBuildAndItsOutcomeRegis inv := open.inventory // The real ask, guard and queue included: only its answers are kept, so the build's outcome can be matched. aForgeAndABuildSeat(t, open) - asked := &[][3]string{} + // Written by the controller's merge path on its own goroutine, read by the test: under one lock. + var mu sync.Mutex + var asks [][3]string var ids []string + asked := func() [][3]string { mu.Lock(); defer mu.Unlock(); return slices.Clone(asks) } realAsk := askABuild askABuild = func(c context.Context, source buildSource, path, ref string) (string, error) { id, err := realAsk(c, source, path, ref) if err == nil { - *asked = append(*asked, [3]string{source.Repository, path, ref}) + mu.Lock() + asks = append(asks, [3]string{source.Repository, path, ref}) ids = append(ids, id) + mu.Unlock() } return id, err } @@ -126,23 +133,26 @@ func TestAMergeOnTheApproveIntoANewRepositoryAsksTheFirstBuildAndItsOutcomeRegis forge() } else { forge() - if len(*asked) != 0 { - t.Fatalf("1146/AC7: the merge asked %v before mesh-delivery's word was kept", *asked) + if len(asked()) != 0 { + t.Fatalf("1146/AC7: the merge asked %v before mesh-delivery's word was kept", asked()) } word() } if !c.wantRegiter { - if len(*asked) != 0 { - t.Fatalf("1146/AC7: a merge whose record holds no Approve asked %v", *asked) + if len(asked()) != 0 { + t.Fatalf("1146/AC7: a merge whose record holds no Approve asked %v", asked()) } return } want := [3]string{"novox/mesh-delivery", "", "main"} - becomes(t, 10*time.Second, "the first build asked", func() bool { return len(*asked) >= 1 }) - if len(*asked) != 1 || (*asked)[0] != want { - t.Fatalf("1146/AC7: the merge asked %v, want %v once", *asked, want) + becomes(t, 10*time.Second, "the first build asked", func() bool { return len(asked()) >= 1 }) + if got := asked(); len(got) != 1 || got[0] != want { + t.Fatalf("1146/AC7: the merge asked %v, want %v once", got, want) } - build := onTrunk(ids[0], "novox/mesh-delivery", "git", "", map[string]any{"module": "mesh-delivery-view", "version": "1"}) + mu.Lock() + first := ids[0] + mu.Unlock() + build := onTrunk(first, "novox/mesh-delivery", "git", "", map[string]any{"module": "mesh-delivery-view", "version": "1"}) build.Commit = commit if _, _, err := takeIn(ctx, inv, build); err != nil { t.Fatalf("1146/AC7: the outcome of the first build was refused: %v", err)