Issue 1585, S19: the strict parser, every manifest problem function and the judge's tables (192 declarations, about 5,200 lines) and their 244 tests leave; composition keeps its own refusals and the helpers it uses. The catalogue's tests decode the manifests they build. ParseManifest, ParseManifestWith, ParseManifestForInputs, CatalogueProblems and the rest are gone; mesh-delivery-indexer/judge holds them. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
85 lines
4.5 KiB
Go
85 lines
4.5 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Each test names the decision it defends: novox/hq ADR 0225, which refines ADR 0049 after issue 263.
|
|
|
|
// The provision a module requires sets the bound on its identity, not the tightest backend anywhere.
|
|
func TestABoundIsTheProvisionsOwn(t *testing.T) {
|
|
store := Manifest{Module: "objects", Provides: []Offer{{Name: "s3-bucket", Scope: ScopeMesh,
|
|
Identity: &OfferIdentity{Max: 20, In: "an S3 access key"}}},
|
|
Receives: map[string]string{"s3-bucket": "/var/lib/mesh/objects/mesh.json"}}
|
|
database := Manifest{Module: "db", Provides: []Offer{{Name: "postgres-database", Scope: ScopeMesh,
|
|
Identity: &OfferIdentity{Max: 63, In: "a PostgreSQL role"}}},
|
|
Receives: map[string]string{"postgres-database": "/var/lib/mesh/db/mesh.json"}}
|
|
if b := store.IdentityBoundOf("s3-bucket"); b.Max != 20 || b.In != "an S3 access key" {
|
|
t.Errorf("an object store's stated bound was not taken: %+v", b)
|
|
}
|
|
if b := database.IdentityBoundOf("postgres-database"); b.Max != 63 {
|
|
t.Errorf("a database's stated bound was not taken: %+v", b)
|
|
}
|
|
// mesh_workstation_keycloak is 25: refused by the object store, accepted by the database.
|
|
if CheckIdentityWithin("workstation", "keycloak", store.IdentityBoundOf("s3-bucket")) == nil {
|
|
t.Error("a 25-character identity fit a 20-character access key")
|
|
}
|
|
if err := CheckIdentityWithin("workstation", "keycloak", database.IdentityBoundOf("postgres-database")); err != nil {
|
|
t.Errorf("a database consumer paid the object store's limit: %v", err)
|
|
}
|
|
}
|
|
|
|
// What an offer leaves unsaid follows from whether its provider can keep a name at all.
|
|
func TestAnUnstatedBoundFollowsWhatTheProviderIsTold(t *testing.T) {
|
|
// Told nothing about its consumers — no receives, nothing served from their identity: no bound.
|
|
// The resolver provision is exactly this, and its consumers paid an object store's limit (263).
|
|
resolver := Manifest{Module: "resolver", Provides: []Offer{{Name: "wildcard-resolution", Scope: ScopeMesh}}}
|
|
if b := resolver.IdentityBoundOf("wildcard-resolution"); b.Bounded() {
|
|
t.Errorf("a provision that is told nothing of its consumers bounds them: %+v", b)
|
|
}
|
|
// Told each consumer and silent about its backend: the old global bound, not none.
|
|
told := Manifest{Module: "told", Provides: []Offer{{Name: "thing", Scope: ScopeMesh}},
|
|
Receives: map[string]string{"thing": "/var/lib/mesh/told/mesh.json"}}
|
|
if b := told.IdentityBoundOf("thing"); b.Max != DefaultIdentityLimit {
|
|
t.Errorf("a provider told its consumers and silent about its backend is not held to %d: %+v",
|
|
DefaultIdentityLimit, b)
|
|
}
|
|
// Serving a value built from the identity is being told it, too.
|
|
serving := Manifest{Module: "serving", Provides: []Offer{{Name: "bucket", Scope: ScopeMesh}},
|
|
Serves: map[string]map[string]any{"bucket": {"name": "b-${consumer:as:dns}"}}}
|
|
if b := serving.IdentityBoundOf("bucket"); b.Max != DefaultIdentityLimit {
|
|
t.Errorf("a provider deriving a name from its consumers is not bounded: %+v", b)
|
|
}
|
|
// And `false` says it outright, even for a provider that receives.
|
|
routes := Manifest{Module: "routes", Provides: []Offer{{Name: "route", Scope: ScopeMesh,
|
|
Identity: &OfferIdentity{None: true}}},
|
|
Receives: map[string]string{"route": "/var/lib/mesh/routes/mesh.json"}}
|
|
if b := routes.IdentityBoundOf("route"); b.Bounded() {
|
|
t.Errorf("`identity: false` still bounds: %+v", b)
|
|
}
|
|
}
|
|
|
|
// The consumer's side of the same judgement the provider's composition makes: what `status` says.
|
|
func TestAnOverflowingRequirementIsNamedFromTheConsumersSide(t *testing.T) {
|
|
bound := IdentityBound{Max: 20, In: "an S3 access key"}
|
|
r := Resolution{Node: "laptop",
|
|
Modules: []Manifest{{Module: "photoalbum"}, {Module: "files"}, {Module: "gallery", Slug: "gal"}},
|
|
Needs: []Needed{
|
|
{Name: "s3-bucket", From: "anchor", For: "photoalbum", Identity: bound},
|
|
{Name: "s3-bucket", From: "anchor", For: "photoalbum", Local: "second", Identity: bound},
|
|
{Name: "s3-bucket", From: "anchor", For: "files", Identity: bound},
|
|
{Name: "s3-bucket", From: "anchor", For: "gallery", Identity: bound},
|
|
{Name: "licence", From: "records", For: "photoalbum", ByRecord: true, Identity: bound},
|
|
}}
|
|
got := r.Overflowing()
|
|
if len(got) != 1 || got[0].Module != "photoalbum" || got[0].Identity != "mesh_laptop_photoalbum" ||
|
|
got[0].Provider != "anchor" {
|
|
t.Fatalf("one overflow, once, was expected: %+v", got)
|
|
}
|
|
if said := got[0].String(); !strings.Contains(said, "an S3 access key keeps 20") ||
|
|
!strings.Contains(said, "slug") {
|
|
t.Fatalf("the overflow does not say what keeps it or the remedy: %s", said)
|
|
}
|
|
}
|