Skip to content

test: in-process e2e via SDK server; fix recovery race with echoed chat.message#12

Merged
ShutovKS merged 1 commit into
devfrom
test/e2e-sdk-server
Jul 14, 2026
Merged

test: in-process e2e via SDK server; fix recovery race with echoed chat.message#12
ShutovKS merged 1 commit into
devfrom
test/e2e-sdk-server

Conversation

@ShutovKS

Copy link
Copy Markdown
Owner

Summary

  • New in-process e2e suite (test/e2e-server.test.ts): boots a real headless OpenCode server via createOpencodeServer, drives it through the SDK client, and observes plugin decisions through the SSE event stream (session.error, tui.toast.show, message.updated) plus the fake provider's call log. Fully isolated per test (temp dirs, XDG_DATA_HOME/XDG_CONFIG_HOME overrides, port: 0).
  • Real bug found and fixed: recovery (recover_original_model, feat: recover to original model after cooldown expires #5) never fired in the live runtime. OpenCode invokes the chat.message hook for the plugin's own retry prompt; pendingModel was set only after promptAsync resolved, so the echoed hook call was treated as a manual model switch — originalModel was overwritten with the fallback and recovery was silently disabled. Fix: set pendingModel/awaitingModel before prompting, roll back if the retry is not accepted.
  • chat.message now backfills state models via getState when a model is present (in the live runtime session.created carries no model, unlike the unit mocks).
  • cascade-model in the fake provider switched to status 400: OpenCode self-retries 429/503 with its own backoff before emitting session.error, masking plugin-driven cascade.
  • New unit test reproduces the echoed-prompt runtime behavior so the regression stays covered without the e2e.

Runtime findings validated by e2e

  • session.error really carries no model in properties — the plugin correctly falls back to state.currentModel (mock/runtime divergence confirmed harmless).
  • Status-path fallback, retry_on_patterns (status-independent path), cascade fallback, and recovery all proven against a live server.

Testing

  • bun run typecheck — clean
  • bun test test/index.test.ts — 59 pass
  • bun test test/e2e-server.test.ts — 4 pass (~25s)
  • bun test test/e2e.test.ts — 7 pass (untouched CLI e2e)

🤖 Generated with Claude Code

…at.message

Add test/e2e-server.test.ts: boots a real headless OpenCode server
(createOpencodeServer) with a fake OpenAI provider, drives it through the
SDK client, and observes plugin decisions via the SSE event stream. Covers
status-path fallback, retry_on_patterns, cascade fallback, and recovery.

The recovery e2e exposed a real bug: OpenCode invokes the chat.message hook
for the plugin's own retry prompt. Since pendingModel was set only after
promptAsync resolved, that echoed hook call was treated as a manual model
switch, overwriting originalModel with the fallback and silently disabling
recover_original_model in the live runtime. Set pendingModel/awaitingModel
before prompting (and roll back if the retry is not accepted).

Also: chat.message now backfills state models via getState when a model is
present (session.created carries no model in the live runtime), and
cascade-model uses 400 (OpenCode self-retries 429/503 before emitting
session.error, masking plugin-driven cascade). New unit test reproduces the
echoed-prompt runtime behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 14, 2026 20:52

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ShutovKS ShutovKS merged commit 4bb1406 into dev Jul 14, 2026
1 check passed
@ShutovKS ShutovKS deleted the test/e2e-sdk-server branch July 14, 2026 20:52
@ShutovKS ShutovKS mentioned this pull request Jul 14, 2026
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.

2 participants