Most of what #16 asks for was already tested through run(args, stdout, stderr) by later issues. This adds only what was missing.
Added:
TestRunMissingDatabaseIsFatal: report and trees with no database exit 1 with the exact "run sfdupes scan first" line and an empty stdout.
TestRunScanSucceedsDespiteWarnings builds its own tree and checks the skip warning and the 1 skipped summary, not only exit 0.
TestRunReportSucceeds and TestRunTreesSucceeds check the exact summary line on stderr.
The scan tests (scanOK, the failing-scan tests, both interrupted-scan tests) capture the process's own stdout with captureStdout, because scan is never given run's stdout writer. Any stdout write in scan now fails them.
TestRunFatalAfterOpenClosesDatabase takes its subcommands from the command tree, so a subcommand wired with a bare RunE instead of runE fails it (comment item 2).
Already covered, unchanged: the four exit-2 cases (TestRunUsageErrors); the nonexistent operand (the renamed test); header and rows of both reports (TestRunReportSucceeds, TestRunTreesSucceeds).
Judgement calls:
The runE guard checks behaviour (each subcommand, failing against a broken database, exits 1 without usage text) rather than comparing function values, which Go cannot do reliably. A future subcommand that never opens the database will need the test adjusted.
Writers are not threaded through the subcommands (item 1): capturing the process's own streams also catches a stray fmt.Println, which a passed-down writer would miss.
The skip-warning test skips under root, as makeReadOnly does; the Docker build runs tests unprivileged.
Model: opus-5-5
Most of what https://git.eeqj.de/sneak/sfdupes/issues/16 asks for was already tested through `run(args, stdout, stderr)` by later issues. This adds only what was missing.
Added:
- `TestRunMissingDatabaseIsFatal`: `report` and `trees` with no database exit 1 with the exact "run sfdupes scan first" line and an empty stdout.
- `TestRunScanSucceedsDespiteWarnings` builds its own tree and checks the skip warning and the `1 skipped` summary, not only exit 0.
- `TestRunReportSucceeds` and `TestRunTreesSucceeds` check the exact summary line on stderr.
- The `scan` tests (`scanOK`, the failing-`scan` tests, both interrupted-scan tests) capture the process's own stdout with `captureStdout`, because `scan` is never given `run`'s stdout writer. Any stdout write in `scan` now fails them.
- `TestRunFatalAfterOpenClosesDatabase` takes its subcommands from the command tree, so a subcommand wired with a bare `RunE` instead of `runE` fails it (comment item 2).
- `TestRunMissingOperandIsFatalNotUsage` renamed `TestRunNonexistentPathIsFatalNotUsage` (item 3).
Already covered, unchanged: the four exit-2 cases (`TestRunUsageErrors`); the nonexistent operand (the renamed test); header and rows of both reports (`TestRunReportSucceeds`, `TestRunTreesSucceeds`).
Judgement calls:
- The `runE` guard checks behaviour (each subcommand, failing against a broken database, exits 1 without usage text) rather than comparing function values, which Go cannot do reliably. A future subcommand that never opens the database will need the test adjusted.
- Writers are not threaded through the subcommands (item 1): capturing the process's own streams also catches a stray `fmt.Println`, which a passed-down writer would miss.
- The skip-warning test skips under root, as `makeReadOnly` does; the Docker build runs tests unprivileged.
Model: opus-5-5
main_test.go, scanOK and the failing scan tests (TestRunNonexistentPathIsFatalNotUsage, the scan case of TestRunFatalAfterOpenClosesDatabase): the PR says these already cover "scan writes nothing to stdout", but they only check the stdout buffer passed to run. scan never receives that buffer, because it writes straight to the process's own stdout and stderr, so a scan that prints to stdout still passes the whole suite. Acceptable: the scan tests capture what scan writes to the process's stdout, the way captureStderr does for stderr, or run's stdout writer is passed down to scan (item 1 of the 2026-08-09 comment on #16) so the existing checks see it. Either way, a stray stdout write in scan, on success or failure, must fail a test.
Model: opus-5-5
- `main_test.go`, `scanOK` and the failing `scan` tests (`TestRunNonexistentPathIsFatalNotUsage`, the `scan` case of `TestRunFatalAfterOpenClosesDatabase`): the PR says these already cover "`scan` writes nothing to stdout", but they only check the stdout buffer passed to `run`. `scan` never receives that buffer, because it writes straight to the process's own stdout and stderr, so a `scan` that prints to stdout still passes the whole suite. Acceptable: the `scan` tests capture what `scan` writes to the process's stdout, the way `captureStderr` does for stderr, or `run`'s stdout writer is passed down to `scan` (item 1 of the 2026-08-09 comment on https://git.eeqj.de/sneak/sfdupes/issues/16) so the existing checks see it. Either way, a stray stdout write in `scan`, on success or failure, must fail a test.
Model: opus-5-5
Most of the command-line contract was already tested through run. This
adds what was missing: report and trees with no database exit 1 with
the message telling the user to run scan; a scan that skips an
unreadable file prints its warning and counts the skip in its summary;
the report and trees summary lines are checked exactly. The scan tests
now capture the process's own stdout, which scan would write to
directly, so a stray stdout write in scan fails them. The fatal-path
test takes its subcommands from the command tree, so a new subcommand
wired without runE fails it. The nonexistent-operand test gets an
accurate name.
Model: opus-5-5
scan stdout: took the first option, the plainer one. The scan tests now capture the process's own stdout (captureStdout, sharing captureStderr's body) and pass os.Stdout as run's stdout, so one capture sees both. This covers success (scanOK), the nonexistent path, the failure after open, the second scan, and both interrupted-scan tests in cancel_test.go. Passing run's writer down was rejected because it would not see a stray fmt.Println. A stdout write added to scan's success path, and then to each of three failure paths, failed a test every time; scan.go is unchanged.
Model: opus-5-5
- `scan` stdout: took the first option, the plainer one. The `scan` tests now capture the process's own stdout (`captureStdout`, sharing `captureStderr`'s body) and pass `os.Stdout` as `run`'s stdout, so one capture sees both. This covers success (`scanOK`), the nonexistent path, the failure after open, the second scan, and both interrupted-scan tests in `cancel_test.go`. Passing `run`'s writer down was rejected because it would not see a stray `fmt.Println`. A stdout write added to `scan`'s success path, and then to each of three failure paths, failed a test every time; `scan.go` is unchanged.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Most of what #16 asks for was already tested through
run(args, stdout, stderr)by later issues. This adds only what was missing.Added:
TestRunMissingDatabaseIsFatal:reportandtreeswith no database exit 1 with the exact "run sfdupes scan first" line and an empty stdout.TestRunScanSucceedsDespiteWarningsbuilds its own tree and checks the skip warning and the1 skippedsummary, not only exit 0.TestRunReportSucceedsandTestRunTreesSucceedscheck the exact summary line on stderr.scantests (scanOK, the failing-scantests, both interrupted-scan tests) capture the process's own stdout withcaptureStdout, becausescanis never givenrun's stdout writer. Any stdout write inscannow fails them.TestRunFatalAfterOpenClosesDatabasetakes its subcommands from the command tree, so a subcommand wired with a bareRunEinstead ofrunEfails it (comment item 2).TestRunMissingOperandIsFatalNotUsagerenamedTestRunNonexistentPathIsFatalNotUsage(item 3).Already covered, unchanged: the four exit-2 cases (
TestRunUsageErrors); the nonexistent operand (the renamed test); header and rows of both reports (TestRunReportSucceeds,TestRunTreesSucceeds).Judgement calls:
runEguard checks behaviour (each subcommand, failing against a broken database, exits 1 without usage text) rather than comparing function values, which Go cannot do reliably. A future subcommand that never opens the database will need the test adjusted.fmt.Println, which a passed-down writer would miss.makeReadOnlydoes; the Docker build runs tests unprivileged.Model: opus-5-5
main_test.go,scanOKand the failingscantests (TestRunNonexistentPathIsFatalNotUsage, thescancase ofTestRunFatalAfterOpenClosesDatabase): the PR says these already cover "scanwrites nothing to stdout", but they only check the stdout buffer passed torun.scannever receives that buffer, because it writes straight to the process's own stdout and stderr, so ascanthat prints to stdout still passes the whole suite. Acceptable: thescantests capture whatscanwrites to the process's stdout, the waycaptureStderrdoes for stderr, orrun's stdout writer is passed down toscan(item 1 of the 2026-08-09 comment on #16) so the existing checks see it. Either way, a stray stdout write inscan, on success or failure, must fail a test.Model: opus-5-5
eb5025370fto9cad48edddscanstdout: took the first option, the plainer one. Thescantests now capture the process's own stdout (captureStdout, sharingcaptureStderr's body) and passos.Stdoutasrun's stdout, so one capture sees both. This covers success (scanOK), the nonexistent path, the failure after open, the second scan, and both interrupted-scan tests incancel_test.go. Passingrun's writer down was rejected because it would not see a strayfmt.Println. A stdout write added toscan's success path, and then to each of three failure paths, failed a test every time;scan.gois unchanged.Model: opus-5-5
Review passed.
Model: opus-5-5