diff --git a/modules/mesh-delivery-indexer/judge/busstate_test.go b/modules/mesh-delivery-indexer/judge/busstate_test.go index 31255dde..b9817b32 100644 --- a/modules/mesh-delivery-indexer/judge/busstate_test.go +++ b/modules/mesh-delivery-indexer/judge/busstate_test.go @@ -86,6 +86,8 @@ func TestBusStateIsJudgedOnce(t *testing.T) { } } // Between the manifests: two modules' names on the bus alike, and a read of bus state that moved (ADR 0596 §2). + sdk := catalogue.Manifest{Module: "mesh-sdk", Build: &catalogue.Build{Artifacts: []catalogue.Artifact{ + {Name: "go", Kind: catalogue.ArtifactPackage, Language: "go"}, {Name: "img", Kind: catalogue.ArtifactImage}}}} entry := func(name string) []catalogue.BusStateEntry { return []catalogue.BusStateEntry{{Name: name, Storage: catalogue.StorageKeyValue, Template: catalogue.TemplateState}} } @@ -99,8 +101,16 @@ func TestBusStateIsJudgedOnce(t *testing.T) { "reader": {Module: "reader", Reads: []string{"owner.cache"}}}, "reader reads \"owner.cache\", which owner has moved"}, {"a read of state still kept", catalogue.Shelf{"owner": {Module: "owner", State: []catalogue.StateDeclaration{{Name: "cache"}}}, "reader": {Module: "reader", Reads: []string{"owner.cache"}}}, ""}, + // DLV-I14 (design 32): the packages section names a declared package, by the builder's name, in a well-formed series. + {"packages naming a declared package", catalogue.Shelf{"mesh-sdk": sdk, "user": {Module: "user", Packages: map[string]string{"mesh-sdk/go": "0.2"}}}, ""}, + {"packages naming no declared package", catalogue.Shelf{"mesh-sdk": sdk, "user": {Module: "user", Packages: map[string]string{"mesh-sdk": "0.1"}}}, + `user's packages name "mesh-sdk", which no manifest declares`}, + {"packages naming an image", catalogue.Shelf{"mesh-sdk": sdk, "user": {Module: "user", Packages: map[string]string{"mesh-sdk/img": "0.1"}}}, + `name "mesh-sdk/img", which no manifest declares`}, + {"packages in an ill-formed series", catalogue.Shelf{"mesh-sdk": sdk, "user": {Module: "user", Packages: map[string]string{"mesh-sdk/go": "v0.2"}}}, + `user's packages accept "mesh-sdk/go" in the series "v0.2"`}, } { - got := strings.Join(BusStateCatalogueProblems(c.shelf), "\n") + got := strings.Join(append(BusStateCatalogueProblems(c.shelf), PackagesCatalogueProblems(c.shelf)...), "\n") if (c.want == "") != (got == "") || !strings.Contains(got, c.want) { t.Errorf("%s: between the manifests: %q, want %q", c.name, got, c.want) } diff --git a/modules/mesh-delivery-indexer/judge/packages.go b/modules/mesh-delivery-indexer/judge/packages.go new file mode 100644 index 00000000..61dabb15 --- /dev/null +++ b/modules/mesh-delivery-indexer/judge/packages.go @@ -0,0 +1,41 @@ +package judge + +import ( + "fmt" + "maps" + "slices" + "strings" + + "git.novox.be/novox/mesh-sdk/go/catalogue" +) + +// PackagesCatalogueProblems are the rules of a manifest's `packages` no single manifest can be judged against (DLV-I14; +// ADR 0606 §4, design 32 *A package, its series, and the series its users accept*): each name is a package some manifest +// of the shelf declares, named as the builder names it (`/`), and each series is `.`. +func PackagesCatalogueProblems(shelf catalogue.Shelf) []string { + declared := map[string]bool{} + for _, module := range shelfOrder(shelf) { + if b := shelf[module].Build; b != nil { + for _, a := range b.Artifacts { + if a.Kind == catalogue.ArtifactPackage { + declared[module+"/"+a.Name] = true + } + } + } + } + var problems []string + for _, module := range shelfOrder(shelf) { + accepts := shelf[module].Packages + for _, name := range slices.Sorted(maps.Keys(accepts)) { + if !declared[name] { + problems = append(problems, fmt.Sprintf("%s's packages name %q, which no manifest declares as a package "+ + "(a package is named /; design 32)", module, name)) + } + if s := strings.TrimSpace(accepts[name]); !seriesForm.MatchString(s) { + problems = append(problems, fmt.Sprintf("%s's packages accept %q in the series %q, which is no . "+ + "such as 0.1 (design 32)", module, name, accepts[name])) + } + } + } + return problems +} diff --git a/modules/mesh-delivery-indexer/judge/rules.go b/modules/mesh-delivery-indexer/judge/rules.go index 7a77bda6..e369eb96 100644 --- a/modules/mesh-delivery-indexer/judge/rules.go +++ b/modules/mesh-delivery-indexer/judge/rules.go @@ -4692,6 +4692,8 @@ func CatalogueProblems(shelf catalogue.Shelf) []string { problems = append(problems, ObjectsCatalogueProblems(shelf)...) // Bus state in `requires`: a name on the bus has one owner, and the new form has no readers (ADR 0595, ADR 0596 §2). problems = append(problems, BusStateCatalogueProblems(shelf)...) + // A manifest's packages name a declared package in a well-formed series (design 32, DLV-I14). + problems = append(problems, PackagesCatalogueProblems(shelf)...) // A module that hears a restricted stream runs as an account of its own (novox/hq ADR 0376 §2). problems = append(problems, RestrictedHearingProblems(shelf)...) // A work-queue stream is never asked for by another module's consumes (novox/hq ADR 0436 §6).