115 lines
3.6 KiB
TypeScript
115 lines
3.6 KiB
TypeScript
// File: src/modules/ai/ai-settings.service.spec.ts
|
|
// Change Log
|
|
// - 2026-05-21: เพิ่ม regression tests สำหรับ AI feature toggle cache/DB behavior.
|
|
|
|
import { Test, TestingModule } from '@nestjs/testing';
|
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
|
import { AiSettingsService } from './ai-settings.service';
|
|
import { SystemSetting } from './entities/system-setting.entity';
|
|
import { AiAvailableModel } from './entities/ai-available-model.entity';
|
|
|
|
const DEFAULT_REDIS_TOKEN = 'default_IORedisModuleConnectionToken';
|
|
|
|
describe('AiSettingsService', () => {
|
|
const mockSettingRepo = {
|
|
findOne: jest.fn(),
|
|
save: jest.fn(),
|
|
manager: {
|
|
transaction: jest.fn(),
|
|
},
|
|
};
|
|
|
|
const mockAiModelRepo = {
|
|
find: jest.fn(),
|
|
findOne: jest.fn(),
|
|
save: jest.fn(),
|
|
create: jest.fn(),
|
|
update: jest.fn(),
|
|
softDelete: jest.fn(),
|
|
};
|
|
|
|
const mockRedis = {
|
|
get: jest.fn(),
|
|
set: jest.fn(),
|
|
del: jest.fn(),
|
|
};
|
|
|
|
let service: AiSettingsService;
|
|
|
|
beforeEach(async () => {
|
|
jest.clearAllMocks();
|
|
const module: TestingModule = await Test.createTestingModule({
|
|
providers: [
|
|
AiSettingsService,
|
|
{
|
|
provide: getRepositoryToken(SystemSetting),
|
|
useValue: mockSettingRepo,
|
|
},
|
|
{
|
|
provide: getRepositoryToken(AiAvailableModel),
|
|
useValue: mockAiModelRepo,
|
|
},
|
|
{ provide: DEFAULT_REDIS_TOKEN, useValue: mockRedis },
|
|
],
|
|
}).compile();
|
|
service = module.get<AiSettingsService>(AiSettingsService);
|
|
});
|
|
|
|
it('ควรอ่านค่า enabled จาก Redis cache เมื่อมีค่าอยู่แล้ว', async () => {
|
|
mockRedis.get.mockResolvedValue('false');
|
|
|
|
await expect(service.getAiFeaturesEnabled()).resolves.toBe(false);
|
|
expect(mockSettingRepo.findOne).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('ควร fallback ไป DB และเขียน cache เมื่อ Redis cache miss', async () => {
|
|
mockRedis.get.mockResolvedValue(null);
|
|
mockSettingRepo.findOne.mockResolvedValue({ settingValue: 'true' });
|
|
|
|
await expect(service.getAiFeaturesEnabled()).resolves.toBe(true);
|
|
expect(mockRedis.set).toHaveBeenCalledWith(
|
|
'system_settings:AI_FEATURES_ENABLED',
|
|
'true',
|
|
'EX',
|
|
30
|
|
);
|
|
});
|
|
|
|
it('ควรอัปเดต DB ใน transaction แล้ว invalid cache หลังสำเร็จ', async () => {
|
|
const transactionalRepo = {
|
|
findOne: jest.fn().mockResolvedValue({ settingValue: 'true' }),
|
|
save: jest.fn().mockResolvedValue({ settingValue: 'false' }),
|
|
create: jest.fn(),
|
|
};
|
|
mockSettingRepo.manager.transaction.mockImplementation(
|
|
async (
|
|
callback: (manager: {
|
|
getRepository: () => typeof transactionalRepo;
|
|
}) => Promise<void>
|
|
) => callback({ getRepository: () => transactionalRepo })
|
|
);
|
|
|
|
await service.setAiFeaturesEnabled(false, 7);
|
|
|
|
expect(transactionalRepo.save).toHaveBeenCalledWith(
|
|
expect.objectContaining({ settingValue: 'false', updatedBy: 7 })
|
|
);
|
|
expect(mockRedis.del).toHaveBeenCalledWith(
|
|
'system_settings:AI_FEATURES_ENABLED'
|
|
);
|
|
});
|
|
|
|
it('ควรใช้ gemma4:e4b เป็นค่า active model เริ่มต้นเมื่อยังไม่มี system setting', async () => {
|
|
mockRedis.get.mockResolvedValue(null);
|
|
mockSettingRepo.findOne.mockResolvedValue(null);
|
|
|
|
await expect(service.getActiveModel()).resolves.toBe('gemma4:e4b');
|
|
expect(mockRedis.set).toHaveBeenCalledWith(
|
|
'system_settings:AI_ACTIVE_MODEL',
|
|
'gemma4:e4b',
|
|
'EX',
|
|
30
|
|
);
|
|
});
|
|
});
|