Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 29s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 2m42s
Deploy / Build Web Image (push) Failing after 27s
Deploy / Build AI Services Image (push) Failing after 29s
E2E Tests / Playwright E2E (push) Failing after 43s
Deploy / Build API Image (push) Failing after 1m31s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 6s
Security Scanning / Trivy Scan — API Image (push) Failing after 5m35s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 3m45s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Security Scanning / Trivy Scan — Web Image (push) Failing after 13m51s
Security Scanning / Trivy Filesystem Scan (push) Failing after 14m46s
Security Scanning / Security Gate (push) Has been cancelled
387 lines
11 KiB
Markdown
387 lines
11 KiB
Markdown
# Module MCP - Hướng Dẫn Tham Khảo Nhanh & Kiểm Thử
|
|
|
|
## 📋 Tổng Quan Module
|
|
|
|
| Khía cạnh | Chi tiết |
|
|
|--------|---------|
|
|
| **Vị trí** | `apps/api/src/modules/mcp/` |
|
|
| **Tổng số tệp** | 4 tệp nguồn (2 TypeScript + 1 cấu hình module + 1 xuất) |
|
|
| **Tệp kiểm thử** | 1 tệp kiểm thử (174 dòng) |
|
|
| **Kiến trúc** | Chỉ có tầng trình bày (đơn giản hóa) |
|
|
| **Framework kiểm thử** | Vitest với Globals được bật |
|
|
| **Độ phủ kiểm thử** | Controller được kiểm thử kỹ, Module/Infrastructure chưa được kiểm thử |
|
|
|
|
---
|
|
|
|
## 📁 Danh Sách Tệp (Đầy Đủ)
|
|
|
|
```
|
|
✅ TESTED ✅ SOURCE ❌ NOT TESTED
|
|
─────────────────────────────────────────────────────────────
|
|
MCP module/ mcp/
|
|
├── mcp.module.ts [Module config - 22 lines]
|
|
├── index.ts [Re-export - 1 line]
|
|
└── presentation/
|
|
├── mcp-transport.controller.ts [Controller - 102 lines]
|
|
└── __tests__/
|
|
└── mcp-transport.controller.spec.ts ✅ [174 lines]
|
|
```
|
|
|
|
### Chi Tiết Tệp
|
|
|
|
| Tệp | Loại | Dòng | Trạng thái | Mục đích |
|
|
|------|------|-------|--------|---------|
|
|
| `index.ts` | Xuất | 1 | ✅ | Điểm vào module |
|
|
| `mcp.module.ts` | Cấu hình | 22 | ⚠️ | Thiết lập module NestJS |
|
|
| `mcp-transport.controller.ts` | Controller | 102 | ✅ | Vận chuyển HTTP/SSE |
|
|
| `mcp-transport.controller.spec.ts` | Kiểm thử | 174 | ✅ | Kiểm thử controller |
|
|
|
|
---
|
|
|
|
## 🏗️ Sơ Đồ Kiến Trúc
|
|
|
|
### Cấu Trúc DDD Hiện Tại (Đơn Giản Hóa)
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────┐
|
|
│ Presentation Layer (Only) ✅ │
|
|
├─────────────────────────────────────────────────┤
|
|
│ • McpTransportController │
|
|
│ - GET /mcp/servers (list) │
|
|
│ - GET /mcp/:serverName/sse (connect) │
|
|
│ - POST /mcp/:serverName/messages (send) │
|
|
├─────────────────────────────────────────────────┤
|
|
│ Dependencies: │
|
|
│ • McpRegistryService (external) │
|
|
│ • JwtAuthGuard (auth layer) │
|
|
│ • SSEServerTransport (external) │
|
|
└─────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Các Tầng Còn Thiếu (Chưa Được Triển Khai)
|
|
|
|
```
|
|
❌ Domain Layer
|
|
- No entities, value objects, events
|
|
- No business logic abstractions
|
|
|
|
❌ Application Layer
|
|
- No CQRS handlers
|
|
- No command/query objects
|
|
- No use case orchestration
|
|
|
|
❌ Infrastructure Layer
|
|
- No repositories
|
|
- No external service adapters
|
|
- No persistence logic
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 Tổng Quan Kiểm Thử
|
|
|
|
### Thống Kê Tệp Kiểm Thử
|
|
|
|
```
|
|
File: mcp-transport.controller.spec.ts
|
|
├── Total Tests: 11
|
|
├── Test Suites: 4 describe blocks
|
|
├── Total Lines: 174
|
|
└── Coverage Areas:
|
|
├── Security Decorators: 4 tests
|
|
├── listServers(): 2 tests
|
|
├── handleSse(): 3 tests
|
|
└── handleMessage(): 2 tests
|
|
```
|
|
|
|
### Phân Tích Kiểm Thử Theo Bộ
|
|
|
|
```
|
|
1. Security Decorators (4 tests)
|
|
├── JwtAuthGuard applied
|
|
├── listServers throttle (30 req/60s)
|
|
├── handleSse throttle (5 req/60s) ⚡ stricter
|
|
└── handleMessage throttle (30 req/60s)
|
|
|
|
2. listServers (2 tests)
|
|
├── Returns server list
|
|
└── Handles empty list
|
|
|
|
3. handleSse (3 tests)
|
|
├── Throws NOT_FOUND for missing server
|
|
├── Creates transport & connects
|
|
└── Cleans up on connection close
|
|
|
|
4. handleMessage (2 tests)
|
|
├── Throws BAD_REQUEST for missing sessionId
|
|
└── Throws NOT_FOUND for expired session
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 Các Lớp & Phương Thức Chính
|
|
|
|
### McpIntegrationModule
|
|
```typescript
|
|
class McpIntegrationModule implements OnModuleInit {
|
|
constructor(
|
|
typesenseClient: TypesenseClientService,
|
|
mcpRegistry: McpRegistryService,
|
|
logger: LoggerService,
|
|
) {}
|
|
|
|
async onModuleInit(): Promise<void> {
|
|
// 1. Set typesense client on registry
|
|
// 2. Re-initialize servers
|
|
// 3. Log initialized servers
|
|
}
|
|
}
|
|
```
|
|
|
|
### McpTransportController
|
|
```typescript
|
|
@Controller('mcp')
|
|
@UseGuards(JwtAuthGuard)
|
|
class McpTransportController {
|
|
private transports: Map<string, SSEServerTransport>
|
|
|
|
constructor(registry: McpRegistryService) {}
|
|
|
|
@Get('servers')
|
|
@Throttle(30/60s)
|
|
listServers(): { servers: string[] }
|
|
|
|
@Get(':serverName/sse')
|
|
@Throttle(5/60s) // Stricter limit for SSE
|
|
async handleSse(serverName, user, req, res): Promise<void>
|
|
|
|
@Post(':serverName/messages')
|
|
@Throttle(30/60s)
|
|
async handleMessage(serverName, user, req, res): Promise<void>
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 Các Mẫu Kiểm Thử Được Sử Dụng
|
|
|
|
### 1. Mẫu Mock (Vitest)
|
|
```typescript
|
|
// Mocking external module classes
|
|
vi.mock('@goodgo/mcp-servers', () => ({
|
|
SSEServerTransport: class MockSSEServerTransport {
|
|
sessionId = 'mock-session-id';
|
|
handlePostMessage = vi.fn().mockResolvedValue(undefined);
|
|
constructor(path: string, res: unknown) {}
|
|
},
|
|
}));
|
|
```
|
|
|
|
### 2. Mẫu Mock Dịch Vụ
|
|
```typescript
|
|
const mockRegistry = {
|
|
getServerNames: vi.fn(),
|
|
getServer: vi.fn(),
|
|
};
|
|
```
|
|
|
|
### 3. Mẫu Xác Minh Decorator
|
|
```typescript
|
|
// Using Reflect API for NestJS decorators
|
|
const guards = Reflect.getMetadata('__guards__', McpTransportController);
|
|
const throttleLimit = Reflect.getMetadata(
|
|
'THROTTLER:LIMITdefault',
|
|
McpTransportController.prototype.listServers,
|
|
);
|
|
```
|
|
|
|
### 4. Mẫu Kiểm Thử Lỗi
|
|
```typescript
|
|
// Testing HTTP errors
|
|
await expect(
|
|
controller.handleSse('nonexistent', user, req, res)
|
|
).rejects.toThrow(HttpException);
|
|
|
|
// Checking status code
|
|
try {
|
|
await controller.handleSse(...);
|
|
} catch (error) {
|
|
expect((error as HttpException).getStatus()).toBe(HttpStatus.NOT_FOUND);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📚 Các Mẫu Kiểm Thử Từ Các Module Khác
|
|
|
|
### Module Auth - Mẫu Handler Đơn Giản
|
|
```typescript
|
|
// Minimal dependencies, focused testing
|
|
describe('LoginUserHandler', () => {
|
|
let handler: LoginUserHandler;
|
|
let mockTokenService = { generateTokenPair: vi.fn() };
|
|
|
|
beforeEach(() => {
|
|
handler = new LoginUserHandler(mockTokenService as any);
|
|
});
|
|
|
|
it('generates token pair', async () => {
|
|
mockTokenService.generateTokenPair.mockResolvedValue(tokenPair);
|
|
const result = await handler.execute(command);
|
|
expect(result).toEqual(tokenPair);
|
|
});
|
|
});
|
|
```
|
|
|
|
### Module Payments - Mẫu Handler Phức Tạp
|
|
```typescript
|
|
// Multiple dependencies, rich testing scenarios
|
|
describe('CreatePaymentHandler', () => {
|
|
let handler: CreatePaymentHandler;
|
|
let mockPaymentRepo: { [K in keyof IPaymentRepository]: ReturnType<typeof vi.fn> };
|
|
let mockGatewayFactory: { getGateway: ReturnType<typeof vi.fn> };
|
|
let mockEventBus: { publish: ReturnType<typeof vi.fn> };
|
|
|
|
beforeEach(() => {
|
|
mockPaymentRepo = {
|
|
findById: vi.fn(),
|
|
save: vi.fn().mockResolvedValue(undefined),
|
|
// ... more methods
|
|
};
|
|
handler = new CreatePaymentHandler(mockPaymentRepo as any, ...);
|
|
});
|
|
|
|
it('creates payment successfully', async () => {
|
|
// Rich test scenario with multiple assertions
|
|
expect(result.paymentId).toBeDefined();
|
|
expect(mockPaymentRepo.save).toHaveBeenCalledTimes(1);
|
|
expect(mockEventBus.publish).toHaveBeenCalled();
|
|
});
|
|
});
|
|
```
|
|
|
|
### Domain Payments - Mẫu DDD
|
|
```typescript
|
|
// Explicit imports, entity behavior testing
|
|
import { describe, it, expect } from 'vitest';
|
|
|
|
describe('PaymentEntity', () => {
|
|
it('should create payment with events', () => {
|
|
const payment = PaymentEntity.createNew(...);
|
|
|
|
expect(payment.status).toBe('PENDING');
|
|
expect(payment.domainEvents).toHaveLength(1);
|
|
expect(payment.domainEvents[0]).toBeInstanceOf(PaymentCreatedEvent);
|
|
});
|
|
|
|
it('should not refund non-completed payment', () => {
|
|
const payment = createPayment(); // helper
|
|
const result = payment.markRefunded();
|
|
expect(result.isErr).toBe(true);
|
|
});
|
|
});
|
|
```
|
|
|
|
### Dịch Vụ Infrastructure - Mẫu Crypto
|
|
```typescript
|
|
// Complex external service testing
|
|
describe('ZalopayService', () => {
|
|
let service: ZalopayService;
|
|
|
|
beforeEach(() => {
|
|
const mockConfig = {
|
|
get: vi.fn((key) => env[key]),
|
|
getOrThrow: vi.fn((key) => env[key] || throw),
|
|
};
|
|
service = new ZalopayService(mockConfig as any);
|
|
});
|
|
|
|
it('should verify valid callback', () => {
|
|
const mac = crypto.createHmac('sha256', key2).update(dataStr).digest('hex');
|
|
const result = service.verifyCallback({ data: dataStr, mac });
|
|
|
|
expect(result.isValid).toBe(true);
|
|
expect(result.orderId).toBe('expected-order-id');
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 Chạy Kiểm Thử
|
|
|
|
### Lệnh Kiểm Thử
|
|
```bash
|
|
# Run all MCP tests
|
|
pnpm test -- src/modules/mcp
|
|
|
|
# Run with watch mode
|
|
pnpm test -- --watch src/modules/mcp
|
|
|
|
# Run with coverage
|
|
pnpm test -- --coverage src/modules/mcp
|
|
|
|
# Run specific test file
|
|
pnpm test -- src/modules/mcp/presentation/__tests__/mcp-transport.controller.spec.ts
|
|
```
|
|
|
|
### Cấu Hình Vitest
|
|
```typescript
|
|
// apps/api/vitest.config.ts
|
|
{
|
|
test: {
|
|
globals: true, // vi, describe, it, expect available globally
|
|
environment: 'node',
|
|
include: ['src/**/*.spec.ts'], // Matches *.spec.ts pattern
|
|
exclude: ['**/*.integration.spec.ts', 'node_modules'],
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 💡 Khuyến Nghị
|
|
|
|
### Ngay Lập Tức (Ưu Tiên Cao)
|
|
- ✅ Controller được kiểm thử kỹ - duy trì điều này
|
|
- 📝 Thêm kiểm thử cho `McpIntegrationModule.onModuleInit()`
|
|
- 📝 Kiểm thử dependency injection của module
|
|
|
|
### Tương Lai (Ưu Tiên Thấp Hơn)
|
|
- 🏗️ Nếu logic domain được thêm vào, tạo kiểm thử domain
|
|
- 🏗️ Nếu application handler được thêm vào, theo mẫu module payments
|
|
- 🏗️ Thêm kiểm thử tích hợp cho toàn bộ vòng đời SSE
|
|
|
|
### Các Phương Pháp Tốt Nhất Cần Tuân Theo
|
|
1. **Sử dụng mẫu globals** cho các kiểm thử đơn giản (như kiểm thử controller hiện có)
|
|
2. **Sử dụng import tường minh** cho các kiểm thử domain phức tạp
|
|
3. **Sử dụng helper factory** cho thiết lập entity phức tạp
|
|
4. **Sử dụng Reflect API** để xác minh decorator
|
|
5. **Kiểm thử cả happy path VÀ các trường hợp lỗi**
|
|
6. **Sử dụng khớp regex** cho các xác nhận thông báo lỗi
|
|
7. **Xác minh lời gọi dịch vụ** với `toHaveBeenCalledWith()`
|
|
|
|
---
|
|
|
|
## 📖 Tham Chiếu Vị Trí Tệp
|
|
|
|
```
|
|
GoodGo Platform Root
|
|
├── apps/api/src/modules/mcp/ ← MCP Module
|
|
│ ├── index.ts
|
|
│ ├── mcp.module.ts
|
|
│ └── presentation/
|
|
│ ├── mcp-transport.controller.ts
|
|
│ └── __tests__/
|
|
│ └── mcp-transport.controller.spec.ts
|
|
│
|
|
├── apps/api/vitest.config.ts ← Test config
|
|
├── apps/api/package.json ← Test scripts
|
|
│
|
|
└── MCP_MODULE_EXPLORATION.md ← Full documentation
|
|
```
|
|
|
|
---
|
|
|
|
Được tạo: ngày 11 tháng 4 năm 2026
|