
Testing
Write Agent Runtime E2E tests for LobeChat with minimal mocks, real model-bank config, and flexible LLM spies.
Overview
Testing is an agent skill for the Ship phase that documents LobeChat Agent Runtime E2E patterns with PGLite, in-memory Redis, and vi.spyOn-based LLM fakes.
Install
npx skills add https://github.com/lobehub/lobe-chat --skill testingWhat is this skill?
- Minimal mock principle: only PGLite DB and two in-memory Redis managers
- Real model-bank, Mecha engines, AgentRuntimeService, and Coordinator—not stubbed
- vi.spyOn preferred over vi.mock for per-test LLM and stream scenarios
- Default model gpt-5 for stable model-bank availability
- getTestDB() from @lobechat/database/test-utils per beforeEach setup
- 3 external dependencies mocked: PGLite DB and 2 in-memory Redis managers
- Default E2E model: gpt-5
Adoption & trust: 814 installs on skills.sh; 78.4k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your agent runtime tests either over-mock internals or cannot vary LLM streams and tool calls per scenario.
Who is it for?
Developers working in lobe-chat who need agent runtime E2E tests aligned with official test-utils and model-bank defaults.
Skip if: Greenfield projects without LobeChat packages or builders who only need shallow UI component tests.
When should I use this skill?
Writing or refactoring LobeChat Agent Runtime E2E tests that should keep model-bank and runtime services real.
What do I get? / Deliverables
You run E2E suites that exercise real runtime wiring with only three external dependencies mocked and adjustable stream spies per test.
- beforeEach getTestDB() database setup blocks
- createOpenAIStreamResponse-style helpers for stream scenarios
- E2E specs that respect minimal-mock boundaries
Recommended Skills
Journey fit
Agent runtime end-to-end tests belong in Ship when you validate coordinator behavior before release—not during initial ideation. Testing subphase covers E2E patterns, mock boundaries, and stream/tool-call assertions for agent stacks.
How it compares
Framework-specific E2E doctrine—not a generic Vitest cheat sheet or Playwright browser testing skill.
Common Questions / FAQ
Who is testing for?
LobeChat contributors and indie builders extending agent runtime code who want production-faithful E2E coverage.
When should I use testing?
In Ship testing before you merge agent runtime changes, or in Build backend/agent-tooling when you add new coordinator paths.
Is testing safe to install?
Check the Security Audits panel on this Prism page; test skills may reference shell and package installs in your repo.
SKILL.md
READMESKILL.md - Testing
# Agent Runtime E2E Testing Guide ## Core Principles ### Minimal Mock Principle Only mock **three external dependencies**: | Dependency | Mock | Description | | ---------- | -------------------------- | ------------------------------------------------------- | | Database | PGLite | In-memory database from `@lobechat/database/test-utils` | | Redis | InMemoryAgentStateManager | Memory implementation | | Redis | InMemoryStreamEventManager | Memory implementation | **NOT mocked:** - `model-bank` - Uses real model config - `Mecha` (AgentToolsEngine, ContextEngineering) - `AgentRuntimeService` - `AgentRuntimeCoordinator` ### Use vi.spyOn, not vi.mock Different tests need different LLM responses. `vi.spyOn` provides: - Flexible return values per test - Easy testing of different scenarios - Better test isolation ### Default Model: gpt-5 - Always available in `model-bank` - Stable across model updates ## Technical Implementation ### Database Setup ```typescript import { LobeChatDatabase } from '@lobechat/database'; import { getTestDB } from '@lobechat/database/test-utils'; let testDB: LobeChatDatabase; beforeEach(async () => { testDB = await getTestDB(); }); ``` ### OpenAI Stream Response Helper ```typescript export const createOpenAIStreamResponse = (options: { content?: string; toolCalls?: Array<{ id: string; name: string; arguments: string }>; finishReason?: 'stop' | 'tool_calls'; }) => { const { content, toolCalls, finishReason = 'stop' } = options; return new Response( new ReadableStream({ start(controller) { const encoder = new TextEncoder(); if (content) { const chunk = { id: 'chatcmpl-mock', object: 'chat.completion.chunk', model: 'gpt-5', choices: [{ index: 0, delta: { content }, finish_reason: null }], }; controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`)); } // ... tool_calls handling // ... finish chunk controller.enqueue(encoder.encode('data: [DONE]\n\n')); controller.close(); }, }), { headers: { 'content-type': 'text/event-stream' } }, ); }; ``` ### State Management ```typescript import { InMemoryAgentStateManager, InMemoryStreamEventManager, } from '@/server/modules/AgentRuntime'; const stateManager = new InMemoryAgentStateManager(); const streamEventManager = new InMemoryStreamEventManager(); const service = new AgentRuntimeService(serverDB, userId, { coordinatorOptions: { stateManager, streamEventManager }, queueService: null, streamEventManager, }); ``` ### Mock OpenAI API ```typescript const fetchSpy = vi.spyOn(globalThis, 'fetch'); it('should handle text response', async () => { fetchSpy.mockResolvedValueOnce(createOpenAIStreamResponse({ content: 'Response text' })); // ... execute test }); it('should handle tool calls', async () => { fetchSpy.mockResolvedValueOnce( createOpenAIStreamResponse({ toolCalls: [ { id: 'call_123', name: 'lobe-web-browsing____search', arguments: JSON.stringify({ query: 'weather' }), }, ], finishReason: 'tool_calls', }), ); // ... execute test }); ``` ## Notes 1. **Test isolation**: Clean `InMemoryAgentStateManager` and `InMemoryStreamEventManager` after each test 2. **Timeout**: E2E tests may need longer timeouts 3. **Debug**: Use `DEBUG=lobe-server:*` for detailed logs # Database Model Testing Guide Test `packages/database` Model layer. ## Dual Environment Verification (Required) ```bash # 1. Client environment (fast) cd packages/database && TEST_SERVER_DB=0 bunx vitest run --silent='passed-only' '[file]' # 2. Server environment (compatibility) cd packages/database && TEST_SERVER_DB=1 bunx vitest run --silent