Files
mesh-controller/internal/catalogue/seats_test.go
T
jschoubben 7232d6df4b A module may declare its own seats (novox/hq ADR 0118)
The manifest carries seats and uses; registration refuses a mesh-* name, a
duplicate declarer, an undeclared uses or claim, a seat with no protocol, a
scope mismatch, and a holder that does not answer what its seat promises.

The parser stops judging unknown claim names, because it cannot: another
module may declare that seat, and one manifest cannot tell. The test that
encoded the old rule is rewritten to assert the refusal at registration, and
a new one pins the case the parser could not have distinguished.

Tools are declared for the first time, under their own key — serves already
means a provision's facts.
2026-09-26 22:17:17 +02:00

232 lines
8.9 KiB
Go

package catalogue
import (
"encoding/json"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
)
// Defends novox/hq ADR 0110: a seat is a module assignment from a closed set.
// The set is closed, and changing it is a decision.
//
// **The count is asserted, and every entry names the record that made it a seat**, so the next
// person changing the set finds the argument rather than a number to edit — the pattern the host's
// vocabulary test follows. If this fails because a seat was added, the fix is a record in novox/hq
// and a row in to-be 26, not a new number here.
func TestTheSeatsAreAClosedSetAndEachNamesItsDecision(t *testing.T) {
record := regexp.MustCompile(`^novox/hq ADR \d{4}$`)
seen := map[string]bool{}
delivered := map[string]string{}
for _, s := range Seats() {
if seen[s.Name] {
t.Errorf("%s is in the set twice", s.Name)
}
seen[s.Name] = true
if !record.MatchString(s.Decision) {
t.Errorf("%s names %q as its decision; every seat names the record that made it one",
s.Name, s.Decision)
}
switch s.Scope {
case ScopeNode, ScopeSite, ScopeMesh:
default:
t.Errorf("%s is held per %q, which is not a scope", s.Name, s.Scope)
}
if s.Delivers != "" {
// Two seats answering for one provision would put the question "which one?" back,
// which is the question a seat exists to answer.
if other, twice := delivered[s.Delivers]; twice {
t.Errorf("%s and %s both deliver %q", other, s.Name, s.Delivers)
}
delivered[s.Delivers] = s.Name
}
}
if len(Seats()) != 14 {
t.Errorf("the mesh defines %d seats rather than 14; the set is closed, so a change here is "+
"a decision (novox/hq ADR 0110): %s", len(Seats()), seatNames())
}
}
func claimed(claims string) []byte {
return []byte(`{"module":"thing","version":"1","provides":[{"name":"npm-package-registry","scope":"mesh"}],"claims":` + claims + `}`)
}
// **The refusal moved, it did not go** (novox/hq ADR 0118, superseding 0110). A module may now
// declare its own seats, so whether a claimed seat exists is a fact about the *catalogue* and not
// about the manifest in front of the parser: a claim on a seat another registered module declares
// is perfectly good, and the parser cannot tell the two cases apart. So the parser accepts it and
// registration refuses it — the same guarantee, at the same moment work would otherwise start,
// from a set nobody maintains by hand.
func TestAClaimOnASeatNobodyDeclaresIsRefusedAtRegistration(t *testing.T) {
m, err := ParseManifest(claimed(`[{"name":"the-anything","scope":"node"}]`))
if err != nil {
t.Fatalf("the parser judged a claim it cannot judge alone: %v", err)
}
problems := CatalogueProblems(Shelf{m.Module: m})
if len(problems) == 0 {
t.Fatal("a module invented a seat by claiming it, and registration allowed it")
}
joined := strings.Join(problems, "; ")
if !strings.Contains(joined, "the-anything") || !strings.Contains(joined, "no module declares") {
t.Fatalf("the refusal does not say the seat is nobody's: %v", problems)
}
}
// And the same claim is fine once something declares that seat, which is the case the parser
// could not have distinguished.
func TestAClaimOnASeatAnotherModuleDeclaresIsAccepted(t *testing.T) {
claimant, err := ParseManifest(claimed(`[{"name":"the-anything","scope":"node"}]`))
if err != nil {
t.Fatal(err)
}
declarer := Manifest{Module: "someone", Seats: []SeatDeclaration{
{Name: "the-anything", Scope: ScopeNode, Accepts: []string{"work"}},
}}
if problems := CatalogueProblems(Shelf{claimant.Module: claimant, "someone": declarer}); len(problems) != 0 {
t.Fatalf("a claim on a declared seat was refused: %v", problems)
}
}
func TestASeatClaimedAtAnotherScopeIsRefused(t *testing.T) {
_, err := ParseManifest(claimed(`[{"name":"npm-package-registry","scope":"node"}]`))
if err == nil {
t.Fatal("a mesh seat was held per node")
}
if !strings.Contains(err.Error(), "mesh seat") {
t.Fatalf("the refusal does not say which scope the seat is: %v", err)
}
}
func TestADeliveringSeatIsOnlyHeldByAModuleThatProvides(t *testing.T) {
// Holding it makes the module the mesh's answer for the provision. A module that cannot answer
// would be the answer anyway, and every consumer would be sent to it.
raw := []byte(`{"module":"thing","version":"1","claims":[{"name":"git","scope":"mesh"}]}`)
_, err := ParseManifest(raw)
if err == nil {
t.Fatal("a module holding the git seat need not provide git")
}
if !strings.Contains(err.Error(), `does not provide "git"`) {
t.Fatalf("the refusal does not say what is missing: %v", err)
}
}
func TestAClaimThatIsMalformedIsRefusedOnceForThat(t *testing.T) {
// Not a second time for being unknown: one mistake, one line.
_, err := ParseManifest(claimed(`[{"name":"Not A Name","scope":"node"}]`))
if err == nil {
t.Fatal("a malformed claim was accepted")
}
if strings.Contains(err.Error(), "not a seat") {
t.Fatalf("a malformed claim was also called unknown: %v", err)
}
}
// Every module in use claims a seat in the set, so closing it refuses nothing that runs.
//
// Read from the catalogue beside this checkout and from this repository's own manifest, the two
// places a manifest lives (ADR 0069). The private-network module's manifest is composed in code,
// and its claim is checked where it is composed.
func TestEveryManifestInUseClaimsASeatTheMeshDefines(t *testing.T) {
paths, _ := filepath.Glob("../../../mesh-catalog/modules/*/module.json")
if len(paths) == 0 {
t.Skip("the catalogue is not beside this checkout")
}
paths = append(paths, "../../module.json")
var checked int
for _, path := range paths {
raw, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
// Leniently, so a manifest refused for something unrelated is not reported as a seat
// problem, and the seat check below is the only thing this test holds a module to.
var m Manifest
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatalf("%s: %v", path, err)
}
for _, problem := range claimProblems(m) {
t.Errorf("%s: %s", path, problem)
}
checked += len(m.Claims)
}
if checked == 0 {
t.Fatal("no claims were checked, so this proved nothing")
}
}
// The holder of a seat answers among several providers.
func registryShelf() map[string]Manifest {
return shelf(
Manifest{Module: "gitea", Version: "1", Provides: FromAnywhere("npm-package-registry"),
Claims: []Claim{{Name: "npm-package-registry", Scope: ScopeMesh}}},
Manifest{Module: "verdaccio", Version: "1", Provides: FromAnywhere("npm-package-registry")},
Manifest{Module: "builder", Version: "1", Requires: []string{"npm-package-registry"}},
)
}
func twoRegistries() map[string][]Provider {
return map[string][]Provider{"npm-package-registry": {
{Node: "anchor", At: "anchor.internal", Module: "gitea"},
{Node: "archive", At: "archive.internal", Module: "verdaccio"},
}}
}
func giteaHoldsTheSeat() []Held {
return []Held{{Claim: "npm-package-registry", Scope: ScopeMesh, Node: "anchor", Module: "gitea"}}
}
func TestTheSeatsHolderAnswersWhenSeveralProvide(t *testing.T) {
// The whole point: a second registry beside the holder harms nothing, and nobody pins.
got, err := Resolve(registryShelf(), []string{"builder"}, reachable(),
World{Offered: twoRegistries(), Held: giteaHoldsTheSeat()})
if err != nil {
t.Fatal(err)
}
if len(got.Needs) != 1 || got.Needs[0].From != "anchor" {
t.Fatalf("the seat's holder did not answer: %v", got.Needs)
}
}
func TestAPinStillWinsOverTheSeat(t *testing.T) {
// A consumer coupled to one provider's contents has said so, and the seat does not overrule it.
got, err := Resolve(registryShelf(), []string{"builder"}, reachable(),
World{Offered: twoRegistries(), Held: giteaHoldsTheSeat(),
Pinned: map[string]string{"npm-package-registry": "archive"}})
if err != nil {
t.Fatal(err)
}
if len(got.Needs) != 1 || got.Needs[0].From != "archive" {
t.Fatalf("the pin was overruled by the seat: %v", got.Needs)
}
}
func TestWithTheSeatUnheldSeveralProvidersAreStillRefused(t *testing.T) {
// No seat held is no choice made, and ADR 0009's rule stands: never guessed.
_, err := Resolve(registryShelf(), []string{"builder"}, reachable(),
World{Offered: twoRegistries()})
if err == nil {
t.Fatal("one of two registries was picked with nobody holding the seat")
}
if !strings.Contains(err.Error(), "pin") {
t.Fatalf("the refusal does not say how to choose: %v", err)
}
}
func TestTheHolderIsTheModuleNotTheMachine(t *testing.T) {
// Two modules on one machine could provide the same thing; only the one holding the seat
// answers. A holder matched by node alone would send consumers to whichever came first.
providers := []Provider{
{Node: "anchor", At: "anchor.internal", Module: "verdaccio"},
{Node: "anchor", At: "anchor.internal", Module: "gitea"},
}
holder, held := HolderAmong("npm-package-registry", providers, giteaHoldsTheSeat())
if !held || holder.Module != "gitea" {
t.Fatalf("the holder was not told apart from a neighbour: %+v", holder)
}
}