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.
128 lines
4.0 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|