feat(testing): add cross-platform E2E test suite#78
Merged
Conversation
907c2dd to
7b99c0e
Compare
7b99c0e to
4c310f2
Compare
Claude Code, Goose, Gemini CLI, and Codex CLI all run natively on Windows. Amp is WSL-only so it remains Linux-only. This change adds the plumbing to make all four agents work on native Windows without requiring WSL. Key design decisions: - Centralize all platform branching in a new `platform.py` module (single monkeypatch target for tests; no scattered sys.platform checks). - Goose and Statusline use different config dirs on Windows (%APPDATA%/Block and %APPDATA%/claude-statusline respectively); helpers return tilde paths on Unix for backward compatibility. - Gemini CLI actively destroys symlinks when rewriting settings.json (GitHub issue #10960, closed "not planned"). Windows Gemini gets file copies instead of symlinks via a new copy_mode_targets mechanism; preserved fields are read from the live target before each cache rebuild to survive Gemini rewrites. - Windows symlinks require Developer Mode; PermissionError surfaces a step-by-step enable guide instead of a generic message. - PowerShell completions use Register-ArgumentCompleter with bash_source mode, setting COMP_WORDS/COMP_CWORD so Click returns contextual completions. - uv path resolution respects UV_TOOL_DIR override on all platforms. - CI matrix extended to windows-latest.
Replace is_windows() boolean with Platform enum (MACOS, LINUX, WINDOWS, WSL) and detect_platform() using platform.uname().release for WSL detection. Adopt shellingham for shell detection on all platforms -- PSModulePath was inherited by every Windows process, falsely identifying all shells as PowerShell. Query PowerShell for its profile path via subprocess instead of hardcoding ~/Documents (OneDrive silently redirects it). Drop click-pwsh (broken under uv).
Fix Python 3 SyntaxError from bare-comma except clauses. Rewrite PowerShell completion to use Click's bash_complete mode with correct COMP_CWORD cursor-position logic. Add backup and confirmation to create_file_copy when target has user modifications. Wire copy-mode awareness into global pending-change and first-run checks. Add APPDATA fallback for stripped Windows environments, filter Amp skills on Windows, and add shellingham upper-bound pin.
On Windows, Path.home() reads USERPROFILE (not HOME), and str(Path) uses backslashes. Tests that only set HOME caused 75 failures on Windows CI because Path.home() resolved to the real C:\Users\runneradmin. - Add autouse _patch_home_from_env fixture in conftest.py that makes Path.home() follow the HOME env var at call time via a lazy closure, so all tests that set HOME get correct isolation without any per-test changes - Update mock_home fixture to also set APPDATA for Windows parity - Fix test_agents.py: use Path.as_posix() when comparing symlink targets so ~\.claude\CLAUDE.md comparisons work cross-platform - Fix test_bootstrap_installer.py: use UV_TOOL_DIR override instead of XDG_DATA_HOME in tool-source tests (UV_TOOL_DIR is the canonical env var and takes precedence on all platforms); use as_posix() for path assertions; clear UV_TOOL_DIR in tests that check default path structure - Fix test_bootstrap_updater.py: same UV_TOOL_DIR approach for pyvenv.cfg tests - Fix test_session_search_core.py: use Path.as_posix() when comparing session_to_json path output; fix matches_term path test to not rely on forward-slash path separators in str(path) - Fix test_session_search_readers.py: also set USERPROFILE in _patch_home helper (belt-and-suspenders alongside the autouse fixture)
Four root causes: (1) Click CliRunner uses cp1252 on Windows, causing UnicodeDecodeError for skill markdown with emoji — fixed via PYTHONUTF8=1 in autouse fixture. (2) Path assertions using forward slashes fail when Path() stringifies with backslashes — normalized with .replace/as_posix(). (3) repo_score() converts POSIX session paths to backslash form, breaking all string comparisons — fixed with .as_posix() in production code.
a8e6f56 to
34f44e5
Compare
4c310f2 to
c04ceb6
Compare
Path.read_text() without encoding uses the system default (cp1252 on Windows). SKILL.md files contain UTF-8 emoji characters that can't be decoded by cp1252, causing UnicodeDecodeError in all skill CLI commands on Windows.
c04ceb6 to
d921ad9
Compare
Cross-platform testing belongs in the E2E workflow (e2e.yml) which runs on all 3 OSes. The matrix in ci.yml caused the job name to change from bare "checks" to "checks (ubuntu-latest)" / "checks (windows-latest)", which no longer matched the ruleset's required status check context.
Subprocess-based E2E tests that invoke the real CLI binary via `python -m ai_rules.cli` against an isolated temp HOME. Catches the class of platform-specific bugs (broken imports, wrong paths, encoding issues) that pass in-process CliRunner tests but fail on real OSes. New `.github/workflows/e2e.yml` runs the suite on ubuntu, macos, and windows — separate from the github-config-managed `ci.yml`.
text=True uses the parent process's locale encoding (cp1252 on Windows), not the child's PYTHONIOENCODING. Rich box-drawing characters crash the cp1252 decoder, leaving stdout as None. Explicit encoding="utf-8" overrides locale-based pipe decoding.
d921ad9 to
677502d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack: #74 → this PR
Summary
ai-agent-rulesCLI binary viapython -m ai_rules.cliagainst an isolated tempHOME, validating that commands actually work on each OStests/e2e/directory with 13 test cases covering--help,status,validate,list-agents,diff,install --dry-run, andcompletions status.github/workflows/e2e.ymlruns the E2E suite onubuntu-latest,macos-latest, andwindows-latest(separate from the github-config-managedci.yml)pyproject.tomlupdated withe2emarker and--ignore=tests/e2einaddoptssojust testexcludes E2E by defaultJustfilegainstest-e2erecipe using-n 0(not-p no:xdist, which crashes when-n autois inaddopts)TestWindowsSpecific) uses@pytest.mark.skipif(sys.platform != "win32")and exercises PowerShell completions via the Windows support in feat: add native Windows support #74Key fixture design:
e2e_homereturns(home_path, env_overrides)dict passed tosubprocess.run(env=...)— monkeypatch only affects the current process, not child subprocessessubprocess.run(encoding="utf-8")instead oftext=True— on Windows,text=Trueusescp1252on the parent side, crashing on Rich's Unicode box-drawing charactersNO_COLOR=1+PYTHONIOENCODING=utf-8+PYTHONUTF8=1in subprocess env for clean cross-platform assertionsrun_cliextendsPYTHONPATHwithsrc/soimportlib.resourcesresolves the dev package configRelated: shell-configs#65