4 Commits
Author SHA1 Message Date
jschoubben 3a9deb8cdb Merge pull request 'R4g (mesh-lab): the checker judges the lab; its scripts become tests (design 47)' (#108) from feat/r4g into main
Reviewed-on: #108
2026-10-23 17:54:01 +00:00
jschoubben 4653b17e18 Delete merge-check.sh; the type check runs before npm test, where the Node level runs it (issue 1295, S5 root; ADR 0529)
mesh/repo-check head 4653b17e18a7 merged into trunk 72ea2988be50 — fail: mesh/repo-check is required here and this head defines no merge-check.sh — a perso…
mesh/delivery delivered
mesh/delivery-group group feat/r4g merged: every member is merged
mesh/check pass 4653b17e18a7 on 3a9deb8cdbd9 | unit 39 run, 5 kept, 1 skipped | integration 39 run, 12 kept, 1 skipped | composition passed
2026-10-23 15:37:13 +02:00
jschoubben bdb927f83d Delete asks/merge-check.sh; the proof against the clones is an integration test, the shell syntax a unit test (issue 1295, S5 asks; ADR 0529) 2026-10-23 15:37:08 +02:00
jschoubben 49ee611847 Delete replays/merge-check.sh; tests that raise containers or a bus move to the integration level (issue 1295, S5 replays; ADR 0529)
gofmt and vet are the Go build module's; the unit level keeps the register and the bed's reading of a change.
2026-10-23 15:36:51 +02:00
14 changed files with 104 additions and 175 deletions
+17
View File
@@ -0,0 +1,17 @@
package asks
import (
"os/exec"
"testing"
)
// live-acceptance.sh is run by a person at the terminal and by no test: its syntax is judged here, as the lab's script
// judged it with `sh -n` (novox/hq ADR 0529).
func TestTheLiveAcceptanceScriptIsValidShell(t *testing.T) {
if _, err := exec.LookPath("sh"); err != nil {
t.Skip("NOT TESTED HERE: no sh on the path")
}
if out, err := exec.Command("sh", "-n", "live-acceptance.sh").CombinedOutput(); err != nil {
t.Fatalf("live-acceptance.sh is not valid shell: %v\n%s", err, out)
}
}
-37
View File
@@ -1,37 +0,0 @@
#!/bin/sh
# mesh-check-toolchain: go
#
# The proof of the operator's answers (novox/hq ADR 0259), the Go part of the lab's own check: run by the
# build seat in the mesh's Go toolchain, its own container, because the lab's merge-check.sh declares it
# (`# mesh-check-also: go asks/merge-check.sh`) — and by hand: `sh asks/merge-check.sh`.
#
# Formatted, vetted, and run against the clones beside the lab: on the build seat those are the controller,
# the runtime and the catalogue **the mesh runs**, so every pull request of the lab proves the answers the
# operator would get today. By hand, MESH_LAB_REPOS, or MESH_LAB_CONTROLLER, MESH_LAB_TOOLS and
# MESH_LAB_CATALOGUE, point it at branches under review. It takes about four minutes when it runs.
#
# **Said, never passed silently**: where a clone is missing, or the controller predates the proof's fixture,
# it says NOT RUN and why, in capitals, and passes; a proof that runs and fails, fails the check.
set -eu
cd "$(dirname "$0")"
export GOPRIVATE=git.novox.be
unformatted=$(gofmt -l .)
if [ -n "$unformatted" ]; then
echo "not gofmt'd:"
echo "$unformatted"
exit 1
fi
sh -n live-acceptance.sh
go vet ./...
out=$(mktemp)
status=0
go test -count=1 -timeout 25m -v -run '^(TestTheOperatorsAnswerEndToEnd|TestTheFakePhoneShowsHTMLAsTelegramDoes)$' . >"$out" 2>&1 || status=$?
grep -E '^(--- | --- |ok|FAIL|PASS)' "$out" || true
if grep -q 'NOT RUN' "$out"; then
echo "NOT RUN: THE PROOF OF THE OPERATOR'S ANSWERS DID NOT RUN HERE:"
grep -o 'NOT RUN: .*' "$out" | head -3
fi
[ "$status" -eq 0 ] || tail -60 "$out"
rm -f "$out"
exit "$status"
+2
View File
@@ -1,3 +1,5 @@
//go:build integration
package asks
import (
-37
View File
@@ -1,37 +0,0 @@
#!/bin/sh
# mesh-check-toolchain: typescript
# mesh-check-also: go replays/merge-check.sh
# mesh-check-also: go asks/merge-check.sh
#
# The lab's own check (novox/hq ADR 0237 as amended): the second layer of a pull request's merge check,
# `mesh/repo-check`, run by the build seat — and by hand.
#
# The mesh builds no module from the lab, so no gate runs for its pull requests (`mesh/merge-gate` says the
# change touches no module of the graph); this is all that is checked before a change to it merges. The lab
# is in two languages, and each part runs in its own toolchain's container (novox/hq issue 302):
#
# - this script, in the TypeScript toolchain: installed from the lock file, type-checked, sources and
# tests, and the unit suite;
# - replays/merge-check.sh, in the Go toolchain, declared above: the replays register and its prover
# formatted, vetted and compiled, and the register's own tests;
# - asks/merge-check.sh, in the Go toolchain, declared above: the proof of the operator's answers, run
# against the controller, runtime and catalogue cloned beside the lab (novox/hq ADR 0259).
#
# **Said, never passed silently**: the integration suite raises a lab on a workstation, and the replays
# raise containers through the container runtime — neither is given to code nobody has approved. The
# replays run from the lab's main, reviewed, in every gate of a core change; a change to them is proven by
# `go run ./replays/cmd/prove` by hand before it merges.
set -eu
npm ci --no-audit --no-fund --loglevel=error
npm run typecheck
npm test
# By hand, with Go at hand, the Go part runs here too; on the build seat it runs in the Go toolchain.
if command -v go >/dev/null 2>&1; then
sh replays/merge-check.sh
sh asks/merge-check.sh
else
echo "replays/ and asks/ (Go) are checked by their merge-check.sh, which the build seat runs in the Go toolchain"
fi
echo "NOT RUN HERE: the integration suite and the replays — they need a lab and the container runtime"
+1
View File
@@ -8,6 +8,7 @@
},
"scripts": {
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
"pretest": "npm run typecheck",
"test": "node --test --experimental-strip-types 'test/*.test.ts'",
"test:integration": "node --experimental-strip-types src/cli.ts suite",
"check": "npm run typecheck && npm test && npm run test:integration",
+67
View File
@@ -0,0 +1,67 @@
//go:build integration
package replays
import (
"context"
"fmt"
"strings"
"testing"
"time"
)
// **R-studio — a check that asks an address the program does not bind fails the bed, not a machine**
// (novox/hq ADR 0240 Phase D, done when). The hosted database suite's studio binds the address the runtime
// puts in HOSTNAME, so it answered only on its network address while its image's own check asked localhost:
// unhealthy for ever while working, until the module set HOSTNAME=0.0.0.0. Adopted by name without proof, it
// would have put back a good build.
//
// The replay is that image in miniature: a web server that binds $HOSTNAME's address, and an image check that
// asks localhost. Adopted as the module declared it before the fix, the bed fails it; with the fix, it proves
// it. The image is built here and removed after, with everything the bed made.
func TestBedFailsTheStudiosFalseUnhealthy(t *testing.T) {
docker, ok := DockerFromEnv()
if !ok {
t.Skip("no container runtime: the studio is a container")
}
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Minute)
defer cancel()
if err := docker.Pull(ctx, "busybox:1.36"); err != nil {
t.Fatal(err)
}
tag := fmt.Sprintf("mesh-replay-studio:%d", time.Now().UnixNano())
if err := docker.Build(ctx, tag, StudioDockerfile); err != nil {
t.Fatal(err)
}
defer docker.RemoveImage(context.Background(), tag)
was := BedFloor
BedFloor = 15 * time.Second
defer func() { BedFloor = was }()
studio := func(env map[string]any) BedResource {
r := map[string]any{"id": "studio", "type": "container", "name": "supabase-studio", "image": tag,
"health": map[string]any{"kind": "runtime", "grace": "10s"}}
if env != nil {
r["env"] = env
}
return BedResource{Module: "supabase", ID: "studio", Resource: r, Listens: map[string]int{}}
}
before := docker.Prove(ctx, studio(nil), nil)
if !before.Failed || !strings.Contains(before.Said, "does not see it") {
t.Fatalf("the studio's false unhealthy was not failed on the bed: %+v", before)
}
t.Logf("before the fix: %s", before.Said)
after := docker.Prove(ctx, studio(map[string]any{"HOSTNAME": "0.0.0.0"}), nil)
if !after.Proved {
t.Fatalf("the studio with HOSTNAME=0.0.0.0 was not proved: %+v", after)
}
t.Logf("with the fix: %s", after.Said)
}
// StudioDockerfile is the studio in miniature: it serves on the address HOSTNAME names, and its own check
// asks localhost, as the studio's image does.
const StudioDockerfile = `FROM busybox:1.36
RUN mkdir -p /www && echo studio > /www/index.html
HEALTHCHECK --interval=5s --timeout=2s --retries=2 CMD wget -q -O /dev/null http://localhost:3000/ || exit 1
CMD addr=$(grep -w "$HOSTNAME" /etc/hosts | head -n 1 | cut -f 1); exec httpd -f -p "${addr:-$HOSTNAME}:3000" -h /www
`
+7 -57
View File
@@ -2,7 +2,6 @@ package replays
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
@@ -58,62 +57,6 @@ func TestBedProvesEveryChangedHealthCheck(t *testing.T) {
}
}
// **R-studio — a check that asks an address the program does not bind fails the bed, not a machine**
// (novox/hq ADR 0240 Phase D, done when). The hosted database suite's studio binds the address the runtime
// puts in HOSTNAME, so it answered only on its network address while its image's own check asked localhost:
// unhealthy for ever while working, until the module set HOSTNAME=0.0.0.0. Adopted by name without proof, it
// would have put back a good build.
//
// The replay is that image in miniature: a web server that binds $HOSTNAME's address, and an image check that
// asks localhost. Adopted as the module declared it before the fix, the bed fails it; with the fix, it proves
// it. The image is built here and removed after, with everything the bed made.
func TestBedFailsTheStudiosFalseUnhealthy(t *testing.T) {
docker, ok := DockerFromEnv()
if !ok {
t.Skip("no container runtime: the studio is a container")
}
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Minute)
defer cancel()
if err := docker.Pull(ctx, "busybox:1.36"); err != nil {
t.Fatal(err)
}
tag := fmt.Sprintf("mesh-replay-studio:%d", time.Now().UnixNano())
if err := docker.Build(ctx, tag, StudioDockerfile); err != nil {
t.Fatal(err)
}
defer docker.RemoveImage(context.Background(), tag)
was := BedFloor
BedFloor = 15 * time.Second
defer func() { BedFloor = was }()
studio := func(env map[string]any) BedResource {
r := map[string]any{"id": "studio", "type": "container", "name": "supabase-studio", "image": tag,
"health": map[string]any{"kind": "runtime", "grace": "10s"}}
if env != nil {
r["env"] = env
}
return BedResource{Module: "supabase", ID: "studio", Resource: r, Listens: map[string]int{}}
}
before := docker.Prove(ctx, studio(nil), nil)
if !before.Failed || !strings.Contains(before.Said, "does not see it") {
t.Fatalf("the studio's false unhealthy was not failed on the bed: %+v", before)
}
t.Logf("before the fix: %s", before.Said)
after := docker.Prove(ctx, studio(map[string]any{"HOSTNAME": "0.0.0.0"}), nil)
if !after.Proved {
t.Fatalf("the studio with HOSTNAME=0.0.0.0 was not proved: %+v", after)
}
t.Logf("with the fix: %s", after.Said)
}
// StudioDockerfile is the studio in miniature: it serves on the address HOSTNAME names, and its own check
// asks localhost, as the studio's image does.
const StudioDockerfile = `FROM busybox:1.36
RUN mkdir -p /www && echo studio > /www/index.html
HEALTHCHECK --interval=5s --timeout=2s --retries=2 CMD wget -q -O /dev/null http://localhost:3000/ || exit 1
CMD addr=$(grep -w "$HOSTNAME" /etc/hosts | head -n 1 | cut -f 1); exec httpd -f -p "${addr:-$HOSTNAME}:3000" -h /www
`
// 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) {
@@ -159,3 +102,10 @@ func TestTheBedProvesWhatTheChangeTouches(t *testing.T) {
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
}
+2
View File
@@ -1,3 +1,5 @@
//go:build integration
package replays
import (
+2 -7
View File
@@ -1,3 +1,5 @@
//go:build integration
package replays
import (
@@ -111,13 +113,6 @@ func statementOf(path string) string {
return string(raw)
}
func orDefault(s, fallback string) string {
if s == "" {
return fallback
}
return s
}
func firstLineOf(s string) string {
line, _, _ := strings.Cut(strings.TrimSpace(s), "\n")
if len(line) > 400 {
-28
View File
@@ -1,28 +0,0 @@
#!/bin/sh
# mesh-check-toolchain: go
#
# The Go part of the lab's own check (novox/hq issue 302): the replays register and its prover, run by the
# build seat in the mesh's Go toolchain, its own container, because the lab's merge-check.sh declares it
# (`# mesh-check-also: go replays/merge-check.sh`) — and by hand, from anywhere:
# `sh replays/merge-check.sh`.
#
# 1. formatted and vetted — the register, the replays and the prover compile, tests included;
# 2. the tests that need nothing but Go and git: every replay registered whole (TestEveryReplayIsWhole),
# and the bed's reading of what a change touches.
#
# **Said, never passed silently**: the replays themselves raise containers and a bus of the release the mesh
# runs; this container holds no container runtime, so they are not run here. They run from the lab's main,
# reviewed, in the gate of every core change, and a change to one is proven by `go run ./cmd/prove` before
# it merges.
set -eu
cd "$(dirname "$0")"
unformatted=$(gofmt -l .)
if [ -n "$unformatted" ]; then
echo "not gofmt'd:"
echo "$unformatted"
exit 1
fi
go vet ./...
go test -count=1 -run '^(TestEveryReplayIsWhole|TestTheBedProvesWhatTheChangeTouches)$' .
echo "NOT RUN HERE: the replays themselves — they raise containers, and this container holds no container runtime"
-9
View File
@@ -649,15 +649,6 @@ var Register = []Replay{
"mesh-host member's validator, and passes",
Package: "./cmd/mesh-controller", Test: "TestAGroupsComposition",
Files: []string{"cmd/mesh-controller/replay867_test.go"}},
// mesh-config refused to propose mesh-issues' owners as carrying a secret: the SDK's CheckSecret judged a
// lowercase name with a digit (modules/asus-zephyrus-g14) whole and took it for random (mesh-sdk #52).
{ID: "R1439", Issue: 1439, Kind: InRepository, Repository: "mesh-sdk",
Fix: "7bf4a8053a497bdd33746d181b6fa11b211c0f94",
What: "a list of module names with digits in them was refused as a secret, so no owner could be proposed",
Asserts: "CheckSecret lets lowercase names with digits through, and the catalogue's owners list with them",
Module: "go", Package: "./outward", Test: "TestReplay1439",
Files: []string{"go/outward/replay1439_test.go", "go/outward/replay1439_owners.json"},
Home: "mesh-sdk", HomeRef: "ef76b9f2eac0cbbf94b1d4dcadb7a383723cd7a6"},
}
// Find is the replay of that id, or of that issue.
+2
View File
@@ -1,3 +1,5 @@
//go:build integration
package replays
import (
+2
View File
@@ -1,3 +1,5 @@
//go:build integration
package replays
import (
+2
View File
@@ -1,3 +1,5 @@
//go:build integration
package replays
import (