This project uses Polly.js for robust E2E testing. It records and replays HTTP interactions with upstream LLM providers, ensuring tests are fast, reliable, and run without API keys.
The E2E tests are split by API type:
- Chat API:
packages/backend/src/services/__tests__/e2e_vcr_chat.test.ts(Cases:cases/chat/) - Messages API:
packages/backend/src/services/__tests__/e2e_vcr_messages.test.ts(Cases:cases/messages/)
- Dynamic Discovery: Each suite loads
.jsonfiles from its respective directory incases/. - Cassette Recording (Polly.js):
- Requests to upstream providers are intercepted.
- In Record Mode, real API calls are made and saved to
__cassettes__/as JSON files. - In Replay Mode, the network is completely mocked using these saved JSON files.
- Validation: The test verifies that the
Dispatcherlogic correctly transforms the upstream data into a valid Unified response.
Backend tests run on Vitest.
packages/backend/vitest.config.tsis the single backend test-runner config.packages/backend/test/vitest.global-setup.tscreates a temporary file-backed SQLite database, generates Drizzle metadata if needed, runs migrations once, and removes the temp directory after the run.packages/backend/test/vitest.setup.tsinstalls stable logger/debug test doubles for each worker.- Root
bunfig.tomlintentionally blocks rawbun testat the repo root and points contributors tocd packages/backend && bun run test. packages/backend/bunfig.tomlintentionally blocks rawbun testinpackages/backendand points contributors tobun run test.
Vitest restores mocks reliably, but shared dependencies should still follow these rules:
- Use the shared setup: Common modules like
src/utils/loggerare mocked once invitest.setup.ts. - Robust Mocking: If you mock a module in a specific test file, your mock MUST implement the relevant public interface of that module.
- Prefer Spying: If you need to assert that a shared dependency was called, use
vi.spyOnorregisterSpyrather than replacing the whole module repeatedly.
import { logger } from "src/utils/logger";
import { expect, test, vi } from "vitest";
test("my test", () => {
const infoSpy = vi.spyOn(logger, "info");
// ... run code ...
expect(infoSpy).toHaveBeenCalled();
});From the repo root:
bun run testOr from the backend package:
cd packages/backend
bun run testNote:
bun testis intentionally blocked both at repo root and inpackages/backend. Usebun run testinstead.
From the repo root:
bun run test:watchOr from the backend package:
cd packages/backend
bun run test:watchTip: You can also run this via the VS Code task Bun: Backend Tests.
To capture new network interactions for ALL tests:
# From project root
PLEXUS_TEST_API_KEY="your-openai-key" \
PLEXUS_TEST_ANTHROPIC_API_KEY="your-anthropic-key" \
bun run update-cassettesNote: The suite automatically scrubs sensitive headers and model names before saving to disk.
To test the full system manually (Frontend + Backend):
- Start the development stack:
bun dev
- Open the Dashboard at
http://localhost:4000. - Send requests to the API proxy at
http://localhost:4000/v1/...usingcurlortestcommands/test_request.ts.
The following environment variables are used during Record Mode:
| Variable | Description | Default |
|---|---|---|
PLEXUS_TEST_API_KEY |
Chat-compatible API Key. | scrubbed_key |
PLEXUS_TEST_ANTHROPIC_API_KEY |
Messages API Key. | scrubbed_key |
PLEXUS_TEST_BASE_URL |
Base URL for Chat provider. | https://api.upstream.mock/openai/v1 |
PLEXUS_TEST_ANTHROPIC_BASE_URL |
Base URL for Messages provider. | https://api.anthropic.com/v1 |