gofmt and vet are the Go build module's; the unit level keeps the register and the bed's reading of a change.
112 lines
4.4 KiB
Go
112 lines
4.4 KiB
Go
package replays
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// **The catalogue's bed** (novox/hq ADR 0240 rule 7, to-be 48 §8, Phase D): every long-running resource whose
|
|
// declared `health` or image the change touches is started alone and its check must see the program within
|
|
// its grace. Run by every merge check of the catalogue on the build seat, with the change's catalogue at
|
|
// MESH_REPLAY_CATALOGUE; by hand against the catalogue beside the lab. What changed is against
|
|
// MESH_BED_BASE (origin/main by default) in that checkout.
|
|
func TestBedProvesEveryChangedHealthCheck(t *testing.T) {
|
|
root := orDefault(os.Getenv("MESH_REPLAY_CATALOGUE"), filepath.Join("..", "..", "mesh-catalog"))
|
|
if _, err := os.Stat(filepath.Join(root, "modules")); err != nil {
|
|
t.Skipf("no catalogue at %s (MESH_REPLAY_CATALOGUE names it)", root)
|
|
}
|
|
base := orDefault(os.Getenv("MESH_BED_BASE"), "origin/main")
|
|
show := func(ref, path string) ([]byte, error) {
|
|
return exec.Command("git", "-c", "safe.directory=*", "-C", root, "show", ref+":"+path).Output()
|
|
}
|
|
if _, err := exec.Command("git", "-c", "safe.directory=*", "-C", root, "rev-parse", "--verify", base).Output(); err != nil {
|
|
// What changed cannot be told, so every declared check is proved: the bed never passes what it did
|
|
// not look at.
|
|
t.Logf("%s is not in the catalogue at %s (%v): every declared check is proved", base, root, err)
|
|
}
|
|
changed, err := ChangedHealth(root, base, show)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(changed) == 0 {
|
|
t.Logf("no declared check or image of a long-running resource changed against %s: nothing to prove", base)
|
|
return
|
|
}
|
|
docker, ok := DockerFromEnv()
|
|
if !ok {
|
|
t.Fatalf("%d changed check(s) and no container runtime to prove them on", len(changed))
|
|
}
|
|
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Minute)
|
|
defer cancel()
|
|
for _, r := range changed {
|
|
manifest, _ := os.ReadFile(filepath.Join(root, "modules", r.Module, "module.json"))
|
|
v := docker.Prove(ctx, r, FilesOf(manifest))
|
|
switch {
|
|
case v.Failed:
|
|
t.Errorf("FAILS ON THE BED: %s", v.Said)
|
|
case v.Proved:
|
|
t.Logf("proved: %s", v.Said)
|
|
default:
|
|
t.Logf("NOT PROVED HERE: %s", v.Said)
|
|
}
|
|
}
|
|
}
|
|
|
|
// What the bed proves is what the change touches: a long-running container whose declared check or image
|
|
// changed, or that is new; never a step, never one left as it was.
|
|
func TestTheBedProvesWhatTheChangeTouches(t *testing.T) {
|
|
root := t.TempDir()
|
|
git := func(args ...string) {
|
|
t.Helper()
|
|
cmd := exec.Command("git", append([]string{"-C", root, "-c", "user.email=lab@example", "-c", "user.name=lab"}, args...)...)
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("git %v: %v %s", args, err, out)
|
|
}
|
|
}
|
|
write := func(module, body string) {
|
|
t.Helper()
|
|
if err := os.MkdirAll(filepath.Join(root, "modules", module), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(root, "modules", module, "module.json"), []byte(body), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
git("init", "-q", "-b", "main")
|
|
write("web", `{"module":"web","resources":[{"id":"server","type":"container","image":"web@sha256:a","health":{"kind":"runtime"}},
|
|
{"id":"seed","type":"container","image":"web@sha256:a","run-once":true,"health":{"kind":"runtime"}}]}`)
|
|
write("db", `{"module":"db","resources":[{"id":"server","type":"container","image":"db@sha256:a","health":{"kind":"runtime"}}]}`)
|
|
git("add", "-A")
|
|
git("commit", "-qm", "base")
|
|
write("web", `{"module":"web","resources":[{"id":"server","type":"container","image":"web@sha256:b","health":{"kind":"runtime"}},
|
|
{"id":"seed","type":"container","image":"web@sha256:b","run-once":true,"health":{"kind":"runtime"}}]}`)
|
|
write("cache", `{"module":"cache","resources":[{"id":"server","type":"container","image":"cache@sha256:a","health":{"kind":"tcp","endpoint":"redis"}},
|
|
{"id":"plain","type":"container","image":"cache@sha256:a"}]}`)
|
|
show := func(ref, path string) ([]byte, error) {
|
|
return exec.Command("git", "-C", root, "show", ref+":"+path).Output()
|
|
}
|
|
changed, err := ChangedHealth(root, "main", show)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var got []string
|
|
for _, r := range changed {
|
|
got = append(got, r.Module+"."+r.ID)
|
|
}
|
|
if strings.Join(got, ",") != "cache.server,web.server" {
|
|
t.Fatalf("the bed would prove %v; want the new module's checked container and the one whose image moved", got)
|
|
}
|
|
}
|
|
|
|
func orDefault(s, fallback string) string {
|
|
if s == "" {
|
|
return fallback
|
|
}
|
|
return s
|
|
}
|