rastrillo / fiesta Public

Pin the harness's prompt failure on a browser that cannot start

Review of Task 3, Minor 8.

The boot change in New (cancel the contexts yourself when the browser
never started; register chromedp.Cancel only once it has) had only
the commit body and a comment to defend it, and a sync from
rastrillo's harness could put the hang back. The test runs this
binary again on TestScreenPassesACleanPage with FIESTA_CHROME set to
nothing, in a subprocess because the hang holds the whole binary,
and asserts a failure inside 30 seconds that says "harness: browser
boot:" and "/nonexistent" and never "test timed out".

Green first run (0.01s). Red against the SEED shape of New, put back
in a scratch copy: "the failed boot took 30.037s to fail, over the
30s budget: the hang is back", the two missing phrases, and "the
subprocess hit go test's alarm instead of failing". Reverted.
Human

Prompt

Task 3 fix round 1: the review at task-3-review.md, items Important 1 and Minor 1 to 8, as the coordinator ruled them.

From this commit's own message. The prompt ledger has no entries for it — which is what a squashed, rebased or amended commit looks like, and what every commit looks like in a repo without a ledger.

Summary

Adds TestAFailedBootFailsPromptly, which reruns TestScreenPassesACleanPage in a subprocess with FIESTA_CHROME=/nonexistent and requires it to fail by the boot's own name inside 30 seconds.
Agent notes
The test calls ChromePath first, so it fails or skips by the same policy as every drive even though the subprocess needs no browser.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 6777d089e2f5d8d2fd863b24a93172bd49d4a40a parent 6a5cd06
1 file changed, +52 −0
  • internal/harness/boot_test.go +52 −0

CI passed — run details

CI log
=== gofmt ===
=== no-rastrillo ===
go test -run '^(TestFiestaNeverImportsRastrillo|TestThePublicPackagesLinkNoBrowser)$' -count=1 .
ok  	amadan.net/rastrillo/fiesta	0.065s
=== suite ===
--- shard 0 of 6 ---
==> shard 0 of 6: make root
go build ./...
go vet ./...
FIESTA_TEST_REQUIRE_NODE=1 go test -count=1 ./...
ok  	amadan.net/rastrillo/fiesta	0.124s
ok  	amadan.net/rastrillo/fiesta/check	0.035s
ok  	amadan.net/rastrillo/fiesta/internal/axe	0.006s
?   	amadan.net/rastrillo/fiesta/internal/harness	[no test files]
ok  	amadan.net/rastrillo/fiesta/internal/markup	0.007s
ok  	amadan.net/rastrillo/fiesta/internal/nodetest	0.276s
--- shard 1 of 6 ---
==> shard 1 of 6: make staticcheck
go run honnef.co/go/tools/cmd/staticcheck@v0.8.2 -tags browser ./...
--- shard 2 of 6 ---
==> shard 2 of 6: make govulncheck
go run golang.org/x/vuln/cmd/govulncheck@v1.8.0 ./...
No vulnerabilities found.
--- shard 3 of 6 ---
==> shard 3 of 6: make gitleaks
go run github.com/zricethezav/gitleaks/v8@v8.30.1 git --no-banner --redact --log-opts='--full-history --diff-filter=tuxdb HEAD' .
8:55PM INF Unknown SCM platform. Use --platform to include links in findings. host=amadan.net
8:55PM INF 1 commits scanned.
8:55PM INF scanned ~1738293 bytes (1.74 MB) in 1.44s
8:55PM INF no leaks found
--- shard 4 of 6 ---
==> shard 4 of 6: make browser.internal.harness
./hack/gotest.sh browser 'internal.harness'
gotest: on DOCS-T1, driving the harness's own choice (/usr/bin/chromium)
ok  	amadan.net/rastrillo/fiesta/internal/harness	6.706s
--- shard 5 of 6 ---
==> shard 5 of 6: make browser.check
./hack/gotest.sh browser 'check'
gotest: on DOCS-T1, driving the harness's own choice (/usr/bin/chromium)
ok  	amadan.net/rastrillo/fiesta/check	1.626s
diff --git a/internal/harness/boot_test.go b/internal/harness/boot_test.go
new file mode 100644
index 0000000..f222fb1
--- /dev/null
+++ b/internal/harness/boot_test.go
@@ -0,0 +1,52 @@
+//go:build browser
+
+package harness
+
+import (
+ "os"
+ "os/exec"
+ "strings"
+ "testing"
+ "time"
+)
+
+// bootBudget is how long a failed boot may take to fail. The real
+// failure takes milliseconds; the hang this guards against lasted
+// until go test's own ten-minute alarm.
+const bootBudget = 30 * time.Second
+
+// TestAFailedBootFailsPromptly pins the boot path in New. With a
+// browser that cannot start, chromedp's graceful Cancel waits forever
+// on a process that never existed (the context watcher takes the one
+// token Cancel would have read), so New must tear the contexts down
+// itself and only register Cancel once a browser is up. Measured in a
+// subprocess, because the hang holds the whole test binary: this
+// binary runs TestScreenPassesACleanPage again with FIESTA_CHROME
+// pointing at nothing, and the run has to fail, by the boot's own
+// name, inside the budget.
+//
+// ChromePath first: the subprocess needs no browser, but the policy
+// does. With none, this fails as every drive does, or skips when
+// FIESTA_BROWSER_OPTIONAL says so.
+func TestAFailedBootFailsPromptly(t *testing.T) {
+ ChromePath(t)
+ cmd := exec.Command(os.Args[0], "-test.run=^TestScreenPassesACleanPage$", "-test.count=1", "-test.timeout="+bootBudget.String())
+ cmd.Env = append(os.Environ(), "FIESTA_CHROME=/nonexistent", "FIESTA_BROWSER_OPTIONAL=")
+ start := time.Now()
+ out, err := cmd.CombinedOutput()
+ took := time.Since(start)
+ if err == nil {
+ t.Fatalf("a boot with FIESTA_CHROME=/nonexistent passed:\n%s", out)
+ }
+ if took > bootBudget {
+ t.Errorf("the failed boot took %s to fail, over the %s budget: the hang is back", took, bootBudget)
+ }
+ for _, want := range []string{"harness: browser boot:", "/nonexistent"} {
+ if !strings.Contains(string(out), want) {
+ t.Errorf("the failure does not say %q; the boot did not fail as itself:\n%s", want, out)
+ }
+ }
+ if strings.Contains(string(out), "test timed out") {
+ t.Errorf("the subprocess hit go test's alarm instead of failing:\n%s", out)
+ }
+}