Files
gochat/channels/fake/tests/integration.test.ts
T
2026-07-09 18:14:45 +08:00

225 lines
7.2 KiB
TypeScript

import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import http from 'node:http';
import type { AddressInfo } from 'node:net';
import { createServer } from '../src/server.js';
import { store } from '../src/store/memory-store.js';
// Start a mock GoChat webhook server to capture the POSTs from FakeMessagePlatform.
let mockGochatPort: number;
let mockGochatServer: http.Server;
let receivedByGochat: any[] = [];
function startMockGochat(): Promise<http.Server> {
return new Promise((resolve) => {
const srv = http.createServer((req, res) => {
let body = '';
req.on('data', (chunk) => { body += chunk; });
req.on('end', () => {
if (req.method === 'POST') {
try {
receivedByGochat.push(JSON.parse(body));
} catch { /* ignore */ }
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok' }));
});
});
srv.listen(0, '127.0.0.1', () => {
const addr = srv.address() as AddressInfo;
mockGochatPort = addr.port;
resolve(srv);
});
});
}
let fakeApp: http.Server;
let fakePort: number;
function startFakeServer(webhookUrl: string): Promise<http.Server> {
const app = createServer({
port: 0,
gochatWebhookUrl: webhookUrl,
gochatFakeToken: '',
});
return new Promise((resolve) => {
const srv = app.listen(0, '127.0.0.1', () => {
const addr = srv.address() as AddressInfo;
fakePort = addr.port;
resolve(srv as unknown as http.Server);
});
});
}
async function fakeRequest(path: string, method: string = 'GET', body?: any): Promise<any> {
const res = await fetch(`http://127.0.0.1:${fakePort}${path}`, {
method,
headers: body ? { 'Content-Type': 'application/json' } : undefined,
body: body ? JSON.stringify(body) : undefined,
});
return { status: res.status, data: await res.json() };
}
beforeAll(async () => {
mockGochatServer = await startMockGochat();
fakeApp = await startFakeServer(`http://127.0.0.1:${mockGochatPort}/webhooks/fake/fake_test_1`);
});
afterAll(async () => {
await new Promise<void>((resolve) => fakeApp.close(() => resolve()));
await new Promise<void>((resolve) => mockGochatServer.close(() => resolve()));
});
beforeEach(() => {
store.reset();
receivedByGochat = [];
});
describe('FakeMessagePlatform', () => {
describe('/health', () => {
it('returns ok status', async () => {
const res = await fakeRequest('/health');
expect(res.status).toBe(200);
expect(res.data.status).toBe('ok');
expect(res.data.service).toBe('fake-message-platform');
});
});
describe('/api/send', () => {
it('sends a message to GoChat webhook', async () => {
const res = await fakeRequest('/api/send', 'POST', {
inbox_identifier: 'fake_test_1',
sender_id: 'customer_001',
sender_name: 'Test Customer',
content: 'Hello, I need help',
});
expect(res.status).toBe(200);
expect(res.data.status).toBe('sent');
expect(res.data.message_id).toBeDefined();
expect(res.data.gochat_status).toBe(200);
// Verify GoChat received the webhook
expect(receivedByGochat).toHaveLength(1);
expect(receivedByGochat[0].event).toBe('message.incoming');
expect(receivedByGochat[0].content).toBe('Hello, I need help');
expect(receivedByGochat[0].sender_id).toBe('customer_001');
});
it('returns 400 for missing required fields', async () => {
const res = await fakeRequest('/api/send', 'POST', {
inbox_identifier: 'fake_test_1',
});
expect(res.status).toBe(400);
expect(res.data.error).toContain('required');
});
});
describe('/api/agent/online + /api/status', () => {
it('marks an agent as online and shows in status', async () => {
const res = await fakeRequest('/api/agent/online', 'POST', {
agent_id: '1',
agent_name: 'Admin',
});
expect(res.status).toBe(200);
expect(res.data.online).toBe(true);
const statusRes = await fakeRequest('/api/status');
expect(statusRes.data.online_agents).toHaveLength(1);
expect(statusRes.data.online_agents[0].agent_id).toBe('1');
expect(statusRes.data.online_agents[0].agent_name).toBe('Admin');
});
it('marks an agent as offline', async () => {
await fakeRequest('/api/agent/online', 'POST', {
agent_id: '2',
agent_name: 'Agent2',
});
const offlineRes = await fakeRequest('/api/agent/offline', 'POST', {
agent_id: '2',
});
expect(offlineRes.status).toBe(200);
expect(offlineRes.data.online).toBe(false);
const statusRes = await fakeRequest('/api/status');
expect(statusRes.data.online_agents).toHaveLength(0);
});
});
describe('/api/reset', () => {
it('clears all state', async () => {
await fakeRequest('/api/agent/online', 'POST', {
agent_id: '1',
agent_name: 'Admin',
});
await fakeRequest('/api/send', 'POST', {
inbox_identifier: 'fake_test_1',
sender_id: 'cust_1',
sender_name: 'C',
content: 'hi',
});
const res = await fakeRequest('/api/reset', 'POST');
expect(res.status).toBe(200);
const statusRes = await fakeRequest('/api/status');
expect(statusRes.data.online_agents).toHaveLength(0);
expect(statusRes.data.total_sent).toBe(0);
});
});
describe('/receive', () => {
it('stores GoChat outbound messages', async () => {
const res = await fakeRequest('/receive', 'POST', {
message_id: 123,
conversation_id: 456,
content: 'Hello from agent',
content_type: 'text',
sender: { id: 1, name: 'Agent Wang', type: 'agent' },
});
expect(res.status).toBe(200);
expect(res.data.message_id).toBe(123);
const msgRes = await fakeRequest('/api/messages');
expect(msgRes.data.received).toHaveLength(1);
expect(msgRes.data.received[0].content).toBe('Hello from agent');
expect(msgRes.data.received[0].sender.type).toBe('agent');
});
});
describe('/api/typing', () => {
it('sends typing event to GoChat', async () => {
const res = await fakeRequest('/api/typing', 'POST', {
inbox_identifier: 'fake_test_1',
sender_id: 'customer_001',
typing: true,
});
expect(res.status).toBe(200);
expect(res.data.typing).toBe(true);
expect(receivedByGochat[0].event).toBe('typing.start');
});
it('sends typing stop event', async () => {
const res = await fakeRequest('/api/typing', 'POST', {
inbox_identifier: 'fake_test_1',
sender_id: 'customer_001',
typing: false,
});
expect(res.status).toBe(200);
expect(res.data.typing).toBe(false);
expect(receivedByGochat[0].event).toBe('typing.stop');
});
});
describe('/api/close', () => {
it('sends session.end event', async () => {
const res = await fakeRequest('/api/close', 'POST', {
inbox_identifier: 'fake_test_1',
conversation_id: 'ext_conv_1',
sender_id: 'customer_001',
});
expect(res.status).toBe(200);
expect(receivedByGochat[0].event).toBe('session.end');
expect(receivedByGochat[0].content).toBe('[session ended]');
});
});
});