Files
mesh-catalog/modules/gitea/cmd/repositories/manifest_test.go
T

172 lines
6.5 KiB
Go

package main
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
)
// 1124/AC1: the manifest declares `repositories` under config as a trusted object value with an empty default, as
// it declares agent-read-teams (ADR 0365 §3, ADR 0348), renders it into a file of its own, and runs this program
// at the module's start and whenever that file changes, as a run-once process (1124/AC6: its failed exit is the
// node-engine's gate, ADR 0136) declared last, so that gate stops nothing of the module after it.
func TestManifestDeclaresTheValueAndItsTrigger(t *testing.T) {
raw, err := os.ReadFile("../../module.json")
if err != nil {
t.Fatal(err)
}
var m struct {
Config map[string]map[string]any `json:"config"`
Resources []map[string]any `json:"resources"`
Build struct {
Artifacts []map[string]any `json:"artifacts"`
} `json:"build"`
}
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatal(err)
}
v := m.Config[ConfigKey]
if v == nil || v["type"] != "object" || v["trusted"] != true {
t.Fatalf("config %s = %v, want a trusted object", ConfigKey, v)
}
if d, ok := v["default"].(map[string]any); !ok || len(d) != 0 {
t.Fatalf("its default is %v, want {}", v["default"])
}
var file, process map[string]any
for _, r := range m.Resources {
if r["id"] == "repositories-file" {
file = r
}
}
if file == nil || file["type"] != "file" {
t.Fatalf("no file repositories-file")
}
// 1128/AC1: a plain template the provisioner client fills, so an approved value lands at once: a file the
// controller merges (merge, takes-config) takes a config change only on the node's next send (ADR 0348 §10).
if file["merge"] != nil || file["takes-config"] != nil {
t.Errorf("repositories-file is merged by the controller (merge %v, takes-config %v), want a plain template",
file["merge"], file["takes-config"])
}
text, err := os.ReadFile("../../" + file["template"].(string))
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(text), "${config:"+ConfigKey+"}") {
t.Fatalf("the template %s does not say ${config:%s}: %s", file["template"], ConfigKey, text)
}
filled := filepath.Join(t.TempDir(), "repositories.json")
value := `{"mesh-delivery":{"private":false,"description":"d"}}`
if err := os.WriteFile(filled, []byte(strings.ReplaceAll(strings.ReplaceAll(string(text), "${config:"+ConfigKey+"}", value), "${config:keep-main-rules}", "false")), 0o600); err != nil {
t.Fatal(err)
}
if v, err := ReadValue(filled); err != nil || len(v) != 1 || v["mesh-delivery"].Description != "d" {
t.Errorf("the filled template reads as %v, %v; want mesh-delivery", v, err)
}
if ReadKeepMainRules(filled) {
t.Error("the filled template with keep-main-rules false reads as true")
}
if k := m.Config["keep-main-rules"]; k == nil || k["type"] != "boolean" || k["default"] != false {
t.Errorf("config keep-main-rules = %v, want a boolean defaulting to false", k)
}
for _, r := range m.Resources {
if r["type"] == "process" && has(strs(r["restart-on"]), file["id"].(string)) {
process = r
}
}
if process == nil {
t.Fatalf("no process runs again when %s changes", file["id"])
}
if process["run-once"] != true {
t.Errorf("the process is not run-once: %v", process)
}
if last := m.Resources[len(m.Resources)-1]; last["id"] != process["id"] {
t.Errorf("the last resource is %v, want %v: a refused step gates whatever of the module comes after it", last["id"], process["id"])
}
if process["health"] != nil {
t.Errorf("the process declares a health check: %v", process["health"])
}
if strings.Join(strs(process["run"]), " ") != "./repositories sync" || process["artifact"] != "repositories" {
t.Errorf("the process runs %v from %v", process["run"], process["artifact"])
}
env, _ := process["env"].(map[string]any)
for _, k := range []string{"MESH_GITEA_URL", "MESH_GITEA_ADMIN_USER", "MESH_GITEA_ADMIN_PASSWORD_FILE", "MESH_REPOSITORIES_FILE", "MESH_REPOSITORIES_OUTCOME"} {
if env[k] == nil {
t.Errorf("the process is not given %s", k)
}
}
found := false
for _, a := range m.Build.Artifacts {
if a["name"] == "repositories" && a["from"] == "cmd/repositories" && a["binary"] == "repositories" {
found = true
}
}
if !found {
t.Error("no bundle builds cmd/repositories")
}
// Like agent-read-teams: the same kind of value, the same way of applying it.
teams := m.Config["agent-read-teams"]
if teams["trusted"] != v["trusted"] || teams["type"] != v["type"] {
t.Errorf("repositories is declared unlike agent-read-teams: %v vs %v", v, teams)
}
}
// 0534-2: the manifest declares main-mergers as a trusted list naming the operator's login by default, merges it into
// the module's runtime config file (with url, which the TypeScript part reads), and the repositories program is given
// that file and runs again when it changes.
func TestManifestDeclaresTheMainMergers(t *testing.T) {
raw, err := os.ReadFile("../../module.json")
if err != nil {
t.Fatal(err)
}
var m struct {
Config map[string]map[string]any `json:"config"`
Resources []map[string]any `json:"resources"`
}
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatal(err)
}
v := m.Config[MergersKey]
if v == nil || v["type"] != "list" || v["trusted"] != true || strings.Join(strs(v["default"]), ",") != "jschoubben" {
t.Fatalf("config %s = %v, want a trusted list defaulting to the operator's login", MergersKey, v)
}
var cfg, process map[string]any
for _, r := range m.Resources {
if r["id"] == "runtime-config" {
cfg = r
}
if r["name"] == "gitea-repositories" {
process = r
}
}
if cfg == nil || !has(strs(cfg["takes-config"]), MergersKey) || cfg["merge"] != "json" {
t.Fatalf("runtime-config does not take %s: %v", MergersKey, cfg)
}
text, err := os.ReadFile("../../" + cfg["template"].(string))
if err != nil {
t.Fatal(err)
}
file := filepath.Join(t.TempDir(), "config.json")
_ = os.WriteFile(file, text, 0o600)
if got, err := ReadMergers(file); err != nil || strings.Join(got, ",") != "jschoubben" {
t.Errorf("the template's mergers read as %v, %v; want the default", got, err)
}
env, _ := process["env"].(map[string]any)
if env["MESH_GITEA_CONFIG_FILE"] != cfg["path"] {
t.Errorf("the repositories program reads %v, want %v", env["MESH_GITEA_CONFIG_FILE"], cfg["path"])
}
if !has(strs(process["restart-on"]), "runtime-config") {
t.Errorf("the repositories program does not run again when the mergers change: %v", process["restart-on"])
}
}
func has(list []string, want string) bool {
for _, s := range list {
if s == want {
return true
}
}
return false
}