114 lines
5.1 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|