End-to-end failover test
September 11, 2026 · View on GitHub
Verifies dsh-daruma actually fails over at runtime: the primary channel returns
429, daruma trips it and switches to the fallback channel, and the task
completes.
One command, any OS (recommended)
pnpm run build # the check runs the built lib/
pnpm run e2e:failover # Windows, Linux, macOS — same script
scripts/e2e-failover.mjs creates a throwaway DSH home under the OS temp
directory, links this checkout's dsh-daruma/daruma-core into a headless
profile, starts the mock LLM server on :3099, runs one task and asserts:
- the task prints
mock completion from mock-band exits 0, - the profile-local
failover-log.jsonlhas abootline and afailoverlinemock::mock-a -> mock::mock-bwith reasonRATE_LIMIT, - the profile-local
channel-health.jsonmarksmock::mock-aasCOOLDOWNandmock::mock-basHEALTHY.
Useful flags: --dsh <command|path-to-bin.js> (test a specific host build),
--mock-port <port>, --keep (keep the temp home for inspection). The
production ~/.dsh is never touched.
Running inside WSL? WSL appends the Windows PATH, so a bare
dshcan resolve to the Windows install under/mnt/c/…; Linux node then fails to load itssharp/koffibinaries. The script refuses such a host before starting anything (exit 2) and prints the fix — install the host inside the distro and put its bin directory first on PATH:npm install --global @deepseek-ai/dsh@0.1.0-rc.7 export PATH="$(npm prefix --global)/bin:$PATH" pnpm run e2e:failover
The manual walkthrough below is the same scenario step by step, for when you want to watch it in the browser or debug a specific stage.
One-command web test environment
Windows (PowerShell wrapper) and any OS (Node):
powershell -ExecutionPolicy Bypass -File scripts/start-daruma-test.ps1
pnpm run start:test
Starts the mock LLM server and the daruma-test DSH web profile together
(http://127.0.0.1:3081), then cleans both up on Ctrl+C. Open the browser to
see the channel-status dock and the backup-channel panel.
Prerequisites
- A built
dsh-daruma(pnpm --filter dsh-daruma build). - A test profile with
dsh-darumalinked in and a local mock provider (see below).
1. Start the mock LLM server
scripts/mock-llm-server.mjs serves an OpenAI-compatible endpoint:
mock-a returns 429, every other model returns a valid completion.
node scripts/mock-llm-server.mjs 3099
2. Create an isolated headless profile
dsh plugin --profile daruma-headless --help # init
dsh plugin --profile daruma-headless add \
link:./packages/daruma-core link:./packages/dsh-daruma # link the plugin
Then edit ~/.dsh/profiles/daruma-headless/package.json to add
@deepseek-ai/dsh-headless to dsh.profile.bundles, and write two files:
~/.dsh/profiles/daruma-headless/settings.yaml:
llm-pi-ai:
providers:
mock:
api: openai-completions
apiKeyEnv: MOCK_API_KEY
baseURL: http://127.0.0.1:3099/v1
retryPolicy:
mode: normal
maxRetries: 0
models:
- { id: mock-a, contextWindow: 65536 }
- { id: mock-b, contextWindow: 65536 }
agent-default-model:
provider: mock
model: mock-a
~/.dsh/profiles/daruma-headless/cordis.patch.yml (the profile lives under
$DSH_HOME/profiles/…; use whatever absolute paths your platform needs —
C:/Users/<you>/… on Windows, /home/<you>/… or /Users/<you>/… on
Linux/macOS):
- id: settings
name: '@deepseek-ai/dsh-settings-file'
config:
path: C:/Users/<you>/.dsh/profiles/daruma-headless/settings.yaml
- id: dsh-daruma
name: dsh-daruma
config:
channels:
- { provider: mock, model: mock-a }
- { provider: mock, model: mock-b }
failureBudget: 1
# REQUIRED: isolate the state file (and its sibling failover log) from
# production. Without this, mock channels pollute
# ~/.dsh/daruma/channel-health.json and future audits read fake entries.
stateFile: C:/Users/<you>/.dsh/profiles/daruma-headless/channel-health.json
The settings.path points at the profile-local file, so the global
~/.dsh/settings.yaml is never touched. The stateFile likewise keeps the
plugin's persisted health (and, since 0.1.4, the failover-log.jsonl written
next to it) inside the test profile instead of ~/.dsh/daruma/. When
stateFile is left unset the plugin defaults to $DSH_HOME/daruma/… (or
~/.dsh/daruma/… when DSH_HOME is unset) on every platform.
3. Run the task
$env:MOCK_API_KEY='dummy' # PowerShell; use `export MOCK_API_KEY=dummy` on Linux/macOS
dsh --profile daruma-headless "Reply with exactly: OK"
Expected output: mock completion from mock-b, exit code 0. The mock server
log shows the failover sequence:
[mock] request model=mock-a stream=true -> 429
[mock] request model=mock-b stream=true -> 200
4. Post-run assertions
After the task completes, verify three things:
- Production isolation.
~/.dsh/daruma/channel-health.jsonis unchanged (nomock::entries appeared; mock channels live only in the profile-localstateFile). - Durable failover log. The profile-local
failover-log.jsonl(sibling of the configuredstateFile) contains akind:"boot"line and akind:"failover"line with"from":"mock::mock-a","to":"mock::mock-b","reason":"RATE_LIMIT". - Success reset. Run the task once more — this time with the primary
healthy (restart the mock server so
mock-areturns 200, or pointagent-default-modelatmock-awith a healthy server). After it completes, the profile-localchannel-health.jsonshowsmock-aback atHEALTHYwithconsecutiveFailures: 0— theagent/pre-stepsuccess hook closed the circuit that the first run had tripped.