Files
Edu/services/classes/test/unit/classes.service.test.ts
SpecialX 2ba4250165
Some checks failed
CI Go / test (push) Has been cancelled
CI Proto / lint (push) Has been cancelled
CI Python / test (push) Has been cancelled
CI TypeScript / test (push) Has been cancelled
feat(p1): complete P1 foundation stage
- monorepo: pnpm workspace + go.work + pyproject.toml + commitlint/husky
- infra: docker-compose (minimal + full profiles) + init-sql + prometheus
- arch-scan: multi-language scanner skeleton (TS/Go/Python/Proto)
- shared-proto: buf v2 + classes.proto (ClassService CRUD contract)
- api-gateway: Go/Gin + JWT HS256 auth + reverse proxy + request ID
- classes: NestJS golden template (error system + observability + middleware + CRUD + tests)
- teacher-portal: Next.js + paper-feel UI design system
- CI/CD: 4 workflows (go/ts/py/proto)
- docs: migration guide + project_rules + coding-standards + git-workflow + ui-design-system + 004 + 9 module READMEs + known-issues + spec/plan migration + roadmap
2026-07-07 23:39:37 +08:00

105 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { Mock } from 'vitest';
import { ClassesService } from '../../src/classes/classes.service.js';
import type { ClassesRepository } from '../../src/classes/classes.repository.js';
import { ValidationError, NotFoundError } from '../../src/shared/errors/application-error.js';
describe('ClassesService', () => {
let service: ClassesService;
let mockRepository: {
create: Mock;
findById: Mock;
list: Mock;
update: Mock;
delete: Mock;
};
beforeEach(() => {
mockRepository = {
create: vi.fn(),
findById: vi.fn(),
list: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
};
// 测试场景mock 对象通过 unknown 转换为 ClassesRepository规则允许测试中 as 转换)
service = new ClassesService(mockRepository as unknown as ClassesRepository);
});
describe('create', () => {
it('should create a class successfully', async () => {
const dto = { name: 'Class 1A', gradeId: '550e8400-e29b-41d4-a716-446655440000' };
const mockClass = {
id: 'cls-1',
name: 'Class 1A',
gradeId: '550e8400-e29b-41d4-a716-446655440000',
createdAt: new Date('2026-01-01'),
updatedAt: new Date('2026-01-01'),
};
mockRepository.create.mockResolvedValue(mockClass);
const result = await service.create(dto);
expect(result.id).toBe('cls-1');
expect(mockRepository.create).toHaveBeenCalledWith(expect.objectContaining(dto));
});
});
describe('update', () => {
it('should throw ValidationError when no fields provided', async () => {
await expect(service.update('cls-1', {})).rejects.toThrow(ValidationError);
});
it('should update class successfully', async () => {
const mockClass = {
id: 'cls-1',
name: 'Updated',
gradeId: 'g1',
createdAt: new Date(),
updatedAt: new Date(),
};
mockRepository.update.mockResolvedValue(mockClass);
const result = await service.update('cls-1', { name: 'Updated' });
expect(result.name).toBe('Updated');
expect(mockRepository.update).toHaveBeenCalledWith('cls-1', { name: 'Updated' });
});
});
describe('getById', () => {
it('should throw NotFoundError when class not found', async () => {
mockRepository.findById.mockResolvedValue(undefined);
await expect(service.getById('not-exist')).rejects.toThrow(NotFoundError);
});
});
describe('list', () => {
it('should list classes without gradeId filter', async () => {
mockRepository.list.mockResolvedValue([]);
await service.list();
expect(mockRepository.list).toHaveBeenCalledWith(undefined);
});
it('should list classes with gradeId filter', async () => {
mockRepository.list.mockResolvedValue([]);
await service.list('grade-1');
expect(mockRepository.list).toHaveBeenCalledWith('grade-1');
});
});
describe('delete', () => {
it('should throw NotFoundError when class not found', async () => {
mockRepository.findById.mockResolvedValue(undefined);
await expect(service.delete('not-exist')).rejects.toThrow(NotFoundError);
});
it('should delete class', async () => {
mockRepository.findById.mockResolvedValue({ id: 'cls-1' });
mockRepository.delete.mockResolvedValue(undefined);
await service.delete('cls-1');
expect(mockRepository.delete).toHaveBeenCalledWith('cls-1');
});
});
});