package builder import ( "context" "encoding/json" "fmt" "os" "os/exec" "path/filepath" "strings" "testing" "time" "github.com/novox/mesh-controller/internal/artifacts" "github.com/novox/mesh-controller/internal/facts" ) // A merge check on the build seat (novox/hq to-be 45 ยง9), against a real container runtime and a real // registry: the repository's own merge-check.sh runs with the facts the controller keeps, beside a // throwaway store and bus of **the versions the mesh runs**, and everything raised is removed. // // MESH_TEST_DOCKER=1 MESH_TEST_REGISTRY=127.0.0.1:15000 go test ./internal/builder -run Check func TestTheStoreAndBusAChecksStandsOnAreTheOnesTheMeshRuns(t *testing.T) { if got := StoreImage("17.11"); got != "postgres:17-alpine" { t.Errorf("a store at 17.11 is checked against %s", got) } if got := StoreImage("16.4 (Debian 16.4-1.pgdg120+1)"); got != "postgres:16-alpine" { t.Errorf("a store at 16.4 is checked against %s", got) } if got := BusImage("2.11.17"); got != "nats:2.11.17-alpine" { t.Errorf("a bus at 2.11.17 is checked against %s", got) } } // aRepository is a git repository holding these files, committed, and its head. func aCheckedRepository(t *testing.T, files map[string]string) (string, string) { t.Helper() dir := t.TempDir() git := func(args ...string) string { cmd := exec.Command("git", args...) cmd.Dir = dir cmd.Env = append(os.Environ(), "GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@example.org", "GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@example.org") out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("git %v: %v\n%s", args, err, out) } return strings.TrimSpace(string(out)) } git("init", "--quiet", "-b", "main") for name, body := range files { if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o755); err != nil { t.Fatal(err) } } git("add", "-A") git("commit", "--quiet", "-m", "x") return dir, git("rev-parse", "HEAD") } func checkEnvironment(t *testing.T) string { t.Helper() if os.Getenv("MESH_TEST_DOCKER") != "1" || os.Getenv("MESH_TEST_REGISTRY") == "" { t.Skip("MESH_TEST_DOCKER=1 and MESH_TEST_REGISTRY: a check raises containers and reads the registry") } registry := os.Getenv("MESH_TEST_REGISTRY") body, err := json.Marshal(facts.Facts{Format: facts.Format, Taken: time.Now().UTC(), Versions: facts.Versions{Bus: "2.11.17", Store: "17.11"}, Machines: []facts.Machine{{Name: "abcdef", Length: 6}}}) if err != nil { t.Fatal(err) } if _, err := (artifacts.Store{Address: registry}).PutTagged(t.Context(), facts.Repository, facts.Tag, facts.MediaType, body); err != nil { t.Fatal(err) } return registry } // goToolchain is the Go image a check's script runs in here: the base the controller's own image is built on. const goToolchain = "golang@sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c" func labelled(id string) []string { out, _ := exec.Command("docker", "ps", "-aq", "--filter", "label="+BuildLabel+"="+id).Output() return strings.Fields(string(out)) } func TestACheckRunsTheRepositorysOwnScriptBesideTheMeshsVersionsAndLeavesNothing(t *testing.T) { registry := checkEnvironment(t) // The script proves what it was given: the facts, a store that answers, a bus that answers, and // writes the gate's verdict where it is told to. script := `set -e test -s "$MESH_FACTS" grep -q '"bus": "2.11.17"' "$MESH_FACTS" || grep -q '"bus":"2.11.17"' "$MESH_FACTS" case "$MESH_TEST_POSTGRES" in postgres://*127.0.0.1:*) ;; *) echo "no store: $MESH_TEST_POSTGRES"; exit 1;; esac case "$MESH_TEST_NATS" in nats://127.0.0.1:*) ;; *) echo "no bus: $MESH_TEST_NATS"; exit 1;; esac test "$MESH_CHECK_REPOSITORY" = "novox/mesh-controller" test "$MESH_CHECK_CHANGED" = "a.go,b.go" test ! -S /var/run/docker.sock || { echo "the check holds the container runtime's socket"; exit 1; } echo '{"verdict":"warning","summary":"a merge rebuilds 14 module(s)"}' > "$MESH_CHECK_VERDICT" echo checked ` repo, head := aCheckedRepository(t, map[string]string{CheckScript: script}) id := fmt.Sprintf("check-test-%d", time.Now().UnixNano()) v, err := Check(t.Context(), Command, CheckSpec{ID: id, Repository: repo, Ref: head, Owner: "novox", Repo: "mesh-controller", Number: 7, Paths: []string{"a.go", "b.go"}, Toolchain: goToolchain}, t.TempDir(), registry, GitCredential{}, nil) if err != nil { t.Fatal(err) } if v.Verdict != "warning" || v.Summary != "a merge rebuilds 14 module(s)" || !strings.Contains(v.Report, "checked") { t.Fatalf("the check answered %+v", v) } if left := labelled(id); len(left) > 0 { t.Errorf("the check left %d container(s) behind", len(left)) } } func TestAFailingCheckFailsAndOneThatCannotRunIsNeverAPass(t *testing.T) { registry := checkEnvironment(t) repo, head := aCheckedRepository(t, map[string]string{CheckScript: "echo 'resource \"x.service\": refused'; exit 3\n"}) v, err := Check(t.Context(), Command, CheckSpec{ID: fmt.Sprintf("check-fail-%d", time.Now().UnixNano()), Repository: repo, Ref: head, Owner: "novox", Repo: "mesh-controller", Toolchain: goToolchain}, t.TempDir(), registry, GitCredential{}, nil) if err != nil { t.Fatal(err) } if v.Verdict != "fail" || !strings.Contains(v.Summary, "refused") { t.Fatalf("a failing script answered %+v", v) } // Past its bound: an error, not a pass. was := CheckTimeout CheckTimeout = 25 * time.Second t.Cleanup(func() { CheckTimeout = was }) repo, head = aCheckedRepository(t, map[string]string{CheckScript: "sleep 120\n"}) v, err = Check(context.Background(), Command, CheckSpec{ID: fmt.Sprintf("check-slow-%d", time.Now().UnixNano()), Repository: repo, Ref: head, Owner: "novox", Repo: "mesh-controller", Toolchain: goToolchain}, t.TempDir(), registry, GitCredential{}, nil) if err == nil && v.Verdict == "pass" { t.Fatalf("a check past its bound passed: %+v", v) } if err == nil && v.Verdict != "error" { t.Fatalf("a check past its bound answered %+v", v) } // No facts: it cannot run, and says so. repo, head = aCheckedRepository(t, map[string]string{CheckScript: "exit 0\n"}) _, err = Check(t.Context(), Command, CheckSpec{ID: "check-nofacts", Repository: repo, Ref: head, Owner: "novox", Repo: "mesh-controller", Toolchain: goToolchain}, t.TempDir(), "127.0.0.1:1", GitCredential{}, nil) if err == nil || !strings.Contains(err.Error(), "facts snapshot") { t.Fatalf("a check with no facts said %v", err) } }