# 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
