ADR 0593 §7: the composer with its refusals leaves mesh-sdk, so mesh-sdk carries no composition rule. The catalogue functions only the node-engine's composition reaches (Resolve, Resolution's Compose, Declaration, DataItems, LeftOut, Rules and the rest, the settings, placeholder and template refusals, the rendering helpers) are copied with their tests into internal/compose/resolution; a method of a mesh-sdk shape becomes a function taking it first. The composition calls its own copies, and the captured catalogue its tests read lives here now, shared by mesh-compose's tests. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
73 lines
2.9 KiB
Go
73 lines
2.9 KiB
Go
package resolution
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
mcat "git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
func registryShelf() map[string]mcat.Manifest {
|
|
return shelf(mcat.Manifest{Module: "gitea", Version: "1", Provides: mcat.FromAnywhere("npm-package-registry"),
|
|
Claims: []mcat.Claim{{Name: "npm-package-registry", Scope: mcat.ScopeMesh}}}, mcat.Manifest{Module: "verdaccio", Version: "1", Provides: mcat.FromAnywhere("npm-package-registry")}, mcat.Manifest{Module: "builder", Version: "1", Requires: []string{"npm-package-registry"}},
|
|
)
|
|
}
|
|
|
|
func twoRegistries() map[string][]mcat.Provider {
|
|
return map[string][]mcat.Provider{"npm-package-registry": {
|
|
{Node: "anchor", At: "anchor.internal", Module: "gitea"},
|
|
{Node: "archive", At: "archive.internal", Module: "verdaccio"},
|
|
}}
|
|
}
|
|
|
|
func giteaHoldsTheSeat() []mcat.Held {
|
|
return []mcat.Held{{Claim: "npm-package-registry", Scope: mcat.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(), mcat.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(), mcat.World{Offered: twoRegistries(), Held: giteaHoldsTheSeat(),
|
|
Pinned: map[string]mcat.Chosen{"npm-package-registry": {Node: "archive", Module: "verdaccio"}}})
|
|
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(), mcat.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 := []mcat.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)
|
|
}
|
|
}
|