132 lines
5.6 KiB
Go
132 lines
5.6 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func module(t *testing.T, root, name, body string, files map[string]string) {
|
|
t.Helper()
|
|
dir := filepath.Join(root, "modules", name)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "module.json"), []byte(body), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for path, text := range files {
|
|
full := filepath.Join(dir, filepath.FromSlash(path))
|
|
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(full, []byte(text), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Each of the settings form's names is found where it stands: the declaration, a mergeable file's keys, a
|
|
// placeholder in the manifest, and a placeholder in a template file the manifest names (novox/hq ADR 0348 §1).
|
|
func TestEveryOldNameIsFound(t *testing.T) {
|
|
root := t.TempDir()
|
|
module(t, root, "declares", `{"module":"declares","settings":{"size":{"kind":"preference","default":1,"why":"w"}},`+
|
|
`"resources":[{"id":"f","type":"file","content":"${setting:size}"}]}`, nil)
|
|
module(t, root, "takes", `{"module":"takes","resources":[{"id":"f","type":"file","merge":"json",`+
|
|
`"takes-settings":["a"],"content":"{}"}]}`, nil)
|
|
module(t, root, "asks", `{"module":"asks","serves":{"x":{"d":"${setting:domain}"}}}`, nil)
|
|
module(t, root, "templated", `{"module":"templated","config":{"size":{"type":"number","description":"d",`+
|
|
`"default":1}},"resources":[{"id":"f","type":"file","template":"files/conf"}]}`,
|
|
map[string]string{"files/conf": "size = ${setting:size}\n"})
|
|
module(t, root, "moved", `{"module":"moved","config":{"size":{"type":"number","description":"d","default":1}},`+
|
|
`"resources":[{"id":"f","type":"file","template":"files/conf"},{"id":"g","type":"file","merge":"json",`+
|
|
`"takes-config":[],"content":"{\"settings\": 1}"}]}`,
|
|
map[string]string{"files/conf": "size = ${config:size}\n# change the settings, never this file\n"})
|
|
module(t, root, "none", `{"module":"none","resources":[{"id":"f","type":"file","content":"no settings here"}]}`, nil)
|
|
got, err := Check(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := map[string][]string{
|
|
"asks": {"${setting:…}"},
|
|
"declares": {"${setting:…}", "settings"},
|
|
"takes": {"takes-settings"},
|
|
"templated": {"${setting:…} in files/conf"},
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("found %v, want %v", got, want)
|
|
}
|
|
}
|
|
|
|
// A module not on the list says no old name; one on the list that says none is taken off it; a line the trunk's
|
|
// list does not hold is refused, so the list only shrinks until it is empty (novox/hq ADR 0348 §11).
|
|
func TestTheListOfModulesNotYetMovedOnlyShrinks(t *testing.T) {
|
|
found := map[string][]string{"old": {"settings"}, "forgotten": {"takes-settings"}}
|
|
unmoved := map[string]bool{"old": true, "done": true, "new": true}
|
|
base := map[string]bool{"old": true, "done": true, "forgotten": true}
|
|
refused, stale, added := Judge(found, unmoved, base)
|
|
if !reflect.DeepEqual(refused, []string{"forgotten"}) || !reflect.DeepEqual(stale, []string{"done", "new"}) ||
|
|
!reflect.DeepEqual(added, []string{"new"}) {
|
|
t.Fatalf("refused %v, stale %v, added %v", refused, stale, added)
|
|
}
|
|
}
|
|
|
|
func TestTheListIsReadWithoutItsComments(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "unmoved")
|
|
if err := os.WriteFile(path, []byte("# why\n\nmailu\n searxng \n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := Unmoved(path)
|
|
if err != nil || !reflect.DeepEqual(got, map[string]bool{"mailu": true, "searxng": true}) {
|
|
t.Fatalf("%v %v", got, err)
|
|
}
|
|
}
|
|
|
|
// The catalogue itself: every moved module says config alone, and the list names exactly the modules that
|
|
// still say settings.
|
|
func TestTheCatalogueSaysConfigInEveryMovedModule(t *testing.T) {
|
|
found, err := Check(filepath.Join("..", ".."))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
unmoved, err := Unmoved("unmoved")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
refused, stale, added := Judge(found, unmoved, seed)
|
|
for _, m := range refused {
|
|
t.Errorf("%s is not on checks/config/unmoved and says %s", m, strings.Join(found[m], ", "))
|
|
}
|
|
for _, m := range stale {
|
|
t.Errorf("checks/config/unmoved names %s, which says no old name", m)
|
|
}
|
|
for _, m := range added {
|
|
t.Errorf("checks/config/unmoved gains %s, which its first copy did not hold", m)
|
|
}
|
|
}
|
|
|
|
// A mergeable file that names no keys takes every key of its module's layer (novox/hq ADR 0326 §3): the settings
|
|
// form's last shape, which declares none of what it takes, so mesh-config could set none of it (ADR 0348 §10). It
|
|
// is found like an old name, in a module that says no other (issue 735); one naming its keys, an empty list
|
|
// included, is not.
|
|
func TestAMergeableFileTakingTheWholeLayerIsFound(t *testing.T) {
|
|
root := t.TempDir()
|
|
module(t, root, "whole", `{"module":"whole","resources":[{"id":"runtime-config","type":"file","merge":"json",`+
|
|
`"template":"files/config.json"}]}`, map[string]string{"files/config.json": "{}\n"})
|
|
module(t, root, "names", `{"module":"names","config":{"url":{"type":"string","description":"d","default":""}},`+
|
|
`"resources":[{"id":"runtime-config","type":"file","merge":"json","takes-config":["url"],"content":"{}"}]}`, nil)
|
|
module(t, root, "none", `{"module":"none","resources":[{"id":"c","type":"file","merge":"json",`+
|
|
`"takes-config":[],"content":"{}"}]}`, nil)
|
|
module(t, root, "plain", `{"module":"plain","resources":[{"id":"c","type":"file","content":"{}"}]}`, nil)
|
|
got, err := Check(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := map[string][]string{"whole": {"runtime-config taking its whole layer"}}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("found %v, want %v", got, want)
|
|
}
|
|
}
|