From 3456f34fa82a94f1178e09d7ce3f616cdc4e4401 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 5 Nov 2026 16:28:52 +0100 Subject: [PATCH 1/2] =?UTF-8?q?View:=20no=20go/delivery=20left;=20wants,?= =?UTF-8?q?=20haves,=20presence=20and=20stuck=20through=20their=20clients,?= =?UTF-8?q?=20the=20gate's,=20the=20catalog's=20and=20the=20assigner's=20s?= =?UTF-8?q?eats,=20the=20builder's=20builds,=20the=20build=20step,=20the?= =?UTF-8?q?=20viewstate;=20the=20holders=20and=20buckets=20it=20names=20ar?= =?UTF-8?q?e=20its=20own=20constants=20checked=20against=20its=20manifest?= =?UTF-8?q?=20(D-11;=20ADR=200634=20=C2=A71,=20=C2=A72;=20issue=201966)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i --- .../cmd/mesh-delivery-cli/main_test.go | 90 +++-- .../cmd/mesh-delivery-view-tools/main.go | 52 +-- .../cmd/mesh-delivery-view-tools/main_test.go | 40 ++- .../mesh-delivery-view-tools/owners_test.go | 49 ++- .../tiers_rows_test.go | 48 +-- .../mesh-delivery-view/integration_test.go | 192 +++++----- .../cmd/mesh-delivery-view/main.go | 46 +-- modules/mesh-delivery-view/go.mod | 13 + .../internal/view/answers_test.go | 24 +- .../internal/view/asks_test.go | 51 ++- .../internal/view/backlog.go | 19 +- .../internal/view/backlog_test.go | 12 +- .../view/fingerprints_newform_test.go | 25 +- .../internal/view/fingerprints_test.go | 44 +-- .../internal/view/history.go | 9 +- .../mesh-delivery-view/internal/view/holds.go | 21 +- .../internal/view/holds_test.go | 19 +- .../internal/view/motion.go | 14 + .../mesh-delivery-view/internal/view/names.go | 88 +++++ .../mesh-delivery-view/internal/view/reach.go | 87 ++--- .../internal/view/reach_test.go | 71 ++-- .../internal/view/serve_test.go | 6 +- .../internal/view/status.go | 40 ++- .../mesh-delivery-view/internal/view/tiers.go | 63 ++-- .../mesh-delivery-view/internal/view/usage.go | 68 ++-- .../mesh-delivery-view/internal/view/view.go | 337 +++++++++--------- .../internal/view/view_test.go | 97 ++--- .../internal/view/wants_test.go | 147 ++++---- modules/mesh-delivery-view/manifest_test.go | 39 ++ 29 files changed, 1035 insertions(+), 776 deletions(-) create mode 100644 modules/mesh-delivery-view/internal/view/motion.go create mode 100644 modules/mesh-delivery-view/internal/view/names.go diff --git a/modules/mesh-delivery-view/cmd/mesh-delivery-cli/main_test.go b/modules/mesh-delivery-view/cmd/mesh-delivery-cli/main_test.go index db7552df..47a4ebf5 100644 --- a/modules/mesh-delivery-view/cmd/mesh-delivery-cli/main_test.go +++ b/modules/mesh-delivery-view/cmd/mesh-delivery-cli/main_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "mesh-delivery-view/internal/view" "os" "path/filepath" "strings" @@ -12,19 +13,25 @@ import ( occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-sdk/go/delivery" - - "mesh-delivery-view/internal/view" + "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" + "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" + "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate" ) func serve(t *testing.T) string { t.Helper() m := view.New() - placed, err := delivery.EncodePlacement(delivery.Placement{Node: "a", Modules: []delivery.PlacedModule{{Module: "x"}}}) + placed, err := meshmoduleassignerclient.EncodePlacement(meshmoduleassigner.Placement{Node: "a", Modules: []meshmoduleassigner.PlacedModule{{Module: "x"}}}) if err != nil { t.Fatal(err) } - m.Apply(view.Entry{Bucket: delivery.PlacementBucket, Key: "placement.a", Value: placed}) + m.Apply(view.Entry{Bucket: view.PlacementBucket, Key: "placement.a", Value: placed}) dir, err := os.MkdirTemp(os.Getenv("TMPDIR"), "c") if err != nil { t.Fatal(err) @@ -74,17 +81,17 @@ func TestFailsLoudWhenTheViewIsNotThere(t *testing.T) { // 1134/AC6 func TestPrintsTheCandidateGroupsAndTheFingerprintComparison(t *testing.T) { // 1241/AC6 m := view.New() const head = "0123456789abcdef0123456789abcdef01234567" - cand, err := delivery.EncodeCandidate(delivery.Candidate{Group: "feat/x", Pulls: []delivery.CandidatePull{{Head: delivery.Head{Repository: "novox/mesh-delivery", Number: 12, Head: head}, Modules: []string{"a"}}}, - HeadsHash: mustHash(t, delivery.Head{Repository: "novox/mesh-delivery", Number: 12, Head: head})}) + cand, err := json.Marshal(meshcatalog.CandidatesByGroupValue{Group: "feat/x", Pulls: []meshcatalog.CandidatesByGroupValuePullsItem{{Repository: "novox/mesh-delivery", Number: 12, Head: head, Modules: []string{"a"}}}, + Heads: mustHash(t, meshdeliveryclient.Head{Repository: "novox/mesh-delivery", Number: 12, Head: head})}) if err != nil { t.Fatal(err) } - mod, err := delivery.EncodeModule(delivery.Module{None: "no tree", Repository: "novox/mesh-delivery", Commit: head}) + mod, err := meshcatalogclient.EncodeModule(meshcatalog.ModulesByModuleValue{None: "no tree", Repository: "novox/mesh-delivery", Commit: head}) if err != nil { t.Fatal(err) } - m.Apply(view.Entry{Bucket: delivery.CandidatesBucket, Key: "feat/x", Value: cand}) - m.Apply(view.Entry{Bucket: delivery.ModulesBucket, Key: "a", Value: mod}) + m.Apply(view.Entry{Bucket: view.CandidatesBucket, Key: "feat/x", Value: cand}) + m.Apply(view.Entry{Bucket: view.ModulesBucket, Key: "a", Value: mod}) dir, err := os.MkdirTemp(os.Getenv("TMPDIR"), "c") if err != nil { t.Fatal(err) @@ -108,9 +115,9 @@ func TestPrintsTheCandidateGroupsAndTheFingerprintComparison(t *testing.T) { // } } -func mustHash(t *testing.T, h delivery.Head) string { +func mustHash(t *testing.T, h meshdeliveryclient.Head) string { t.Helper() - s, err := delivery.HeadsHash([]delivery.Head{h}) + s, err := meshdeliveryclient.HeadsHash([]meshdeliveryclient.Head{h}) if err != nil { t.Fatal(err) } @@ -121,19 +128,19 @@ func mustHash(t *testing.T, h delivery.Head) string { // the gate's, as mesh-sdk go/delivery EncodeAsk writes it under the key MergeAskKey gives (as internal/view asks_test.go). func TestPrintsTheOpenAsksInTheTableAndTheJSON(t *testing.T) { // 1345/AC2 m := view.New() - heads := []delivery.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: "0123456789abcdef0123456789abcdef01234567"}} - key, err := delivery.MergeAskKey("feat/x", heads) + heads := []meshdeliveryclient.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: "0123456789abcdef0123456789abcdef01234567"}} + key, err := meshdeliveryclient.MergeAskKey("feat/x", heads) if err != nil { t.Fatal(err) } - ask := oc.Ask{ID: delivery.AskID(key), Headline: "Merge into main: mesh-delivery#12", Explanation: "Merges one pull request.", Who: occ.Operator, + ask := oc.Ask{ID: meshdeliveryclient.AskID(key), Headline: "Merge into main: mesh-delivery#12", Explanation: "Merges one pull request.", Who: occ.Operator, Expires: occ.FormatTime(time.Now().Add(time.Hour)), OnExpiry: "the merge stays held", Options: []oc.Option{{ID: "approve", Label: "Approve", Does: "merges", Level: occ.Approve, Binds: "sha256:00"}}} - value, err := delivery.EncodeAsk(delivery.Ask{Group: "feat/x", Heads: heads, Ask: rawAsk(t, ask)}) + value, err := meshdeliveryclient.EncodeAsk(meshdelivery.AsksByMergeGroupHeadsValue{Group: "feat/x", Heads: askHeads(heads), Ask: rawAsk(t, ask)}) if err != nil { t.Fatal(err) } - m.Apply(view.Entry{Bucket: delivery.AsksBucket, Key: key, Value: value}) + m.Apply(view.Entry{Bucket: view.AsksBucket, Key: key, Value: value}) dir, err := os.MkdirTemp(os.Getenv("TMPDIR"), "c") if err != nil { t.Fatal(err) @@ -198,35 +205,35 @@ func serveWith(t *testing.T, m *view.Model) string { // ask is refused with the phone as the reason (1297/B9b). func TestTheAnswerCommandAnswersAnAskNeedingNoProof(t *testing.T) { // 1297/B9b AC const fp = "src2:2222222222222222222222222222222222222222222222222222222222222222" - key, err := delivery.HoldAskKey("plex", fp) + key, err := asks.HoldAskKey("plex", fp) if err != nil { t.Fatal(err) } - hold := oc.Ask{ID: delivery.AskID(key), Headline: "plex is held", Explanation: "ace failed the check.", Who: occ.Operator, Expires: occ.FormatTime(time.Now().Add(time.Hour)), OnExpiry: "stays held", + hold := oc.Ask{ID: meshdeliveryclient.AskID(key), Headline: "plex is held", Explanation: "ace failed the check.", Who: occ.Operator, Expires: occ.FormatTime(time.Now().Add(time.Hour)), OnExpiry: "stays held", Options: []oc.Option{{ID: "release", Label: "Release", Does: "goes on", Level: occ.Approve}, {ID: "keep", Label: "Keep", Does: "stays", Level: occ.Approve}}} - value, _ := delivery.EncodeAssignerAsk(key, delivery.AssignerAsk{Ask: rawAsk(t, hold)}) - heads := []delivery.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: "0123456789abcdef0123456789abcdef01234567"}} - mkey, err := delivery.MergeAskKey("feat/x", heads) + value, _ := asks.EncodeAsk(key, asks.Ask{Ask: rawAsk(t, hold)}) + heads := []meshdeliveryclient.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: "0123456789abcdef0123456789abcdef01234567"}} + mkey, err := meshdeliveryclient.MergeAskKey("feat/x", heads) if err != nil { t.Fatal(err) } - merge := oc.Ask{ID: delivery.AskID(mkey), Headline: "Merge", Explanation: "Merges.", Who: occ.Operator, Expires: occ.FormatTime(time.Now().Add(time.Hour)), OnExpiry: "held", + merge := oc.Ask{ID: meshdeliveryclient.AskID(mkey), Headline: "Merge", Explanation: "Merges.", Who: occ.Operator, Expires: occ.FormatTime(time.Now().Add(time.Hour)), OnExpiry: "held", Options: []oc.Option{{ID: "approve", Label: "Approve", Does: "merges", Level: occ.Approve, Binds: "sha256:00"}}} - mv, err := delivery.EncodeAsk(delivery.Ask{Group: "feat/x", Heads: heads, Ask: rawAsk(t, merge)}) + mv, err := meshdeliveryclient.EncodeAsk(meshdelivery.AsksByMergeGroupHeadsValue{Group: "feat/x", Heads: askHeads(heads), Ask: rawAsk(t, merge)}) if err != nil { t.Fatal(err) } m, w := view.New(), &putter{puts: map[string][]byte{}} m.SetWriter(w) - m.Apply(view.Entry{Bucket: delivery.AssignerAsksBucket, Key: key, Value: value}) - m.Apply(view.Entry{Bucket: delivery.AsksBucket, Key: mkey, Value: mv}) + m.Apply(view.Entry{Bucket: view.AssignerAsksBucket, Key: key, Value: value}) + m.Apply(view.Entry{Bucket: view.AsksBucket, Key: mkey, Value: mv}) socket := serveWith(t, m) var out, errOut bytes.Buffer if code := run([]string{"answer", key, "release", "the", "check", "was", "a", "flake"}, socket, &out, &errOut); code != 0 { t.Fatalf("exit %d: %s", code, errOut.String()) } - a, err := delivery.DecodeViewAnswer(w.puts[key+".view"]) + a, err := viewstate.DecodeViewAnswer(w.puts[key+".view"]) if err != nil || a.Option != "release" || a.Words != "the check was a flake" || !strings.Contains(out.String(), key) { t.Errorf("answer = %+v, %v; out %q", a, err, out.String()) } @@ -250,21 +257,21 @@ func TestTheAnswerCommandAnswersAnAskNeedingNoProof(t *testing.T) { // 1297/B9b // the hold ask through the view, also one answered already. func TestTheHoldCommandsListAndAnswerHolds(t *testing.T) { fp := "src2:04efd1a1aa" + strings.Repeat("2", 54) - key, err := delivery.HoldAskKey("systemd-resolved", fp) + key, err := asks.HoldAskKey("systemd-resolved", fp) if err != nil { t.Fatal(err) } - hold := oc.Ask{ID: delivery.AskID(key), Headline: "systemd-resolved is held", Explanation: "ace failed the check.", Who: occ.Operator, Expires: occ.FormatTime(time.Now().Add(time.Hour)), OnExpiry: "stays held", + hold := oc.Ask{ID: meshdeliveryclient.AskID(key), Headline: "systemd-resolved is held", Explanation: "ace failed the check.", Who: occ.Operator, Expires: occ.FormatTime(time.Now().Add(time.Hour)), OnExpiry: "stays held", Options: []oc.Option{{ID: "keep", Label: "Keep", Does: "stays", Level: occ.Acknowledge}, {ID: "release", Label: "Release", Does: "goes on", Level: occ.Acknowledge}}} - value, _ := delivery.EncodeAssignerAsk(key, delivery.AssignerAsk{Ask: rawAsk(t, hold)}) - hkey, _ := delivery.HeldKey("systemd-resolved", fp) - held, _ := delivery.EncodeHeld(delivery.Held{Node: "ace", Words: "health failed, phase running: dns timeout"}) - ans, _ := delivery.EncodeAnswer(delivery.Answer{Option: "keep", Warrant: hold.ID + "@2026-10-25T04:00:00Z", Words: "the operator chose keep"}) + value, _ := asks.EncodeAsk(key, asks.Ask{Ask: rawAsk(t, hold)}) + hkey, _ := meshwantsclient.HeldKey("systemd-resolved", fp) + held, _ := meshwantsclient.EncodeHeld(meshwantsclient.Held{Node: "ace", Words: "health failed, phase running: dns timeout"}) + ans, _ := meshdeliveryclient.EncodeAnswer(meshdeliveryclient.Answer{Option: "keep", Warrant: hold.ID + "@2026-10-25T04:00:00Z", Words: "the operator chose keep"}) m, w := view.New(), &putter{puts: map[string][]byte{}} m.SetWriter(w) - m.Apply(view.Entry{Bucket: delivery.AssignerAsksBucket, Key: key, Value: value}) - m.Apply(view.Entry{Bucket: delivery.AssignerWantsBucket, Key: hkey, Value: held}) - m.Apply(view.Entry{Bucket: delivery.AnswersBucket, Key: key, Value: ans}) + m.Apply(view.Entry{Bucket: view.AssignerAsksBucket, Key: key, Value: value}) + m.Apply(view.Entry{Bucket: view.AssignerWantsBucket, Key: hkey, Value: held}) + m.Apply(view.Entry{Bucket: view.AnswersBucket, Key: key, Value: ans}) socket := serveWith(t, m) var out, errOut bytes.Buffer @@ -280,7 +287,7 @@ func TestTheHoldCommandsListAndAnswerHolds(t *testing.T) { if code := run([]string{"hold-release", "systemd-resolved", "04efd1a1", "a", "flake"}, socket, &out, &errOut); code != 0 { t.Fatalf("exit %d: %s", code, errOut.String()) } - if a, err := delivery.DecodeViewAnswer(w.puts[key+".view"]); err != nil || a.Option != "release" || a.Asking != hold.ID || a.Words != "a flake" { + if a, err := viewstate.DecodeViewAnswer(w.puts[key+".view"]); err != nil || a.Option != "release" || a.Asking != hold.ID || a.Words != "a flake" { t.Errorf("the view's answer = %+v, %v", a, err) } errOut.Reset() @@ -298,3 +305,12 @@ func rawAsk(t *testing.T, a oc.Ask) json.RawMessage { } return b } + +// askHeads is the heads of a merge ask as the seat carries them. +func askHeads(heads []meshdeliveryclient.Head) []meshdelivery.AsksByMergeGroupHeadsValueHeadsItem { + out := make([]meshdelivery.AsksByMergeGroupHeadsValueHeadsItem, len(heads)) + for i, h := range heads { + out[i] = meshdelivery.AsksByMergeGroupHeadsValueHeadsItem{Repository: h.Repository, Number: int64(h.Number), Head: h.Head} + } + return out +} diff --git a/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/main.go b/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/main.go index 24999173..8a28f8b6 100644 --- a/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/main.go +++ b/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/main.go @@ -6,23 +6,23 @@ package main import ( "encoding/json" "fmt" + "mesh-delivery-view/internal/view" "os" "strings" "time" - "mesh-delivery-view/internal/view" - + "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient" + "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-indexer/takein" - "git.novox.be/novox/mesh-sdk/go/delivery" - - "git.novox.be/novox/mesh-sdk/go/provisions" - "git.novox.be/novox/mesh-sdk/go/views" - + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate" + "git.novox.be/novox/mesh-delivery/sdk/buildstep" + "git.novox.be/novox/mesh-delivery/sdk/enginereads" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/busretry" - - "git.novox.be/novox/mesh-delivery/sdk/enginereads" + "git.novox.be/novox/mesh-sdk/go/provisions" + "git.novox.be/novox/mesh-sdk/go/views" ) // ManifestReadsTool lists, per node, what its engine states it reads, by asking the node-engine seat's verb of every node @@ -59,13 +59,13 @@ func manifestReads(r view.Reader, call enginereads.Caller) (any, error) { return nil, fmt.Errorf("the haves cannot be read: %w", err) } for _, key := range keys { - node, _, isNode, err := delivery.ParseHaveKey(key) + node, _, isNode, err := nodeengineclient.ParseHaveKey(key) if err != nil || !isNode { continue } set[node] = true if raw, err := r.HavesState.Get(key); err == nil && raw != nil { - if h, err := delivery.DecodeHaveNode(raw); err == nil && h.Record != nil { + if h, err := nodeengineclient.DecodeHaveNode(raw); err == nil && h.Record != nil { records[node] = h.Record.ManifestReads } } @@ -74,7 +74,7 @@ func manifestReads(r view.Reader, call enginereads.Caller) (any, error) { if r.PresenceState.Keys != nil { if keys, err := r.PresenceState.Keys(); err == nil { for _, key := range keys { - if node, part, _, err := delivery.ParsePresenceKey(key); err == nil && part == "" && node != "" { + if node, part, _, err := meshwatchdogsclient.ParsePresenceKey(key); err == nil && part == "" && node != "" { set[node] = true } } @@ -206,7 +206,7 @@ func Tools(r view.Reader, u view.UsageReader) []stdio.Tool { "predefined.json holds it with the merge it was read at; a merge older than that never replaces it. mesh-config " + "publishes the predefined keys from it after its take-over. Read-only.", Run: func(map[string]any) (any, error) { - return view.ControlRecord(r.PredefinedState, delivery.IndexerPredefinedState, delivery.PredefinedListKey) + return view.ControlRecord(r.PredefinedState, view.IndexerPredefinedState, view.PredefinedListKey) }}, {Name: view.ConfigCandidatesReadTool, Description: "Each open pull request's config declarations as the indexer keeps them (novox/hq ADR 0565 §2): per pull " + @@ -216,7 +216,7 @@ func Tools(r view.Reader, u view.UsageReader) []stdio.Tool { "key": map[string]any{"type": "string", "description": "one pull request's key, owner.repository.number"}}}, Run: func(args map[string]any) (any, error) { key, _ := args["key"].(string) - return view.ControlRecord(r.ConfigCandidatesState, delivery.IndexerConfigCandidatesState, key) + return view.ControlRecord(r.ConfigCandidatesState, view.IndexerConfigCandidatesState, key) }}, {Name: view.PackagesReadTool, Description: "The mesh packages as the indexer publishes them (novox/hq ADR 0593 §2): per mesh Go module (its module " + @@ -230,11 +230,11 @@ func Tools(r view.Reader, u view.UsageReader) []stdio.Tool { key := "" if module, _ := args["module"].(string); strings.TrimSpace(module) != "" { var err error - if key, err = delivery.PackagesKey(strings.TrimSpace(module)); err != nil { + if key, err = view.PackagesKey(strings.TrimSpace(module)); err != nil { return nil, err } } - return view.ControlRecord(r.PackagesState, delivery.PackagesState, key) + return view.ControlRecord(r.PackagesState, view.PackagesState, key) }}, {Name: view.CandidatesReadTool, Description: "Every candidate the indexer holds, by group, each as stored under candidates.; with module, only those with a " + @@ -395,8 +395,8 @@ func read(state stdio.KeptState) view.StateRead { // owners), never their buckets. func ownersRead(ask func(key string, body any) (json.RawMessage, error)) view.Reader { // Each owner's read tool as the view's invokes names it. - indexer := func(tool string) view.StateRead { return view.OwnerRead(ask, delivery.IndexerModule+"."+tool) } - assigner := func(tool string) view.StateRead { return view.OwnerRead(ask, delivery.AssignerModule+"."+tool) } + indexer := func(tool string) view.StateRead { return view.OwnerRead(ask, view.IndexerModule+"."+tool) } + assigner := func(tool string) view.StateRead { return view.OwnerRead(ask, view.AssignerModule+"."+tool) } return view.Reader{State: indexer("indexer_read_modules"), CandidatesState: indexer("indexer_read_candidates"), WantsState: assigner("assigner_read_wants"), DifferencesState: assigner("assigner_read_differences"), HeardState: indexer("indexer_read_heard"), @@ -413,20 +413,20 @@ func main() { r.SignersState = read(stdio.State("mesh-vault.signers")) r.ConditionsState, r.AttemptsState = read(stdio.State(view.StatusConditionsState)), read(stdio.State(view.StatusAttemptsState)) r.PresenceState, r.StuckState = read(stdio.State(view.StatusPresenceState)), read(stdio.State(view.StatusStuckState)) - r.SourceState = read(stdio.State(delivery.GateModule + ".source")) + r.SourceState = read(stdio.State(view.GateModule + ".source")) r.ProvisionsState = read(stdio.State(provisions.ProvisionsRead)) - r.BuildLog = stdio.Log(delivery.BuildLogRead).Since + r.BuildLog = stdio.Log(builds.BuildLogRead).Since r.Pending = pendingOf u := view.UsageReader{Haves: r.HavesState, Resolved: read(stdio.State("mesh-config.resolved")), Tries: read(stdio.State("mesh-delivery-checker.tries")), Steps: map[string]view.StateRead{}, Pauses: map[string]view.StateRead{}, Backlog: backlogOf(socket())} - for _, lang := range delivery.AskLanguages { - module, _ := delivery.BuildModuleOf(lang) - u.Steps[lang] = read(stdio.State(module + "." + delivery.StepsState)) - u.Pauses[lang] = read(stdio.State(module + "." + delivery.PausesState)) + for _, lang := range buildstep.AskLanguages { + module, _ := buildstep.BuildModuleOf(lang) + u.Steps[lang] = read(stdio.State(module + "." + view.StepsState)) + u.Pauses[lang] = read(stdio.State(module + "." + buildstep.PausesState)) } // view_first_views and view_first_view_again: where the view's process stands with its first views of the indexer's and // the assigner's states (ADR 0615 §6), and a re-read by hand. - tools := append(Tools(r, u), views.StatusTools(delivery.ViewModule, "view_first_views", "view_first_view_again", stdio.State(views.StatusState), meshhandactsclient.Recorder{Ask: stdio.Ask}, time.Now)...) + tools := append(Tools(r, u), views.StatusTools(viewstate.ViewModule, "view_first_views", "view_first_view_again", stdio.State(views.StatusState), meshhandactsclient.Recorder{Ask: stdio.Ask}, time.Now)...) if err := stdio.Serve("", tools); err != nil { fmt.Fprintf(os.Stderr, "[mesh-delivery-view-tools] %v\n", err) os.Exit(1) @@ -435,7 +435,7 @@ func main() { // pendingOf asks the indexer's tool indexer_pending (declared in invokes): what it holds back and why (ADR 0633 §5). func pendingOf() (takein.PendingAnswer, error) { - raw, err := stdio.Ask(delivery.IndexerModule+".indexer_pending", map[string]any{}) + raw, err := stdio.Ask(view.IndexerModule+".indexer_pending", map[string]any{}) if err != nil { return takein.PendingAnswer{}, err } diff --git a/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/main_test.go b/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/main_test.go index ad6d1119..b617d94e 100644 --- a/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/main_test.go +++ b/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/main_test.go @@ -4,20 +4,22 @@ import ( "crypto/ed25519" "encoding/json" "fmt" + "mesh-delivery-view/internal/view" "reflect" "strings" "sync/atomic" "testing" "time" + "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" + "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" + "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" + "git.novox.be/novox/mesh-delivery/sdk/enginereads" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/busretry" - "git.novox.be/novox/mesh-sdk/go/delivery" - - "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" - "git.novox.be/novox/mesh-delivery/sdk/enginereads" - - "mesh-delivery-view/internal/view" + "git.novox.be/novox/mesh-sdk/go/keys" ) type fake map[string]string @@ -203,24 +205,24 @@ func TestBuilderBuildsListsKeysAsStored(t *testing.T) { // the start of a later build of the same fingerprint not counted, and narrows by module through the log's key. t0 := time.Date(2026, 10, 29, 22, 0, 0, 0, time.UTC) entry := func(event string, at time.Duration) []byte { - e := delivery.BuildLogEntry{Event: event, Module: "netcheck", FP: fp2A, Repository: "novox/mesh-catalog", Directory: "modules/netcheck", + e := builds.BuildLogEntry{Event: event, Module: "netcheck", FP: fp2A, Repository: "novox/mesh-catalog", Directory: "modules/netcheck", Commit: strings.Repeat("7", 40), Node: "ace", At: t0.Add(at)} - if event == delivery.BuildEndedEvent { - e.Outcome = delivery.BuildDone + if event == builds.BuildEndedEvent { + e.Outcome = builds.BuildDone } - raw, _ := delivery.EncodeBuildLogEntry(e) + raw, _ := builds.EncodeBuildLogEntry(e) return raw } var asked string r.BuildLog = func(at time.Time, keys string, limit int) (*stdio.LogPage, error) { asked = keys - return &stdio.LogPage{Entries: []stdio.LogEntry{{Sequence: 1, Value: entry(delivery.BuildStartedEvent, 0)}, - {Sequence: 2, Value: entry(delivery.BuildEndedEvent, 90*time.Second)}, {Sequence: 3, Value: entry(delivery.BuildStartedEvent, time.Hour)}, + return &stdio.LogPage{Entries: []stdio.LogEntry{{Sequence: 1, Value: entry(builds.BuildStartedEvent, 0)}, + {Sequence: 2, Value: entry(builds.BuildEndedEvent, 90*time.Second)}, {Sequence: 3, Value: entry(builds.BuildStartedEvent, time.Hour)}, // an entry of another kind (the old reference form's close, design 51), of a form no build has: passed over {Sequence: 4, Value: []byte(`{"v":99,"event":"old-form-closed","at":"2026-10-29T23:00:00Z","nodes":["ace"]}`)}}}, nil } out, err = run(t, r, view.BuildHistoryTool, map[string]any{"module": "netcheck", "since": t0.Format(time.RFC3339)}) - if h, _ := out.(view.HistoryAnswer); err != nil || len(h.Builds) != 1 || h.Builds[0].Seconds != 90 || h.Builds[0].Outcome != delivery.BuildDone || asked != "netcheck" || h.More { + if h, _ := out.(view.HistoryAnswer); err != nil || len(h.Builds) != 1 || h.Builds[0].Seconds != 90 || h.Builds[0].Outcome != builds.BuildDone || asked != "netcheck" || h.More { t.Errorf("history = %+v, %v (keys %q), want the one end at 90 s", out, err, asked) } } @@ -491,10 +493,10 @@ func TestAssignerDifferencesAndNodeHavesToolsAnswerAsStored(t *testing.T) { // L3 s6b (ADR 0550 §2): node_trusts answers for every node in one call whether its record names the vault's key the // vault publishes, and every is true only once all do. vaultPub, _, _ := ed25519.GenerateKey(nil) - vaultKey, _ := json.Marshal(delivery.NewSignerKey(vaultPub, time.Now())) - vaultFP := delivery.SignerFingerprint(vaultPub) + vaultKey, _ := json.Marshal(meshvaultclient.NewSignerKey(vaultPub, time.Now())) + vaultFP := meshvaultclient.SignerFingerprint(vaultPub) record := func(node string, trusts ...string) string { - raw, _ := delivery.EncodeHaveNode(delivery.HaveNode{V: delivery.Version, Record: &delivery.NodeRecord{Node: node, Trusts: trusts}}) + raw, _ := nodeengineclient.EncodeHaveNode(nodeengine.HaveByNodeNodeValue{V: keys.Version, Record: &nodeengine.NodeRecord{Node: node, Trusts: trusts}}) return string(raw) } for _, row := range []struct { @@ -561,7 +563,7 @@ func TestAssignerDifferencesAndNodeHavesToolsAnswerAsStored(t *testing.T) { // ADR 0593 §2 (D2): indexer_packages answers the indexer's published packages, one Go module by its path or all. func packagesToolAnswersThePublishedModules(t *testing.T) { - key, err := delivery.PackagesKey("git.novox.be/novox/mesh-sdk/go") + key, err := view.PackagesKey("git.novox.be/novox/mesh-sdk/go") if err != nil { t.Fatal(err) } @@ -601,10 +603,10 @@ func manifestReadsAsksEveryNode(t *testing.T) { return json.Marshal(nodeengine.ManifestReadsAnswer{V: 1, Node: node, ManifestReads: []string{"form:new"}, StatedAt: time.Now().Format(time.RFC3339Nano)}) } record := func(node string, reads ...string) string { - raw, _ := delivery.EncodeHaveNode(delivery.HaveNode{Record: &delivery.NodeRecord{Node: node, ManifestReads: reads}}) + raw, _ := nodeengineclient.EncodeHaveNode(nodeengine.HaveByNodeNodeValue{Record: &nodeengine.NodeRecord{Node: node, ManifestReads: reads}}) return string(raw) } - on, _ := delivery.EncodePresence(delivery.Presence{Online: true}) + on, _ := meshwatchdogsclient.EncodeNodePresence(meshwatchdogsclient.NodePresence{Online: true}) r := view.Reader{HavesState: fake{"ace.node": record("ace"), "g14.node": record("g14", "word:module"), "ace.web": "{}"}.reader().State, PresenceState: fake{"novox": string(on)}.reader().State} started := time.Now() diff --git a/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/owners_test.go b/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/owners_test.go index 7a14849f..80ad8589 100644 --- a/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/owners_test.go +++ b/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/owners_test.go @@ -3,17 +3,14 @@ package main import ( "encoding/json" "fmt" + "mesh-delivery-view/internal/view" "sort" "strings" "testing" "time" - "git.novox.be/novox/mesh-sdk/go/delivery" - "git.novox.be/novox/mesh-sdk/go/views" - "git.novox.be/novox/mesh-delivery/sdk/graph" - - "mesh-delivery-view/internal/view" + "git.novox.be/novox/mesh-sdk/go/views" ) // source is a fake owner state as a views.Source: what the owner's read tool answers from. @@ -49,45 +46,45 @@ func (k kept) History(key string) ([]views.Revision, error) { return k.history[k // the owners' read tools (the real owner side, views.Read, over a fake state) what it answered reading the states. func ownersAnswerTheToolsAsBefore(t *testing.T) { cand := `{"v":1,"group":"feat/x","base":3,"heads":"h","pulls":[{"modules":["zsh"],"fps":{"zsh":{"fp2":"` + fp2B + `","manifest":"m"}}}]}` - pkgKey, err := delivery.PackagesKey("git.novox.be/novox/mesh-sdk/go") + pkgKey, err := view.PackagesKey("git.novox.be/novox/mesh-sdk/go") if err != nil { t.Fatal(err) } states := map[string]source{ - delivery.IndexerModule + ".modules": {"a": withFP2, "b": withNone}, - delivery.IndexerModule + ".candidates": {"feat/x": cand, "fix": `{"v":1,"group":"fix","base":3,"heads":"h","pulls":[{"modules":["netcheck"]}]}`}, - delivery.AssignerModule + ".wants": {"want.ace.zsh": `{"fp":"` + fp2A + `"}`, "want.novox.zsh": `{"fp":"` + fp2B + `"}`, + view.IndexerModule + ".modules": {"a": withFP2, "b": withNone}, + view.IndexerModule + ".candidates": {"feat/x": cand, "fix": `{"v":1,"group":"fix","base":3,"heads":"h","pulls":[{"modules":["netcheck"]}]}`}, + view.AssignerModule + ".wants": {"want.ace.zsh": `{"fp":"` + fp2A + `"}`, "want.novox.zsh": `{"fp":"` + fp2B + `"}`, "held." + keyB: `{"node":"ace","words":"failed"}`}, - delivery.AssignerModule + ".differences": {"ace.zsh": `{"unexplained":true}`, "novox.zsh": `{"rule":3}`}, - delivery.IndexerModule + "." + delivery.IndexerPredefinedState: {delivery.PredefinedListKey: `{"v":1,"keys":["dir:state"]}`}, - delivery.IndexerModule + "." + delivery.IndexerConfigCandidatesState: {"novox.r.7": `{"v":1,"pull":"novox/r#7"}`}, - delivery.IndexerModule + "." + delivery.PackagesState: {pkgKey: `{"v":1,"module":"git.novox.be/novox/mesh-sdk/go","commit":"c1"}`}, - delivery.IndexerModule + "." + graph.State: {}, + view.AssignerModule + ".differences": {"ace.zsh": `{"unexplained":true}`, "novox.zsh": `{"rule":3}`}, + view.IndexerModule + "." + view.IndexerPredefinedState: {view.PredefinedListKey: `{"v":1,"keys":["dir:state"]}`}, + view.IndexerModule + "." + view.IndexerConfigCandidatesState: {"novox.r.7": `{"v":1,"pull":"novox/r#7"}`}, + view.IndexerModule + "." + view.PackagesState: {pkgKey: `{"v":1,"module":"git.novox.be/novox/mesh-sdk/go","commit":"c1"}`}, + view.IndexerModule + "." + graph.State: {}, } // The kept history of the indexer's modules and heard and the assigner's wants (each declared with more than one // revision in its owner's manifest), which indexer_module_history, indexer_heard and view_tiers' times read. at := time.Date(2026, 11, 4, 9, 0, 0, 0, time.UTC) const heard1 = `{"v":1,"subject":"s","sequence":41,"repository":"novox/mesh-catalog","commit":"9cdac09a"}` - states[delivery.IndexerModule+".heard"] = source{"forge": heard1} + states[view.IndexerModule+".heard"] = source{"forge": heard1} histories := map[string]map[string][]views.Revision{ - delivery.IndexerModule + ".modules": {"a": {{Revision: 7, At: at, Op: "put", Value: json.RawMessage(withNone)}, + view.IndexerModule + ".modules": {"a": {{Revision: 7, At: at, Op: "put", Value: json.RawMessage(withNone)}, {Revision: 9, At: at.Add(time.Minute), Op: "put", Value: json.RawMessage(withFP2)}}}, - delivery.IndexerModule + ".heard": {"forge": {{Revision: 3, At: at, Op: "put", Value: json.RawMessage(heard1)}}}, - delivery.AssignerModule + ".wants": {"want.ace.zsh": {{Revision: 12, At: at, Op: "put", Value: json.RawMessage(`{"fp":"` + fp2A + `"}`)}}}, + view.IndexerModule + ".heard": {"forge": {{Revision: 3, At: at, Op: "put", Value: json.RawMessage(heard1)}}}, + view.AssignerModule + ".wants": {"want.ace.zsh": {{Revision: 12, At: at, Op: "put", Value: json.RawMessage(`{"fp":"` + fp2A + `"}`)}}}, } // The direct reading, as the tools read the states before (the reads the manifest no longer has). st := func(name string) view.StateRead { return fake(states[name]).reader().State } - direct := view.Reader{State: st(delivery.IndexerModule + ".modules"), CandidatesState: st(delivery.IndexerModule + ".candidates"), - WantsState: st(delivery.AssignerModule + ".wants"), DifferencesState: st(delivery.AssignerModule + ".differences"), - PredefinedState: st(delivery.IndexerModule + "." + delivery.IndexerPredefinedState), - ConfigCandidatesState: st(delivery.IndexerModule + "." + delivery.IndexerConfigCandidatesState), - PackagesState: st(delivery.IndexerModule + "." + delivery.PackagesState), - GraphState: st(delivery.IndexerModule + "." + graph.State)} + direct := view.Reader{State: st(view.IndexerModule + ".modules"), CandidatesState: st(view.IndexerModule + ".candidates"), + WantsState: st(view.AssignerModule + ".wants"), DifferencesState: st(view.AssignerModule + ".differences"), + PredefinedState: st(view.IndexerModule + "." + view.IndexerPredefinedState), + ConfigCandidatesState: st(view.IndexerModule + "." + view.IndexerConfigCandidatesState), + PackagesState: st(view.IndexerModule + "." + view.PackagesState), + GraphState: st(view.IndexerModule + "." + graph.State)} // The owners' read tools, answered by the owner's own Read over the same state; any other address is refused. tools := map[string]string{} for name := range states { owner, state, _ := strings.Cut(name, ".") - short := map[string]string{delivery.IndexerModule: "indexer_read_", delivery.AssignerModule: "assigner_read_"}[owner] + short := map[string]string{view.IndexerModule: "indexer_read_", view.AssignerModule: "assigner_read_"}[owner] tools[owner+"."+short+strings.ReplaceAll(state, "-", "_")] = name } asked := map[string]int{} @@ -161,7 +158,7 @@ func ownersAnswerTheToolsAsBefore(t *testing.T) { } // Each owner state the tools read was asked of its owner's read tool. for tool, name := range tools { - if name != delivery.IndexerModule+"."+graph.State && asked[tool] == 0 { + if name != view.IndexerModule+"."+graph.State && asked[tool] == 0 { t.Errorf("%s was never asked", tool) } } diff --git a/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/tiers_rows_test.go b/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/tiers_rows_test.go index baa4406a..45f0f905 100644 --- a/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/tiers_rows_test.go +++ b/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/tiers_rows_test.go @@ -4,18 +4,22 @@ import ( "encoding/json" "errors" "fmt" + "mesh-delivery-view/internal/view" "reflect" "strings" "testing" "time" + "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" + "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" + "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-indexer/takein" - "git.novox.be/novox/mesh-sdk/go/delivery" - "git.novox.be/novox/mesh-delivery/sdk/graph" "git.novox.be/novox/mesh-sdk/go/provisions" - - "mesh-delivery-view/internal/view" ) // encoded is v as its contract's encoder writes it, failing the test when it refuses. @@ -37,23 +41,23 @@ func tiersToolSaysEachModulesWait(t *testing.T) { fp := func(c string) string { return "src2:" + strings.Repeat(c, 64) } manifest := strings.Repeat("d", 64) mod := func(f string, src uint64) string { - b, err := delivery.EncodeModule(delivery.Module{FP2: f, Manifest: manifest, Src: src, FPSrc: src, Repository: "novox/mesh-catalog", Commit: "c"}) + b, err := meshcatalogclient.EncodeModule(meshcatalog.ModulesByModuleValue{Fp2: f, Manifest: manifest, Src: int64(src), FpSrc: int64(src), Repository: "novox/mesh-catalog", Commit: "c"}) return encoded(t, b, err) } node := func(g graph.Node) string { b, err := graph.Encode(g) return encoded(t, b, err) } - build := func(b delivery.BuilderBuild) string { - v, err := delivery.EncodeBuilderBuild(b) + build := func(b builds.BuilderBuild) string { + v, err := builds.EncodeBuilderBuild(b) return encoded(t, v, err) } - want := func(w delivery.Want) string { - v, err := delivery.EncodeWant(w) + want := func(w meshwantsclient.Want) string { + v, err := meshwantsclient.EncodeWant(w) return encoded(t, v, err) } - have := func(h delivery.Have) string { - v, err := delivery.EncodeHave(h) + have := func(h nodeengine.HaveByNodeModuleValue) string { + v, err := nodeengineclient.EncodeHave(h) return encoded(t, v, err) } key := func(k string, err error) string { @@ -70,22 +74,22 @@ func tiersToolSaysEachModulesWait(t *testing.T) { "lib": node(graph.Node{FP: fp("c"), Repository: "novox/mesh-catalog"}), }.reader().State r.Builds = fake{ - key(delivery.BuilderBuildKey("db", fp("a"))): build(delivery.BuilderBuild{State: delivery.BuildDone, Manifest: manifest}), - key(delivery.BuilderBuildKey("web", fp("b"))): build(delivery.BuilderBuild{State: delivery.BuildDone, Manifest: manifest}), - key(delivery.BuilderBuildKey("lib", fp("c"))): build(delivery.BuilderBuild{State: delivery.BuildQueued, Waits: "@novox/x@1.2.0"}), + key(builds.BuilderBuildKey("db", fp("a"))): build(builds.BuilderBuild{State: builds.BuildDone, Manifest: manifest}), + key(builds.BuilderBuildKey("web", fp("b"))): build(builds.BuilderBuild{State: builds.BuildDone, Manifest: manifest}), + key(builds.BuilderBuildKey("lib", fp("c"))): build(builds.BuilderBuild{State: builds.BuildQueued, Waits: "@novox/x@1.2.0"}), }.reader().State r.WantsState = fake{ - key(delivery.WantKey("ace", "db")): want(delivery.Want{FP: fp("a"), Self: true}), - key(delivery.WantKey("ace", "web")): want(delivery.Want{Self: true, FP: fp("f"), Newest: fp("b"), Rule: delivery.RuleProvisions, Words: "the provision ace.web.database of web on ace is asked, not provided"}), - key(delivery.WantKey("g14", "web")): want(delivery.Want{Self: true, FP: fp("f"), Newest: fp("b"), Rule: delivery.RuleCanary, Words: "the canary"}), + key(meshwantsclient.WantKey("ace", "db")): want(meshwantsclient.Want{Fp: fp("a"), Self: true}), + key(meshwantsclient.WantKey("ace", "web")): want(meshwantsclient.Want{Self: true, Fp: fp("f"), Newest: fp("b"), Rule: meshwantsclient.RuleProvisions, Words: "the provision ace.web.database of web on ace is asked, not provided"}), + key(meshwantsclient.WantKey("g14", "web")): want(meshwantsclient.Want{Self: true, Fp: fp("f"), Newest: fp("b"), Rule: meshwantsclient.RuleCanary, Words: "the canary"}), }.reader().State r.WantsState.History = func(string) ([]view.Revision, error) { return []view.Revision{{At: since}}, nil } - r.HavesState = fake{key(delivery.HaveKey("ace", "db")): have(delivery.Have{FP: fp("a"), Phase: delivery.PhaseRunning, Health: delivery.HealthOK})}.reader().State + r.HavesState = fake{key(nodeengineclient.HaveKey("ace", "db")): have(nodeengine.HaveByNodeModuleValue{Fp: fp("a"), Phase: nodeengineclient.PhaseRunning, Health: nodeengineclient.HealthOK})}.reader().State p, err := provisions.Encode(provisions.Provision{Provision: "database", Consumer: provisions.End{Module: "web", Node: "ace"}, Provider: provisions.End{Module: "db", Node: "ace"}, State: provisions.Asked, Why: "the build db runs does not offer it", At: since}) r.ProvisionsState = fake{key(provisions.Key("ace", "web", "database", "")): encoded(t, p, err)}.reader().State - off, err := delivery.EncodePresence(delivery.Presence{Online: false}) - r.PresenceState = fake{key(delivery.PresenceNodeKey("g14")): encoded(t, off, err)}.reader().State + off, err := meshwatchdogsclient.EncodeNodePresence(meshwatchdogsclient.NodePresence{Online: false}) + r.PresenceState = fake{key(meshwatchdogsclient.PresenceNodeKey("g14")): encoded(t, off, err)}.reader().State out, err := run(t, r, view.TiersTool, map[string]any{"revision": float64(9)}) got, _ := out.(view.Tiers) @@ -99,7 +103,7 @@ func tiersToolSaysEachModulesWait(t *testing.T) { } web := got.Tiers[1][0] if web.Module != "web" || !reflect.DeepEqual(web.After, []string{"db"}) || len(web.Waits) != 2 || - web.Waits[0].Edge != "want" || web.Waits[0].Node != "ace" || web.Waits[0].Rule != delivery.RuleProvisions || !web.Waits[0].Since.Equal(since) || + web.Waits[0].Edge != "want" || web.Waits[0].Node != "ace" || web.Waits[0].Rule != meshwantsclient.RuleProvisions || !web.Waits[0].Since.Equal(since) || web.Waits[1].Edge != "provision database" || !strings.Contains(web.Waits[1].Words, "asked: the build db runs does not offer it") { t.Errorf("web = %+v, want after db, held on ace by the provision rule and its provision asked (g14 is away)", web) } @@ -152,7 +156,7 @@ func tiersToolSaysEachModulesWait(t *testing.T) { graph.ReaderWaitWords("ace", []string{"tool-runner"}, "web", short(fp("b"))): "reader capability tool-runner", "the provision ace.web.database of web on ace is asked, not provided": "want", } { - r.WantsState = fake{key(delivery.WantKey("ace", "web")): want(delivery.Want{Self: true, FP: fp("f"), Newest: fp("b"), Rule: delivery.RuleProvisions, Words: words})}.reader().State + r.WantsState = fake{key(meshwantsclient.WantKey("ace", "web")): want(meshwantsclient.Want{Self: true, Fp: fp("f"), Newest: fp("b"), Rule: meshwantsclient.RuleProvisions, Words: words})}.reader().State out, err := run(t, r, view.TiersTool, map[string]any{"revision": float64(9)}) got, _ := out.(view.Tiers) if err != nil || len(got.Tiers) != 2 { diff --git a/modules/mesh-delivery-view/cmd/mesh-delivery-view/integration_test.go b/modules/mesh-delivery-view/cmd/mesh-delivery-view/integration_test.go index 69f5a9dd..9b1a4fd8 100644 --- a/modules/mesh-delivery-view/cmd/mesh-delivery-view/integration_test.go +++ b/modules/mesh-delivery-view/cmd/mesh-delivery-view/integration_test.go @@ -6,24 +6,33 @@ import ( "context" "encoding/json" "io" + "mesh-delivery-view/internal/view" "os" "path/filepath" "strings" "testing" "time" + "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" + "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" + occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" + oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" + "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" + "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" + "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate" + "git.novox.be/novox/mesh-delivery/sdk/buildstep" + "git.novox.be/novox/mesh-sdk/go/process" + "git.novox.be/novox/mesh-sdk/go/views/viewstest" "github.com/nats-io/nats-server/v2/server" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" - - occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" - oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-sdk/go/delivery" - "git.novox.be/novox/mesh-sdk/go/process" - - "git.novox.be/novox/mesh-sdk/go/views/viewstest" - - "mesh-delivery-view/internal/view" ) // serveOwners serves the six states of the indexer and the assigner the view's process follows by their first views @@ -32,7 +41,7 @@ import ( func serveOwners(t *testing.T, ctx context.Context, js jetstream.JetStream, nc *nats.Conn) { t.Helper() for _, o := range ownersFollowed { - viewstest.Serve(t, ctx, js, nc, o.owner, o.state, o.tool, "n_"+delivery.ViewModule) + viewstest.Serve(t, ctx, js, nc, o.owner, o.state, o.tool, "n_"+viewstate.ViewModule) } } @@ -61,7 +70,7 @@ func TestProcessWholeSeesTheBuckets(t *testing.T) { // 1134/AC8 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() kvs := map[string]jetstream.KeyValue{} - for _, b := range []string{delivery.HaveBucket, delivery.PlacementBucket, delivery.PresenceBucket, delivery.StuckBucket} { + for _, b := range []string{view.HaveBucket, view.PlacementBucket, view.PresenceBucket, view.StuckBucket} { kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: b, History: 5}) if err != nil { t.Fatal(err) @@ -74,8 +83,8 @@ func TestProcessWholeSeesTheBuckets(t *testing.T) { // 1134/AC8 t.Fatal(err) } } - haveValue := func(build string, phase delivery.Phase, health delivery.Health) []byte { - b, err := delivery.EncodeHave(delivery.Have{Build: build, Phase: phase, Health: health}) + haveValue := func(build string, phase string, health string) []byte { + b, err := nodeengineclient.EncodeHave(nodeengine.HaveByNodeModuleValue{Build: build, Phase: phase, Health: health}) if err != nil { t.Fatal(err) } @@ -83,20 +92,20 @@ func TestProcessWholeSeesTheBuckets(t *testing.T) { // 1134/AC8 } // The seat's placement (issue 774, ADR 0549 §3): x and y on a, nothing on b. placeValue := func(node string, modules ...string) []byte { - p := delivery.Placement{Node: node} + p := meshmoduleassigner.Placement{Node: node} for _, m := range modules { - p.Modules = append(p.Modules, delivery.PlacedModule{Module: m}) + p.Modules = append(p.Modules, meshmoduleassigner.PlacedModule{Module: m}) } - v, err := delivery.EncodePlacement(p) + v, err := meshmoduleassignerclient.EncodePlacement(p) if err != nil { t.Fatal(err) } return v } - put(delivery.PlacementBucket, "placement.a", placeValue("a", "x", "y")) - put(delivery.PlacementBucket, "placement.b", placeValue("b")) - put(delivery.HaveBucket, "a.x", haveValue("b1", delivery.PhaseChecking, delivery.HealthWaits)) - put(delivery.HaveBucket, "b.x", haveValue("b1", delivery.PhaseRunning, delivery.HealthOK)) + put(view.PlacementBucket, "placement.a", placeValue("a", "x", "y")) + put(view.PlacementBucket, "placement.b", placeValue("b")) + put(view.HaveBucket, "a.x", haveValue("b1", nodeengineclient.PhaseChecking, nodeengineclient.HealthWaits)) + put(view.HaveBucket, "b.x", haveValue("b1", nodeengineclient.PhaseRunning, nodeengineclient.HealthOK)) dir, err := os.MkdirTemp(os.Getenv("TMPDIR"), "i") if err != nil { @@ -145,7 +154,7 @@ func TestProcessWholeSeesTheBuckets(t *testing.T) { // 1134/AC8 t.Fatalf("counted = %d, want 2 (a.x, a.y): %+v", s.Counted(), s.Rows) } - put(delivery.HaveBucket, "a.x", haveValue("b1", delivery.PhaseRunning, delivery.HealthOK)) + put(view.HaveBucket, "a.x", haveValue("b1", nodeengineclient.PhaseRunning, nodeengineclient.HealthOK)) rowsMatch(func(s view.Snapshot) bool { r := byKey(s, "a", "x"); return r.Phase == "running" && r.Health == "ok" }) } @@ -173,7 +182,7 @@ func TestProcessWholeShowsWhatTheWatchmanSays(t *testing.T) { // 1168/AC5 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() kvs := map[string]jetstream.KeyValue{} - for _, b := range []string{delivery.HaveBucket, delivery.PlacementBucket, delivery.PresenceBucket, delivery.StuckBucket} { + for _, b := range []string{view.HaveBucket, view.PlacementBucket, view.PresenceBucket, view.StuckBucket} { kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: b, History: 5}) if err != nil { t.Fatal(err) @@ -182,15 +191,15 @@ func TestProcessWholeShowsWhatTheWatchmanSays(t *testing.T) { // 1168/AC5 } presence := func(key string, online bool) { t.Helper() - b, _ := delivery.EncodePresence(delivery.Presence{Online: online}) - if _, err := kvs[delivery.PresenceBucket].Put(ctx, key, b); err != nil { + b, _ := meshwatchdogsclient.EncodeNodePresence(meshwatchdogsclient.NodePresence{Online: online}) + if _, err := kvs[view.PresenceBucket].Put(ctx, key, b); err != nil { t.Fatal(err) } } stuck := func(moved bool) { t.Helper() - b, _ := delivery.EncodeStuck(delivery.Stuck{Bucket: delivery.HaveBucket, Key: "a.x", Waited: "a report from a", Since: time.Now(), Moved: moved}) - if _, err := kvs[delivery.StuckBucket].Put(ctx, delivery.HaveBucket+".a.x", b); err != nil { + b, _ := meshwatchdogsclient.EncodeStuck(meshwatchdogsclient.Stuck{Bucket: view.HaveBucket, Key: "a.x", Waited: "a report from a", Since: time.Now(), Moved: moved}) + if _, err := kvs[view.StuckBucket].Put(ctx, view.HaveBucket+".a.x", b); err != nil { t.Fatal(err) } } @@ -260,7 +269,7 @@ func TestProcessWholeShowsTheIndexersBucketsOnceTheyExist(t *testing.T) { // 124 } ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() - for _, b := range []string{delivery.HaveBucket, delivery.PlacementBucket, delivery.PresenceBucket, delivery.StuckBucket} { + for _, b := range []string{view.HaveBucket, view.PlacementBucket, view.PresenceBucket, view.StuckBucket} { if _, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: b, History: 5}); err != nil { t.Fatal(err) } @@ -289,21 +298,21 @@ func TestProcessWholeShowsTheIndexersBucketsOnceTheyExist(t *testing.T) { // 124 break } } - cands, err := js.KeyValue(ctx, delivery.CandidatesBucket) + cands, err := js.KeyValue(ctx, view.CandidatesBucket) if err != nil { t.Fatal(err) } - mods, err := js.KeyValue(ctx, delivery.ModulesBucket) + mods, err := js.KeyValue(ctx, view.ModulesBucket) if err != nil { t.Fatal(err) } const head = "0123456789abcdef0123456789abcdef01234567" - hash, _ := delivery.HeadsHash([]delivery.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: head}}) - cand, err := delivery.EncodeCandidate(delivery.Candidate{Group: "feat/x", HeadsHash: hash, Pulls: []delivery.CandidatePull{{Head: delivery.Head{Repository: "novox/mesh-delivery", Number: 12, Head: head}, Modules: []string{"a"}}}}) + hash, _ := meshdeliveryclient.HeadsHash([]meshdeliveryclient.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: head}}) + cand, err := json.Marshal(meshcatalog.CandidatesByGroupValue{Group: "feat/x", Heads: hash, Pulls: []meshcatalog.CandidatesByGroupValuePullsItem{{Repository: "novox/mesh-delivery", Number: 12, Head: head, Modules: []string{"a"}}}}) if err != nil { t.Fatal(err) } - mod, err := delivery.EncodeModule(delivery.Module{None: "no tree", Repository: "novox/mesh-delivery", Commit: head}) + mod, err := meshcatalogclient.EncodeModule(meshcatalog.ModulesByModuleValue{None: "no tree", Repository: "novox/mesh-delivery", Commit: head}) if err != nil { t.Fatal(err) } @@ -370,23 +379,23 @@ func TestProcessWholeShowsTheOpenAsksAndDropsTheAnsweredOnes(t *testing.T) { // go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }() t.Cleanup(func() { stop(); <-done }) - gate, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: delivery.AsksBucket, History: delivery.AsksHistory}) + gate, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: view.AsksBucket, History: view.AsksHistory}) if err != nil { t.Fatal(err) } - teller, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: delivery.AnswersBucket, History: delivery.AnswersHistory}) + teller, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: view.AnswersBucket, History: view.AnswersHistory}) if err != nil { t.Fatal(err) } - heads := []delivery.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: "0123456789abcdef0123456789abcdef01234567"}} - key, err := delivery.MergeAskKey("feat/x", heads) + heads := []meshdeliveryclient.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: "0123456789abcdef0123456789abcdef01234567"}} + key, err := meshdeliveryclient.MergeAskKey("feat/x", heads) if err != nil { t.Fatal(err) } - ask := oc.Ask{ID: delivery.AskID(key), Headline: "Merge into main: mesh-delivery#12", Explanation: "Merges one pull request.", Who: occ.Operator, + ask := oc.Ask{ID: meshdeliveryclient.AskID(key), Headline: "Merge into main: mesh-delivery#12", Explanation: "Merges one pull request.", Who: occ.Operator, Expires: occ.FormatTime(time.Now().Add(time.Hour)), OnExpiry: "the merge stays held", Options: []oc.Option{{ID: "approve", Label: "Approve", Does: "merges", Level: occ.Approve, Binds: "sha256:00"}}} - value, err := delivery.EncodeAsk(delivery.Ask{Group: "feat/x", Heads: heads, Ask: rawAsk(t, ask)}) + value, err := meshdeliveryclient.EncodeAsk(meshdelivery.AsksByMergeGroupHeadsValue{Group: "feat/x", Heads: askHeads(heads), Ask: rawAsk(t, ask)}) if err != nil { t.Fatal(err) } @@ -409,7 +418,7 @@ func TestProcessWholeShowsTheOpenAsksAndDropsTheAnsweredOnes(t *testing.T) { // wait(func(s view.Snapshot) bool { return len(s.Asks) == 1 && s.Asks[0].Key == key && s.Asks[0].How == "answer on the phone" }, "the open ask never showed") - answer, err := delivery.EncodeAnswer(delivery.Answer{Option: "approve", Warrant: ask.ID + "@2026-10-18T12:00:00Z", Words: "the operator, via telegram, chose Approve"}) + answer, err := meshdeliveryclient.EncodeAnswer(meshdeliveryclient.Answer{Option: "approve", Warrant: ask.ID + "@2026-10-18T12:00:00Z", Words: "the operator, via telegram, chose Approve"}) if err != nil { t.Fatal(err) } @@ -461,17 +470,17 @@ func TestProcessWholeListsEveryDifference(t *testing.T) { // 1297/B8 AC go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }() t.Cleanup(func() { stop(); <-done }) - kv, err := js.KeyValue(ctx, delivery.AssignerWantsBucket) + kv, err := js.KeyValue(ctx, view.AssignerWantsBucket) if err != nil { t.Fatal(err) } const fpOld = "src2:1111111111111111111111111111111111111111111111111111111111111111" const fpNew = "src2:2222222222222222222222222222222222222222222222222222222222222222" - for key, d := range map[string]delivery.Want{ - "want.ace.plex": {FP: fpNew, Composed: "sha256:" + strings.Repeat("c", 64)}, - "want.g14.plex": {FP: fpOld, Composed: "sha256:" + strings.Repeat("c", 64), Newest: fpNew, Rule: delivery.RuleCanary, Words: "the canary ace does not run it healthy yet"}, + for key, d := range map[string]meshwantsclient.Want{ + "want.ace.plex": {Fp: fpNew, Composed: "sha256:" + strings.Repeat("c", 64)}, + "want.g14.plex": {Fp: fpOld, Composed: "sha256:" + strings.Repeat("c", 64), Newest: fpNew, Rule: meshwantsclient.RuleCanary, Words: "the canary ace does not run it healthy yet"}, } { - v, err := delivery.EncodeWant(d) + v, err := meshwantsclient.EncodeWant(d) if err != nil { t.Fatal(err) } @@ -494,7 +503,7 @@ func TestProcessWholeListsEveryDifference(t *testing.T) { // 1297/B8 AC } wait(func(s view.Snapshot) bool { d := s.Wants - return len(d) == 2 && d[0].Node == "ace" && d[0].State == view.WantPending && d[1].Node == "g14" && d[1].State == view.WantHeldBack && d[1].Rule == delivery.RuleCanary + return len(d) == 2 && d[0].Node == "ace" && d[0].State == view.WantPending && d[1].Node == "g14" && d[1].State == view.WantHeldBack && d[1].Rule == meshwantsclient.RuleCanary }, "the two wants never showed with where they stand") if err := kv.Delete(ctx, "want.g14.plex"); err != nil { t.Fatal(err) @@ -536,10 +545,10 @@ func TestProcessWholeWritesTheAnswerToAnAskNeedingNoProof(t *testing.T) { // 129 } return kv } - askBucket := bucket(delivery.AssignerAsksBucket, delivery.AssignerAsksHistory) - viewBucket := bucket(delivery.ViewAnswersBucket, delivery.ViewAnswersHistory) - teller := bucket(delivery.AnswersBucket, delivery.AnswersHistory) - gate := bucket(delivery.AsksBucket, delivery.AsksHistory) + askBucket := bucket(view.AssignerAsksBucket, view.AssignerAsksHistory) + viewBucket := bucket(viewstate.ViewAnswersBucket, viewstate.ViewAnswersHistory) + teller := bucket(view.AnswersBucket, view.AnswersHistory) + gate := bucket(view.AsksBucket, view.AsksHistory) dir, err := os.MkdirTemp(os.Getenv("TMPDIR"), "i") if err != nil { @@ -560,14 +569,14 @@ func TestProcessWholeWritesTheAnswerToAnAskNeedingNoProof(t *testing.T) { // 129 t.Cleanup(func() { stop(); <-done }) const fp = "src2:2222222222222222222222222222222222222222222222222222222222222222" - key, err := delivery.HoldAskKey("plex", fp) + key, err := asks.HoldAskKey("plex", fp) if err != nil { t.Fatal(err) } - ask := oc.Ask{ID: delivery.AskID(key), Headline: "plex is held after a failed health check", Explanation: "ace failed the check.", Who: occ.Operator, + ask := oc.Ask{ID: meshdeliveryclient.AskID(key), Headline: "plex is held after a failed health check", Explanation: "ace failed the check.", Who: occ.Operator, Expires: occ.FormatTime(time.Now().Add(time.Hour)), OnExpiry: "the fingerprint stays held", Options: []oc.Option{{ID: "release", Label: "Release", Does: "lets the move go on", Level: occ.Approve}, {ID: "keep", Label: "Keep held", Does: "keeps it held", Level: occ.Approve}}} - value, _ := delivery.EncodeAssignerAsk(key, delivery.AssignerAsk{Ask: rawAsk(t, ask)}) + value, _ := asks.EncodeAsk(key, asks.Ask{Ask: rawAsk(t, ask)}) if _, err := askBucket.Put(ctx, key, value); err != nil { t.Fatal(err) } @@ -593,20 +602,20 @@ func TestProcessWholeWritesTheAnswerToAnAskNeedingNoProof(t *testing.T) { // 129 if err != nil { t.Fatalf("the view wrote no answer under %s.view: %v", key, err) } - if a, err := delivery.DecodeViewAnswer(e.Value()); err != nil || a.Option != "release" { + if a, err := viewstate.DecodeViewAnswer(e.Value()); err != nil || a.Option != "release" { t.Fatalf("answer = %+v, %v", a, err) } wait(func(s view.Snapshot) bool { return len(s.Asks) == 1 && s.Asks[0].Chosen == "release" }, "the ask never showed the view's choice") // The merge's Approve is answered on the phone alone. - heads := []delivery.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: "0123456789abcdef0123456789abcdef01234567"}} - mkey, err := delivery.MergeAskKey("feat/x", heads) + heads := []meshdeliveryclient.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: "0123456789abcdef0123456789abcdef01234567"}} + mkey, err := meshdeliveryclient.MergeAskKey("feat/x", heads) if err != nil { t.Fatal(err) } - merge := oc.Ask{ID: delivery.AskID(mkey), Headline: "Merge", Explanation: "Merges.", Who: occ.Operator, Expires: occ.FormatTime(time.Now().Add(time.Hour)), OnExpiry: "held", + merge := oc.Ask{ID: meshdeliveryclient.AskID(mkey), Headline: "Merge", Explanation: "Merges.", Who: occ.Operator, Expires: occ.FormatTime(time.Now().Add(time.Hour)), OnExpiry: "held", Options: []oc.Option{{ID: "approve", Label: "Approve", Does: "merges", Level: occ.Approve, Binds: "sha256:00"}}} - mv, err := delivery.EncodeAsk(delivery.Ask{Group: "feat/x", Heads: heads, Ask: rawAsk(t, merge)}) + mv, err := meshdeliveryclient.EncodeAsk(meshdelivery.AsksByMergeGroupHeadsValue{Group: "feat/x", Heads: askHeads(heads), Ask: rawAsk(t, merge)}) if err != nil { t.Fatal(err) } @@ -622,7 +631,7 @@ func TestProcessWholeWritesTheAnswerToAnAskNeedingNoProof(t *testing.T) { // 129 } // The teller fills the ask: it leaves the list. - answer, err := delivery.EncodeAnswer(delivery.Answer{Option: "release", Warrant: ask.ID + "@2026-10-23T12:00:00Z", Words: "answered in the view"}) + answer, err := meshdeliveryclient.EncodeAnswer(meshdeliveryclient.Answer{Option: "release", Warrant: ask.ID + "@2026-10-23T12:00:00Z", Words: "answered in the view"}) if err != nil { t.Fatal(err) } @@ -656,28 +665,28 @@ func TestProcessWholeShowsTheBacklogOfBuildAsks(t *testing.T) { // 1887/A8a AC } ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() - stream, _ := delivery.AskStream(delivery.AskGo) + stream, _ := buildstep.AskStream(buildstep.AskGo) st, err := js.CreateStream(ctx, jetstream.StreamConfig{Name: stream, Subjects: []string{"mesh.seat.node-build-go.accept.>"}, Retention: jetstream.WorkQueuePolicy}) if err != nil { t.Fatal(err) } var checkWorker jetstream.Consumer - for _, kind := range delivery.AskKinds { - name, _ := delivery.AskWorker(delivery.AskGo, kind) - subject, _ := delivery.AskSubject(delivery.AskGo, kind) + for _, kind := range buildstep.AskKinds { + name, _ := buildstep.AskWorker(buildstep.AskGo, kind) + subject, _ := buildstep.AskSubject(buildstep.AskGo, kind) c, err := st.CreateOrUpdateConsumer(ctx, jetstream.ConsumerConfig{Durable: name, FilterSubject: subject, AckPolicy: jetstream.AckExplicitPolicy}) if err != nil { t.Fatal(err) } - if kind == delivery.AskCheck { + if kind == buildstep.AskCheck { checkWorker = c } } - subject, _ := delivery.AskSubject(delivery.AskGo, delivery.AskCheck) + subject, _ := buildstep.AskSubject(buildstep.AskGo, buildstep.AskCheck) oldest := time.Now().UTC().Add(-2 * time.Minute) for i, id := range []string{"m0000001aaaaaa", "m0000002bbbbbb", "m0000003cccccc", "m0000004dddddd"} { - body, err := delivery.EncodeStepAsk(delivery.StepAsk{ID: id, Kind: delivery.AskCheck, Language: delivery.AskGo, Asker: "mesh-delivery-checker", At: oldest.Add(time.Duration(i) * time.Second), - Check: &delivery.CheckJob{Fetch: map[string]any{"repository": "novox/x", "commit": "0123456789abcdef0123456789abcdef01234567"}, Levels: []string{delivery.LevelUnit}}}) + body, err := buildstep.EncodeStepAsk(buildstep.StepAsk{ID: id, Kind: buildstep.AskCheck, Language: buildstep.AskGo, Asker: "mesh-delivery-checker", At: oldest.Add(time.Duration(i) * time.Second), + Check: &buildstep.CheckJob{Fetch: buildstep.Fetch{Repository: "novox/x", Commit: "0123456789abcdef0123456789abcdef01234567"}, Levels: []string{buildstep.LevelUnit}}}) if err != nil { t.Fatal(err) } @@ -755,7 +764,7 @@ func TestProcessWholeSaysTheSourceRevisionIsDelivered(t *testing.T) { t.Fatal(err) } kvs := map[string]jetstream.KeyValue{} - for _, name := range []string{delivery.SourceBucket, delivery.ModulesBucket, delivery.AssignerWantsBucket, delivery.HaveBucket} { + for _, name := range []string{view.SourceBucket, view.ModulesBucket, view.AssignerWantsBucket, view.HaveBucket} { kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: name, History: 5}) if err != nil { t.Fatal(err) @@ -780,23 +789,23 @@ func TestProcessWholeSaysTheSourceRevisionIsDelivered(t *testing.T) { t.Cleanup(func() { stop(); <-done }) fp := "src2:" + strings.Repeat("2", 64) - mod, _ := delivery.EncodeModule(delivery.Module{FP2: fp, Manifest: strings.Repeat("a", 64), Repository: "novox/web", Commit: strings.Repeat("1", 40), Path: delivery.RootPath}) - want, _ := delivery.EncodeWant(delivery.Want{FP: fp, Composed: "sha256:" + strings.Repeat("c", 64)}) - failed, _ := delivery.EncodeHave(delivery.Have{FP: fp, Phase: delivery.PhaseRunning, Health: delivery.HealthFailed}) - good, _ := delivery.EncodeHave(delivery.Have{FP: fp, Phase: delivery.PhaseRunning, Health: delivery.HealthOK}) - src, _ := delivery.EncodeSource(delivery.Source{Group: "feat/x", Heads: map[string]string{"novox/web": strings.Repeat("1", 40)}}) - for bucket, kv := range map[string][]byte{delivery.ModulesBucket: mod} { + mod, _ := meshcatalogclient.EncodeModule(meshcatalog.ModulesByModuleValue{Fp2: fp, Manifest: strings.Repeat("a", 64), Repository: "novox/web", Commit: strings.Repeat("1", 40), Path: meshcatalogclient.RootPath}) + want, _ := meshwantsclient.EncodeWant(meshwantsclient.Want{Fp: fp, Composed: "sha256:" + strings.Repeat("c", 64)}) + failed, _ := nodeengineclient.EncodeHave(nodeengine.HaveByNodeModuleValue{Fp: fp, Phase: nodeengineclient.PhaseRunning, Health: nodeengineclient.HealthFailed}) + good, _ := nodeengineclient.EncodeHave(nodeengine.HaveByNodeModuleValue{Fp: fp, Phase: nodeengineclient.PhaseRunning, Health: nodeengineclient.HealthOK}) + src, _ := meshdeliveryclient.EncodeSource(meshdelivery.SourceByMainValue{Group: "feat/x", Heads: map[string]string{"novox/web": strings.Repeat("1", 40)}}) + for bucket, kv := range map[string][]byte{view.ModulesBucket: mod} { if _, err := kvs[bucket].Put(ctx, "web", kv); err != nil { t.Fatal(err) } } - if _, err := kvs[delivery.AssignerWantsBucket].Put(ctx, "want.ace.web", want); err != nil { + if _, err := kvs[view.AssignerWantsBucket].Put(ctx, "want.ace.web", want); err != nil { t.Fatal(err) } - if _, err := kvs[delivery.HaveBucket].Put(ctx, "ace.web", failed); err != nil { + if _, err := kvs[view.HaveBucket].Put(ctx, "ace.web", failed); err != nil { t.Fatal(err) } - rev, err := kvs[delivery.SourceBucket].Put(ctx, delivery.SourceMainKey, src) + rev, err := kvs[view.SourceBucket].Put(ctx, meshdeliveryclient.SourceMainKey, src) if err != nil { t.Fatal(err) } @@ -804,7 +813,7 @@ func TestProcessWholeSaysTheSourceRevisionIsDelivered(t *testing.T) { if info, err := events.Info(ctx, jetstream.WithSubjectFilter("mesh.mod.mesh-delivery-view.event.>")); err != nil || len(info.State.Subjects) != 0 { t.Fatalf("an event was said before the revision was delivered: %v %+v", err, info.State) } - if _, err := kvs[delivery.HaveBucket].Put(ctx, "ace.web", good); err != nil { + if _, err := kvs[view.HaveBucket].Put(ctx, "ace.web", good); err != nil { t.Fatal(err) } var msg *jetstream.RawStreamMsg @@ -852,7 +861,7 @@ func TestProcessWholeSaysModulesInMotion(t *testing.T) { t.Fatal(err) } kvs := map[string]jetstream.KeyValue{} - for _, name := range []string{delivery.AssignerWantsBucket, delivery.HaveBucket, delivery.StuckBucket} { + for _, name := range []string{view.AssignerWantsBucket, view.HaveBucket, view.StuckBucket} { kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: name, History: 5}) if err != nil { t.Fatal(err) @@ -889,16 +898,16 @@ func TestProcessWholeSaysModulesInMotion(t *testing.T) { } } want := func(node, module string) { - v, err := delivery.EncodeWant(delivery.Want{FP: fpNew, Composed: digest}) - put(delivery.AssignerWantsBucket, "want."+node+"."+module, v, err) + v, err := meshwantsclient.EncodeWant(meshwantsclient.Want{Fp: fpNew, Composed: digest}) + put(view.AssignerWantsBucket, "want."+node+"."+module, v, err) } - have := func(node, module, fp string, phase delivery.Phase, health delivery.Health, detail string) { - v, err := delivery.EncodeHave(delivery.Have{FP: fp, Phase: phase, Health: health, Detail: detail}) - put(delivery.HaveBucket, node+"."+module, v, err) + have := func(node, module, fp string, phase string, health string, detail string) { + v, err := nodeengineclient.EncodeHave(nodeengine.HaveByNodeModuleValue{Fp: fp, Phase: phase, Health: health, Detail: detail}) + put(view.HaveBucket, node+"."+module, v, err) } - have("ace", "web", fpOld, delivery.PhaseRunning, delivery.HealthOK, "") + have("ace", "web", fpOld, nodeengineclient.PhaseRunning, nodeengineclient.HealthOK, "") want("ace", "web") - have("g14", "web", fpOld, delivery.PhaseRunning, delivery.HealthOK, "") + have("g14", "web", fpOld, nodeengineclient.PhaseRunning, nodeengineclient.HealthOK, "") want("g14", "web") said := func() map[string]jetstream.RawStreamMsg { @@ -936,12 +945,12 @@ func TestProcessWholeSaysModulesInMotion(t *testing.T) { if mv.Subject != "mesh.mod.mesh-delivery-view.event.moving" { t.Errorf("subject = %s", mv.Subject) } - have("ace", "web", fpNew, delivery.PhaseRunning, delivery.HealthOK, "") + have("ace", "web", fpNew, nodeengineclient.PhaseRunning, nodeengineclient.HealthOK, "") st := waitFor("settled.ace.web." + hex) if err := json.Unmarshal(st.Data, &body); err != nil || body.Outcome != "running" { t.Errorf("settled = %+v, %v", body, err) } - have("g14", "web", fpOld, delivery.PhaseHeld, delivery.HealthWaits, "no space left") + have("g14", "web", fpOld, nodeengineclient.PhaseHeld, nodeengineclient.HealthWaits, "no space left") stk := waitFor("stuck.g14.web." + hex) if err := json.Unmarshal(stk.Data, &body); err != nil || body.Outcome != "stopped" || body.Words != "no space left" { t.Errorf("stuck = %+v, %v", body, err) @@ -955,3 +964,12 @@ func TestProcessWholeSaysModulesInMotion(t *testing.T) { t.Errorf("events said by type = %v, want moving 2, settled 1, stuck 1", count) } } + +// askHeads is the heads of a merge ask as the seat carries them. +func askHeads(heads []meshdeliveryclient.Head) []meshdelivery.AsksByMergeGroupHeadsValueHeadsItem { + out := make([]meshdelivery.AsksByMergeGroupHeadsValueHeadsItem, len(heads)) + for i, h := range heads { + out[i] = meshdelivery.AsksByMergeGroupHeadsValueHeadsItem{Repository: h.Repository, Number: int64(h.Number), Head: h.Head} + } + return out +} diff --git a/modules/mesh-delivery-view/cmd/mesh-delivery-view/main.go b/modules/mesh-delivery-view/cmd/mesh-delivery-view/main.go index c8ccf17e..bdcf7ea7 100644 --- a/modules/mesh-delivery-view/cmd/mesh-delivery-view/main.go +++ b/modules/mesh-delivery-view/cmd/mesh-delivery-view/main.go @@ -11,9 +11,9 @@ import ( "encoding/json" "errors" "fmt" - sdkfindings "git.novox.be/novox/mesh-delivery/sdk/findings" "io" "log" + "mesh-delivery-view/internal/view" "net/http" "os" "os/signal" @@ -21,14 +21,14 @@ import ( "syscall" "time" - "github.com/nats-io/nats.go" - "github.com/nats-io/nats.go/jetstream" - - "git.novox.be/novox/mesh-sdk/go/delivery" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate" + sdkfindings "git.novox.be/novox/mesh-delivery/sdk/findings" + "git.novox.be/novox/mesh-delivery/sdk/trunk" "git.novox.be/novox/mesh-sdk/go/process" "git.novox.be/novox/mesh-sdk/go/views" - - "mesh-delivery-view/internal/view" + "github.com/nats-io/nats.go" + "github.com/nats-io/nats.go/jetstream" ) const ( @@ -87,7 +87,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro model.SetDeliveredEvent(func(e view.DeliveredEvent) error { id := fmt.Sprintf("delivered.%d", e.Revision) if e.Repository != "" { // a repository's reached head (ADR 0556 clause 3) - key, _ := delivery.RepositoryKey(e.Repository) + key, _ := trunk.RepositoryKey(e.Repository) id = fmt.Sprintf("reached.%s.%d.%d", key, e.Revision, len(e.NotYet)) if len(e.NotYet) > 0 { sum := sha256.Sum256([]byte(strings.Join(e.NotYet, ","))) @@ -117,7 +117,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro return err } defer calls.Close() - reader := &views.ProcessReader{Module: delivery.ViewModule, Call: calls.Call, Logf: logger.Printf} + reader := &views.ProcessReader{Module: viewstate.ViewModule, Call: calls.Call, Logf: logger.Printf} findings := process.AwaitBucket(ctx, js, conn, findingsBucket, "a first view unanswered past its bound is said in the log alone", logger.Printf) reader.Findings = func() views.FindingWriter { return sdkfindings.Writer(ctx, findings.KV) } for _, o := range ownersFollowed { @@ -126,12 +126,12 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro } } open := buckets{js: js, reader: reader} - for _, bucket := range []string{delivery.HaveBucket, delivery.PlacementBucket, delivery.PresenceBucket, delivery.StuckBucket, delivery.CandidatesBucket, delivery.ModulesBucket, delivery.AsksBucket, delivery.AnswersBucket, delivery.BuilderBuildsBucket, delivery.AssignerWantsBucket, delivery.SourceBucket, delivery.AssignerAsksBucket, delivery.ViewAnswersBucket, delivery.TakenBucket} { + for _, bucket := range []string{view.HaveBucket, view.PlacementBucket, view.PresenceBucket, view.StuckBucket, view.CandidatesBucket, view.ModulesBucket, view.AsksBucket, view.AnswersBucket, builds.BuilderBuildsBucket, view.AssignerWantsBucket, view.SourceBucket, view.AssignerAsksBucket, viewstate.ViewAnswersBucket, view.TakenBucket} { go follow(ctx, open, bucket, false, model, logger) } // The modules' and the source's history give each fingerprint's first commit and each head's source revision (ADR 0556). // The modules' picture holds no history: from its first view on, each change it takes is the history (finding of DLV-M4). - for _, bucket := range []string{delivery.ModulesBucket, delivery.SourceBucket} { + for _, bucket := range []string{view.ModulesBucket, view.SourceBucket} { go follow(ctx, open, bucket, true, model, logger) } reader.Start(ctx, js, cred, process.AwaitBucket(ctx, js, conn, firstViewsBucket, "view_first_views reads nothing and view_first_view_again is not heard", logger.Printf)) @@ -156,9 +156,9 @@ type kvWriter struct { } func (w kvWriter) Put(key string, value []byte) error { - kv, err := w.js.KeyValue(w.ctx, delivery.ViewAnswersBucket) + kv, err := w.js.KeyValue(w.ctx, viewstate.ViewAnswersBucket) if err != nil { - return fmt.Errorf("the bucket %s: %w", delivery.ViewAnswersBucket, err) + return fmt.Errorf("the bucket %s: %w", viewstate.ViewAnswersBucket, err) } _, err = kv.Put(w.ctx, key, value) return err @@ -166,9 +166,9 @@ func (w kvWriter) Put(key string, value []byte) error { // putReach writes a reach value under its key in the view's own state reach (ADR 0556 clause 2), its only writer. func putReach(ctx context.Context, js jetstream.JetStream, w view.ReachWrite) error { - kv, err := js.KeyValue(ctx, delivery.ReachBucket) + kv, err := js.KeyValue(ctx, viewstate.ReachBucket) if err != nil { - return fmt.Errorf("the bucket %s: %w", delivery.ReachBucket, err) + return fmt.Errorf("the bucket %s: %w", viewstate.ReachBucket, err) } _, err = kv.Put(ctx, w.Key, w.Value) return err @@ -177,20 +177,20 @@ func putReach(ctx context.Context, js jetstream.JetStream, w view.ReachWrite) er // The view's own states of its first views (views.StatusState) and of its findings, where the condition keeper raises a // first view unanswered past its bound (ADR 0615 §6). var ( - firstViewsBucket = delivery.ViewModule + "_" + views.StatusState - findingsBucket = delivery.ViewModule + "_findings" + firstViewsBucket = viewstate.ViewModule + "_" + views.StatusState + findingsBucket = viewstate.ViewModule + "_findings" ) // ownersFollowed are the indexer's and the assigner's states the view's process follows by their first views, each by its // owner's read tool (as the view's invokes names it) and views family (its events on the shared events stream); the // view's tools ask the other six through the owners' read tools on each call. var ownersFollowed = []struct{ owner, state, tool string }{ - {delivery.AssignerModule, delivery.PlacementState, delivery.AssignerModule + ".assigner_read_placement"}, - {delivery.AssignerModule, "wants", delivery.AssignerModule + ".assigner_read_wants"}, - {delivery.AssignerModule, "asks", delivery.AssignerModule + ".assigner_read_asks"}, - {delivery.IndexerModule, "candidates", delivery.IndexerModule + ".indexer_read_candidates"}, - {delivery.IndexerModule, "modules", delivery.IndexerModule + ".indexer_read_modules"}, - {delivery.IndexerModule, "taken", delivery.IndexerModule + ".indexer_read_taken"}, + {view.AssignerModule, view.PlacementState, view.AssignerModule + ".assigner_read_placement"}, + {view.AssignerModule, "wants", view.AssignerModule + ".assigner_read_wants"}, + {view.AssignerModule, "asks", view.AssignerModule + ".assigner_read_asks"}, + {view.IndexerModule, "candidates", view.IndexerModule + ".indexer_read_candidates"}, + {view.IndexerModule, "modules", view.IndexerModule + ".indexer_read_modules"}, + {view.IndexerModule, "taken", view.IndexerModule + ".indexer_read_taken"}, } // buckets opens what the view watches: an owner's state as its picture (firstview), any other bucket on the bus. diff --git a/modules/mesh-delivery-view/go.mod b/modules/mesh-delivery-view/go.mod index fce17415..14731581 100644 --- a/modules/mesh-delivery-view/go.mod +++ b/modules/mesh-delivery-view/go.mod @@ -3,6 +3,19 @@ module mesh-delivery-view go 1.26.0 require ( + git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f github.com/nats-io/nats-server/v2 v2.11.17 diff --git a/modules/mesh-delivery-view/internal/view/answers_test.go b/modules/mesh-delivery-view/internal/view/answers_test.go index 4717494b..26368532 100644 --- a/modules/mesh-delivery-view/internal/view/answers_test.go +++ b/modules/mesh-delivery-view/internal/view/answers_test.go @@ -12,7 +12,9 @@ import ( occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-sdk/go/delivery" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate" ) // memWriter is the view's bucket answers in memory. @@ -36,18 +38,18 @@ func (w *memWriter) Put(key string, value []byte) error { // none yet; the value is `{"v":1,"ask":}`, as the gate's minus group and heads). func holdAsk(t *testing.T) (Entry, oc.Ask) { t.Helper() - key, err := delivery.HoldAskKey("plex", fpNew) + key, err := asks.HoldAskKey("plex", fpNew) if err != nil { t.Fatal(err) } - a := oc.Ask{ID: delivery.AskID(key), Headline: "plex is held after a failed health check", Explanation: "ace failed the check on the new build.", Who: occ.Operator, + a := oc.Ask{ID: meshdeliveryclient.AskID(key), Headline: "plex is held after a failed health check", Explanation: "ace failed the check on the new build.", Who: occ.Operator, Expires: occ.FormatTime(t0.Add(23 * time.Hour)), OnExpiry: "the fingerprint stays held", Options: []oc.Option{{ID: "release", Label: "Release", Does: "lets the move go on", Level: occ.Approve}, {ID: "keep", Label: "Keep held", Does: "keeps it held", Level: occ.Approve}}} - v, err := delivery.EncodeAssignerAsk(key, delivery.AssignerAsk{Ask: rawAsk(t, a)}) + v, err := asks.EncodeAsk(key, asks.Ask{Ask: rawAsk(t, a)}) if err != nil { t.Fatal(err) } - return Entry{Bucket: delivery.AssignerAsksBucket, Key: key, Value: v, At: t0}, a + return Entry{Bucket: AssignerAsksBucket, Key: key, Value: v, At: t0}, a } func TestAnAskNeedingNoProofIsOpenInTheViewWithItsOptions(t *testing.T) { // 1297/B9 @@ -77,16 +79,16 @@ func TestTheViewWritesItsAnswerUnderTheAsksViewKey(t *testing.T) { // 1297/B9a A if !ok || len(w.puts) != 1 { t.Fatalf("puts = %v, want the one key %s.view", w.puts, e.Key) } - a, err := delivery.DecodeViewAnswer(raw) + a, err := viewstate.DecodeViewAnswer(raw) if err != nil || a.Option != "release" || a.Words != "the check was a flake" { t.Errorf("answer = %+v, %v", a, err) } // The key is the SDK's: the bucket's key parses back to the ask. - if back, err := delivery.ParseViewAnswerKey(e.Key + ".view"); err != nil || back != e.Key { + if back, err := viewstate.ParseViewAnswerKey(e.Key + ".view"); err != nil || back != e.Key { t.Errorf("parse = %q, %v", back, err) } // The view hears its own answer back from its bucket and shows the ask as answered, waiting for the teller. - fill(m, Entry{Bucket: delivery.ViewAnswersBucket, Key: e.Key + ".view", Value: raw, At: t0}) + fill(m, Entry{Bucket: viewstate.ViewAnswersBucket, Key: e.Key + ".view", Value: raw, At: t0}) got := m.Snapshot().Asks if len(got) != 1 || got[0].Chosen != "release" { t.Errorf("asks = %+v, want the ask with chosen release", got) @@ -220,11 +222,11 @@ func TestAStaleViewAnswerOfAnotherAskingIsNotTheChoice(t *testing.T) { // 1297/B m := New() e, a := holdAsk(t) fill(m, e) - stale, err := delivery.EncodeViewAnswer(delivery.ViewAnswer{Asking: "an-earlier-asking", Option: "release"}) + stale, err := viewstate.EncodeViewAnswer(viewstate.ViewAnswer{Asking: "an-earlier-asking", Option: "release"}) if err != nil { t.Fatal(err) } - fill(m, Entry{Bucket: delivery.ViewAnswersBucket, Key: e.Key + ".view", Value: stale, At: t0}) + fill(m, Entry{Bucket: viewstate.ViewAnswersBucket, Key: e.Key + ".view", Value: stale, At: t0}) if got := m.Snapshot().Asks; len(got) != 1 || got[0].Chosen != "" { t.Errorf("a stale answer shows as the choice: %+v", got) } @@ -234,7 +236,7 @@ func TestAStaleViewAnswerOfAnotherAskingIsNotTheChoice(t *testing.T) { // 1297/B if err := m.Answer(e.Key, "keep", ""); err != nil { t.Fatal(err) } - if v, err := delivery.DecodeViewAnswer(w.puts[e.Key+".view"]); err != nil || v.Asking != a.ID { + if v, err := viewstate.DecodeViewAnswer(w.puts[e.Key+".view"]); err != nil || v.Asking != a.ID { t.Errorf("answer = %+v, %v; want asking %s", v, err, a.ID) } } diff --git a/modules/mesh-delivery-view/internal/view/asks_test.go b/modules/mesh-delivery-view/internal/view/asks_test.go index cdc58a24..23ca7a31 100644 --- a/modules/mesh-delivery-view/internal/view/asks_test.go +++ b/modules/mesh-delivery-view/internal/view/asks_test.go @@ -9,9 +9,11 @@ import ( "testing" "time" + "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-sdk/go/delivery" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" ) const askHeadA = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" @@ -20,31 +22,33 @@ const askHeadA = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" // whole, under the id its key gives. func askEntry(t *testing.T, group, headline, words string) Entry { t.Helper() - heads := []delivery.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: askHeadA}} - key, err := delivery.MergeAskKey(group, heads) + heads := []meshdeliveryclient.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: askHeadA}} + key, err := meshdeliveryclient.MergeAskKey(group, heads) if err != nil { t.Fatal(err) } - a := oc.Ask{ID: delivery.AskID(key), Headline: headline, Explanation: words, Who: occ.Operator, Expires: occ.FormatTime(t0.Add(23 * time.Hour)), OnExpiry: "the merge stays held", + a := oc.Ask{ID: meshdeliveryclient.AskID(key), Headline: headline, Explanation: words, Who: occ.Operator, Expires: occ.FormatTime(t0.Add(23 * time.Hour)), OnExpiry: "the merge stays held", Options: []oc.Option{{ID: "approve", Label: "Approve", Does: "merges", Level: occ.Approve, Binds: "sha256:00"}}} - v, err := delivery.EncodeAsk(delivery.Ask{Group: group, Heads: heads, Ask: rawAsk(t, a)}) + v, err := meshdeliveryclient.EncodeAsk(meshdelivery.AsksByMergeGroupHeadsValue{Group: group, Heads: askHeads(heads), Ask: rawAsk(t, a)}) if err != nil { t.Fatal(err) } - return Entry{Bucket: delivery.AsksBucket, Key: key, Value: v, At: t0} + return Entry{Bucket: AsksBucket, Key: key, Value: v, At: t0} } // answerEntry is the teller's answer to the first asking of the key; answerToAsking to the asking with this router id. The // answer's warrant is `@