fix(tests): restore hidden-subcommand completion coverage#3492
Conversation
test_completion_validation had rotted into a vacuous test: it read `wt config shell init <shell>` (which under dynamic completions emits only the shell wrapper, no flag lines) and checked a hardcoded `hidden_flags = ["internal"]` set for a flag removed in #269. The three per-shell validators were no-op stubs returning `Vec::new()`, so the test could never fail on a leak. Rewrite it to assert the real, current invariant against the dynamic completion engine: `hide = true` *subcommands* (`select`, `hook run-pipeline`, `hook approvals`, `config completions`/ `previous-branch`/`hints`/`ci-status`) never appear in completions. Each context also asserts a visible sibling is present, so a broken invocation returning no candidates can't pass vacuously. Hidden flags are intentionally not checked: the completion assembly (hide_non_positional_options_for_completion) deliberately surfaces every long flag, hidden ones included, when the user types `--`. Closes #3491
The config-context assertions checked completions/previous-branch/hints/ ci-status for absence under `wt config`, but those aren't direct children of `wt config`: `completions` lives under `wt config shell` and the other three under `wt config state`. clap never surfaces grandchildren at a parent level, so the checks passed regardless of `hide = true` — the same vacuous shape this test was rewritten to eliminate. Probe `wt config shell` and `wt config state` directly, where the items are real direct children and their absence is actually caused by hide filtering.
worktrunk-bot
left a comment
There was a problem hiding this comment.
Self-review — the rewrite is sound. I drove the source independently: select, hook run-pipeline/approvals, config shell completions, and config state previous-branch/hints/ci-status are all #[command(hide = true)], the visible siblings (switch, pre-merge via inject_hook_subcommands, config shell install, config state cache) exist, and affected tests (linux, advisory) passed on this head. Keying on COMPLETE=fish is enough because hide-filtering happens in the clap_complete engine before shell formatting.
One doc nit inline — the guard comment says "hidden flag absent" while describing subcommand checks, which slightly undercuts the flag-vs-subcommand distinction the PR is drawing.
| /// | ||
| /// Each context also asserts a visible sibling is present. That guard is | ||
| /// load-bearing: without it, a broken invocation returning *no* candidates would | ||
| /// satisfy every "hidden flag absent" check vacuously — the exact failure mode |
There was a problem hiding this comment.
| /// satisfy every "hidden flag absent" check vacuously — the exact failure mode | |
| /// satisfy every "hidden subcommand absent" check vacuously — the exact failure mode |
Minor: this guard covers subcommand checks, and the whole point of the surrounding comment is that flags and subcommands are different invariants — so "flag" here reads as a slip.
There was a problem hiding this comment.
Fixed in 849ab95 — reworded to "hidden subcommand absent" so the comment matches the check it describes and keeps the flag-vs-subcommand distinction clean.
Problem
test_completion_validationintests/integration_tests/completion_validation.rshad stopped guarding its invariant — that internal/deprecatedhide = trueitems stay out of generated shell completions. The test still ran and passed, but could essentially never fail on a leak (an invisible-failure-mode gap). Two independent causes:wt config shell init <shell>, which back when the test was written (Add black-box completion tests for zsh, bash, and fish #117) emitted static completion definitions (complete -c wt … -l internal). Completions later moved to a dynamic engine (COMPLETE=<shell> wt, assembled insrc/completion.rs), soconfig shell initnow emits only the shell wrapper function — no flag or subcommand lines at all.extract_flagstherefore returned empty sets for all three shells.Severity::Errorpath checked a hardcodedhidden_flags = ["internal"], and--internalwas removed in Replace --internal flag with WORKTRUNK_DIRECTIVE_FILE environment variable #269. The three per-shell validators (validate_fish/validate_bash/validate_zsh) were no-op stubs returningVec::new().So the surviving assertion was just "these three commands exit 0."
Root-cause verification
I built
wtand drove the dynamic engine directly to settle the question the issue flagged (does re-pointing the test at realhide = trueflags restore coverage, or surface a leak?):wt <Tab>omitsselect;wt hook <Tab>omitsrun-pipelineandapprovals;wt config <Tab>omitscompletions,previous-branch,hints,ci-status.wt merge --<Tab>offers--squash,--commit,--rebase,--remove,--ff,--verify,--no-verifyalongside the visible flags. That's the documented behavior ofhide_non_positional_options_for_completion("all-hidden = all shown") — every long flag is surfaced when the user explicitly types--. So "hidden flags never appear in completions" is not an invariant the current design holds; re-pointing the old assertion at thehide = trueflag list would have been wrong.The living, meaningful invariant is therefore about subcommands, not flags.
Solution
Rewrite the test to assert that
hide = truesubcommands never appear in completions, driving the actual dynamic engine (COMPLETE=fish wt -- <words…>). Each context also asserts a visible sibling is present — that guard is load-bearing: without it, a broken invocation returning no candidates would satisfy every "hidden item absent" check vacuously, which is exactly how the old test rotted. Removes the obsoleteconfig shell initflag-extraction machinery and the three no-op validators (dead code perCLAUDE.md). A doc comment records why hidden flags are deliberately not checked.Testing
Verified empirically that the asserted candidate sets match the built binary's dynamic completion output for each context, and that the visible-sentinel assertions keep the test non-vacuous.
Note for review
I chose to restore coverage (assert the subcommand invariant) rather than delete the now-obsolete test. If you'd rather drop it entirely — the subcommand exclusion leans partly on clap_complete's own hidden-item filtering — that's a reasonable alternative call.
Closes #3491 — automated triage