Commit Graph

22 Commits

Author SHA1 Message Date
Maximilian Roos 1eded27943 Simplify forge, shell integration, and removal internals (#3662)
This consolidates cross-cutting models that had accumulated parallel
representations, while preserving the CLI and config interfaces.

## What changed

- Forge identity now flows through one `ForgeKind`, with boundary-safe
network-host classification shared by CI, remote references, and
structured repository metadata. Branded SSH aliases such as
`github-personal` remain outside provider dispatch and receive an
actionable `forge.platform` diagnostic when forge data is requested.
- Worktree removal now uses one owned target type and rechecks uncached
worktree topology immediately before compare-and-swap branch deletion,
retaining branches that gained a live or locked checkout.
- Shell integration no longer implements the retired single-file
directive writer. Stale wrappers receive repair guidance, execution
fails closed, and child processes cannot inherit the retired
sourceable-file capability.
- Zsh and Git-version probes are shared, while dead mocks, redundant
dependency edges, serializer detours, pass-through types, and duplicate
tests are removed.

The removal guard deliberately distinguishes live, stale-prunable, and
locked registrations. The detached cleanup path performs the same
record-aware check before deleting a branch ref.

## Testing

`cargo run -- hook pre-merge --yes` passed after merging current
`origin/main`: 4,489 tests, Clippy, formatting, lockfile checks, docs,
doctests, and snapshot review.

> _This was written by Claude Code on behalf of max_.
2026-07-30 02:10:07 -07:00
Maximilian Roos 6d09125b7b test: converge suite on semantic boundaries (#3663)
This follows the first test-simplification tranche by converging the
remaining suite around distinct semantic and pragmatic contracts rather
than raw case count. The branch removes false-confidence tests, invalid
setup variants, repetitive snapshots, and expensive PTY overlap while
strengthening the retained route, precondition, and interaction proofs.

## What changed

- Replace obsolete CI-status integration mocks and blank snapshots with
direct provider semantics, mixed-priority cases, and strict
GitHub/GitLab route assertions.
- Remove free-riding merge, push, remove, list, security, config, and
switch cases whose setup never reached the named behavior; consolidate
repetitive direct cases into labeled tables.
- Reduce the switch picker from 42 PTYs to 19 distinct terminal
contracts, using causal release gates for asynchronous loading and
repaint behavior.
- Add a cached main-only picker fixture, eliminating 138 unnecessary Git
subprocesses across the retained PTYs, and integrate it with main's
generated hermetic standard fixture.
- Tighten test guidance around proving setup preconditions and mock
invocation routes, and correct the comments-tab help text and generated
mirrors.

## Reviewer map

- `tests/integration_tests/ci_status.rs` and
`src/commands/list/ci_status/`: provider semantics and route coverage.
- `tests/integration_tests/switch_picker.rs`, `src/commands/picker/`,
and `src/testing/`: retained PTY contracts, causal mocks, and fixture
design.
- `tests/integration_tests/config_show.rs`, `src/config/deprecation.rs`,
`src/config/expansion.rs`, and worktree type/resolve tests:
direct-boundary consolidation.
- `tests/CLAUDE.md`: the testing rules extracted from the
false-confidence cases found during the survey.

The measured loop removed 98 tests, 65 snapshots, and 23 picker PTYs.
Controlled warm Nextest execution improved from a 79.593-second mean to
72.574 seconds (8.8%), while comparable production-line coverage moved
from 97.32% to 97.23%. The tracked PR diff is a net deletion of more
than 5,700 lines.

## Validation

- `cargo run -- hook pre-merge --yes` after syncing current `main`:
4,468 passed, one configured skip; docs, doctests, clippy, formatting,
policy checks, and snapshots green.
- `task coverage` on the completed change before the base sync: 4,465
passed, one configured skip; 97.23% comparable production-line coverage.
- Three independent final audits found no remaining lost beliefs,
fixture hazards, or safe PTY consolidations.

> _This was written by Claude Code on behalf of max_.
2026-07-30 00:01:32 -07:00
Maximilian Roos b6ae21d6e1 refactor: prefer raw strings over escaped literals (#2150)
Converts ~29 string literals across 16 files from escaped form
(`"\\d+"`, `"{\"name\": 1}"`) to raw-string form (`r"\d+"`, `r#"{"name":
1}"#`) where possible. Strings containing control escapes (`\n`, `\t`,
`\u{1b}`, etc.) are left alone — raw strings can't represent those.

No behavior change; `cargo check/clippy -D warnings/fmt/test` all clean
locally.

> _This was written by Claude Code on behalf of max-sixty_

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-12 18:19:14 -07:00
Maximilian Roos b174658e29 Split directive file into CD (raw path) and EXEC (shell) files (#2118)
The shell wrapper previously used a single `WORKTRUNK_DIRECTIVE_FILE`
where wt wrote shell commands (`cd '/path'`, arbitrary `--execute`
payloads). This meant the cd path went through shell parsing — any
content wt wrote was sourced as shell.

This splits the protocol into two files with different trust levels:

- **`WORKTRUNK_DIRECTIVE_CD_FILE`** — raw path, read with `cd -- "$(<
file)"`. No shell parsing, no escaping, no injection surface. Safe to
pass through to alias/hook child processes.
- **`WORKTRUNK_DIRECTIVE_EXEC_FILE`** — arbitrary shell (from
`--execute`), sourced by the wrapper. Scrubbed from alias/hook child
environments so hook bodies cannot inject shell into the parent session.

When a nested `wt` inside an alias body tries `--execute` without the
EXEC file, the command is dropped with a warning linking to #2101 for
user feedback.

The old `WORKTRUNK_DIRECTIVE_FILE` is silently honored for one release
(users who upgrade wt without restarting their shell). Bash, zsh, fish,
and PowerShell self-update on restart; nushell requires `wt config shell
install`.

Closes #2101

> _This was written by Claude Code on behalf of @max-sixty_

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-12 13:52:04 -07:00
Maximilian Roos a2482f1da9 refactor: change test git_command() to return Cmd instead of Command (#1718)
The test infrastructure's `git_command()` returned
`std::process::Command`, so test git commands bypassed `Cmd`'s debug
logging (`$ git status [ctx]`) and timing traces (`[wt-trace]`). This
changes it to return `worktrunk::shell_exec::Cmd`.

## Approach

Added `configure_git_env(Cmd, &Path) -> Cmd` alongside the existing
`configure_git_cmd(&mut Command)`. The `Command` version is kept because
`configure_wt_cmd` (which configures wt binary commands) still needs it
— wt commands go through `Command`, not `Cmd`.

Key changes in `tests/common/mod.rs`:
- `TestRepoBase::git_command()` and `TestRepo::git_command()` now return
`Cmd`
- All wrapper methods (`run_git`, `git_output`, `head_sha`, etc.) use
`.run()` instead of `.output()`
- `BareRepoTest::new()` and `NestedBareRepoTest::new()` use
`Cmd::new("git")` for init

247 call sites across 25 files converted: `.output()` → `.run()` and
`.status()` → `.run()` on git command chains. Zero `Command::new("git")`
remaining in the test directory.

Follows #1714 and #1716 which converted raw `Cmd::new("git")` and
`Command::new("git")` in test bodies to `repo.run_command()`.

> _This was written by Claude Code on behalf of maximilian_

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:23:23 -07:00
Maximilian Roos eca9beb232 fix: warn when --config path doesn't exist, fix shell quoting in docs (#895)
* fix: warn when --config path doesn't exist, fix shell quoting in docs

Bug fixes from adversarial testing:

1. --config path warning: When users pass --config with a non-existent
   file path, wt now warns instead of silently falling back to defaults.
   Note: WORKTRUNK_CONFIG_PATH env var doesn't trigger this warning since
   it's commonly used for test isolation with intentionally absent paths.

2. Shell quoting in docs: Template variables are automatically shell-escaped,
   so user-added quotes cause issues with special characters. Fixed examples
   in hook docs and tips-patterns that incorrectly showed quoted variables.

Also adds:
- Test verifying --squash is correctly ignored with --no-commit
- Tests for ANSI escape sequence handling in branch names

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: skip ANSI branch name test on Windows

Git for Windows with MSYS2 bash behaves differently and may accept
branch names containing control characters. The test verifies Unix git
behavior, so restrict it to Unix platforms.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-28 15:57:58 -08:00
Maximilian Roos 928b3017cd test: remove verbose assertion messages and doc comments (#458)
* Simplify assert messages by removing redundant descriptions

Remove verbose assertion messages that repeat the condition being tested.
Messages like "Should contain indicator" when asserting `contains("●")` are
redundant since the code is self-documenting. This reduces noise in tests
while maintaining clarity through meaningful assertion failures.

* test: remove verbose assertion messages and doc comments

Removed facile test verbosity across three categories:

1. **Doc comments** (~236 lines): Removed `/// Test X` comments that just
   restated test function names. Kept multi-line explanations that add context.

2. **Test names** (9 functions): Shortened overly verbose names by removing
   filler words while maintaining clarity:
   - test_merge_when_primary_not_on_default_but_default_has_worktree →
     test_merge_primary_not_on_default_with_default_worktree
   - test_complete_switch_option_prefix_shows_options_not_branches →
     test_complete_switch_option_prefix_shows_options

3. **Assertion messages** (19 instances): Removed messages that just restate
   the check:
   - assert!(path.exists(), "Path should exist") → assert!(path.exists())
   - assert!(x.is_file(), "Should be a file") → assert!(x.is_file())

Kept messages that add semantic meaning (e.g., "hook should have run") or
include debugging variables.

Total: 279 lines removed across 31 files. All 723 integration + 401 unit tests
passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* test: remove unreferenced snapshot files from renamed tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 16:51:44 -08:00
Maximilian Roos 4709409ccb Consolidate test git commands using helper methods (#339)
* Consolidate test git commands using helper methods

Migrate ~180 instances of manual `Command::new("git")` calls to use
TestRepo helper methods for better consistency and reduced boilerplate:

- `repo.run_git(&["..."])` - run in repo root, panic on failure
- `repo.run_git_in(&path, &["..."])` - run in specific dir, panic on failure
- `repo.git_command()` - get configured Command for output capture

Changes:
- Add `run_git_in()` to `TestRepoBase` trait so BareRepoTest and
  NestedBareRepoTest also get this method
- Migrate tests in switch.rs, remove.rs, merge.rs, list.rs, completion.rs,
  shell_wrapper.rs, push.rs, security.rs, list_config.rs, list_column_alignment.rs
- Migrate internal TestRepo methods to use git_command() with chained env vars
- Remove unused `use std::process::Command` imports where no longer needed

Remaining intentional uses of Command::new("git"):
- TestRepoBase::git_command() and TestRepo::git_command() definitions
- BareRepoTest constructors for `git init --bare` (can't use git_command
  because init takes path as argument, not current_dir)
- Standalone bare repo test in remove.rs (to be refactored separately)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Move BareRepoTest to common module and refactor bare repo test

- Move `BareRepoTest` from bare_repository.rs to common/mod.rs for reuse
- Add `wt_command()` helper method to BareRepoTest
- Refactor `test_remove_default_branch_no_tautology` to use shared BareRepoTest
- Remove unused imports (canonicalize, Command, TempDir) from remove.rs

This reduces the standalone bare repo test from 70+ lines to ~15 lines.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Move commit_in method to TestRepoBase trait

Consolidate BareRepoTest::commit_in_worktree into the shared
TestRepoBase trait as commit_in(). This allows all test harnesses
to share this method without duplication.

- Add commit_in() to TestRepoBase trait (tests/common/mod.rs:619)
- Remove duplicate commit_in_worktree from BareRepoTest
- Update 16 callers in bare_repository.rs
- Update 1 caller in remove.rs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-31 15:46:01 -08:00
Maximilian Roos bceedcade7 Fix test isolation for wt commands and improve test API (#314)
Tests that spawn `wt` must be isolated from the host environment to
prevent directive leakage (test commands writing to user's shell
directive file), config pollution, and git interference.

Changes:
- Add `TestRepo::wt_command()` returning pre-configured Command
- Rename `clean_cli_env` → `configure_wt_cmd` (parallel to git)
- Make `git_command()` consistent (no args, chain `.args()`)
- Fix approval_ui.rs and readme_sync.rs missing isolation
- Add `#[must_use]` to command builders
- Document pattern in tests/CLAUDE.md

The public API is now 3 symmetric methods:
- `repo.wt_command()` - for wt commands with TestRepo
- `repo.git_command()` - for git commands
- `wt_command()` - for wt without TestRepo (help tests)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-30 20:29:27 -08:00
Maximilian Roos 87321281d4 Replace --internal flag with WORKTRUNK_DIRECTIVE_FILE environment variable (#269)
* Replace --internal flag with WT_DIRECTIVE_FILE environment variable

* Rename directive file environment variable to WORKTRUNK_DIRECTIVE_FILE

* Update stale comments referencing directive scripts

Codex review identified comments that still referenced "directive scripts"
after migrating to file-based directive passing. Updated to reference
"data output" which is the current terminology.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Address documentation review feedback

- Add WORKTRUNK_DIRECTIVE_FILE to env var tables (cli.rs, config.md)
- Update stale "directive mode" terminology in test comments to use
  "shell integration" or "data output" as appropriate
- Regenerate help snapshot

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Refactor progressive table output to use output module

Remove finalize_non_tty method and simplify non-TTY output path to use
output::table() directly, consistent with buffered mode. Add WORKTRUNK_SHELL
environment variable support for PowerShell-specific path escaping. Update
documentation and templates accordingly.

* Preserve directive script exit code in PowerShell

Add logic to use the directive script's exit code when wt succeeds, and
add redaction for WORKTRUNK_DIRECTIVE_FILE in test snapshots.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-20 21:33:25 -08:00
Maximilian Roos 7723fce280 more rstest (#150)
* Add temp_home fixture for tests that need custom home directory

Tests that manipulate shell config files or other home directory content were creating `TempDir::new()` manually. Convert these to use an `#[rstest]` fixture for consistency and cleaner test code.

The `temp_home` fixture:
- Creates a temporary directory for use as a fake HOME
- Automatically cleans up when dropped
- Makes test intent clearer by appearing in function signature

This reduces boilerplate across multiple test files (config_init, config_show, configure_shell, hook_show, list_config) and enables better test composition.

* Refactor tests to use repo_with_remote fixture

Replace manual setup_remote() calls with #[from(repo_with_remote)]
rstest fixture parameter to reduce boilerplate in test functions.
Also normalize import ordering across test files.
2025-12-11 04:03:09 -08:00
Maximilian Roos 1a8fb80d87 Add fixture-based test repo initialization for faster tests
Replace `git init` with pre-initialized fixture template to save ~10ms per test.
The fixture at tests/fixtures/template-repo/ contains a minimal git repo with
one deterministic commit (hash 23e5a15, timestamp 2025-01-01T00:00:00Z).

Changes:
- TestRepo::new() now copies fixture instead of running git init
- TestRepo::empty() added for tests needing uninitialized repos
- Removed ~320 redundant `repo.commit("Initial commit")` calls from tests
- Reduced fixture from 32 files to 9 (removed sample hooks, logs, etc.)
- Fixed path canonicalization in TestRepo::empty() for Windows compatibility
- Added typos exception for commit hash substring 'ede'

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-10 23:33:02 -08:00
Maximilian Roos 6151cfa23b Revert "Remove SHA redaction from snapshots"
This reverts a previous commit that removed SHA redaction from test snapshots.
The SHA values can be non-deterministic due to various factors (e.g., commit timestamp, environment variables), causing tests to fail when they change unexpectedly.

By re-introducing SHA redaction, we ensure that snapshot tests remain stable and only capture the relevant structural output, preventing unnecessary failures due to transient SHA differences.
2025-11-26 23:28:18 -08:00
Maximilian Roos 56db53d984 Feat: Format TOML arrays as multiline for improved readability 2025-11-26 15:34:07 -08:00
Maximilian Roos 1ff77e73c1 refactor: Remove NUL terminator-related snapshot filters
Removes obsolete filters and rewords comments to reflect the simplified
shell integration protocol, which no longer uses NUL terminators
for synchronization or directives.
2025-11-24 19:48:25 -08:00
Maximilian Roos c14a20c9a7 refactor: Simplify shell integration protocol for robustness
Updates the `--internal` flag's output format from NUL-terminated directives
to a shell script emitted on stdout at the end of execution. User-facing
messages now stream exclusively to stderr in real-time.

This change:
- Eliminates the need for FIFOs and background processes in shell wrappers, simplifying implementation.
- Ensures all user messages stream directly to the terminal, improving real-time feedback.
- Uses standard POSIX single-quote escaping for paths, making it more robust against injection.
- Aligns with proven patterns like direnv (stderr for logs, stdout for script).

Removes support for Elvish, Nushell, PowerShell, and Xonsh shells due to their
complexities in implementing the previous streaming NUL-byte directive protocol.
Bash, Zsh, and Fish are now simpler and more reliably supported.

Updates documentation, code comments, and integration tests to reflect the new protocol.
2025-11-24 19:31:35 -08:00
Maximilian Roos 7a05a54aaa Refactor: Replace expect with unwrap in tests
Replaces most uses of `.expect(...)` with `.unwrap()` in test code. This is a common pattern in Rust tests for operations that are expected to succeed, simplifying the code.
2025-11-23 21:00:31 -08:00
Maximilian Roos 9c80aad297 fix: Fix CI failures on Windows and Ubuntu (#34)
* feat: Move diffstat display to `handle_push`

This ensures that the diffstat is consistently displayed after a push operation,
rather than after every commit or squash. This makes the output more logical.

* feat: Standardize snapshot settings and redact more volatile env vars

Standardize `insta` snapshot settings to remove more volatile environment variables that cause diff churn.

Also canonicalize worktree paths to handle macOS symlinks (e.g., /var -> /private/var), ensuring consistent snapshot outputs across different environments.

* fix: Fix CI failures on Windows and Ubuntu

- Wrap BetaCommand import with #[cfg(unix)] to fix Windows clippy error
- Use /bin/echo instead of echo in LLM tests to fix Ubuntu PATH issues

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-21 23:44:22 -08:00
Maximilian Roos 2a56fe6439 feat: Indicate main worktree in list output
The main worktree now displays a `^` symbol in the `main↕` column to clearly distinguish it from other worktrees and branches. This adds clarity when quickly scanning the list output.
2025-11-21 14:12:02 -08:00
Maximilian Roos 81ace9ff03 refactor: Standardize CLI command setup for tests
This refactoring introduces `wt_command` and `configure_cli_command` helper functions in `tests/common/mod.rs` to centralize the setup of `wt` CLI commands in integration tests.

The new helpers ensure all test commands:
- Clear host `GIT_*` and `WORKTRUNK_*` environment variables.
- Force color output (`CLICOLOR_FORCE=1`) for consistent snapshot testing.
- Set a default terminal width of 150 columns (`COLUMNS=150`) if not already specified.

This change reduces duplication across integration tests by moving common command environment configuration into reusable functions. Existing calls to `Command::new(get_cargo_bin("wt"))` have been replaced with `wt_command()`, and manual environment variable settings have been replaced with calls to `configure_cli_command`.

A new helper `set_temp_home_env` is also introduced to simplify setting `HOME` and `XDG_CONFIG_HOME` for commands requiring an isolated home directory.
2025-11-06 15:23:41 -08:00
Maximilian Roos a41464ce67 feat: Add Windows CI support and cross-platform testing
- Add Windows to CI matrix (ubuntu, macos, windows)
- Configure Windows CI to use Git Bash
- Add 10min timeout for test suite
- Mark Unix-specific tests with #[cfg(unix)]
- Make file permissions tests cross-platform
- Escape Windows paths in snapshot regex filters
- Disable flaky bash PTY test with timing race
- Add git author identity to bare_repository tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 07:34:02 -08:00
Maximilian Roos c55f77c496 feat(security): Add directive injection vulnerability tests
This commit introduces a new integration test suite focused on preventing
shell directive injection through user-controlled strings.

The tests ensure that malicious content in branch names, commit messages,
file paths, and error output cannot be misinterpreted as internal
`__WORKTRUNK_EXEC__` or `__WORKTRUNK_CD__` directives.

A new `TestRepo::commit_with_message` helper is added to facilitate
creating commits with custom, potentially malicious, messages.

These tests verify that various attack vectors, including NUL byte
injection (where prevented by Git/Rust), newline injection, and direct
directive strings, do not lead to arbitrary command execution when
processed or displayed by worktrunk.
2025-11-01 11:53:36 -07:00