feat(ai-runtime): complete ai runtime policy refactor (ADR-035)
CI / CD Pipeline / build (push) Successful in 4m16s
CI / CD Pipeline / deploy (push) Successful in 11m51s

This commit is contained in:
2026-06-12 08:07:15 +07:00
parent 71c5e88181
commit 0227b7b982
63 changed files with 3566 additions and 451 deletions
@@ -0,0 +1,102 @@
// File: backend/src/modules/ai/tests/vram-monitor.service.spec.ts
// Change Log:
// - 2026-06-11: สร้าง unit tests สำหรับ VramMonitorService (US5)
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { VramMonitorService } from '../services/vram-monitor.service';
import axios from 'axios';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
describe('VramMonitorService', () => {
let service: VramMonitorService;
const mockConfigService = {
get: jest.fn((key: string, defaultValue?: unknown): unknown => {
const config: Record<string, unknown> = {
OLLAMA_URL: 'http://localhost:11434',
GPU_TOTAL_VRAM_MB: 8192, // mock total 8GB
};
return config[key] !== undefined ? config[key] : defaultValue;
}),
};
beforeEach(async () => {
jest.clearAllMocks();
const module: TestingModule = await Test.createTestingModule({
providers: [
VramMonitorService,
{ provide: ConfigService, useValue: mockConfigService },
],
}).compile();
service = module.get<VramMonitorService>(VramMonitorService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('getVramHeadroom', () => {
it('ควรคำนวณ headroom ถูกต้องเมื่อ Ollama คืนข้อมูลโมเดลปกติ', async () => {
mockedAxios.get.mockResolvedValue({
data: {
models: [
{
name: 'typhoon2.5-np-dms:latest',
size_vram: 4 * 1024 * 1024 * 1024,
}, // 4GB
{ name: 'other-model', size_vram: 2 * 1024 * 1024 * 1024 }, // 2GB
],
},
});
const headroom = await service.getVramHeadroom();
expect(headroom.querySuccess).toBe(true);
expect(headroom.totalMb).toBe(8192);
expect(headroom.usedMb).toBe(6144); // 4GB + 2GB = 6GB (6144MB)
expect(headroom.availableMb).toBe(2048); // 8GB - 6GB = 2GB (2048MB)
expect(headroom.mainModelVramMb).toBe(4096); // 4GB main model (4096MB)
});
it('ควรคำนวณ headroom เป็น safe default (0 available) เมื่อ Ollama query ล้มเหลว', async () => {
mockedAxios.get.mockRejectedValue(new Error('Connection timeout'));
const headroom = await service.getVramHeadroom();
expect(headroom.querySuccess).toBe(false);
expect(headroom.availableMb).toBe(0);
expect(headroom.usedMb).toBe(8192);
expect(headroom.mainModelVramMb).toBe(0);
});
});
describe('hasVramCapacity', () => {
it('ควรคืน true เมื่อ headroom พอตามค่าที่ขอ', async () => {
mockedAxios.get.mockResolvedValue({
data: {
models: [
{
name: 'typhoon2.5-np-dms:latest',
size_vram: 4 * 1024 * 1024 * 1024,
},
],
},
});
const result = await service.hasVramCapacity(3000); // query available is 4096MB
expect(result).toBe(true);
});
it('ควรคืน false เมื่อ headroom ไม่พอตามค่าที่ขอ', async () => {
mockedAxios.get.mockResolvedValue({
data: {
models: [
{
name: 'typhoon2.5-np-dms:latest',
size_vram: 6 * 1024 * 1024 * 1024,
}, // 6GB used
],
},
});
const result = await service.hasVramCapacity(3000); // query available is 2048MB, required 3000MB
expect(result).toBe(false);
});
});
});