No test covers the CLI surface: exit codes, stream separation, or the TSV writers #16
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?
There is no
main_test.go. All 43 existing test functions call internal helpers —collectDupeGroups,collectTreeGroups,syncScan,hashRuns— and none of them ever callsrunScan,runReport,runTrees, ormain. The entire user-visible contract is untested.README-mandated behaviours with no coverage at all:
scanwith noPATHoperand,reportortreeswith a positional argument (README §Error handling).PATHoperand that does not exist, a missing database forreportandtrees.scanwrites nothing to stdout" (README §scan mode) — nothing asserts stdout is empty.These are also the behaviours most likely to regress silently, because nothing about them is exercised by the internal-helper tests.
Definition of done
main_test.gobuilds the binary once viaTestMain(or drives the cobra root command in-process, once #4 makes that possible withoutos.Exit) and, for each case, asserts the exit code, the exact stdout bytes, and the exact stderr line where the README specifies one.scancases additionally assert stdout is empty.script/test.make checkgreen.Best done after #4, which removes the
os.Exitcalls that currently make these paths untestable in-process.Three additions to this issue's scope, all surfaced by the independent review of #4 (merged as
2a055c0). They belong here rather than in their own issues because each is about making the CLI surface properly testable.1. Thread the output writers through the subcommands.
run(args []string, stderr io.Writer)now exists as a single in-process entry point, but it only routes cobra's own output. The subcommands still write to the package-globalos.Stdoutandos.Stderr, so a test that wants to assert on program output has to monkey-patchos.Stdout. The concrete cost today:TestRunScanSucceedsDespiteWarningscannot assert that the warning it is named for was actually emitted. Thread the writers down so tests can capture output by passing it in.2. Add a guard test for
runE. The exit-code classification added in #4 works because each subcommand is wired through arunEadapter that setsSilenceUsage/SilenceErrorsand wraps failures infatalError. That is opt-in: a future subcommand wired with a bareRunEwould have its runtime failures misclassified as exit 2 with a usage dump — precisely the regression #4 existed to prevent, reintroduced silently. Walkroot.Commands()in a test and assert every command goes through the adapter.3. Rename
TestRunMissingOperandIsFatalNotUsage. It covers a nonexistentPATHoperand, which is fatal (exit 1). "Missing operand" is README's phrase forscaninvoked with no operand at all, which is a usage error (exit 2) and is covered by a different test. Suggested:TestRunNonexistentPathIsFatalNotUsage.Item 1 in particular should be done before the rest of this issue's table-driven tests are written, since it determines whether they can assert on output directly or have to go through the filesystem.