Skip to content

Comments

Consolidate E2E test suite into cli repo#474

Open
khaong wants to merge 41 commits intomainfrom
alex/consolidate-e2e-tests
Open

Consolidate E2E test suite into cli repo#474
khaong wants to merge 41 commits intomainfrom
alex/consolidate-e2e-tests

Conversation

@khaong
Copy link
Contributor

@khaong khaong commented Feb 24, 2026

Summary

  • Moves the consolidated E2E test suite from entire-cli-e2e-tests into e2e/ as the single E2E suite
  • Removes the old shadow-hook based suite at cmd/entire/cli/e2e_test/
  • Updates mise tasks to point at new e2e/tests/... path with optional filter args

What's in e2e/

  • 3 agents: Claude Code, Gemini CLI, OpenCode — all run via ForEachAgent
  • E2E_AGENT env var for CI matrix targeting (when set, only matching agent registers)
  • //go:build e2e tag on all test files (invisible to normal go build/go test)
  • 37 tests covering: single/multi session, auto-commit, attribution, rewind, stash workflows, split commits, subagent flows, session lifecycle, edge cases, checkpoint metadata validation
  • Artifact capture (git log, tree, checkpoint metadata, entire logs, console transcript) for failure debugging
  • exploratory/ directory for on-demand test scenarios not run by CI

Key design decisions

  • Always uses os.MkdirTemp instead of t.TempDir() — Go's nested subtest dirs confuse some agents' path resolution
  • OpenCode gets opencode.json config written to allow external_directory permission in non-interactive mode
  • filepath.EvalSymlinks resolves macOS /var/private/var so agent CLIs see consistent paths

Test plan

  • mise run test:e2e:claude TestSingleSessionManualCommit — PASS
  • mise run test:e2e:opencode TestSingleSessionManualCommit — PASS
  • go build ./... — clean (e2e tests invisible without build tag)
  • go test -tags=e2e -list '.*' ./e2e/tests/... — all 37 tests listed
  • Full suite run against Claude
  • Full suite run against Gemini
  • Full suite run agains Opencode
  • CI workflow update

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings February 24, 2026 05:54
@cursor
Copy link

cursor bot commented Feb 24, 2026

PR Summary

Medium Risk
Moderate risk due to large test-suite relocation and CI workflow changes (new dependencies, agent initialization/auth, and artifact handling) that could cause flaky or failing E2E runs despite no core runtime logic changes.

Overview
Migrates E2E coverage into a new top-level e2e/ suite (agents + shared testutil + entire CLI wrapper) and deletes the previous cmd/entire/cli/e2e_test/ shadow-hook based harness and scenarios.

Adds a tmux-driven interactive runner and per-agent registration/gating (Claude/Gemini/OpenCode), plus a new set of //go:build e2e tests that exercise checkpointing/rewind/attribution and various commit flows using the real entire binary.

Updates the GitHub Actions e2e.yml workflow to install tmux, build/install entire, warm up OpenCode, configure Claude API-key auth, write artifacts to E2E_ARTIFACT_DIR, and upload them; also updates linting exclusions for e2e/ and ignores e2e/artifacts/ in git.

Written by Cursor Bugbot for commit ed6e83c. Configure here.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR consolidates E2E test infrastructure by moving the comprehensive test suite from a separate repository into e2e/ within the CLI repo, replacing the older shadow-hook based suite at cmd/entire/cli/e2e_test/.

Changes:

  • Adds new consolidated E2E test suite in e2e/ with 37 tests covering single/multi-session workflows, auto-commit, attribution, rewind, stash, split commits, and edge cases
  • Removes old shadow-hook based E2E suite from cmd/entire/cli/e2e_test/
  • Updates mise tasks to point at e2e/tests/... with optional filter arguments for running specific tests

Reviewed changes

Copilot reviewed 40 out of 41 changed files in this pull request and generated no comments.

Show a summary per file
File Description
mise.toml Updates E2E test tasks to target new e2e/tests/... path with filter arguments
e2e/testutil/repo.go Core test utilities for repo setup, agent execution, and git operations
e2e/testutil/metadata.go Checkpoint and session metadata structures for validation
e2e/testutil/assertions.go Test assertion helpers for checkpoint validation and verification
e2e/testutil/artifacts.go Artifact capture system for debugging failed tests
e2e/tests/*.go 37 comprehensive E2E tests across multiple workflow scenarios
e2e/agents/*.go Agent abstraction layer supporting Claude Code, Gemini CLI, and OpenCode
e2e/entire/entire.go CLI wrapper functions for entire commands in tests
cmd/entire/cli/e2e_test/*.go Removed old E2E test suite files (11 files deleted)
.gitignore Adds e2e/artifacts/ to ignored files

@khaong
Copy link
Contributor Author

khaong commented Feb 24, 2026

bugbot run

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

@khaong khaong force-pushed the alex/consolidate-e2e-tests branch from 6963fc2 to d9ac795 Compare February 24, 2026 12:54
@khaong khaong marked this pull request as ready for review February 24, 2026 12:55
@khaong khaong requested a review from a team as a code owner February 24, 2026 12:55
@khaong khaong enabled auto-merge February 24, 2026 12:55
@khaong khaong disabled auto-merge February 24, 2026 12:58
@khaong khaong enabled auto-merge February 24, 2026 13:03
@Soph
Copy link
Collaborator

Soph commented Feb 24, 2026

I tried this locally, and I couldn't get it to work at all :(

Claude thinks:

⏺ That's the bug. StartSession has the comment:
  // Locally, we skip CLAUDE_CONFIG_DIR so the Keychain-based auth works.

  But RunPrompt always sets CLAUDE_CONFIG_DIR, even locally. On macOS, Claude Code uses Keychain for auth, which is tied to the default ~/.claude path. When you override CLAUDE_CONFIG_DIR to a temp dir, Claude Code can't find auth credentials in the Keychain for that path → "Not logged in".

  RunPrompt needs the same CI-only guard that StartSession has. But this is the branch author's code, not ours. Do you want me to fix it, or flag it to the branch author?

I'm not sure if this is a me thing or why it works for you, so stopping trying to fix it :(

Also:

  1. files_touched per-checkpoint validation is gone from most tests
  2. Shadow branch cleanup checks are gone
  3. Subagent task checkpoint fields not validated (to be fair, those were logs only so fine to skip them)

And last but not least: it's just testing the binary in path, I feel mise run test:e2e makes it look like if it would test what's checked out and that's what we should do, right?

khaong and others added 17 commits February 25, 2026 09:58
Moves the entire-cli-e2e-tests suite into e2e/ as the single E2E test
suite. Uses real git hooks, tmux interactive sessions, artifact capture,
and ForEachAgent multi-agent execution.

Key features:
- 3 agents: Claude Code, Gemini CLI, OpenCode
- E2E_AGENT env var for CI matrix targeting
- //go:build e2e tag (tests don't compile in normal builds)
- Artifact capture for failure debugging
- Per-agent concurrency gating and timeout scaling
- Deep checkpoint metadata validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The consolidated E2E suite with real hooks, tmux, and artifact capture
now lives at e2e/. The old suite's unique scenarios have been ported,
and its internal-logic coverage is handled by existing unit/integration
tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updates test:e2e, test:e2e:claude, test:e2e:gemini, and test:e2e:opencode
tasks to point at ./e2e/tests/... and adds optional filter args. Fixes
gemini agent name to gemini-cli to match agent registration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: e5405a1ee475
The consolidated E2E suite needs tmux for interactive session tests
and the entire binary on PATH. Also prints entire version before
running tests for debugging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 2f51fe6d2c6f
CI runners don't have global git user.name/user.email configured,
causing `git commit` to fail. Set per-repo identity after git init.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 197748497091
Create ~/.claude/.claude.json with primaryApiKey so Claude Code's
interactive TUI uses API key auth instead of trying OAuth login,
which isn't possible on headless CI runners.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 8e2188ca94aa
On CI runners (no macOS Keychain), use isolatedConfigDir with
CLAUDE_CONFIG_DIR so Claude Code picks up ANTHROPIC_API_KEY
from the environment instead of trying the OAuth browser flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: ca3321c4f01b
- Harden TestStashModificationsToTrackedFiles prompt for opencode
- Add WaitForSessionMetadata to handle race where checkpoint branch
  advances before session metadata is fully committed
- Upload e2e artifacts on CI for failure debugging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 3909f9373db2
Relax golangci-lint rules for e2e/ (test infrastructure, not production
code) and fix missing imports that caused compilation errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: ceb86081b9a8
opencode's first-run DB migration and node_modules resolve can race
with test execution (upstream issue #6935, "Cannot find package jose").
Retry `opencode --version` until initialization completes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: e4a2bbc441e5
Non-Claude agents (opencode) store transcripts in pretty-printed JSON,
not JSONL. The per-line parse check produced ~60 log lines of noise
per test while providing no signal. Keep the non-empty check only.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: cc64c19b8a48
opencode's TUI occasionally fails to render on CI, producing a
completely empty tmux pane. Retry once if the pane is empty after
the 15s startup timeout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: e845678e905b
opencode --version doesn't trigger DB schema creation or node_modules
resolution. Use a trivial `opencode run` prompt instead so the SQLite
tables and jose/auth modules are fully initialized before parallel
tests start.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: b5d954fb2751
When WaitFor fails during startup dialog handling, the tmux session
was returned alongside the error. Callers t.Fatalf before registering
defer session.Close(), leaking the tmux process.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 087f3b6dc713
opencode sometimes amends the first commit instead of creating a
second one. Explicitly instruct "create a NEW commit (do not amend)".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: edfc32757ede
Interactive agents can show their prompt pattern before git commit
lands on disk. Convert AssertNewCommits from a one-shot assertion to
a 10s polling loop so it handles this timing gap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: c451382e63b0
khaong and others added 14 commits February 25, 2026 09:58
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: b735aeb73034
Gemini 2.5 Flash struggles with deterministic instruction following
in E2E tests. Try 3-flash-preview for better compliance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 940608ac4db5
StartSession was missing --model, causing gemini to auto-route to
gemini-3.1-pro-preview (much slower, causing timeouts). Now uses
gemini-3-flash-preview consistently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 7289c79c62df
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: bc0543ef148b
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: cc8dbfd61fcf
Gemini CLI checks CI=true in isHeadlessMode() and forces non-interactive
mode, causing all StartSession-based tests to fail on GitHub Actions.
Unsetting CI in the tmux session lets gemini detect the PTY and launch
its interactive TUI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 0ac2a9e7eb50
Gemini's isHeadlessMode() checks both CI and GITHUB_ACTIONS env vars.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 7e28298b45f2
On CI, gemini shows first-run auth selection and hooks trust dialogs
before reaching the input prompt. Expand the dismissal loop to handle
"Enter to select" (auth) and "Enter to confirm" (trust) screens.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: c61c95596e76
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 15fdc925b22b
Each agent now implements Bootstrap() for CI setup (auth config,
warmup). Workflows call `go run ./e2e/bootstrap` instead of
agent-specific shell steps. Also restores cancel-in-progress: true.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 23c2ea43e80d
Ports the deleted TestE2E_ResumeInRelocatedRepo to an integration test.
Verifies that entire resume reads checkpoint data from the git metadata
branch (which travels with the repo) and writes transcripts to the
current project dir, not any stored path from checkpoint creation time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 9132805c2911
- Create e2e/README.md from the old E2E repo's CLAUDE.md, adapted
  for the consolidated structure (commands, debugging, CI workflows)
- Update CLAUDE.md to reference e2e/README.md and fix stale paths
- Fix duplicate import lint error in e2e/bootstrap/main.go

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: cc6f59d3bc72
Move the debug-entire-cli-e2e skill into project scope as debug-e2e,
updated for the consolidated repo structure (e2e/tests/, e2e/artifacts/).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 6f620def70d7
Add IsTransientError to Agent interface with per-agent implementations:
- Gemini: 500/INTERNAL, incomplete JSON, rate limits, RESOURCE_EXHAUSTED
- Claude: overloaded (529), rate limits, connection errors
- OpenCode: same as Claude (shared Anthropic API)

RunPrompt in testutil retries once after 5s on transient errors,
reducing flaky test failures from intermittent API issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 44385f3a7892
@khaong khaong force-pushed the alex/consolidate-e2e-tests branch from ec0233d to cf9b3f7 Compare February 24, 2026 23:03
dvydra and others added 6 commits February 25, 2026 10:50
RunPrompt unconditionally set CLAUDE_CONFIG_DIR to an isolated temp
directory, causing Claude Code to skip macOS Keychain/OAuth auth and
report "Not logged in" for team plan users without ANTHROPIC_API_KEY.
StartSession already gated this behind CI—make RunPrompt consistent.

Also add [e2e] stderr logging to Bootstrap, RunPrompt, and StartSession
so auth mode is immediately visible when debugging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 394af38831af
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 7798d81810f3
Verify git, tmux, entire, and all registered agent binaries are on $PATH
before running any tests, failing early with a clear error instead of
cryptic failures deep in test execution.

Adds Binary() to the Agent interface as the single source of truth for
each agent's CLI binary name, used by both preflight checks and the
existing RunPrompt/StartSession/Output.Command code paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 1b175070b86e
We are adding this to Claude's environment to stop it from trying to
clone Claude's "marketplace plugins" Git repo.  This meant that on a
user's system it was trying to use their SSH credentials to clone from
Github, which is disruptive to say the least (on some unnamed
individuals' machines that broke things because their
Secretive/TPM-based SSH keychain couldn't keep up with the number of
requests).

Just disable this for E2E testing.

Co-authored-by: alex <alex@entire.io>
This should make it so that we are always using "vanilla" Git settings,
regardless of what weird tricks a user's ~/.gitconfig does.

Co-authored-by: alex <alex@entire.io>
Gemini's prompt pattern can match intermediate TUI output before the
agent finishes writing files to disk. Replace one-shot AssertFileExists
with WaitForFileExists (30s poll) to handle the race.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 883125295b18
@khaong
Copy link
Contributor Author

khaong commented Feb 25, 2026

I tried this locally, and I couldn't get it to work at all :(

Claude thinks:

⏺ That's the bug. StartSession has the comment:
  // Locally, we skip CLAUDE_CONFIG_DIR so the Keychain-based auth works.

  But RunPrompt always sets CLAUDE_CONFIG_DIR, even locally. On macOS, Claude Code uses Keychain for auth, which is tied to the default ~/.claude path. When you override CLAUDE_CONFIG_DIR to a temp dir, Claude Code can't find auth credentials in the Keychain for that path → "Not logged in".

  RunPrompt needs the same CI-only guard that StartSession has. But this is the branch author's code, not ours. Do you want me to fix it, or flag it to the branch author?

I'm not sure if this is a me thing or why it works for you, so stopping trying to fix it :(

Also:

  1. files_touched per-checkpoint validation is gone from most tests
  2. Shadow branch cleanup checks are gone
  3. Subagent task checkpoint fields not validated (to be fair, those were logs only so fine to skip them)

And last but not least: it's just testing the binary in path, I feel mise run test:e2e makes it look like if it would test what's checked out and that's what we should do, right?

@Soph - Yup, it was a real problem 🤦‍♂️

It turns out that I've got my claude set up just a little bit different to the 'standard OAuth' flow.

Thanks to @dvydra @toothbrush we've got to the bottom of it and fixed some other 'user scope' transgressions the tests were doing.

I think longer term we want to move these tests to docker in any case, just to be 100% certain about blast radius.

khaong and others added 3 commits February 25, 2026 14:27
Port testreport tool from original e2e repo and wire it into mise tasks.
E2e tasks now use gotestsum to capture JSON test events and generate
a summary report (report.txt + report.nocolor.txt) in the artifact dir.

- Add e2e/cmd/testreport for parsing go test JSON into readable reports
- Add gotestsum and tmux to mise tools
- Simplify CI e2e workflow to let mise tasks manage artifact dirs
- Add /testreport to .gitignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 24613a91e2b6
OpenCode sometimes exits 0 despite fatal errors (e.g. "Token refresh
failed: 400") that only appear in stderr. This caused tests to proceed
as if the prompt succeeded, leading to confusing failures later.

- Add detectStderrError to catch "Error:" lines in stderr on exit 0
- Add "Token refresh failed" to transient error patterns for retry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 1b54f6374ffa
@khaong
Copy link
Contributor Author

khaong commented Feb 25, 2026

@Soph and as for the other points:

1. files_touched per-checkpoint validation

TestCheckpointMetadataDeepValidation validates files_touched end-to-end, and the assertion helpers (AssertCheckpointFilesTouched, AssertCheckpointFilesTouchedContains) are available for any test that needs them. Detailed per-field checkpoint validation is also covered extensively in integration tests (17 test files in cmd/entire/cli/integration_test/).

2. Shadow branch cleanup checks

TestShadowBranchCleanedAfterAgentCommit covers the e2e path. Detailed shadow branch lifecycle is covered in integration tests.

3. Testing the binary in PATH

Intentional design - the suite is meant to run against any installed binary for version comparison and regression detection. TestMain prints the version and stamps entire-version.txt into the artifact dir. Also added the version to the mise task output so it's explicit upfront what binary is being tested.

Show which binary is being tested upfront, matching the original repo's
pattern. Also includes the uncommitted opencode stderr detection fix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: f03cd80c7159
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants