ateapi: fail DB tests instead of skipping unless Docker is absent - #1345
Closed
Joe Betz (jpbetz) wants to merge 1 commit into
Closed
ateapi: fail DB tests instead of skipping unless Docker is absent#1345Joe Betz (jpbetz) wants to merge 1 commit into
Joe Betz (jpbetz) wants to merge 1 commit into
Conversation
requireAdminPool and requirePool turned any PostgreSQL testcontainer startup error into t.Skipf with a message claiming Docker was required, without ever checking for Docker. A reaper timeout, an image pull failure or a daemon hiccup therefore removed a whole package of database tests from the run while the job still reported success: a CI run has already gone green with the controlapi DB suite skipped. Route both call sites through one helper that decides fail-versus-skip. In CI the helper always fails, because the runners have Docker and any container error there is a real failure. Locally it skips only when a cheap preflight proves Docker is absent, and fails on everything else, so a container problem stays visible. The preflight mirrors how testcontainers finds the daemon: it walks its own list and uses the first endpoint that answers, so an unset or dead DOCKER_HOST is not on its own proof that Docker is missing. Only when neither DOCKER_HOST nor any of the default and rootless socket paths answers does the preflight report absence, and anything it cannot decide counts as present, because only proven absence justifies dropping a test. The verdict is computed once per run, since a daemon does not come and go mid-binary and every failing test asks. The helper lives in dockerenv rather than storetest because storetest imports atepg, so the atepg tests cannot import storetest back; dockerenv is the leaf package both already share. Test semantics are otherwise unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LYXeYhZaGKwgY57jvfMMYF
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
Contributor
Author
|
Sorry, I need to self review this first. |
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
requireAdminPool(cmd/ateapi/internal/store/storetest/storetest.go) andrequirePool(
cmd/ateapi/internal/store/atepg/atepg_test.go) turned any PostgreSQL testcontainer startuperror into
without ever checking whether Docker was actually there. Both call sites now route through one
helper,
dockerenv.FailOrSkip, that decides fail-versus-skip in a single place:CIorGITHUB_ACTIONSset) it always fails. The runners have Docker, so anycontainer error there is a real failure.
everything else.
The preflight (
dockerenv.Unavailable) mirrors how testcontainers-go actually finds the daemon:testcontainers walks its own list (
tc.hostproperty,DOCKER_HOST, the Docker context,/var/run/docker.sock, thedocker.hostproperty, the rootless socket paths) and uses the firstendpoint that answers. So an unset or dead
DOCKER_HOSTis not on its own proof that Docker ismissing — the preflight also probes the default and rootless socket paths, and only reports
absence when none of them answers. Anything it cannot decide (a scheme it cannot dial, a daemon
that accepts the connection but misbehaves) counts as present, because only proven absence
justifies dropping a test. The verdict is memoized: a daemon does not come and go mid-binary, and
every failing test in a ~150-test package asks.
Why
CI has already gone green with the controlapi DB suite skipped — 154 tests silently removed from
the run while the job reported success. A reaper timeout, an image pull failure or a daemon hiccup
was indistinguishable from "this developer has no Docker", and the message asserted the second
without checking. This is the fail-not-skip follow-up discussed on #1235 (see also #1230).
Placement note for reviewers
The helper lives in
cmd/ateapi/internal/store/dockerenv, not instoretest, becausestoretestimports
atepg(forSetupPostgresPersistence) andatepg_test.gois an in-package test — soimporting
storetestback is a hard cycle.dockerenvis the leaf package both fixtures alreadyshare, and it already owns the other half of the Docker story (
Configure). Its package doc nowsays it owns both.
Files
No vendor changes, no dependency changes, no workflow changes, no other test semantics touched
(the 30x500ms ping loops, the
sync.Oncefixtures and theTestMainshutdown paths areunchanged).
Testing done
Unit tests:
failorskip_test.gotable-tests the policy against a faketesting.TB(CI set -> fatal even without Docker; CI set with Docker -> fatal; Docker absent locally -> skip;
any other error with Docker present -> fatal), the preflight against an injectable dialer
(12 cases covering
DOCKER_HOSTunix/tcp/npipe/no-scheme, missing sockets, and the fallback tothe default socket paths), and the memoization.
All three policy branches were proved end-to-end on real test binaries at both call sites, not
just in the unit test. Note that the obvious command —
DOCKER_HOST=tcp://127.0.0.1:1 CI=true go test ...— cannot prove the fatal path: testcontainers ignores the unreachableDOCKER_HOSTand falls through to/var/run/docker.sock, so the container starts and the testpasses ("Resolved Docker Host: unix:///var/run/docker.sock", ok 1.979s). The real proofs used a
stripped environment (
env -i, nodockerCLI onPATH) and, for the no-Docker-at-all cases, anunprivileged mount namespace (
unshare -rmwith a tmpfs over/run) so the default socket reallyis gone:
CI=true, no docker CLI, no socketDOCKER_HOST,DOCKER_API_VERSION=0.1to force a container errorCI=true, no docker CLIDOCKER_HOST+ live default socket + forced container errorGITHUB_ACTIONS=truewithCIunset, no DockerF and G are the review's two should-fix findings: before them, a machine with a working daemon but
no
dockerCLI (rootless, socket-mounted dev containers, a corrupt context store), or with a staleDOCKER_HOSTexported, would still have silently skipped every DB test — the same class of holethis change exists to close, just narrower.
Open questions for review
-voutput to proveTestContractSuiteran" step in.github/workflows/pr-workflow.yamlwas left out. With everycontainer error fatal under CI, "green with the DB suites skipped" is already unreachable, and a
grep step would need either a second ~2-minute run of the DB packages or a
PIPESTATUSpipelinethat risks swallowing the
go testexit code. Happy to add it if you want belt and braces.testcontainers' own waits used to decide skip-or-run; they now decide fail-or-pass. If CI starts
flaking on slow container pulls, that budget is the knob — but widening it pre-emptively would
be guessing. Out of scope here.
CI/GITHUB_ACTIONS.pr-workflow.yamlrunsgo test -race -v ./...directly onubuntu-latest, where the runner sets both, andhack/run-root-tests.shuses
sudo -E env "PATH=${PATH}"so they survive the privilege change. A future job that runsgo testinside a container without forwarding either would silently degrade to the oldskip-on-anything behavior. Accepting both variables is the cheap mitigation; a hard opt-out
(fail unless explicitly told otherwise) would be the strict version if you want it.
FailOrSkip's messages are now generic ("container startup failed ...")and the two call sites wrap their own error with
fmt.Errorf("PostgreSQL testcontainer: %w", containerErr), so the next container fixture in the tree inherits the policy without inheritinga Postgres-specific message. Shout if you would rather keep the subject in the helper.
🤖 Generated with Claude Code
https://claude.ai/code/session_01LYXeYhZaGKwgY57jvfMMYF