Files
mesh-controller/cmd/mesh-controller/rotate_vault_test.go
T
mesh-tools 97a2d841c7 Land feature 57's controller work on its release branch
The controller plans config, templates and moved providers' provisions,
hands bus accounts to the vault and judges the new manifest words, on the
feature's SDK and node-engine validator (feature 57, config and provisioning).
2026-10-15 21:27:33 +00:00

125 lines
5.4 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"strings"
"testing"
"github.com/novox/mesh-controller/internal/inventory"
)
// `rotate <provision>` reaches the vault for a provider filled the new way (issue 767), and the controller writes no
// value of its own for it: its holders go to the vault, named by provider and provision, and no other holder does.
func TestARotationOfAProvisionTheVaultOwnsGoesToTheVaultAndNotTheControllersStore(t *testing.T) {
holders := []inventory.Holder{
{Provision: "postgres-database", Consumer: "g14", ConsumerModule: "gitea", Provider: "novox"},
{Provision: "postgres-database", Consumer: "ace", ConsumerModule: "umami", Provider: "novox"},
{Provision: "postgres-database", Consumer: "ace", ConsumerModule: "n8n", Provider: "ace"},
}
offering := func(provider, provision string) (string, error) {
if provider == "novox" {
return "postgres", nil
}
return "postgres-old", nil
}
var asked []string
moved := func(module string) (bool, error) {
asked = append(asked, module)
return module == "postgres", nil
}
vault, today, err := rotateRoutes(holders, offering, moved)
if err != nil {
t.Fatal(err)
}
if len(vault) != 1 || vault[0].Provider != "novox" || vault[0].Provision != "postgres-database" ||
vault[0].Module != "postgres" || len(vault[0].Holders) != 2 {
t.Fatalf("to the vault: %+v", vault)
}
if len(today) != 1 || today[0].Provider != "ace" {
t.Fatalf("today's path rotates a holder the vault owns, or misses one it does not: %+v", today)
}
if fmt.Sprint(asked) != "[postgres postgres-old]" {
t.Fatalf("the switch was read for %v, not the offering module", asked)
}
}
// A switch that cannot be read, or a provider whose offering module cannot be found, routes nothing: an error
// before anything changes, never today's path by default.
func TestARotationWhoseOwnerCannotBeReadChangesNothing(t *testing.T) {
holders := []inventory.Holder{{Provision: "postgres-database", Consumer: "g14", ConsumerModule: "gitea",
Provider: "novox"}}
broken := func(string) (bool, error) { return false, errors.New("the catalogue is unreadable") }
if _, _, err := rotateRoutes(holders, func(string, string) (string, error) { return "postgres", nil }, broken); err == nil {
t.Fatal("an unreadable switch fell back to today's path")
}
fine := func(string) (bool, error) { return true, nil }
if _, _, err := rotateRoutes(holders, func(string, string) (string, error) { return "", errors.New("no store") },
fine); err == nil {
t.Fatal("an unreadable provider fell back to today's path")
}
}
// What the vault is told: the provider and provision, and --consumer and --module as the one consumer whose own
// login is remade; nothing named when the operator named nobody, so a shared value is rotated for every holder.
func TestTheVaultIsToldTheProvisionAndTheConsumerTheOperatorNamed(t *testing.T) {
group := rotateGroup{Provider: "novox", Provision: "postgres-database", Module: "postgres"}
for _, c := range []struct {
consumer, module string
want, absent []string
}{
{"", "", []string{`"provider":"novox"`, `"provision":"postgres-database"`, `"why":"yearly"`,
`"by":"g14/claude-code"`}, []string{`"consumer"`, `"consumer_module"`}},
{"g14", "", []string{`"consumer":"g14"`}, []string{`"consumer_module"`}},
{"", "gitea", []string{`"consumer_module":"gitea"`}, []string{`"consumer":`}},
{"g14", "gitea", []string{`"consumer":"g14"`, `"consumer_module":"gitea"`}, nil},
} {
var said string
ask := func(verb string, args map[string]any) (json.RawMessage, error) {
raw, _ := json.Marshal(map[string]any{"verb": verb, "args": args})
said = string(raw)
return json.Marshal(vaultChanged{Name: "provision/novox/postgres-database", By: "g14/claude-code",
Each: []vaultChanged{{Name: "pair/novox/postgres-database/g14/gitea", Generation: 3}}})
}
words, err := rotateProvisionInVault(ask, group, c.consumer, c.module, "g14/claude-code", "yearly")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(said, `"verb":"rotate"`) {
t.Fatalf("%+v: the vault was asked %s", c, said)
}
for _, w := range c.want {
if !strings.Contains(said, w) {
t.Errorf("%+v: the vault was not told %s: %s", c, w, said)
}
}
for _, a := range c.absent {
if strings.Contains(said, a) {
t.Errorf("%+v: the vault was told %s: %s", c, a, said)
}
}
if !strings.Contains(words, "pair/novox/postgres-database/g14/gitea (generation 3)") {
t.Errorf("%+v: the operator was not told whose login was remade: %s", c, words)
}
}
}
// The vault rotates only with a why: asked of the vault without one, nothing is sent; and the vault's refusal (a
// shared value named for one consumer) is the operator's answer, not a rotation in the controller's store.
func TestARotationInTheVaultSaysWhyAndPassesOnItsRefusal(t *testing.T) {
group := rotateGroup{Provider: "anchor", Provision: "stand-in-api", Module: "stand-in"}
sent := 0
ask := func(string, map[string]any) (json.RawMessage, error) {
sent++
return nil, errors.New("not rotated: provision/anchor/stand-in-api is one value every holder shares")
}
if _, err := rotateProvisionInVault(ask, group, "", "", "g14/claude-code", " "); err == nil || sent != 0 {
t.Fatalf("a rotation without a why reached the vault: %v", err)
}
if _, err := rotateProvisionInVault(ask, group, "g14", "", "g14/claude-code", "suspected"); err == nil ||
!strings.Contains(err.Error(), "every holder shares") {
t.Fatalf("the vault's refusal was not said: %v", err)
}
}