Files
ai-learning-platform/backend/src/modules/sandbox/tests/sandbox.service.spec.ts
T

146 lines
4.7 KiB
TypeScript

import { Test, TestingModule } from '@nestjs/testing';
import { HttpException, HttpStatus } from '@nestjs/common';
import { SandboxService } from '../sandbox.service';
import { PrismaService } from '../../../prisma/prisma.service';
import { AIGatewayService } from '../../ai/ai-gateway.service';
describe('SandboxService', () => {
let service: SandboxService;
let prisma: PrismaService;
let aiGateway: AIGatewayService;
const mockPrisma = {
user: {
findUnique: jest.fn(),
},
sandboxSession: {
findMany: jest.fn(),
findUnique: jest.fn(),
count: jest.fn(),
create: jest.fn(),
upsert: jest.fn(),
},
};
const mockAIGateway = {
chat: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
SandboxService,
{ provide: PrismaService, useValue: mockPrisma },
{ provide: AIGatewayService, useValue: mockAIGateway },
],
}).compile();
service = module.get<SandboxService>(SandboxService);
prisma = module.get<PrismaService>(PrismaService);
aiGateway = module.get<AIGatewayService>(AIGatewayService);
jest.clearAllMocks();
});
describe('chat', () => {
it('should throw if user not found', async () => {
mockPrisma.user.findUnique.mockResolvedValue(null);
await expect(
service.chat(1, 'test-conv', 'gpt-3.5', [{ role: 'user', content: 'Hello' }])
).rejects.toThrow(HttpException);
});
it('should throw if user not active', async () => {
mockPrisma.user.findUnique.mockResolvedValue({ id: 1, status: 'INACTIVE' });
await expect(
service.chat(1, 'test-conv', 'gpt-3.5', [{ role: 'user', content: 'Hello' }])
).rejects.toThrow(HttpException);
});
it('should throw if daily quota exceeded for new conversation', async () => {
mockPrisma.user.findUnique.mockResolvedValue({
id: 1, status: 'ACTIVE', sandboxDaily: 10
});
mockPrisma.sandboxSession.findUnique.mockResolvedValue(null);
mockPrisma.sandboxSession.count.mockResolvedValue(10);
await expect(
service.chat(1, 'new-conv', 'gpt-3.5', [{ role: 'user', content: 'Hello' }])
).rejects.toThrow(HttpException);
});
it('should not count quota for existing conversation', async () => {
mockPrisma.user.findUnique.mockResolvedValue({
id: 1, status: 'ACTIVE', sandboxDaily: 10
});
mockPrisma.sandboxSession.findUnique.mockResolvedValue({ id: 1 });
mockAIGateway.chat.mockResolvedValue('AI回复');
mockPrisma.sandboxSession.upsert.mockResolvedValue({ id: 2 });
const result = await service.chat(1, 'existing-conv', 'gpt-3.5', [
{ role: 'user', content: 'Hello' }
]);
expect(result.reply).toBe('AI回复');
expect(mockPrisma.sandboxSession.count).not.toHaveBeenCalled();
});
it('should call AI gateway and save session', async () => {
mockPrisma.user.findUnique.mockResolvedValue({
id: 1, status: 'ACTIVE', sandboxDaily: 10
});
mockPrisma.sandboxSession.findUnique.mockResolvedValue(null);
mockPrisma.sandboxSession.count.mockResolvedValue(5);
mockAIGateway.chat.mockResolvedValue('AI回复');
mockPrisma.sandboxSession.upsert.mockResolvedValue({ id: 1 });
const result = await service.chat(1, 'test-conv', 'gpt-3.5', [
{ role: 'user', content: 'Hello' }
]);
expect(result.reply).toBe('AI回复');
expect(result.conversationId).toBe('test-conv');
expect(result.sessionId).toBeDefined();
expect(mockAIGateway.chat).toHaveBeenCalledWith('gpt-3.5', expect.any(Array), undefined);
expect(mockPrisma.sandboxSession.upsert).toHaveBeenCalled();
});
});
describe('getHistory', () => {
it('should return paginated history', async () => {
const mockSessions = [
{ id: 1, conversationId: 'conv-1', model: 'gpt-3.5', title: 'Test', createdAt: new Date(), tokens: 10 }
];
mockPrisma.sandboxSession.findMany.mockResolvedValue(mockSessions);
mockPrisma.sandboxSession.count.mockResolvedValue(1);
const result = await service.getHistory(1, { page: 1, pageSize: 20 });
expect(result).toEqual({
items: mockSessions,
total: 1,
page: 1,
pageSize: 20,
});
});
});
describe('getQuota', () => {
it('should return quota info', async () => {
mockPrisma.user.findUnique.mockResolvedValue({
id: 1, sandboxDaily: 10
});
mockPrisma.sandboxSession.count.mockResolvedValue(3);
const result = await service.getQuota(1);
expect(result).toEqual({
dailyLimit: 10,
used: 3,
remaining: 7,
});
});
});
});