CatalogTools moves into the index package so its row sits in the existing catalog test. Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
141 lines
5.2 KiB
Go
141 lines
5.2 KiB
Go
package index
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
|
|
"git.novox.be/novox/mesh-delivery/sdk/graph"
|
|
)
|
|
|
|
// capturedKeys are the field names of the first object at path (a list's first element when the value is a list) in the
|
|
// old catalog module's answer captured with mesh_call on 2026-11-03 (testdata/catalog; trimmed to a few modules, and the
|
|
// manifest of catalog_module to three of its keys).
|
|
func capturedKeys(t *testing.T, file string, path ...string) []string {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(filepath.Join("testdata", "catalog", file))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return keysAt(t, raw, path...)
|
|
}
|
|
|
|
func keysAt(t *testing.T, raw []byte, path ...string) []string {
|
|
t.Helper()
|
|
var v any
|
|
if err := json.Unmarshal(raw, &v); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, p := range path {
|
|
v = v.(map[string]any)[p]
|
|
if l, ok := v.([]any); ok {
|
|
v = l[0]
|
|
}
|
|
}
|
|
var out []string
|
|
for k := range v.(map[string]any) {
|
|
out = append(out, k)
|
|
}
|
|
slices.Sort(out)
|
|
return out
|
|
}
|
|
|
|
// catalogVerbsAnswerFromTheStates: the seat mesh-catalog's four verbs answer from the indexer's modules, manifests and
|
|
// graph, with the field names of the old catalog module's answers (ADR 0604 §1, §2).
|
|
func catalogVerbsAnswerFromTheStates(t *testing.T) {
|
|
withAcme := strings.Replace(renamed(t, "audit-logger", "acme"), `"module": "acme",`,
|
|
`"module": "acme", "provides": [{"name": "internal-acme-ca", "scope": "mesh", "answered-by": "helper"}],`, 1)
|
|
ix, mods := newFormIndexer(t, mod{"mesh-tools", renamed(t, "avahi", "mesh-tools")},
|
|
mod{"avahi", standingOn(t, catalogueManifest(t, "avahi"), "mesh-tools")},
|
|
mod{"ca-trust", catalogueManifest(t, "ca-trust")}, mod{"acme", withAcme})
|
|
reportToolchain(t, ix, "mesh-build-go", "novox", "go", "go1.27.1")
|
|
c := CatalogRead{
|
|
Modules: func() (map[string]delivery.Module, error) {
|
|
out := map[string]delivery.Module{}
|
|
for k := range mods.vals {
|
|
m, _ := moduleOf(t, mods, k)
|
|
out[k] = m
|
|
}
|
|
return out, nil
|
|
},
|
|
Manifest: func(digest string) (string, error) {
|
|
raw, _, err := ix.manifestRaw(context.Background(), digest)
|
|
return raw, err
|
|
},
|
|
Graph: func() (graph.Graph, error) {
|
|
g := graph.Graph{}
|
|
for k := range ix.Graph.(*memStore).vals {
|
|
g[k] = graphOf(t, ix, k)
|
|
}
|
|
return g, nil
|
|
},
|
|
}
|
|
answer := func(v any, err error) []byte {
|
|
t.Helper()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
raw, _ := json.Marshal(v)
|
|
return raw
|
|
}
|
|
contains := func(got, want []string) bool {
|
|
for _, w := range want {
|
|
if !slices.Contains(got, w) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
// The tools bundle serves the four verbs as mesh-catalog.<verb>, answering from the same states.
|
|
var names []string
|
|
for _, tool := range CatalogTools(c) {
|
|
names = append(names, tool.Name)
|
|
if tool.Name == "mesh-catalog.modules" {
|
|
if got, err := tool.Run(nil); err != nil || !strings.Contains(string(answer(got, nil)), `"module":"avahi"`) {
|
|
t.Errorf("mesh-catalog.modules answered %v, %v", got, err)
|
|
}
|
|
}
|
|
}
|
|
if !slices.Equal(names, []string{"mesh-catalog.modules", "mesh-catalog.module", "mesh-catalog.provides", "mesh-catalog.dependents"}) {
|
|
t.Errorf("the tools bundle serves %v", names)
|
|
}
|
|
raw := answer(c.CatalogModulesAnswer())
|
|
if got, want := keysAt(t, raw, "modules"), capturedKeys(t, "catalog_modules.json", "modules"); !contains(got, want) {
|
|
t.Errorf("modules' fields %v, want the old catalog's %v", got, want)
|
|
}
|
|
if !strings.Contains(string(raw), `"module":"avahi"`) || !strings.Contains(string(raw), `"fp":"src2:`) {
|
|
t.Errorf("modules = %s", raw)
|
|
}
|
|
raw = answer(c.CatalogModuleAnswer("ca-trust"))
|
|
want := slices.DeleteFunc(capturedKeys(t, "catalog_module.json", "module"), func(k string) bool { return k == "ref" }) // the trunk's, no ref
|
|
if got := keysAt(t, raw, "module"); !contains(got, want) || !strings.Contains(string(raw), `"internal-acme-ca"`) {
|
|
t.Errorf("module ca-trust = %s, want the fields %v and its manifest", raw, want)
|
|
}
|
|
if _, err := c.CatalogModuleAnswer("gone"); err == nil || !strings.Contains(err.Error(), "no module gone") {
|
|
t.Errorf("a module the trunk does not hold: %v", err)
|
|
}
|
|
raw = answer(c.CatalogProvidesAnswer("internal-acme-ca"))
|
|
if got, want := keysAt(t, raw, "provides"), capturedKeys(t, "catalog_provides.json", "provides"); !reflect.DeepEqual(got, want) ||
|
|
!reflect.DeepEqual(keysAt(t, raw, "requires"), capturedKeys(t, "catalog_provides.json", "requires")) ||
|
|
!strings.Contains(string(raw), `"provides":[{"module":"acme","commit":"`+merge+`","scope":"mesh"}]`) ||
|
|
!strings.Contains(string(raw), `"requires":[{"module":"ca-trust"`) {
|
|
t.Errorf("provides internal-acme-ca = %s", raw)
|
|
}
|
|
raw = answer(c.CatalogDependentsAnswer("mesh-tools"))
|
|
if got, want := keysAt(t, raw), capturedKeys(t, "catalog_dependents.json"); !reflect.DeepEqual(got, want) ||
|
|
!strings.Contains(string(raw), `"dependents":[{"module":"avahi"`) {
|
|
t.Errorf("dependents of mesh-tools = %s", raw)
|
|
}
|
|
raw = answer(c.CatalogDependentsAnswer("mesh-build-go")) // the build module reaches every Go module it builds
|
|
if !strings.Contains(string(raw), `"avahi"`) || !strings.Contains(string(raw), `"mesh-tools"`) {
|
|
t.Errorf("dependents of mesh-build-go = %s", raw)
|
|
}
|
|
}
|