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(); }); }); });