feat: Phase 1-3 全部完成 — 沙盒增强、学情分析、学习路径
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
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,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user