Skip to content

test(harness): add e2e tests for thinking, tool calls, web search, and model switching - #183

Open
FredLiu876 wants to merge 2 commits into
mainfrom
harness-e2e-compat-tests
Open

FredLiu876 wants to merge 2 commits into
mainfrom
harness-e2e-compat-tests

Conversation

@FredLiu876

@FredLiu876 FredLiu876 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Adds e2e tests that run the real claude-code, codex, and pi harness binaries against real Baseten routes through the harness configs grid setup writes.

What's in the suite

Four behavior groups under TestE2EHarness* (both new files are //go:build e2e):

  • Thinking contract: pi runs an Anthropic-format reasoning route at a level taken from its models.json, claude-code runs a route with an effort ceiling taken from settings.json. The suite asserts thinking actually engages (thinking blocks in the claude stream, no error turns in pi) and greps output for invalid_request_error and "not supported for this model", the exact failure signature of the original incident, so a contract break fails the test instead of degrading silently.
  • Tool calls: claude-code and codex complete a real file task: read task.txt, write answer.txt containing exactly its contents. Asserts tool calls fired and the file content is byte-exact.
  • Web search: claude-code must invoke WebSearch and reply with a version number. The codex cell is a designed skip: harness setup writes no web_search setting and codex's default web-search mode doesn't work against Baseten routes (the cell still runs if a config ever enables it).
  • Model switching: claude-code, codex, and pi each run a two-phase conversation: create stage1.txt and remember a marker word under model A, then resume the session or thread under model B and write the recalled word to stage2.txt. Proves session continuity survives a mid-conversation model switch.

Nothing is hardcoded. Models, routes, effort ceilings, endpoints, and web-search support are read back from the configs grid setup actually wrote (settings.json for claude-code, config.toml + model_catalog_json for codex, models.json for pi), so the suite runs in any org with harness-enabled routes. Below threshold it skips with the precise reason: fewer than two routes skips switching, no reasoning-capable Anthropic-format route skips pi thinking, no effort ceiling skips claude-code thinking.

Safety and portability

  • Fully hermetic: the CLI registry (via BASETEN_CONFIG_DIR) and every scrap of harness state (HOME, CLAUDE_CONFIG_DIR, CODEX_HOME, PI_CODING_AGENT_DIR, the XDG dirs) lives in per-test temp roots, so tests can never touch a developer's or CI's real harness configs.
  • Each harness run is capped at 150s and its children are killed with a process-group SIGKILL afterward so nothing orphans. The temp-root cleanup retries once and then logs instead of failing the test, because codex spawns late background writers that race directory removal.
  • Team resolution degrades gracefully: a no-team dry-run probe runs first, org team list only on failure, and a team-list error of any kind (e.g. a 404 on remotes without the app API) becomes a skip naming the cause rather than a CI failure.
  • Credentials come from --use-session-credential, so the gate env key is used and nothing is minted.

Validation

  • gofmt, go build ./..., go vet ./..., go vet -tags=e2e ./internal/e2e-tests/, and the unit suite (go test ./internal/...) all clean.
  • Live suite ran three times end-to-end against the real endpoint (go test -tags=e2e -count=1 -run TestE2EHarness): all green: 9 subtests with one designed skip (codex web search), ~100-105s per run, zero leftover temp roots.
  • Bug-catch check during development: the pi thinking cell fails against pre-Force adaptive thinking for Pi Anthropic routes #175 pi with the exact 400 from the Slack thread ("thinking.type.enabled" is not supported for this model) and passes with Force adaptive thinking for Pi Anthropic routes #175's forceAdaptiveThinking, i.e. the test that would have caught the original incident.

CI picks this up through the existing BASETEN_E2E_TEST_API_KEY / BASETEN_E2E_TEST_REMOTE_URL secrets (.github/workflows/ci.yml:69-70), e2e runs on ubuntu runners only, and tests skip when the key is absent.

Out of scope

  • No product or CLI code changes: two new test files only.
  • No opencode coverage (not installed locally). The code skips cleanly when the binary is absent, so CI would cover it if present.
  • The pre-Force adaptive thinking for Pi Anthropic routes #175 bug-catch proof was a development-time validation against a scratch tree, not part of the suite.

Copilot AI balanced review requested due to automatic review settings October 22, 2026 18:47
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Assertion gaps and setup-error handling can hide regressions, while Codex web-search detection needs correction.

6 open issues
What changed in this PR

Adds end-to-end coverage for real harness binaries using configurations generated by grid setup, including regression coverage for Pi’s adaptive-thinking integration.

Changes:

  • Adds thinking, file-tool, web-search, and model-switching scenarios for Claude Code, Codex, and Pi.
  • Adds shared helpers for configuration discovery, isolated harness state, bounded subprocess execution, and output parsing.
  • Gates execution on credentials, installed binaries, and available route capabilities without changing product code.
File Description
internal/​e2e-tests/​harness_test.go Adds harness behavior tests and session-continuity checks.
internal/​e2e-tests/​harness_helpers_test.go Adds setup, configuration parsing, process management, and assertion helpers.

Review effort: Balanced

t.Skipf("the grid routes API (/v1/grid/routes) is not deployed on this remote (%s); harness e2e needs a remote with the grid routes feature enabled", remoteURL)
case strings.Contains(msg, "no routes"),
strings.Contains(msg, "no accessible routes"),
strings.Contains(msg, "no usable model metadata"),
}
lastErr = err
}
t.Skipf("none of the org's %d teams have routes %s can call; last probe error: %v", len(listed.Teams), name, lastErr)
Comment on lines +257 to +264
if tools, ok := data["tools"].(map[string]any); ok {
switch search := tools["web_search"].(type) {
case bool:
h.codexWebSearch = search
case string:
h.codexWebSearch = search != "" && search != "disabled" && search != "false"
}
}
t.Helper()
data, err := os.ReadFile(filepath.Join(dir, name))
require.NoError(t, err, "reading %s, which the harness was asked to produce", name)
return strings.TrimRight(string(data), "\n")
Comment thread internal/e2e-tests/harness_test.go Outdated
require.Zero(t, piErrorTurns(objects), "pi ended a turn with an API error; stdout:\n%s\nstderr:\n%s", stdout, stderr)
reply := piAssistantText(objects)
require.Contains(t, reply, harnessThinkingMarker, "pi's final reply lacks the marker; stdout:\n%s\nstderr:\n%s", stdout, stderr)
step(t, "pi thinking seen: %v", piThinkingSeen(objects))
Comment on lines +90 to +91
kind, _ := object["type"].(string)
if strings.Contains(kind, "web_search") {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Have not reviewed in full yet (will wait until copilot stuff looked at if needed), but can you confirm that this communicates with everything via the CLI directly the same way a user can? Also, is this a superset of what is in the existing e2e-ish harness/grid tests in the internal/cmd area? And if so, can those be removed and can the harness install step of CI be moved to as part of this e2e test run and not general purpose? One of my concerns of doing that however is that we may want to run across OS's which this test does not do, so we should discuss how best to do that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants