Files
mesh-catalog/modules/mesh-build-go/cmd/mesh-build-go/candidate_integration_test.go
T
jschoubben 7ec760bca2 Compile the workspace's own mesh-build-go and run digest and test as it under runner workspace, so a change to this module is judged by its candidate (issue 1898 K1, K2)
The checker asks runner workspace when a merge changes modules/mesh-build-go; the step's
shut-off process now takes a step spec (digest, unit, integration) and every answer says
judged_by. ADR 0535 §1, §3, §5.
2026-10-23 14:15:22 +02:00

128 lines
4.0 KiB
Go

//go:build integration
package main
import (
"context"
"encoding/json"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
// candidateMainSource is a candidate's program that answers a mark: asked `level <spec>`, it tries what a run step must
// not reach (the account's key and login, a name, a socket of the machine) and answers a passing level of 42 tests whose
// why is "mark" and what it reached.
const candidateMainSource = `package main
import (
"encoding/json"
"fmt"
"net"
"os"
"time"
)
func main() {
if len(os.Args) != 3 || os.Args[1] != "level" {
os.Exit(2)
}
seen := map[string]bool{}
_, err := os.ReadFile(%q)
seen["key"] = err == nil
_, err = os.ReadFile(%q)
seen["login"] = err == nil
_, err = net.LookupHost("git.novox.be")
seen["resolved"] = err == nil
c, err := net.DialTimeout("tcp", %q, 2*time.Second)
if err == nil {
c.Close()
}
seen["tcp"] = err == nil
raw, _ := json.Marshal(seen)
var spec struct {
Step string ` + "`json:\"step\"`" + `
}
json.Unmarshal([]byte(os.Args[2]), &spec)
out, _ := json.Marshal(map[string]any{"result": map[string]any{"level": spec.Step, "passed": true, "tests": 42,
"packages": []any{}, "why": "mark " + string(raw)}})
fmt.Println(string(out))
}
`
// 1898/AC4 (ADR 0535 §1): a tree whose mesh-build-go program answers a mark: the unit level with the runner workspace
// compiles that program and runs it shut off, answers its mark judged by the candidate at the tree's commit, and the
// program reached no key, no login, no name and no socket of the machine.
func TestTheUnitLevelUnderTheWorkspaceRunnerRunsTheTreesProgramShutOff(t *testing.T) {
needTools(t, "the candidate's program shut off", "go", "git", "bwrap")
home := t.TempDir()
key, login := filepath.Join(home, ".ssh", "mesh_ed25519"), filepath.Join(home, "broker")
for _, f := range []string{key, login} {
if err := os.MkdirAll(filepath.Dir(f), 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(f, []byte("secret"), 0o600); err != nil {
t.Fatal(err)
}
}
tcp, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer tcp.Close()
work, err := os.MkdirTemp("", "w")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.RemoveAll(work) })
tree := filepath.Join(work, "ws1", "tree")
dir := filepath.Join(tree, candidateModule)
if err := os.MkdirAll(filepath.Join(dir, "cmd", "mesh-build-go"), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module meshbuildgo\n\ngo 1.25.0\n"), 0o644); err != nil {
t.Fatal(err)
}
src := fmt.Sprintf(candidateMainSource, key, login, tcp.Addr().String())
if err := os.WriteFile(filepath.Join(dir, "cmd", "mesh-build-go", "main.go"), []byte(src), 0o644); err != nil {
t.Fatal(err)
}
for _, c := range [][]string{{"init", "-q"}, {"add", "-A"}, {"-c", "user.email=a@b", "-c", "user.name=x", "commit", "-qm", "x"}} {
if out, err := exec.Command("git", append([]string{"-C", tree}, c...)...).CombinedOutput(); err != nil {
t.Fatalf("git %v: %v %s", c, err, out)
}
}
head, err := exec.Command("git", "-C", tree, "rev-parse", "HEAD").Output()
if err != nil {
t.Fatal(err)
}
m := &Module{Recipe: Current(), Home: home, Work: work, Cache: t.TempDir()}
m.Run = StepRunner(home)
out, err := m.test(context.Background(), map[string]any{"workspace": "ws1", "level": LevelUnit, "runner": RunnerWorkspace})
if err != nil {
t.Fatal(err)
}
res := out.(*TestResult)
if res.Tests != 42 || !strings.HasPrefix(res.Why, "mark ") {
t.Fatalf("result = %+v, want the candidate's mark", res)
}
if want := "candidate " + strings.TrimSpace(string(head)); res.JudgedBy != want {
t.Errorf("judged by %q, want %q", res.JudgedBy, want)
}
var seen map[string]bool
if err := json.Unmarshal([]byte(strings.TrimPrefix(res.Why, "mark ")), &seen); err != nil || len(seen) != 4 {
t.Fatalf("the program said %q: %v", res.Why, err)
}
for what, reached := range seen {
if reached {
t.Errorf("the candidate's program reached %s", what)
}
}
}