mesh/check failed: integration: github.com/novox/mesh-controller/internal/identity.TestACertificateChainsToTheMeshsOwnAuthority: authority_test.go:45:…
mesh/repo-check head 4357b3688f58 merged into trunk 3d4ad6fefb7c — pass: its merge-check.sh passed
mesh/merge-gate head 4357b3688f58 merged into trunk 3d4ad6fefb7c — pass: group fix/1826-held-config composed: every machine composes with the change as it …
mesh/delivery delivered
mesh/delivery-group group fix/1826-held-config delivered: every member is delivered
210 lines
9.2 KiB
Go
210 lines
9.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
const (
|
|
headSHA = "2222222222222222222222222222222222222222"
|
|
baseSHA = "1111111111111111111111111111111111111111"
|
|
)
|
|
|
|
// fakeManifests serves manifests by ref, and counts what the forge was asked. Issue 1826/AC1 (unit level).
|
|
func fakeManifests(t *testing.T, at map[string]*catalogue.Manifest) {
|
|
t.Helper()
|
|
was := manifestAt
|
|
t.Cleanup(func() { manifestAt = was })
|
|
manifestsReadMu.Lock()
|
|
clear(manifestsRead)
|
|
manifestsReadMu.Unlock()
|
|
manifestAt = func(_ context.Context, _, _, _, ref string) (catalogue.Manifest, bool, error) {
|
|
if m := at[ref]; m != nil {
|
|
return *m, true, nil
|
|
}
|
|
return catalogue.Manifest{}, false, nil
|
|
}
|
|
}
|
|
|
|
// fakeConfig records the calls to mesh-config and answers each action from answers.
|
|
func fakeConfig(t *testing.T, answers map[string]string) *[]map[string]any {
|
|
t.Helper()
|
|
var calls []map[string]any
|
|
was := meshConfigAsk
|
|
t.Cleanup(func() { meshConfigAsk = was })
|
|
meshConfigAsk = func(_ context.Context, args map[string]any) ([]byte, error) {
|
|
calls = append(calls, args)
|
|
return []byte(answers[args["action"].(string)]), nil
|
|
}
|
|
return &calls
|
|
}
|
|
|
|
func manifest(module string, keys map[string]catalogue.SettingDeclaration) *catalogue.Manifest {
|
|
return &catalogue.Manifest{Module: module, Settings: keys}
|
|
}
|
|
|
|
func thePull() pullAt {
|
|
return pullAt{Number: 434, changeSource: changeSource{Owner: "novox", Repo: "mesh-catalog", New: headSHA, Old: baseSHA,
|
|
Paths: []string{"modules/n8n/module.json", "modules/n8n/README.md"}}}
|
|
}
|
|
|
|
// 1826/AC1: a key the head adds is sent with its declaration, under the pull request and the head.
|
|
func TestAKeyThePullRequestAddsIsToldToMeshConfig(t *testing.T) {
|
|
fakeManifests(t, map[string]*catalogue.Manifest{
|
|
baseSHA: manifest("n8n", map[string]catalogue.SettingDeclaration{"port": {Type: "number"}}),
|
|
headSHA: manifest("n8n", map[string]catalogue.SettingDeclaration{"port": {Type: "number"},
|
|
"media-gid": {Type: "number", Required: true}}),
|
|
})
|
|
calls := fakeConfig(t, map[string]string{"pull-declares": `{"kept":1}`})
|
|
tellPullDeclares(context.Background(), io.Discard, []pullAt{thePull()})
|
|
if len(*calls) != 1 {
|
|
t.Fatalf("mesh-config was asked %d times: %v", len(*calls), *calls)
|
|
}
|
|
c := (*calls)[0]
|
|
if c["action"] != "pull-declares" || c["pull"] != "novox/mesh-catalog#434" || c["head"] != headSHA {
|
|
t.Fatalf("the call is %v", c)
|
|
}
|
|
body, _ := json.Marshal(c["declares"])
|
|
var declares map[string]map[string]inventory.DeclaredMirrored
|
|
if err := json.Unmarshal(body, &declares); err != nil || len(declares) != 1 || declares["n8n"] == nil {
|
|
t.Fatalf("declares %s: %v", body, err)
|
|
}
|
|
mods := []inventory.DeclaredConfigMirrored{{Module: "n8n", Settings: declares["n8n"]}}
|
|
if _, has := mods[0].Settings["port"]; has || len(mods[0].Settings) != 1 {
|
|
t.Fatalf("only the added key is told, got %v", mods[0].Settings)
|
|
}
|
|
d := mods[0].Settings["media-gid"]
|
|
if d.Type != "number" || !d.Required {
|
|
t.Fatalf("the declaration is %+v", d)
|
|
}
|
|
}
|
|
|
|
// 1826/AC1: a key whose declaration the head changes is sent; one it leaves as it was is not.
|
|
func TestAnUnchangedKeyIsNotToldAndAChangedOneIs(t *testing.T) {
|
|
fakeManifests(t, map[string]*catalogue.Manifest{
|
|
baseSHA: manifest("n8n", map[string]catalogue.SettingDeclaration{"port": {Type: "number"}, "x": {Type: "string"}}),
|
|
headSHA: manifest("n8n", map[string]catalogue.SettingDeclaration{"port": {Type: "number"}, "x": {Type: "string", Pattern: "a+"}}),
|
|
})
|
|
mods, touched, err := pullDeclarations(context.Background(), thePull())
|
|
if err != nil || !touched || len(mods) != 1 || len(mods[0].Settings) != 1 || mods[0].Settings["x"].Pattern != "a+" {
|
|
t.Fatalf("%+v %v %v", mods, touched, err)
|
|
}
|
|
}
|
|
|
|
// 1826/AC1: a head that changes no module.json, or removes it, is still told, with declares {}: mesh-config re-judges at every
|
|
// head, so a head that reverts the change drops the value held for it (review round 1, finding 1).
|
|
func TestAHeadThatDeclaresNothingIsToldAsEmpty(t *testing.T) {
|
|
fakeManifests(t, map[string]*catalogue.Manifest{baseSHA: manifest("n8n", nil), headSHA: manifest("n8n", nil)})
|
|
calls := fakeConfig(t, nil)
|
|
p := thePull()
|
|
p.Paths = []string{"modules/n8n/README.md"}
|
|
tellPullDeclares(context.Background(), io.Discard, []pullAt{p})
|
|
p = thePull()
|
|
p.Removed = p.Paths
|
|
tellPullDeclares(context.Background(), io.Discard, []pullAt{p})
|
|
if len(*calls) != 2 {
|
|
t.Fatalf("mesh-config was told %v", *calls)
|
|
}
|
|
for _, c := range *calls {
|
|
if d, _ := json.Marshal(c["declares"]); string(d) != "{}" || c["head"] != headSHA {
|
|
t.Fatalf("told %v", c)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1826/AC1: a head whose paths are cut, or whose base is unknown, cannot be read: nothing is told, never an empty set.
|
|
func TestAHeadThatCannotBeReadIsNotToldAsEmpty(t *testing.T) {
|
|
calls := fakeConfig(t, nil)
|
|
p := thePull()
|
|
p.Truncated = true
|
|
tellPullDeclares(context.Background(), io.Discard, []pullAt{p})
|
|
if len(*calls) != 0 {
|
|
t.Fatalf("told %v", *calls)
|
|
}
|
|
}
|
|
|
|
// 1826/AC1: a module.json changed without a declaration changing is told as an empty set, so mesh-config re-judges what it holds.
|
|
func TestAChangedManifestWithNoNewDeclarationIsToldAsEmpty(t *testing.T) {
|
|
m := manifest("n8n", map[string]catalogue.SettingDeclaration{"port": {Type: "number"}})
|
|
fakeManifests(t, map[string]*catalogue.Manifest{baseSHA: m, headSHA: m})
|
|
calls := fakeConfig(t, nil)
|
|
tellPullDeclares(context.Background(), io.Discard, []pullAt{thePull()})
|
|
if len(*calls) != 1 {
|
|
t.Fatalf("calls %v", *calls)
|
|
}
|
|
if d, _ := json.Marshal((*calls)[0]["declares"]); string(d) != "{}" {
|
|
t.Fatalf("declares %s", d)
|
|
}
|
|
}
|
|
|
|
// 1826/AC1: mesh-config not answering is said and goes on; the check is not stopped.
|
|
func TestMeshConfigNotAnsweringIsSaidAndGoneOn(t *testing.T) {
|
|
fakeManifests(t, map[string]*catalogue.Manifest{baseSHA: manifest("n8n", nil),
|
|
headSHA: manifest("n8n", map[string]catalogue.SettingDeclaration{"k": {Type: "string"}})})
|
|
was := meshConfigAsk
|
|
t.Cleanup(func() { meshConfigAsk = was })
|
|
meshConfigAsk = func(context.Context, map[string]any) ([]byte, error) { return nil, io.ErrUnexpectedEOF }
|
|
var said strings.Builder
|
|
tellPullDeclares(context.Background(), &said, []pullAt{thePull()})
|
|
got := heldFor(context.Background(), &said, []pullAt{thePull()})
|
|
if len(got) != 0 || !strings.Contains(said.String(), "mesh-config") {
|
|
t.Fatalf("held %v, said %q", got, said.String())
|
|
}
|
|
}
|
|
|
|
// 1826/AC2: the held values mesh-config answers for the check's pull requests are what the check request carries.
|
|
func TestTheHeldValuesOfTheChecksPullRequestsAreAsked(t *testing.T) {
|
|
calls := fakeConfig(t, map[string]string{"held": `{"held":[{"pull":"novox/mesh-catalog#434","head":"` + headSHA +
|
|
`","node":"ace","module":"n8n","key":"media-gid","value":2000,"approved":"the operator"}]}`})
|
|
other := pullAt{Number: 7, changeSource: changeSource{Owner: "novox", Repo: "mesh-controller", New: baseSHA}}
|
|
got := heldFor(context.Background(), io.Discard, []pullAt{thePull(), other})
|
|
if len(got) != 1 || got[0].Node != "ace" || got[0].Key != "media-gid" || string(got[0].Value) != "2000" {
|
|
t.Fatalf("got %+v", got)
|
|
}
|
|
c := (*calls)[0]
|
|
pulls, _ := json.Marshal(c["pulls"])
|
|
if c["action"] != "held" || string(pulls) != `[{"pull":"novox/mesh-catalog#434","head":"`+headSHA+
|
|
`"},{"pull":"novox/mesh-controller#7","head":"`+baseSHA+`"}]` {
|
|
t.Fatalf("asked %v %s", c, pulls)
|
|
}
|
|
}
|
|
|
|
// 1826/AC2: a value held for a pull request the check is not of is never carried.
|
|
func TestAHeldValueOfAnotherPullRequestIsNotCarried(t *testing.T) {
|
|
fakeConfig(t, map[string]string{"held": `{"held":[{"pull":"novox/mesh-catalog#999","head":"x","node":"ace","module":"n8n","key":"k","value":1}]}`})
|
|
if got := heldFor(context.Background(), io.Discard, []pullAt{thePull()}); len(got) != 0 {
|
|
t.Fatalf("carried %+v", got)
|
|
}
|
|
}
|
|
|
|
// 1826/AC2: the check request carries what mesh-config answered; a member of a composed group asks none.
|
|
func TestTheCheckRequestCarriesTheHeldValues(t *testing.T) {
|
|
fakeManifests(t, map[string]*catalogue.Manifest{baseSHA: manifest("n8n", nil), headSHA: manifest("n8n", nil)})
|
|
fakeConfig(t, map[string]string{"held": `{"held":[{"pull":"novox/mesh-catalog#434","node":"ace","module":"n8n","key":"media-gid","value":2000}]}`})
|
|
request := link.BuildRequest{Check: &link.CheckRequest{}}
|
|
withHeldConfig(context.Background(), io.Discard, &request, []pullAt{thePull()}, true)
|
|
if len(request.Check.HeldConfig) != 1 || request.Check.HeldConfig[0].Key != "media-gid" {
|
|
t.Fatalf("the request carries %+v", request.Check.HeldConfig)
|
|
}
|
|
request = link.BuildRequest{Check: &link.CheckRequest{}}
|
|
withHeldConfig(context.Background(), io.Discard, &request, []pullAt{thePull()}, false)
|
|
if len(request.Check.HeldConfig) != 0 {
|
|
t.Fatalf("a composed member carries %+v", request.Check.HeldConfig)
|
|
}
|
|
}
|
|
|
|
// 1826/AC1: a repository that builds a module of the mesh holds modules; another does not.
|
|
func TestARepositoryThatBuildsAModuleHoldsModules(t *testing.T) {
|
|
entries := []inventory.Entry{{Manifest: catalogue.Manifest{Module: "a"}, Source: inventory.Source{Repository: "novox/mesh-catalog", Path: "modules/a"}}}
|
|
if !holdsModules(entries, "novox", "mesh-catalog") || holdsModules(entries, "novox", "hq") {
|
|
t.Fatal("wrong")
|
|
}
|
|
}
|