internal/state ships test-only constructors in the production build #111
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
internal/state/state_test_helper.gois a normal, non-test Go file inpackage state. It exports two constructors that exist purely for tests:Because the filename does not end in
_test.go, the Go toolchain compiles it into the shippeddnswatcherbinary. Test scaffolding becomes part of the production package's public API — anything importinginternal/statecan callNewForTest()and get a*Statethat bypasses the fx lifecycle entirely.NewForTest()in particular hardcodesDataDir: "", which makesStatePath()resolve to/state.json. Any caller that reachesSave()on such an instance tries to write to the filesystem root.This repo already knows the right pattern
Three sibling packages already solve exactly this problem the idiomatic way, with a file named
export_test.go:internal/config/export_test.gointernal/handlers/export_test.gointernal/notify/export_test.goA
_test.gofile is compiled only during testing, yet its exported identifiers are still visible to the externalpackage state_test. So the helpers keep working, and they stop shipping.internal/stateis the lone holdout.Definition of done
internal/state/state_test_helper.gois renamed tointernal/state/export_test.go(usegit mvso history follows), with no change to the helpers' behaviour or signatures.internal/state/state_test.go(externalpackage state_test) still compiles and passes unchanged — it uses both helpers today, atstate_test.go:90,99,127and eightNewForTest()call sites around:635-1268.make buildmust succeed, andNewForTestmust not appear in the built binary's symbols.NewForTest()'sDataDir: ""default should instead be a required parameter, so no caller can accidentally construct aStatethat writes to/. If you change it, update the call sites; if you decide to leave it, say why in the PR description. Either answer is acceptable — an unexamined one is not.make checkgreen;TODO.mdupdated in the same commit.The finishing commit's title must end with
(closes #N)referencing this issue.Sequencing
Land this after PR #97 merges. #97 rewrites
internal/watcher/watcher_test.goand switches it fromstate.NewForTest()tostate.NewForTestWithDataDir(cfg.DataDir)— which is what stops the watcher suite from sprayingfailed to save state ... open /state.json.tmp: permission deniederrors through everymake checkrun. Starting this before #97 lands will conflict and will make it look like the write-to-root problem is unfixed. Do not touch PR #97.