//go:build integration package main import ( "context" "encoding/json" "fmt" "testing" "github.com/nats-io/nats.go" "github.com/novox/mesh-controller/internal/broker" "github.com/novox/mesh-controller/internal/catalogue" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" ) // Acceptance 5 of milestone 2 (novox/hq ADR 0406 ยง3, issue 974), live, with the store and a real bus: two merges into one // repository within a minute, with two open pull requests, leave each pull request with exactly one queued check, by the // stream's rule. Each merge moves the trunk, and mesh-delivery asks the controller to check both open pull requests again // against it (`delivery check`, which is the controller's own path for a head announced); nothing moves a request, nothing // asks the forge, and the queue is read back whole. func TestTwoMergesWithinAMinuteLeaveEachOpenPullRequestOneQueuedCheck(t *testing.T) { open := aMesh(t) ctx := t.Context() js, _ := aBuildQueue(t) servingBus.Store(js) t.Cleanup(func() { servingBus.Store(nil) }) holdTheDeliverySeat(t, open) was := askDeliveryOwner t.Cleanup(func() { askDeliveryOwner = was }) askDeliveryOwner = func(_ context.Context, _ *nats.Conn, verb string, _ map[string]any) (json.RawMessage, error) { if verb != "groups" { t.Fatalf("asked the owner %s", verb) } return json.RawMessage(`[]`), nil } // The repository the two pull requests are into, as a module the mesh builds from it. if err := open.inventory.RegisterModule(ctx, catalogue.Manifest{Module: "mesh-controller", Version: "1"}, inventory.Source{Repository: "novox/mesh-controller", Ref: "main", BuiltFrom: "c0", Head: "c0"}); err != nil { t.Fatal(err) } heads := map[int]string{7: "7777777777777777777777777777777777777777", 8: "8888888888888888888888888888888888888888"} check := func(number int, trunk string) { t.Helper() p := orderMember{ID: fmt.Sprintf("novox/mesh-controller@%.12s", heads[number]), Repository: "novox/mesh-controller", Base: "main", Head: heads[number], Number: number, Paths: []string{"main.go"}, ModuleDirsSaid: true, BaseSHA: trunk, Ready: number == 8}.pull() p.MergeCheck, p.MergeCheckSaid = true, true if err := (following{open: open, oneShot: true}).PullUpdated(ctx, p); err != nil { t.Fatal(err) } } // Announced against the trunk as it stands, then asked again after each of two merges. for _, trunk := range []string{"1111111111111111111111111111111111111111", "2222222222222222222222222222222222222222", "3333333333333333333333333333333333333333"} { check(7, trunk) check(8, trunk) } q, err := link.ReadQueue(ctx, js, link.TheBuildMachine) if err != nil { t.Fatal(err) } seen := map[int]string{} for _, a := range q.Asks { c := a.Request.Check if c == nil { t.Fatalf("%s is no check", a.ID) } if _, twice := seen[c.Number]; twice { t.Errorf("pull request %d has two queued checks", c.Number) } seen[c.Number] = c.Trunk if a.State != link.AskWaiting { t.Errorf("%s is %s", a.ID, a.State) } if want := "3333333333333333333333333333333333333333"; c.Trunk != want { t.Errorf("pull request %d is queued against %.8s; want the newest trunk %.8s", c.Number, c.Trunk, want) } } if len(seen) != 2 { t.Fatalf("queued checks for %v; want one for each of the two open pull requests", seen) } // By the streams' own count: the ready one on its stream, the other on its. for stream, want := range map[string]uint64{broker.ChecksReadyStream: 1, broker.ChecksOtherStream: 1} { info, err := js.Context().StreamInfo(stream) if err != nil { t.Fatal(err) } if info.State.Msgs != want { t.Errorf("%s holds %d request(s); want %d", stream, info.State.Msgs, want) } } }