Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 35 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1674,21 +1674,44 @@ jobs:
# Run the darwin-tagged unit suite for the shell entrypoint — this is
# the ONLY lane that executes it. The moon `compass-go:test` lane runs
# untagged (`go test ./...`), which compiles the non-gtk4 stub and
# excludes main_test.go; the gtk4-e2e lane compiles the gtk4 build but
# `-run E2E`-filters, so it never executes TestDistDirForExecutable.
# That test defends the .app dist-resolution contract (the resolver
# returns Contents/Resources/dist under a Contents/MacOS executable,
# else dist beside it), which is exactly the behavioral change this
# lane ships — so its regression guard lives here or nowhere.
# excludes main_test.go, machine_test.go and embedded_test.go; the
# gtk4-e2e lane compiles the gtk4 build but `-run E2E`-filters, so it
# never reaches any of them. These tests defend contracts that ARE the
# darwin behaviour this lane ships, so their regression guards live
# here or nowhere:
# - DistDirForExecutable: the .app dist-resolution contract
# (Contents/Resources/dist under a Contents/MacOS executable,
# else dist beside it).
# - Machine*/EnsureMachine*: the podman-machine probe + ensure step,
# including that an unclassifiable CLI answer provisions nothing.
# - RealPreflightDeps*/ClassifyPreflight*: that the darwin machine
# adapter is actually WIRED and that an unmet machine check is
# fatal. This is the pair that catches the silent-skip regression
# (a nil adapter making the check vanish into an all-green
# preflight), so it is the last thing that should run nowhere.
# - BringUpTimeout*: that darwin keeps a window a cold
# `podman machine init` can fit inside.
# `-run` alone exits 0 when it matches nothing (a rename → false
# green), so require the test's own PASS line — a rename or skip reds.
# green), so require each group's own PASS line — a rename or skip
# reds. The filter is explicit rather than the whole package because
# the package also holds GUI E2E tests that need a display.
CGO_ENABLED=1 go -C go test -trimpath \
-run 'TestDistDirForExecutable' -count=1 -v \
-run 'TestDistDirForExecutable|TestMachineReady|TestEnsureMachineReady|TestMachineResourceFloorIsExplicit|TestRealPreflightDeps|TestClassifyPreflight|TestBringUpTimeout' \
-count=1 -v \
./cmd/compass-app/ | tee /tmp/darwin-unit.log
grep -q '^--- PASS: TestDistDirForExecutable' /tmp/darwin-unit.log || {
echo "::error::darwin: TestDistDirForExecutable did not run+pass (renamed or skipped?)"
exit 1
}
for t in TestDistDirForExecutable \
TestMachineReadyRunning \
TestMachineReadyNoMachine \
TestEnsureMachineReadyNoMachineProvisions \
TestEnsureMachineReadyUnclassifiedDoesNotProvision \
TestRealPreflightDepsWiresDarwinMachineAdapter \
TestClassifyPreflightUnwiredDarwinMachineIsFatal \
TestBringUpTimeoutBudgetsDarwinColdProvisioning; do
grep -q "^--- PASS: $t" /tmp/darwin-unit.log || {
echo "::error::darwin: $t did not run+pass (renamed or skipped?)"
exit 1
}
done

# The UI dist the .app stages into Contents/Resources/dist.
moon run compass-ui:build
Expand Down
32 changes: 24 additions & 8 deletions go/cmd/compass-app/embedded.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,21 +373,37 @@ func resolveImage(flagValue string) string {
// the app-side DSN duplicate are gone (§A2 reconciliation 1): under DL-260
// postgres is a container the stack itself starts, so a pre-`up` reachability
// probe has no signal on the cold-start path — `up`-Ready is the DB
// verification. On darwin the machine adapter is wired by T-6; a nil
// MachineReady here leaves that check absent until then (design §A5).
// verification. MachineReady comes from the per-OS machineReadyAdapter: on
// darwin it is the podman-machine ensure step (provision or start the Linux VM,
// then re-probe — design §A5), and on linux it is nil because there is no
// machine. The preflight core keys the check off GOOS and FAILS on darwin when
// the adapter is nil, so this wiring cannot regress into a silently-skipped
// check.
func realPreflight(image string) func(ctx context.Context) error {
deps := preflight.Deps{
GOOS: runtime.GOOS,
PodmanRootless: podmanRootless,
PodmanVersion: podmanVersionAtLeastFloor,
ImagePresent: imagePresent,
}
deps := realPreflightDeps(runtime.GOOS)
params := preflight.Params{AgentImage: image}
return func(ctx context.Context) error {
return classifyPreflight(deps.Run(ctx, params))
}
}

// realPreflightDeps assembles the Deps literal for the given host OS. It takes
// goos as an argument, rather than reading runtime.GOOS itself, so a test
// running on ANY host can assert what the darwin wiring carries — the machine
// check going missing on darwin is the exact regression this seam exists to
// catch, and it is unobservable from a linux test if the builder resolves its
// own OS. The one goos value feeds both the core's check selection and the
// machine adapter, so the two cannot disagree about which host this is.
func realPreflightDeps(goos string) preflight.Deps {
return preflight.Deps{
GOOS: goos,
PodmanRootless: podmanRootless,
PodmanVersion: podmanVersionAtLeastFloor,
MachineReady: machineReadyAdapter(goos),
ImagePresent: imagePresent,
}
}

// classifyPreflight splits the preflight results by severity at the wiring
// boundary and returns only the FATAL failures folded into one legible error
// (nil when none are fatal).
Expand Down
66 changes: 66 additions & 0 deletions go/cmd/compass-app/embedded_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,72 @@ func TestClassifyPreflightHostCapFatalEvenWithAdvisoryUnmet(t *testing.T) {
}
}

// TestRealPreflightDepsWiresDarwinMachineAdapter is the regression guard for the
// silent skip: realPreflightDeps must carry a non-nil MachineReady on darwin. It
// asserts the WIRING, not a probe result, so it fails on a linux CI host the
// moment the adapter is dropped from the Deps literal — the defect was
// invisible precisely because a missing adapter produced no failing check.
func TestRealPreflightDepsWiresDarwinMachineAdapter(t *testing.T) {
deps := realPreflightDeps("darwin")
if deps.MachineReady == nil {
t.Fatal("realPreflightDeps left MachineReady nil on darwin; the machine check would be a wiring failure")
}
if deps.GOOS != "darwin" {
t.Errorf("GOOS = %q, want the injected darwin", deps.GOOS)
}
}

// TestRealPreflightDepsLeavesLinuxMachineUnwired: linux podman is native, so
// there is no machine adapter — the core keys the check off GOOS and omits it
// here. This pins that closing the darwin hole did not add a bogus linux check.
// It asserts only the WIRING: running deps here would shell the real podman
// probes, and the core package already owns the absent-on-linux assertion
// hermetically (preflight.TestRunMachineCheckAbsentOnLinux).
func TestRealPreflightDepsLeavesLinuxMachineUnwired(t *testing.T) {
deps := realPreflightDeps("linux")
if deps.MachineReady != nil {
t.Fatal("realPreflightDeps wired a machine adapter on linux; there is no machine to check")
}
if deps.GOOS != "linux" {
t.Errorf("GOOS = %q, want the injected linux", deps.GOOS)
}
}

// TestClassifyPreflightMachineUnmetIsFatal verifies — rather than assumes — that
// a failing machine check reaches the FATAL fold. classifyPreflight special-cases
// only CheckImage as advisory, so the machine check falls to the default arm;
// this exercises that path end-to-end so the doc comment's "fatal on darwin"
// claim is enforced by a test rather than by reading the switch.
func TestClassifyPreflightMachineUnmetIsFatal(t *testing.T) {
machineErr := errors.New("no podman machine exists")
deps := classifyDeps()
deps.GOOS = "darwin"
deps.MachineReady = func(context.Context) error { return machineErr }
err := classify(t, deps)
if err == nil {
t.Fatal("machine unmet on darwin: classify err = nil, want fatal")
}
if !strings.Contains(err.Error(), machineErr.Error()) {
t.Errorf("fatal error %q does not carry the machine failure", err.Error())
}
}

// TestClassifyPreflightUnwiredDarwinMachineIsFatal: the wiring defect itself is
// fatal, not advisory — a darwin build whose machine adapter went missing
// refuses to launch instead of proceeding on an unverified host.
func TestClassifyPreflightUnwiredDarwinMachineIsFatal(t *testing.T) {
deps := classifyDeps()
deps.GOOS = "darwin"
deps.MachineReady = nil
err := classify(t, deps)
if err == nil {
t.Fatal("unwired machine adapter on darwin: classify err = nil, want fatal")
}
if !strings.Contains(err.Error(), "no podman machine adapter is wired") {
t.Errorf("fatal error %q does not name the wiring defect", err.Error())
}
}

// TestRunStackUpDeadlineExceededNamesBringUpWindow: when the child fails because
// the context deadline was exceeded, the error names the bring-up window (the
// likely cause) rather than surfacing a bare deadline error. Driven with an
Expand Down
Loading
Loading