feat(ai-runtime): complete ai runtime policy refactor (ADR-035)
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user