import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import http from 'node:http'; import { ApiClient, ApiError } from '../src/api-client.js'; let server: http.Server; let port: number; beforeAll(async () => { server = http.createServer((req, res) => { if (req.url === '/api/v1/servers' && req.method === 'GET') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify([{ id: 'srv-1', name: 'slack' }])); } else if (req.url === '/api/v1/servers/srv-1' && req.method === 'GET') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ id: 'srv-1', name: 'slack', transport: 'STDIO' })); } else if (req.url === '/api/v1/servers' && req.method === 'POST') { const chunks: Buffer[] = []; req.on('data', (c: Buffer) => chunks.push(c)); req.on('end', () => { const body = JSON.parse(Buffer.concat(chunks).toString()); res.writeHead(201, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ id: 'srv-new', ...body })); }); } else if (req.url === '/api/v1/missing' && req.method === 'GET') { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not found' })); } else { res.writeHead(404); res.end(); } }); await new Promise((resolve) => { server.listen(0, () => { const addr = server.address(); if (addr && typeof addr === 'object') { port = addr.port; } resolve(); }); }); }); afterAll(() => { server.close(); }); describe('ApiClient', () => { it('performs GET request for list', async () => { const client = new ApiClient({ baseUrl: `http://localhost:${port}` }); const result = await client.get>('/api/v1/servers'); expect(result).toEqual([{ id: 'srv-1', name: 'slack' }]); }); it('performs GET request for single item', async () => { const client = new ApiClient({ baseUrl: `http://localhost:${port}` }); const result = await client.get<{ id: string; name: string }>('/api/v1/servers/srv-1'); expect(result.name).toBe('slack'); }); it('performs POST request', async () => { const client = new ApiClient({ baseUrl: `http://localhost:${port}` }); const result = await client.post<{ id: string; name: string }>('/api/v1/servers', { name: 'github' }); expect(result.id).toBe('srv-new'); expect(result.name).toBe('github'); }); it('throws ApiError on 404', async () => { const client = new ApiClient({ baseUrl: `http://localhost:${port}` }); await expect(client.get('/api/v1/missing')).rejects.toThrow(ApiError); }); it('throws on connection error', async () => { const client = new ApiClient({ baseUrl: 'http://localhost:1' }); await expect(client.get('/anything')).rejects.toThrow(); }); it('sends Authorization header when token provided', async () => { // We need a separate server to check the header let receivedAuth = ''; const authServer = http.createServer((req, res) => { receivedAuth = req.headers['authorization'] ?? ''; res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: true })); }); const authPort = await new Promise((resolve) => { authServer.listen(0, () => { const addr = authServer.address(); if (addr && typeof addr === 'object') resolve(addr.port); }); }); try { const client = new ApiClient({ baseUrl: `http://localhost:${authPort}`, token: 'my-token' }); await client.get('/test'); expect(receivedAuth).toBe('Bearer my-token'); } finally { authServer.close(); } }); });