feat(api): add field encryption, health check specs, and KYC encryption script
- Add field-level encryption service for PII data with AES-256-GCM - Add health check specs for Prisma and Redis indicators - Add MCP controller specs - Add encrypt-existing-kyc migration script for existing KYC data Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { McpTransportController } from '../mcp-transport.controller';
|
||||
|
||||
// Mock SSEServerTransport as a class
|
||||
vi.mock('@goodgo/mcp-servers', () => {
|
||||
return {
|
||||
SSEServerTransport: class MockSSEServerTransport {
|
||||
sessionId = 'mock-session-id';
|
||||
handlePostMessage = vi.fn().mockResolvedValue(undefined);
|
||||
constructor(public path: string, public res: unknown) {}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('McpTransportController', () => {
|
||||
let controller: McpTransportController;
|
||||
let mockRegistry: {
|
||||
getServerNames: ReturnType<typeof vi.fn>;
|
||||
getServer: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockRegistry = {
|
||||
getServerNames: vi.fn(),
|
||||
getServer: vi.fn(),
|
||||
};
|
||||
controller = new McpTransportController(mockRegistry as any);
|
||||
});
|
||||
|
||||
describe('listServers', () => {
|
||||
it('returns list of server names from registry', () => {
|
||||
mockRegistry.getServerNames.mockReturnValue(['search', 'listings']);
|
||||
|
||||
const result = controller.listServers();
|
||||
|
||||
expect(result).toEqual({ servers: ['search', 'listings'] });
|
||||
expect(mockRegistry.getServerNames).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('returns empty array when no servers registered', () => {
|
||||
mockRegistry.getServerNames.mockReturnValue([]);
|
||||
|
||||
const result = controller.listServers();
|
||||
|
||||
expect(result).toEqual({ servers: [] });
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleSse', () => {
|
||||
const mockUser = { sub: 'user-1', email: 'test@example.com' };
|
||||
let mockReq: { on: ReturnType<typeof vi.fn> };
|
||||
let mockRes: Record<string, unknown>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockReq = { on: vi.fn() };
|
||||
mockRes = {};
|
||||
});
|
||||
|
||||
it('throws NOT_FOUND when server does not exist', async () => {
|
||||
mockRegistry.getServer.mockReturnValue(null);
|
||||
|
||||
await expect(
|
||||
controller.handleSse('nonexistent', mockUser as any, mockReq as any, mockRes as any),
|
||||
).rejects.toThrow(HttpException);
|
||||
|
||||
try {
|
||||
await controller.handleSse('nonexistent', mockUser as any, mockReq as any, mockRes as any);
|
||||
} catch (error) {
|
||||
expect((error as HttpException).getStatus()).toBe(HttpStatus.NOT_FOUND);
|
||||
expect((error as HttpException).message).toContain('nonexistent');
|
||||
}
|
||||
});
|
||||
|
||||
it('creates transport and connects to server', async () => {
|
||||
const mockServer = { connect: vi.fn().mockResolvedValue(undefined) };
|
||||
mockRegistry.getServer.mockReturnValue(mockServer);
|
||||
|
||||
await controller.handleSse('search', mockUser as any, mockReq as any, mockRes as any);
|
||||
|
||||
expect(mockRegistry.getServer).toHaveBeenCalledWith('search');
|
||||
expect(mockServer.connect).toHaveBeenCalledOnce();
|
||||
expect(mockReq.on).toHaveBeenCalledWith('close', expect.any(Function));
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleMessage', () => {
|
||||
const mockUser = { sub: 'user-1', email: 'test@example.com' };
|
||||
|
||||
it('throws BAD_REQUEST when sessionId query parameter is missing', async () => {
|
||||
const mockReq = { query: {} } as any;
|
||||
const mockRes = {} as any;
|
||||
|
||||
await expect(
|
||||
controller.handleMessage('search', mockUser as any, mockReq, mockRes),
|
||||
).rejects.toThrow(HttpException);
|
||||
|
||||
try {
|
||||
await controller.handleMessage('search', mockUser as any, mockReq, mockRes);
|
||||
} catch (error) {
|
||||
expect((error as HttpException).getStatus()).toBe(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
});
|
||||
|
||||
it('throws NOT_FOUND when session does not exist', async () => {
|
||||
const mockReq = { query: { sessionId: 'nonexistent-session' } } as any;
|
||||
const mockRes = {} as any;
|
||||
|
||||
await expect(
|
||||
controller.handleMessage('search', mockUser as any, mockReq, mockRes),
|
||||
).rejects.toThrow(HttpException);
|
||||
|
||||
try {
|
||||
await controller.handleMessage('search', mockUser as any, mockReq, mockRes);
|
||||
} catch (error) {
|
||||
expect((error as HttpException).getStatus()).toBe(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user