import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Repository, DataSource } from 'typeorm'; import { AssetsService } from './assets.service'; import { Asset } from '../../entities/asset.entity'; import { AssetLog } from '../../entities/asset-log.entity'; import { Group } from '../../entities/group.entity'; import { GroupMember } from '../../entities/group-member.entity'; import { AssetType, AssetStatus, GroupMemberRole, AssetLogAction } from '../../common/enums'; import { NotFoundException, ForbiddenException, BadRequestException } from '@nestjs/common'; describe('AssetsService', () => { let service: AssetsService; let assetRepository: Repository; let assetLogRepository: Repository; let groupRepository: Repository; let groupMemberRepository: Repository; const mockAsset = { id: 'asset-1', groupId: 'group-1', type: AssetType.ACCOUNT, name: '测试账号', description: '测试描述', accountCredentials: 'encrypted-data', quantity: 1, status: AssetStatus.AVAILABLE, currentBorrowerId: null, createdAt: new Date(), updatedAt: new Date(), }; const mockGroup = { id: 'group-1', name: '测试小组', }; const mockGroupMember = { id: 'member-1', userId: 'user-1', groupId: 'group-1', role: GroupMemberRole.ADMIN, }; const mockDataSource = { createQueryRunner: jest.fn().mockReturnValue({ connect: jest.fn(), startTransaction: jest.fn(), commitTransaction: jest.fn(), rollbackTransaction: jest.fn(), release: jest.fn(), manager: { findOne: jest.fn(), find: jest.fn(), create: jest.fn(), save: jest.fn(), }, }), }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ AssetsService, { provide: getRepositoryToken(Asset), useValue: { create: jest.fn(), save: jest.fn(), find: jest.fn(), findOne: jest.fn(), remove: jest.fn(), }, }, { provide: getRepositoryToken(AssetLog), useValue: { create: jest.fn(), save: jest.fn(), find: jest.fn(), }, }, { provide: getRepositoryToken(Group), useValue: { findOne: jest.fn(), }, }, { provide: getRepositoryToken(GroupMember), useValue: { findOne: jest.fn(), }, }, { provide: DataSource, useValue: mockDataSource, }, ], }).compile(); service = module.get(AssetsService); assetRepository = module.get>(getRepositoryToken(Asset)); assetLogRepository = module.get>(getRepositoryToken(AssetLog)); groupRepository = module.get>(getRepositoryToken(Group)); groupMemberRepository = module.get>(getRepositoryToken(GroupMember)); }); it('should be defined', () => { expect(service).toBeDefined(); }); describe('create', () => { it('应该成功创建资产', async () => { const createDto = { groupId: 'group-1', type: AssetType.ACCOUNT, name: '测试账号', description: '测试描述', }; jest.spyOn(groupRepository, 'findOne').mockResolvedValue(mockGroup as any); jest.spyOn(groupMemberRepository, 'findOne').mockResolvedValue(mockGroupMember as any); jest.spyOn(assetRepository, 'create').mockReturnValue(mockAsset as any); jest.spyOn(assetRepository, 'save').mockResolvedValue(mockAsset as any); jest.spyOn(assetRepository, 'findOne').mockResolvedValue(mockAsset as any); const result = await service.create('user-1', createDto); expect(result).toBeDefined(); expect(groupRepository.findOne).toHaveBeenCalledWith({ where: { id: 'group-1' } }); expect(groupMemberRepository.findOne).toHaveBeenCalled(); }); it('小组不存在时应该抛出异常', async () => { const createDto = { groupId: 'group-1', type: AssetType.ACCOUNT, name: '测试账号', }; jest.spyOn(groupRepository, 'findOne').mockResolvedValue(null); await expect(service.create('user-1', createDto)).rejects.toThrow(NotFoundException); }); it('无权限时应该抛出异常', async () => { const createDto = { groupId: 'group-1', type: AssetType.ACCOUNT, name: '测试账号', }; jest.spyOn(groupRepository, 'findOne').mockResolvedValue(mockGroup as any); jest.spyOn(groupMemberRepository, 'findOne').mockResolvedValue({ ...mockGroupMember, role: GroupMemberRole.MEMBER, } as any); await expect(service.create('user-1', createDto)).rejects.toThrow(ForbiddenException); }); }); describe('findAll', () => { it('应该返回资产列表', async () => { jest.spyOn(assetRepository, 'find').mockResolvedValue([mockAsset] as any); const result = await service.findAll('group-1'); expect(result).toHaveLength(1); expect(result[0].accountCredentials).toBeUndefined(); }); }); describe('borrow', () => { it('应该成功借用资产', async () => { const borrowDto = { reason: '需要使用' }; jest.spyOn(assetRepository, 'findOne').mockResolvedValue(mockAsset as any); jest.spyOn(groupMemberRepository, 'findOne').mockResolvedValue(mockGroupMember as any); jest.spyOn(assetRepository, 'save').mockResolvedValue({ ...mockAsset, status: AssetStatus.IN_USE } as any); jest.spyOn(assetLogRepository, 'create').mockReturnValue({} as any); jest.spyOn(assetLogRepository, 'save').mockResolvedValue({} as any); const result = await service.borrow('user-1', 'asset-1', borrowDto); expect(result.message).toBe('借用成功'); expect(assetRepository.save).toHaveBeenCalled(); expect(assetLogRepository.save).toHaveBeenCalled(); }); it('资产不可用时应该抛出异常', async () => { const borrowDto = { reason: '需要使用' }; jest.spyOn(assetRepository, 'findOne').mockResolvedValue({ ...mockAsset, status: AssetStatus.IN_USE, } as any); await expect(service.borrow('user-1', 'asset-1', borrowDto)).rejects.toThrow(BadRequestException); }); }); describe('return', () => { it('应该成功归还资产', async () => { jest.spyOn(assetRepository, 'findOne').mockResolvedValue({ ...mockAsset, currentBorrowerId: 'user-1', } as any); jest.spyOn(assetRepository, 'save').mockResolvedValue(mockAsset as any); jest.spyOn(assetLogRepository, 'create').mockReturnValue({} as any); jest.spyOn(assetLogRepository, 'save').mockResolvedValue({} as any); const result = await service.return('user-1', 'asset-1', '已归还'); expect(result.message).toBe('归还成功'); expect(assetRepository.save).toHaveBeenCalled(); }); it('非借用人归还时应该抛出异常', async () => { jest.spyOn(assetRepository, 'findOne').mockResolvedValue({ ...mockAsset, currentBorrowerId: 'user-2', } as any); await expect(service.return('user-1', 'asset-1')).rejects.toThrow(ForbiddenException); }); }); describe('remove', () => { it('应该成功删除资产', async () => { jest.spyOn(assetRepository, 'findOne').mockResolvedValue(mockAsset as any); jest.spyOn(groupMemberRepository, 'findOne').mockResolvedValue(mockGroupMember as any); jest.spyOn(assetRepository, 'remove').mockResolvedValue(mockAsset as any); const result = await service.remove('user-1', 'asset-1'); expect(result.message).toBe('删除成功'); expect(assetRepository.remove).toHaveBeenCalled(); }); }); });