Files
mesh-controller/cmd/mesh-controller/new_module_on_merge_test.go
T

114 lines
5.1 KiB
Go

//go:build integration
package main
import (
"context"
"strings"
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/catalogue"
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
"github.com/novox/mesh-controller/internal/inventory"
"github.com/novox/mesh-controller/internal/link"
)
// ADR 0501 §6 (issue 1851, A5): the live case of 2026-10-07 (novox/hq issue 300), on keys. A merge of the catalogue adds
// one module's directory and touches nothing else the mesh holds: nothing asks its first build, it opens no plan, and
// the module is registered from the builder's first done key of it, assigned nowhere, with no build request written.
func TestAMergeAddingAModuleEndsRegisteredFromItsKeyAndAsksNoBuild(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
inv := open.inventory
noBuildAsked(t)
b := aBuilderOnTheBus(t)
if err := inv.RegisterModule(ctx, catalogue.Manifest{Module: "networkmanager", Version: "1"},
inventory.Source{Repository: "novox/mesh-catalog", Seat: "git", Path: "modules/networkmanager", Ref: "main",
BuiltFrom: "c0", Head: "c0"}); err != nil {
t.Fatal(err)
}
watching, stop := context.WithCancel(context.Background())
t.Cleanup(stop)
watchNewModules(watching, open)
const commit = "3da80a4b00000000"
m := link.SourceMoved{Owner: "novox", Repo: "mesh-catalog", Base: "main", Commit: commit,
Paths: []string{"modules/systemd-resolved/module.json", "modules/systemd-resolved/cmd/main.go"},
ModuleDirs: []string{"modules/systemd-resolved"}, ModuleDirsSaid: true}
if err := (following{open: open}).SourceMoved(ctx, m); err != nil {
t.Fatal(err)
}
if plans, err := inv.OpenPlans(ctx); err != nil || len(plans) != 0 {
t.Fatalf("a merge moving nothing the mesh holds opened a plan: %+v %v", plans, err)
}
// The indexer writes the module's trunk value; the builder builds it, then its key is done.
fp := fpOf('7')
manifest := b.indexed(t, "systemd-resolved", "1", "novox/mesh-catalog", "modules/systemd-resolved", commit, fp)
b.built(t, "systemd-resolved", "1", fp, manifest, sdkdelivery.BuildBuilding)
time.Sleep(300 * time.Millisecond)
if _, known := inventoryHolds(t, inv, "systemd-resolved"); known {
t.Fatal("0501 §1: registered from a key still building")
}
if obs := newModuleObservations(time.Now().Add(time.Hour)); len(obs) != 1 || obs[0].Kind != kindNewModuleWaits {
t.Fatalf("0501 §6: the new module waiting on its key past its bound is named by %+v", obs)
}
b.built(t, "systemd-resolved", "1", fp, manifest, sdkdelivery.BuildDone)
becomes(t, 10*time.Second, "the new module registered", func() bool {
_, known := inventoryHolds(t, inv, "systemd-resolved")
return known
})
// The take-in forgets the wait just after the registration it made is seen.
becomes(t, 5*time.Second, "the registered module no longer named waiting", func() bool {
return len(newModuleObservations(time.Now().Add(time.Hour))) == 0
})
requests, err := inv.RequestsNamed(ctx, inventory.BuildRequest{Repository: "novox/mesh-catalog",
Path: "modules/systemd-resolved"}.Name())
if err != nil || len(requests) != 0 {
t.Fatalf("0501 §6: a build request was written: %+v (%v)", requests, err)
}
}
// ADR 0501 §7: a new module whose manifest uses a word this controller does not read is named waiting at once, never
// failed, and registered from the same key, with no second build, when a controller that reads it looks.
func TestANewModuleWithAnUnreadWordWaitsForTheControllerThatReadsIt(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
inv := open.inventory
noBuildAsked(t)
b := aBuilderOnTheBus(t)
if err := inv.RegisterModule(ctx, catalogue.Manifest{Module: "networkmanager", Version: "1"},
inventory.Source{Repository: "novox/mesh-catalog", Seat: "git", Path: "modules/networkmanager", Ref: "main",
BuiltFrom: "c0", Head: "c0"}); err != nil {
t.Fatal(err)
}
fp := fpOf('8')
raw := `{"module":"watchman","version":"1","not-a-word-yet":"5m","build":{"artifacts":[{"name":"a","kind":"archive","from":"files"}]},` +
`"resources":[{"id":"a","type":"archive","path":"/opt/watchman","artifact":"a"}]}`
digest := sdkdelivery.ManifestDigest([]byte(raw))
value, _ := sdkdelivery.EncodeManifest(sdkdelivery.Manifest{Module: "watchman", JSON: raw})
if _, err := b.manifests.Put(ctx, digest, value); err != nil {
t.Fatal(err)
}
trunk, _ := sdkdelivery.EncodeModule(sdkdelivery.Module{FP: "src1:" + fp[5:], FP2: fp, Manifest: digest,
Repository: "novox/mesh-catalog", Commit: "4444444400000000", Path: "modules/watchman"})
if _, err := b.modules.Put(ctx, "watchman", trunk); err != nil {
t.Fatal(err)
}
b.built(t, "watchman", "1", fp, digest, sdkdelivery.BuildDone)
takeNewModules(ctx, open, "watchman")
if _, known := inventoryHolds(t, inv, "watchman"); known {
t.Fatal("0501 §7: a manifest with an unread word was registered")
}
obs := newModuleObservations(time.Now())
if len(obs) != 1 || obs[0].Kind != kindNewModuleWaits || !strings.Contains(obs[0].Summary, `"not-a-word-yet"`) {
t.Fatalf("0501 §7: the module waiting for a reader of its word is named by %+v", obs)
}
builds, _ := inv.Builds(ctx, "watchman", 5)
for _, b := range builds {
if !b.Worked() {
t.Fatalf("0501 §7: a failed build was recorded: %+v", b)
}
}
}