Test the controller as one that judges no manifest, and reset the new-module waits each test leaves (ADR 0578)
Issue 1585: registration publishes the predefined list and judges nothing against it; a definition naming an installation is the indexer's judge's to refuse; a word the controller does not read is set aside and the module registered (0578 §6 changes 0501 §7); a refused verdict holds the module and names it. The process-wide list of new modules waiting is emptied at each take-in test's start and end, so no test reads another's. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -49,8 +49,10 @@ func TestABuildHeardIsRecordedAndRegistered(t *testing.T) {
|
||||
"env": map[string]any{"KC_HOSTNAME": "https://login.mesh-one.be"}}}})
|
||||
_, _, err = takeIn(ctx, open.inventory, link.BuildResult{
|
||||
ID: "b-2", Repository: "/r", On: "anchor", Commit: "0123456789", Manifest: named})
|
||||
if err == nil || !strings.Contains(err.Error(), "does not register it") {
|
||||
t.Fatalf("a definition naming an installation was taken in: %v", err)
|
||||
// Not registered from a source the catalogue does not build it from. That it names an installation is no longer the
|
||||
// controller's to judge (ADR 0578 §3): the indexer's judge refuses it (mesh-delivery-indexer judge's installation rows).
|
||||
if err == nil || !strings.Contains(err.Error(), "recorded and not registered") {
|
||||
t.Fatalf("a definition from a source the catalogue does not build it from was taken in: %v", err)
|
||||
}
|
||||
if shelf, _ := open.inventory.Catalogue(ctx); shelf["idp"].Module != "" {
|
||||
t.Fatal("the refused module was registered anyway")
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
type builderOnTheBus struct {
|
||||
js *broker.JetStream
|
||||
builds, modules, manifests jetstream.KeyValue
|
||||
// verdicts is the indexer's state of verdicts on manifests (ADR 0578 §1).
|
||||
verdicts jetstream.KeyValue
|
||||
}
|
||||
|
||||
func aBuilderOnTheBus(t *testing.T) builderOnTheBus {
|
||||
@@ -40,6 +42,7 @@ func aBuilderOnTheBus(t *testing.T) builderOnTheBus {
|
||||
{&b.builds, sdkdelivery.BuilderBuildsBucket, sdkdelivery.BuilderBuildsHistory},
|
||||
{&b.modules, sdkdelivery.ModulesBucket, sdkdelivery.ModulesHistory},
|
||||
{&b.manifests, sdkdelivery.ManifestsBucket, 1},
|
||||
{&b.verdicts, sdkdelivery.VerdictsBucket, sdkdelivery.VerdictsHistory},
|
||||
} {
|
||||
kv, err := api.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: c.bucket, History: c.history})
|
||||
if err != nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
// 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) {
|
||||
withNoNewModuleWaits(t)
|
||||
open := aMesh(t)
|
||||
ctx := t.Context()
|
||||
inv := open.inventory
|
||||
@@ -66,9 +67,11 @@ func TestAMergeAddingAModuleEndsRegisteredFromItsKeyAndAsksNoBuild(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// ADR 0578 §6 (which changes ADR 0501 §7): a new module whose manifest uses a word this controller does not read is
|
||||
// registered at once, the word set aside, never failed and with no second build; one whose manifest the indexer's judge
|
||||
// refused is held, named in the indexer's words, and never registered (ADR 0578 §3).
|
||||
func TestANewModuleWithAnUnreadWordIsRegisteredAndARefusedOneIsHeld(t *testing.T) {
|
||||
withNoNewModuleWaits(t)
|
||||
open := aMesh(t)
|
||||
ctx := t.Context()
|
||||
inv := open.inventory
|
||||
@@ -94,12 +97,38 @@ func TestANewModuleWithAnUnreadWordWaitsForTheControllerThatReadsIt(t *testing.T
|
||||
}
|
||||
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")
|
||||
if _, known := inventoryHolds(t, inv, "watchman"); !known {
|
||||
t.Fatalf("0578 §6: a manifest with a word set aside is not registered: %+v", newModuleObservations(time.Now()))
|
||||
}
|
||||
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)
|
||||
// The indexer refused another: held, named, not registered.
|
||||
refusedFP := fpOf('9')
|
||||
refusedRaw := `{"module":"refusedmod","version":"1","build":{"artifacts":[{"name":"a","kind":"archive","from":"files"}]},` +
|
||||
`"resources":[{"id":"a","type":"archive","path":"/opt/refusedmod","artifact":"a"}]}`
|
||||
refusedDigest := sdkdelivery.ManifestDigest([]byte(refusedRaw))
|
||||
value, _ = sdkdelivery.EncodeManifest(sdkdelivery.Manifest{Module: "refusedmod", JSON: refusedRaw})
|
||||
if _, err := b.manifests.Put(ctx, refusedDigest, value); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
verdict, _ := sdkdelivery.EncodeManifestVerdict(sdkdelivery.ManifestVerdict{Module: "refusedmod", State: sdkdelivery.VerdictRefused,
|
||||
Problems: []string{"it breaks a rule"}, Judge: "exe:1", At: time.Now()})
|
||||
if _, err := b.verdicts.Put(ctx, refusedDigest, verdict); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
trunk, _ = sdkdelivery.EncodeModule(sdkdelivery.Module{FP: "src1:" + refusedFP[5:], FP2: refusedFP, Manifest: refusedDigest,
|
||||
Repository: "novox/mesh-catalog", Commit: "5555555500000000", Path: "modules/refusedmod"})
|
||||
if _, err := b.modules.Put(ctx, "refusedmod", trunk); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b.built(t, "refusedmod", "1", refusedFP, refusedDigest, sdkdelivery.BuildDone)
|
||||
takeNewModules(ctx, open, "refusedmod")
|
||||
if _, known := inventoryHolds(t, inv, "refusedmod"); known {
|
||||
t.Fatal("0578 §3: a manifest the indexer refused was registered")
|
||||
}
|
||||
// Said at once in the controller's journal; a condition once it waits past the bound (the watchman raises the
|
||||
// indexer's manifest-refused at once).
|
||||
obs := newModuleObservations(time.Now().Add(newModuleWaitBound + time.Minute))
|
||||
if len(obs) != 1 || obs[0].Kind != kindNewModuleWaits || !strings.Contains(obs[0].Summary, "refused the manifest of refusedmod: it breaks a rule") {
|
||||
t.Fatalf("0578 §3: the refused module is not held and named: %+v", obs)
|
||||
}
|
||||
builds, _ := inv.Builds(ctx, "watchman", 5)
|
||||
for _, b := range builds {
|
||||
|
||||
@@ -9,11 +9,7 @@ import (
|
||||
// ADR 0501 §6, §7 (issue 1851, A5b): the condition of a new module reads its key's take-in: one waiting for a controller
|
||||
// that reads its manifest's word is named at once, one waiting for its key only past the bound, each reason said once.
|
||||
func TestANewModuleWaitingIsNamedByWhatItWaitsFor(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
newModulesWaiting.Lock()
|
||||
newModulesWaiting.by = map[string]newModuleWait{}
|
||||
newModulesWaiting.Unlock()
|
||||
})
|
||||
withNoNewModuleWaits(t)
|
||||
word := `waits: watchman's manifest uses the word "key-ttl", which this controller does not read: it is registered when a controller that reads it runs`
|
||||
sayNewModuleWait(newModuleWait{Module: "watchman", Commit: "c1", Why: word, Word: wordIn(word)})
|
||||
sayNewModuleWait(newModuleWait{Module: "view", Commit: "c1", Why: "waits: the build of view at aaaa is building on ace"})
|
||||
@@ -31,3 +27,15 @@ func TestANewModuleWaitingIsNamedByWhatItWaitsFor(t *testing.T) {
|
||||
t.Fatalf("registered modules are still named: %+v", obs)
|
||||
}
|
||||
}
|
||||
|
||||
// withNoNewModuleWaits empties the controller's list of new modules waiting, for a test, at its start and at its end: the
|
||||
// list is the process's, so a test that leaves a module in it is read by the next.
|
||||
func withNoNewModuleWaits(t *testing.T) {
|
||||
reset := func() {
|
||||
newModulesWaiting.Lock()
|
||||
newModulesWaiting.by = map[string]newModuleWait{}
|
||||
newModulesWaiting.Unlock()
|
||||
}
|
||||
reset()
|
||||
t.Cleanup(reset)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,10 @@ import (
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
)
|
||||
|
||||
func TestRegistrationPublishesTheNewestTrunkListAndJudgesEveryModuleAgainstIt(t *testing.T) {
|
||||
// Registration publishes the newest trunk's list of predefined keys. It judges no manifest against it (ADR 0578 §3): the
|
||||
// indexer's judge does, with the list it keeps, and a fallback to a key not on it, or of another type, is refused there
|
||||
// (mesh-delivery-indexer judge's tests).
|
||||
func TestRegistrationPublishesTheNewestTrunkList(t *testing.T) {
|
||||
withoutAPublishedList(t)
|
||||
open := aMesh(t)
|
||||
ctx := t.Context()
|
||||
@@ -28,22 +31,6 @@ func TestRegistrationPublishesTheNewestTrunkListAndJudgesEveryModuleAgainstIt(t
|
||||
t.Fatalf("the controller judges against %v, not the list published", got)
|
||||
}
|
||||
|
||||
// One naming a key not on the list, or of another type, is refused and never enters the catalogue.
|
||||
for i, c := range []struct{ manifest, says string }{
|
||||
{notifierFallingBackTo("font-scale", "number"), "not on the list"},
|
||||
{notifierFallingBackTo("font-size", "string"), "which is a number"},
|
||||
} {
|
||||
id := []string{"b-2", "b-3"}[i]
|
||||
if _, _, err := takeIn(ctx, open.inventory, catalogueBuild(id, c.manifest, theFirstKeys, 10)); err == nil ||
|
||||
!strings.Contains(err.Error(), c.says) {
|
||||
t.Errorf("wanted the registration refused saying %q, got %v", c.says, err)
|
||||
}
|
||||
}
|
||||
src, err := open.inventory.SourceOf(ctx, "notifier")
|
||||
if err != nil || src.BuiltFrom != strings.Repeat("1", 12) {
|
||||
t.Fatalf("a refused build replaced the registered one: %+v %v", src, err)
|
||||
}
|
||||
|
||||
// A newer build's list, which drops agent-managed-settings, replaces the published one.
|
||||
smaller := `{"font-size": {"type": "number", "description": "points", "minimum": 6, "maximum": 72}}`
|
||||
if _, _, err := takeIn(ctx, open.inventory, catalogueBuild("b-4",
|
||||
@@ -60,7 +47,7 @@ func TestRegistrationPublishesTheNewestTrunkListAndJudgesEveryModuleAgainstIt(t
|
||||
t.Fatalf("an older build's list heard later replaced the newer one: %+v", kept)
|
||||
}
|
||||
|
||||
// A build of another repository publishes nothing, and its manifest is judged against the published list.
|
||||
// A build of another repository publishes nothing.
|
||||
other := catalogueBuild("b-6", notifierFallingBackTo("font-size", "number"), theFirstKeys, 99)
|
||||
other.Repository, other.Source.Repository = "http://forge.internal:20000/novox/notifier.git", "novox/notifier"
|
||||
other.Manifest = json.RawMessage(strings.Replace(notifierFallingBackTo("font-size", "number"), `"notifier"`,
|
||||
@@ -71,9 +58,4 @@ func TestRegistrationPublishesTheNewestTrunkListAndJudgesEveryModuleAgainstIt(t
|
||||
if kept, _, _ = open.inventory.PredefinedKept(ctx); kept.Build != "b-4" {
|
||||
t.Fatalf("a build of another repository published its list: %+v", kept)
|
||||
}
|
||||
other.ID, other.Manifest = "b-7", json.RawMessage(strings.Replace(notifierFallingBackTo("agent-managed-settings",
|
||||
"number"), `"notifier"`, `"chimes"`, 1))
|
||||
if _, _, err := takeIn(ctx, open.inventory, other); err == nil || !strings.Contains(err.Error(), "not on the list") {
|
||||
t.Fatalf("a key only another repository's list holds was registered: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user