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}}} } 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}}} } // 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]) } }