The one global 20-character bound made every consumer pay an object
store's key length, even for provisions that keep no name, and a single
overflow refused the provider's whole declaration. An offer now states
its own bound (identity: {max, in} or false); unsaid, a provider told its
consumers keeps 20 and one told nothing keeps none. module check judges
every identity on the longest machine name before merge, and a provider
leaves an overflowing consumer out of its grants and composes, with the
consumer named by push, plan and status (ADR 0225).
160 lines
5.1 KiB
Go
160 lines
5.1 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestEveryCatalogueManifestParses runs the real catalogue through the real gate.
|
|
//
|
|
// Not a fixture: the point is whether the manifests as written are accepted by the control plane that
|
|
// will read them, and a copy of one manifest proves nothing about the other seventy-one.
|
|
// catalogueRoot is the catalogue these checks run over: MESH_CATALOGUE when set, else the checkout
|
|
// beside this one, the way the main layout has it. A check that only ran when somebody remembered a
|
|
// variable was a check nobody ran (novox/hq issue 134, 2026-09-30); it skips only when there is no
|
|
// catalogue to be found at all.
|
|
func catalogueRoot(t *testing.T) string {
|
|
t.Helper()
|
|
if root := os.Getenv("MESH_CATALOGUE"); root != "" {
|
|
return root
|
|
}
|
|
sibling := filepath.Join("..", "..", "..", "mesh-catalog")
|
|
if _, err := os.Stat(filepath.Join(sibling, "modules")); err != nil {
|
|
t.Skip("no catalogue beside this checkout and MESH_CATALOGUE unset")
|
|
}
|
|
return sibling
|
|
}
|
|
|
|
func TestEveryCatalogueManifestParses(t *testing.T) {
|
|
root := catalogueRoot(t)
|
|
found, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json"))
|
|
if err != nil || len(found) == 0 {
|
|
t.Fatalf("no manifests under %s: %v", root, err)
|
|
}
|
|
named, routed := 0, 0
|
|
for _, p := range found {
|
|
raw, err := os.ReadFile(p)
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", p, err)
|
|
}
|
|
m, err := ParseManifest(raw)
|
|
if err != nil {
|
|
t.Errorf("%s: %v", filepath.Base(filepath.Dir(p)), err)
|
|
continue
|
|
}
|
|
for _, l := range m.Listens {
|
|
if l.Name != "" {
|
|
named++
|
|
}
|
|
}
|
|
for port := range RoutedPorts(m) {
|
|
_ = port
|
|
routed++
|
|
}
|
|
}
|
|
t.Logf("%d manifests, %d named endpoints, %d routed endpoints resolved", len(found), named, routed)
|
|
if named == 0 {
|
|
t.Fatal("no endpoint in the catalogue is named, so this proved nothing")
|
|
}
|
|
}
|
|
|
|
// TestNoCatalogueManifestNamesAnInstallation is ADR 0112's check, run over the real catalogue: no
|
|
// definition names a domain or a public address the mesh acts on, and every value that must for now
|
|
// carries its reason (novox/hq ADR 0155, issue 134). The list it prints is the one that shrinks.
|
|
func TestNoCatalogueManifestNamesAnInstallation(t *testing.T) {
|
|
root := catalogueRoot(t)
|
|
found, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json"))
|
|
if err != nil || len(found) == 0 {
|
|
t.Fatalf("no manifests under %s: %v", root, err)
|
|
}
|
|
var named []string
|
|
for _, p := range found {
|
|
raw, err := os.ReadFile(p)
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", p, err)
|
|
}
|
|
m, err := ParseManifest(raw)
|
|
if err != nil {
|
|
t.Errorf("%s: %v", p, err)
|
|
continue
|
|
}
|
|
named = append(named, InstallationProblems(m)...)
|
|
}
|
|
if len(named) > 0 {
|
|
t.Fatalf("%d value(s) name an installation:\n %s", len(named), strings.Join(named, "\n "))
|
|
}
|
|
}
|
|
|
|
// TestEveryCatalogueStoreSaysHowItIsBackedUp is ADR 0214's check over the real catalogue: a module
|
|
// providing a store contributes a backup to node-backup, so a store added is a store backed up.
|
|
func TestEveryCatalogueStoreSaysHowItIsBackedUp(t *testing.T) {
|
|
root := catalogueRoot(t)
|
|
found, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json"))
|
|
if err != nil || len(found) == 0 {
|
|
t.Fatalf("no manifests under %s: %v", root, err)
|
|
}
|
|
stores := 0
|
|
for _, p := range found {
|
|
raw, err := os.ReadFile(p)
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", p, err)
|
|
}
|
|
m, err := ParseManifest(raw)
|
|
if err != nil {
|
|
continue // TestEveryCatalogueManifestParses says why
|
|
}
|
|
for _, o := range m.Provides {
|
|
if storeProvisions[o.Name] {
|
|
stores++
|
|
break
|
|
}
|
|
}
|
|
for _, problem := range CheckBackup(m) {
|
|
t.Error(problem)
|
|
}
|
|
}
|
|
if stores == 0 {
|
|
t.Fatal("no module in the catalogue provides a store, so this proved nothing")
|
|
}
|
|
}
|
|
|
|
// TestEveryCatalogueIdentityFitsWhatItRequires is ADR 0225's check over the real catalogue: every
|
|
// module's identity, on the longest machine name, fits the bound of every provision it wants. An
|
|
// overflow fails here, in the pull request that introduces it, rather than on the provider's machine
|
|
// the first time a real machine's name meets the module's (issue 263).
|
|
func TestEveryCatalogueIdentityFitsWhatItRequires(t *testing.T) {
|
|
root := catalogueRoot(t)
|
|
found, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json"))
|
|
if err != nil || len(found) == 0 {
|
|
t.Fatalf("no manifests under %s: %v", root, err)
|
|
}
|
|
shelf := Shelf{}
|
|
for _, p := range found {
|
|
raw, err := os.ReadFile(p)
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", p, err)
|
|
}
|
|
m, err := ParseManifest(raw)
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", p, err)
|
|
}
|
|
shelf[m.Module] = m
|
|
}
|
|
for _, p := range IdentityProblems(shelf, DefaultLongestMachine) {
|
|
t.Error(p)
|
|
}
|
|
// The night it was found: the resolver provision bounds nothing, and the object store still 20.
|
|
if dns, ok := shelf["dnsmasq"]; ok {
|
|
if b := dns.IdentityBoundOf("wildcard-resolution"); b.Bounded() {
|
|
t.Errorf("the resolver provision bounds its consumers' identities: %+v", b)
|
|
}
|
|
}
|
|
if store, ok := shelf["minio"]; ok {
|
|
if b := store.IdentityBoundOf("s3-bucket"); b.Max != 20 {
|
|
t.Errorf("the object store's access key is not bounded at 20: %+v", b)
|
|
}
|
|
}
|
|
}
|