feat: add e2e tests with Letta Cloud (#149)
E2E testing infrastructure that tests the full message flow against a real Letta Cloud agent. Changes: - Add MockChannelAdapter for simulating inbound/outbound messages - Add e2e/bot.e2e.test.ts with 4 e2e tests: - Simple message/response - /status command - /help command - Conversation context retention - Add 'mock' to ChannelId type - Update CI workflow with separate e2e job (uses secrets) - Add npm run test:e2e script E2E tests require: - LETTA_API_KEY (already in repo secrets) - LETTA_E2E_AGENT_ID (needs to be added) E2E tests are skipped locally without these env vars. Written by Cameron ◯ Letta Code "Trust, but verify." - Ronald Reagan (on e2e testing)
This commit is contained in:
92
e2e/bot.e2e.test.ts
Normal file
92
e2e/bot.e2e.test.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* E2E Tests for LettaBot
|
||||
*
|
||||
* These tests use a real Letta Cloud agent to verify the full message flow.
|
||||
* Requires LETTA_API_KEY and LETTA_E2E_AGENT_ID environment variables.
|
||||
*
|
||||
* Run with: npm run test:e2e
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||
import { LettaBot } from '../src/core/bot.js';
|
||||
import { MockChannelAdapter } from '../src/test/mock-channel.js';
|
||||
import { mkdtempSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
|
||||
// Skip if no API key (local dev without secrets)
|
||||
const SKIP_E2E = !process.env.LETTA_API_KEY || !process.env.LETTA_E2E_AGENT_ID;
|
||||
|
||||
describe.skipIf(SKIP_E2E)('e2e: LettaBot with Letta Cloud', () => {
|
||||
let bot: LettaBot;
|
||||
let mockAdapter: MockChannelAdapter;
|
||||
let tempDir: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Create temp directory for test data
|
||||
tempDir = mkdtempSync(join(tmpdir(), 'lettabot-e2e-'));
|
||||
|
||||
// Set agent ID from secrets
|
||||
process.env.LETTA_AGENT_ID = process.env.LETTA_E2E_AGENT_ID;
|
||||
|
||||
// Initialize bot with test config
|
||||
bot = new LettaBot({
|
||||
model: 'claude-sonnet-4-20250514', // Good balance of speed/quality
|
||||
workingDir: tempDir,
|
||||
agentName: 'e2e-test',
|
||||
});
|
||||
|
||||
// Register mock channel
|
||||
mockAdapter = new MockChannelAdapter();
|
||||
bot.registerChannel(mockAdapter);
|
||||
|
||||
console.log('[E2E] Bot initialized with agent:', process.env.LETTA_E2E_AGENT_ID);
|
||||
}, 30000); // 30s timeout for setup
|
||||
|
||||
afterAll(async () => {
|
||||
// Cleanup temp directory
|
||||
try {
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
});
|
||||
|
||||
it('responds to a simple message', async () => {
|
||||
const response = await mockAdapter.simulateMessage('Say "E2E TEST OK" and nothing else.');
|
||||
|
||||
expect(response).toBeTruthy();
|
||||
expect(response.length).toBeGreaterThan(0);
|
||||
// The agent should respond with something containing our test phrase
|
||||
expect(response.toUpperCase()).toContain('E2E TEST OK');
|
||||
}, 60000); // 60s timeout
|
||||
|
||||
it('handles /status command', async () => {
|
||||
const response = await mockAdapter.simulateMessage('/status');
|
||||
|
||||
expect(response).toBeTruthy();
|
||||
// Status should contain agent info
|
||||
expect(response).toMatch(/agent|status/i);
|
||||
}, 30000);
|
||||
|
||||
it('handles /help command', async () => {
|
||||
const response = await mockAdapter.simulateMessage('/help');
|
||||
|
||||
expect(response).toBeTruthy();
|
||||
expect(response).toContain('LettaBot');
|
||||
expect(response).toContain('/status');
|
||||
}, 10000);
|
||||
|
||||
it('maintains conversation context', async () => {
|
||||
// First message - set context
|
||||
await mockAdapter.simulateMessage('Remember this number: 42424242');
|
||||
|
||||
// Clear messages but keep session
|
||||
mockAdapter.clearMessages();
|
||||
|
||||
// Second message - recall context
|
||||
const response = await mockAdapter.simulateMessage('What number did I just tell you to remember?');
|
||||
|
||||
expect(response).toContain('42424242');
|
||||
}, 120000); // 2 min timeout for multi-turn
|
||||
});
|
||||
Reference in New Issue
Block a user