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).
125 lines
5.4 KiB
Go
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)
|
|
}
|
|
}
|