A module's data section says what it keeps and how precious it is; the backup holder's lines, binding stickiness, retirement on unassign and D13's conditions follow from it, so issue 273's empty replacement is said and an unassigned module's data is remembered, not forgotten.
161 lines
5.3 KiB
Go
161 lines
5.3 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 "))
|
|
}
|
|
}
|
|
|
|
// TestEveryCatalogueModuleDeclaresItsData is ADR 0233's check over the real catalogue (it replaced ADR
|
|
// 0214's store list): every provider that grants says what it keeps for its consumers, nothing writes a
|
|
// backup line by hand, every directory a container writes is declared, and every irreplaceable item is
|
|
// backed up — so a store added is a store backed up, without a list of stores to keep in step.
|
|
func TestEveryCatalogueModuleDeclaresItsData(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{}
|
|
granting := 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
|
|
}
|
|
if len(m.Grants) > 0 {
|
|
granting++
|
|
}
|
|
shelf[m.Module] = m
|
|
}
|
|
for _, problem := range DataProblems(shelf) {
|
|
t.Error(problem)
|
|
}
|
|
if granting == 0 {
|
|
t.Fatal("no module in the catalogue grants a provision, 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)
|
|
}
|
|
}
|
|
}
|