124 lines
4.8 KiB
Go
124 lines
4.8 KiB
Go
package resolution
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
mcat "git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
func busSeatDelivering(t *testing.T, delivers string) {
|
|
t.Helper()
|
|
was := mcat.Seats()
|
|
t.Cleanup(func() { mcat.UseSeats(was) })
|
|
mcat.UseSeats([]mcat.Seat{{Name: "mesh-broker", Scope: mcat.ScopeMesh, Delivers: delivers, Decision: "test"}})
|
|
}
|
|
|
|
func oldBroker() mcat.Manifest {
|
|
return mcat.Manifest{Module: "old-broker", Provides: []mcat.Offer{{Name: "mesh-bus", Scope: mcat.ScopeMesh}},
|
|
Claims: []mcat.Claim{{Name: "mesh-broker", Scope: mcat.ScopeMesh, Serves: brokerVerbs()}}}
|
|
}
|
|
|
|
func newBroker() mcat.Manifest {
|
|
return mcat.Manifest{Module: "new-broker", Provides: []mcat.Offer{{Name: "mesh-bus", Scope: mcat.ScopeMesh}},
|
|
Claims: []mcat.Claim{{Name: "mesh-broker", Scope: mcat.ScopeMesh, Serves: brokerVerbs()}}}
|
|
}
|
|
|
|
// Nothing on record: exactly the old rule. One claimant holds; two are refused.
|
|
func TestWithNoHolderOnRecordTheSoleClaimantHoldsAndTwoAreRefused(t *testing.T) {
|
|
busSeatDelivering(t, "mesh-bus")
|
|
node := mcat.Node{Name: "anchor"}
|
|
|
|
held, problems := checkClaims([]mcat.Manifest{oldBroker()}, node, nil, nil)
|
|
if len(problems) != 0 || len(held) != 1 || held[0].Module != "old-broker" {
|
|
t.Fatalf("a sole claimant did not hold the seat: held=%v problems=%v", held, problems)
|
|
}
|
|
_, problems = checkClaims([]mcat.Manifest{oldBroker(), newBroker()}, node, nil, nil)
|
|
if len(problems) != 1 || !strings.Contains(problems[0], "both claim") {
|
|
t.Fatalf("two claimants with nothing on record were not refused: %v", problems)
|
|
}
|
|
}
|
|
|
|
// With a holder on record, the other eligible assignment is silent: not refused, and not holding.
|
|
func TestTheHolderOnRecordHoldsAndTheOtherClaimantStandsBesideIt(t *testing.T) {
|
|
busSeatDelivering(t, "mesh-bus")
|
|
node := mcat.Node{Name: "anchor"}
|
|
record := []mcat.Held{{Claim: "mesh-broker", Scope: mcat.ScopeMesh, Node: "anchor", Module: "new-broker"}}
|
|
|
|
held, problems := checkClaims([]mcat.Manifest{oldBroker(), newBroker()}, node, nil, record)
|
|
if len(problems) != 0 {
|
|
t.Fatalf("the assignment beside the holder was refused: %v", problems)
|
|
}
|
|
if len(held) != 1 || held[0].Module != "new-broker" {
|
|
t.Fatalf("the holder on record is not the one holding: %v", held)
|
|
}
|
|
}
|
|
|
|
// The record names a node too: an eligible module on another machine holds nothing, and its
|
|
// machine's set still resolves.
|
|
func TestAHolderOnRecordElsewhereLeavesThisMachinesClaimantSilent(t *testing.T) {
|
|
busSeatDelivering(t, "mesh-bus")
|
|
record := []mcat.Held{{Claim: "mesh-broker", Scope: mcat.ScopeMesh, Node: "anchor", Module: "new-broker"}}
|
|
|
|
held, problems := checkClaims([]mcat.Manifest{oldBroker()}, mcat.Node{Name: "laptop"}, nil, record)
|
|
if len(problems) != 0 || len(held) != 0 {
|
|
t.Fatalf("a claimant elsewhere than the recorded holder was not simply silent: held=%v problems=%v",
|
|
held, problems)
|
|
}
|
|
}
|
|
|
|
// A machine being moved is handed its membership for the new bus as a sealed file in its own
|
|
// declaration — the machine's, not any module's (design 28, task 5.2).
|
|
func TestAMembershipForTheNewBusIsComposedAsASealedFile(t *testing.T) {
|
|
r := mcat.Resolution{Node: "anchor"}
|
|
got, err := Compose(r, mcat.Rendering{BusMembership: "sealed-blob"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var found map[string]any
|
|
for _, res := range got.Resources {
|
|
if res["id"] == BusMembershipID() {
|
|
found = res
|
|
}
|
|
}
|
|
if found == nil {
|
|
t.Fatalf("no membership resource in %v", got.Resources)
|
|
}
|
|
if found["path"] != mcat.BusMembershipPath || found["sealed"] != "sealed-blob" || found["mode"] != "0600" {
|
|
t.Fatalf("the membership is not a sealed 0600 file where the host reads it: %v", found)
|
|
}
|
|
// And a machine not being moved is handed nothing.
|
|
got, _ = Compose(r, mcat.Rendering{})
|
|
for _, res := range got.Resources {
|
|
if res["id"] == BusMembershipID() {
|
|
t.Fatal("a machine with no membership on record was handed one")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestARefusalWithNothingOnRecordNamesTheHandover(t *testing.T) {
|
|
busSeatDelivering(t, "mesh-bus")
|
|
elsewhere := []mcat.Held{{Claim: "mesh-broker", Scope: mcat.ScopeMesh, Node: "anchor", Module: "old-broker"}}
|
|
|
|
_, problems := checkClaims([]mcat.Manifest{newBroker()}, mcat.Node{Name: "laptop"}, elsewhere, nil)
|
|
if len(problems) != 1 {
|
|
t.Fatalf("two derived claimants across machines were not refused: %v", problems)
|
|
}
|
|
if !strings.Contains(problems[0], "`seat mesh-broker --to anchor/old-broker`") {
|
|
t.Fatalf("the refusal does not name the handover that records the holder: %s", problems[0])
|
|
}
|
|
}
|
|
|
|
// brokerVerbs are the verbs the seat mesh-broker promises its holder, as the contracts define them: a holder names every one
|
|
// (ADR 0132), so a fixture holder does (issue 1966 ENG-3).
|
|
func brokerVerbs() []string {
|
|
seat, _ := mcat.SeatNamed("mesh-broker")
|
|
var out []string
|
|
for _, v := range seat.Serves {
|
|
if !v.Optional {
|
|
out = append(out, v.Name)
|
|
}
|
|
}
|
|
return out
|
|
}
|