Files
mesh-controller/cmd/mesh-controller/rotate_vault_test.go
T

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