| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- import { describe, it, expect, vi, beforeEach } from 'vitest';
- import { DataSource, Repository, ObjectLiteral } from 'typeorm';
- import { GenericCrudService, UserTrackingOptions, RelationFieldOptions } from '../../src/services/generic-crud.service';
- // 测试实体类
- class TestEntity implements ObjectLiteral {
- id!: number;
- name!: string;
- createdBy?: string;
- updatedBy?: string;
- userId?: string;
- }
- // 测试关联实体类
- class RelatedEntity implements ObjectLiteral {
- id!: number;
- name!: string;
- }
- describe('GenericCrudService', () => {
- let mockDataSource: DataSource;
- let mockRepository: Repository<TestEntity>;
- let crudService: GenericCrudService<TestEntity>;
- beforeEach(() => {
- // 创建模拟数据源和仓库
- mockRepository = {
- createQueryBuilder: vi.fn().mockReturnValue({
- leftJoinAndSelect: vi.fn().mockReturnThis(),
- andWhere: vi.fn().mockReturnThis(),
- orderBy: vi.fn().mockReturnThis(),
- skip: vi.fn().mockReturnThis(),
- take: vi.fn().mockReturnThis(),
- getManyAndCount: vi.fn().mockResolvedValue([[], 0])
- }),
- findOne: vi.fn().mockResolvedValue(null),
- create: vi.fn().mockImplementation((data) => ({ ...data, id: 1 })),
- save: vi.fn().mockImplementation((entity) => Promise.resolve(entity)),
- update: vi.fn().mockResolvedValue({ affected: 1 }),
- delete: vi.fn().mockResolvedValue({ affected: 1 })
- } as any;
- mockDataSource = {
- getRepository: vi.fn().mockReturnValue(mockRepository)
- } as any;
- crudService = new GenericCrudService(mockDataSource, TestEntity);
- });
- describe('构造函数', () => {
- it('应该正确初始化仓库', () => {
- expect(mockDataSource.getRepository).toHaveBeenCalledWith(TestEntity);
- });
- it('应该支持用户跟踪选项', () => {
- const userTrackingOptions: UserTrackingOptions = {
- createdByField: 'creator',
- updatedByField: 'updater',
- userIdField: 'ownerId'
- };
- const serviceWithTracking = new GenericCrudService(
- mockDataSource,
- TestEntity,
- { userTracking: userTrackingOptions }
- );
- expect(serviceWithTracking).toBeDefined();
- });
- it('应该支持关联字段选项', () => {
- const relationFields: RelationFieldOptions = {
- relatedIds: {
- relationName: 'relatedEntities',
- targetEntity: RelatedEntity
- }
- };
- const serviceWithRelations = new GenericCrudService(
- mockDataSource,
- TestEntity,
- { relationFields }
- );
- expect(serviceWithRelations).toBeDefined();
- });
- });
- describe('getList 方法', () => {
- it('应该调用查询构建器获取列表', async () => {
- const mockQueryBuilder = {
- leftJoinAndSelect: vi.fn().mockReturnThis(),
- andWhere: vi.fn().mockReturnThis(),
- orderBy: vi.fn().mockReturnThis(),
- skip: vi.fn().mockReturnThis(),
- take: vi.fn().mockReturnThis(),
- getManyAndCount: vi.fn().mockResolvedValue([[], 0])
- };
- vi.mocked(mockRepository.createQueryBuilder).mockReturnValue(mockQueryBuilder as any);
- const [data, total] = await crudService.getList(1, 10);
- expect(mockRepository.createQueryBuilder).toHaveBeenCalledWith('entity');
- expect(mockQueryBuilder.skip).toHaveBeenCalledWith(0);
- expect(mockQueryBuilder.take).toHaveBeenCalledWith(10);
- expect(data).toEqual([]);
- expect(total).toBe(0);
- });
- it('应该支持关键词搜索', async () => {
- const mockQueryBuilder = {
- leftJoinAndSelect: vi.fn().mockReturnThis(),
- andWhere: vi.fn().mockReturnThis(),
- orderBy: vi.fn().mockReturnThis(),
- skip: vi.fn().mockReturnThis(),
- take: vi.fn().mockReturnThis(),
- getManyAndCount: vi.fn().mockResolvedValue([[], 0])
- };
- vi.mocked(mockRepository.createQueryBuilder).mockReturnValue(mockQueryBuilder as any);
- await crudService.getList(1, 10, 'test', ['name']);
- expect(mockQueryBuilder.andWhere).toHaveBeenCalled();
- });
- });
- describe('getById 方法', () => {
- it('应该根据ID获取实体', async () => {
- const testEntity = { id: 1, name: 'Test' };
- vi.mocked(mockRepository.findOne).mockResolvedValue(testEntity as any);
- const result = await crudService.getById(1);
- expect(mockRepository.findOne).toHaveBeenCalledWith({
- where: { id: 1 },
- relations: []
- });
- expect(result).toEqual(testEntity);
- });
- it('应该支持关联关系', async () => {
- await crudService.getById(1, ['relatedEntity']);
- expect(mockRepository.findOne).toHaveBeenCalledWith({
- where: { id: 1 },
- relations: ['relatedEntity']
- });
- });
- });
- describe('create 方法', () => {
- it('应该创建新实体', async () => {
- const createData = { name: 'New Entity' };
- const savedEntity = { id: 1, ...createData };
- vi.mocked(mockRepository.create).mockReturnValue(savedEntity as any);
- vi.mocked(mockRepository.save).mockResolvedValue(savedEntity as any);
- const result = await crudService.create(createData);
- expect(mockRepository.create).toHaveBeenCalledWith(createData);
- expect(mockRepository.save).toHaveBeenCalledWith(savedEntity);
- expect(result).toEqual(savedEntity);
- });
- it('应该支持用户跟踪', async () => {
- const userTrackingOptions: UserTrackingOptions = {
- createdByField: 'createdBy',
- updatedByField: 'updatedBy'
- };
- const serviceWithTracking = new GenericCrudService(
- mockDataSource,
- TestEntity,
- { userTracking: userTrackingOptions }
- );
- const createData = { name: 'New Entity' };
- const savedEntity = { id: 1, ...createData, createdBy: 'user123', updatedBy: 'user123' };
- vi.mocked(mockRepository.create).mockReturnValue(savedEntity as any);
- vi.mocked(mockRepository.save).mockResolvedValue(savedEntity as any);
- await serviceWithTracking.create(createData, 'user123');
- expect(mockRepository.create).toHaveBeenCalledWith({
- ...createData,
- createdBy: 'user123',
- updatedBy: 'user123',
- userId: 'user123'
- });
- });
- });
- describe('update 方法', () => {
- it('应该更新现有实体', async () => {
- const updateData = { name: 'Updated Entity' };
- const existingEntity = { id: 1, name: 'Original Entity' };
- const updatedEntity = { ...existingEntity, ...updateData };
- vi.mocked(mockRepository.findOne).mockResolvedValue(existingEntity as any);
- vi.mocked(mockRepository.save).mockResolvedValue(updatedEntity as any);
- const result = await crudService.update(1, updateData);
- expect(mockRepository.update).toHaveBeenCalledWith(1, updateData);
- expect(mockRepository.findOne).toHaveBeenCalledWith({
- where: { id: 1 },
- relations: []
- });
- expect(result).toEqual(updatedEntity);
- });
- it('应该返回 null 当实体不存在时', async () => {
- vi.mocked(mockRepository.findOne).mockResolvedValue(null);
- const result = await crudService.update(999, { name: 'Updated' });
- expect(result).toBeNull();
- });
- });
- describe('delete 方法', () => {
- it('应该删除实体', async () => {
- vi.mocked(mockRepository.delete).mockResolvedValue({ affected: 1 } as any);
- const result = await crudService.delete(1);
- expect(mockRepository.delete).toHaveBeenCalledWith(1);
- expect(result).toBe(true);
- });
- it('应该返回 false 当实体不存在时', async () => {
- vi.mocked(mockRepository.delete).mockResolvedValue({ affected: 0 } as any);
- const result = await crudService.delete(999);
- expect(result).toBe(false);
- });
- });
- describe('createQueryBuilder 方法', () => {
- it('应该返回查询构建器', () => {
- const mockQueryBuilder = {} as any;
- vi.mocked(mockRepository.createQueryBuilder).mockReturnValue(mockQueryBuilder);
- const result = crudService.createQueryBuilder('test');
- expect(mockRepository.createQueryBuilder).toHaveBeenCalledWith('test');
- expect(result).toBe(mockQueryBuilder);
- });
- it('应该使用默认别名', () => {
- const mockQueryBuilder = {} as any;
- vi.mocked(mockRepository.createQueryBuilder).mockReturnValue(mockQueryBuilder);
- crudService.createQueryBuilder();
- expect(mockRepository.createQueryBuilder).toHaveBeenCalledWith('entity');
- });
- });
- });
|