diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/packages_integration_test.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/packages_integration_test.go index 069be8be..cac7840e 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/packages_integration_test.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/packages_integration_test.go @@ -5,6 +5,7 @@ package main import ( "context" "encoding/json" + "errors" "os" "strings" "testing" @@ -15,6 +16,8 @@ import ( "git.novox.be/novox/mesh-sdk/go/buscompose" "git.novox.be/novox/mesh-sdk/go/delivery" + + "mesh-delivery-indexer/internal/index" ) // TestAMeshSDKMergeMovesOnlyTheModulesImportingAChangedPackage (ADR 0593 §2-§4; D11): the indexer whole against the @@ -48,7 +51,7 @@ func TestAMeshSDKMergeMovesOnlyTheModulesImportingAChangedPackage(t *testing.T) m1: {"go.mod": "f1", "go.sum": "f2", "stdio.go": "f3", "announce/announce.go": "f4", "buildstep/step.go": "f5"}, m2: {"go.mod": "f1", "go.sum": "f2", "stdio.go": "f3", "announce/announce.go": "f4", "buildstep/step.go": "f6"}, } - var packages jetstream.KeyValue + var packages, seatDefs jetstream.KeyValue js, ctx, _, modules := startIndexer(t, func(context.Context, buildLog) {}, func(ctx context.Context, js jetstream.JetStream) { var err error if packages, err = js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: delivery.PackagesBucket, History: delivery.PackagesHistory}); err != nil { @@ -70,6 +73,14 @@ func TestAMeshSDKMergeMovesOnlyTheModulesImportingAChangedPackage(t *testing.T) } } serveForge(t, js.Conn(), listing, goSumContent) + // The seat definitions' state and stream (DLV-I17), as the grants part makes them from the manifest. + if seatDefs, err = js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: index.SeatDefinitionsBucket, History: 5}); err != nil { + t.Fatal(err) + } + if _, err := js.CreateStream(ctx, jetstream.StreamConfig{Name: "INDEXER_SEAT_DEFINITIONS", + Subjects: []string{"mesh.stream.mesh-delivery-indexer.event." + index.SeatDefinitionChangedEvent}}); err != nil { + t.Fatal(err) + } }) merged := func(repo string, number int, merge string, f delivery.Forge) { t.Helper() @@ -179,8 +190,67 @@ func TestAMeshSDKMergeMovesOnlyTheModulesImportingAChangedPackage(t *testing.T) if now := module("plain", func(delivery.Module) bool { return true }); now.FP2 != plain.FP2 { t.Errorf("plain moved from %s to %s, though it imports nothing the merge changed", plain.FP2, now.FP2) } + + // DLV-I17, I18 (research 081 test 21, design 26): mesh-contracts merges a valid definition, said once; a pull + // request's head whose definition does not parse is kept as its entry naming why while the trunk's stays; it closes + // and its entry goes. + const contractsMerge, badHead = "5555555555555555555555555555555555555555", "6666666666666666666666666666666666666666" + git := `{"format":1,"seat":"git","scope":"mesh","grain":"seat-list","decision":"novox/hq ADR 0111","delivers":"git"}` + seatFiles[contractsMerge] = map[string]string{"seats/index.json": `{"format":1,"seats":["git"]}`, "seats/git/seat.json": git} + seatFiles[badHead] = map[string]string{"seats/index.json": `{"format":1,"seats":["git"]}`, "seats/git/seat.json": `{"format":1,"seat":"git"}`} + contracts := `{"module":"mesh-contracts","version":"1","build":{"artifacts":[{"name":"go","kind":"package","language":"go","paths":["go"],"series":"0.1","generated-from":"."}]}}` + merged("novox/mesh-contracts", 80, contractsMerge, delivery.Forge{Root: "dddddddddddddddddddddddddddddddddddddddd", + Trees: map[string]string{"": "dddddddddddddddddddddddddddddddddddddddd"}, Manifests: map[string]string{"": contracts}}) + entry := func(key string, ok func(index.SeatDefinitionEntry) bool) index.SeatDefinitionEntry { + t.Helper() + var e index.SeatDefinitionEntry + for deadline := time.Now().Add(20 * time.Second); time.Now().Before(deadline); time.Sleep(50 * time.Millisecond) { + if v, err := seatDefs.Get(ctx, key); err == nil && json.Unmarshal(v.Value(), &e) == nil && ok(e) { + return e + } + } + t.Fatalf("%s.%s = %+v within 20 s", index.SeatDefinitionsState, key, e) + return e + } + trunk := entry("git.trunk", func(e index.SeatDefinitionEntry) bool { return e.Commit == contractsMerge && e.Digest != "" }) + pull := func(action string) { + t.Helper() + data, _ := delivery.EncodeForge(delivery.Forge{Kind: delivery.ForgePull, Action: action, Repository: "novox/mesh-contracts", Branch: "feat/y", + Number: 81, Head: badHead, Dirs: []string{""}, DirsKnown: true, Manifests: map[string]string{"": contracts}}) + if _, err := js.Publish(ctx, "mesh.stream.mesh-delivery-forge-bridge.event.forge.novox.mesh-contracts.pull", data); err != nil { + t.Fatal(err) + } + } + pull(delivery.PullUpdated) + pk := index.SeatDefinitionKey("git", index.SourcePull, "novox/mesh-contracts", 81) + if bad := entry(pk, func(e index.SeatDefinitionEntry) bool { return e.Unparsed != "" }); !strings.Contains(bad.Unparsed, "scope") || bad.Definition != nil { + t.Errorf("the pull request's entry %+v, want why it does not parse and no definition", bad) + } + if now := entry("git.trunk", func(index.SeatDefinitionEntry) bool { return true }); now.Digest != trunk.Digest { + t.Errorf("the trunk's entry moved to %+v on a pull request's head", now) + } + pull(delivery.PullClosed) + for deadline := time.Now().Add(20 * time.Second); ; time.Sleep(50 * time.Millisecond) { + if _, err := seatDefs.Get(ctx, pk); errors.Is(err, jetstream.ErrKeyNotFound) { + break + } + if time.Now().After(deadline) { + t.Fatalf("%s is kept 20 s after its pull request closed", pk) + } + } + info, err := js.Stream(ctx, "INDEXER_SEAT_DEFINITIONS") + if err != nil { + t.Fatal(err) + } + // Said: the trunk's git, and the closed pull request's removal; the unparsed head says nothing. + if n := info.CachedInfo().State.Msgs; n != 2 { + t.Errorf("seat-definition-changed said %d times, want 2", n) + } } +// seatFiles are mesh-contracts' files by commit, as gitea_get_file answers them. +var seatFiles = map[string]map[string]string{} + // serveForge answers the forge's tools on the bus as the gitea module does: gitea_commit_trees (the trees of a commit, // or with files the blobs under each directory of mesh-sdk's listing at that commit), gitea_get_file (every go.sum // holds goSum) and gitea_contains. @@ -213,6 +283,17 @@ func serveForge(t *testing.T, nc *nats.Conn, listing map[string]map[string]strin t.Fatal(err) } file, err := nc.Subscribe("mesh.mod.gitea.tool.gitea_get_file", func(m *nats.Msg) { + var a struct{ Repo, Path, Ref string } + _ = json.Unmarshal(m.Data, &a) + if a.Repo == "mesh-contracts" { // the seat definitions (DLV-I17): a path the commit does not hold is a 404 + if v, ok := seatFiles[a.Ref][a.Path]; ok { + reply(m, map[string]any{"file": map[string]any{"content": v}}) + } else { + raw, _ := json.Marshal(map[string]any{"error": "gitea: 404 Not Found", "node": "n0"}) + _ = m.Respond(raw) + } + return + } reply(m, map[string]any{"file": map[string]any{"content": goSum}}) }) if err != nil {