Files
mcpctl/src/cli/tests/api-client.test.ts
Michal b8c5cf718a
Some checks failed
CI / lint (pull_request) Has been cancelled
CI / typecheck (pull_request) Has been cancelled
CI / test (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
CI / package (pull_request) Has been cancelled
feat: implement v2 3-tier architecture (mcpctl → mcplocal → mcpd)
- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery
- Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers
- Add mcpd auth (login/logout) and MCP proxy endpoints
- Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag
- Add tiered health monitoring, shell completions, e2e integration tests
- 57 test files, 597 tests passing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:42:06 +00:00

101 lines
3.7 KiB
TypeScript

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<void>((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<Array<{ id: string; name: string }>>('/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<number>((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();
}
});
});