From 2ddfe84148b7c281a4d750eb0a42f657d285f8f4 Mon Sep 17 00:00:00 2001 From: luch <32321690+luch1994@users.noreply.github.com> Date: Mon, 30 Sep 2024 18:23:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E5=8D=95=E6=B5=8B=20(#437)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/package.json | 4 +- server/src/models/__test/base.entity.spec.ts | 30 -- .../models/__test/surveyMeta.entity.spec.ts | 57 +++ .../modules/auth/__test/user.service.spec.ts | 12 +- .../__test/messagePushingTask.dto.spec.ts | 18 +- .../__test/messagePushingTask.service.spec.ts | 80 +++- .../updateMessagePushingTask.dto.spec.ts | 5 - .../services/messagePushingTask.service.ts | 4 +- .../__test/collaborator.service.spec.ts | 395 +++++++++++------- .../__test/dataStatistic.controller.spec.ts | 12 +- .../__test/dataStatistic.service.spec.ts | 12 +- .../__test/downloadTask.controller.spec.ts | 11 +- .../__test/downloadTask.service.spec.ts | 80 +++- .../survey/__test/mockResponseSchema.ts | 8 +- .../survey/__test/session.service.spec.ts | 144 +++++++ .../survey/__test/survey.controller.spec.ts | 205 +++++---- .../__test/surveyHistory.service.spec.ts | 4 +- .../__test/surveyMeta.controller.spec.ts | 27 +- .../survey/__test/surveyMeta.service.spec.ts | 200 +++++---- .../survey/services/downloadTask.service.ts | 6 +- .../__test/clientEncrypt.service.spec.ts | 9 +- .../__test/counter.service.spec.ts | 1 + .../__test/mockResponseSchema.ts | 4 +- .../__test/responseSchema.controller.spec.ts | 316 +++++++------- .../__test/responseScheme.service.spec.ts | 19 +- .../__test/surveyResponse.controller.spec.ts | 8 +- .../__test/surveyResponse.service.spec.ts | 11 +- .../_test/workspace.controller.spec.ts | 139 +++--- .../workspace/_test/workspace.service.spec.ts | 74 +++- .../_test/workspaceMember.controller.spec.ts | 16 +- .../_test/workspaceMember.service.spec.ts | 74 +++- .../utils/{ => __test}/messagePushing.spec.ts | 2 +- .../pages/download/components/TaskList.vue | 2 +- .../stores/composables/usePageEdit.ts | 4 +- .../questions/widgets/BaseChoice/index.jsx | 1 - web/src/render/components/QuestionWrapper.vue | 6 +- web/src/render/stores/survey.js | 4 +- 37 files changed, 1261 insertions(+), 743 deletions(-) delete mode 100644 server/src/models/__test/base.entity.spec.ts create mode 100644 server/src/models/__test/surveyMeta.entity.spec.ts create mode 100644 server/src/modules/survey/__test/session.service.spec.ts rename server/src/utils/{ => __test}/messagePushing.spec.ts (98%) diff --git a/server/package.json b/server/package.json index 27a929ca..57b81d0f 100644 --- a/server/package.json +++ b/server/package.json @@ -95,7 +95,9 @@ "^.+\\.(t|j)s$": "ts-jest" }, "collectCoverageFrom": [ - "**/*.(t|j)s" + "**/*.(t|j)s", + "!**/*.module.ts", + "!**/upgrade.*.ts" ], "coverageDirectory": "../coverage", "testEnvironment": "node", diff --git a/server/src/models/__test/base.entity.spec.ts b/server/src/models/__test/base.entity.spec.ts deleted file mode 100644 index eb4c90c9..00000000 --- a/server/src/models/__test/base.entity.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { BaseEntity } from '../base.entity'; -import { RECORD_STATUS } from 'src/enums'; - -describe('BaseEntity', () => { - let baseEntity: BaseEntity; - - beforeEach(() => { - baseEntity = new BaseEntity(); - }); - - it('should initialize default info before insert', () => { - const now = Date.now(); - baseEntity.initDefaultInfo(); - - expect(baseEntity.curStatus.status).toBe(RECORD_STATUS.NEW); - expect(baseEntity.curStatus.date).toBeCloseTo(now, -3); - expect(baseEntity.statusList).toHaveLength(1); - expect(baseEntity.statusList[0].status).toBe(RECORD_STATUS.NEW); - expect(baseEntity.statusList[0].date).toBeCloseTo(now, -3); - expect(baseEntity.createDate).toBeCloseTo(now, -3); - expect(baseEntity.updateDate).toBeCloseTo(now, -3); - }); - - it('should update updateDate before update', () => { - const now = Date.now(); - baseEntity.onUpdate(); - - expect(baseEntity.updateDate).toBeCloseTo(now, -3); - }); -}); diff --git a/server/src/models/__test/surveyMeta.entity.spec.ts b/server/src/models/__test/surveyMeta.entity.spec.ts new file mode 100644 index 00000000..d927ad7f --- /dev/null +++ b/server/src/models/__test/surveyMeta.entity.spec.ts @@ -0,0 +1,57 @@ +import { SurveyMeta } from '../surveyMeta.entity'; +import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums'; + +// 模拟日期 +const mockDateNow = Date.now(); + +describe('SurveyMeta Entity', () => { + let surveyMeta: SurveyMeta; + + // 在每个测试之前,初始化 SurveyMeta 实例 + beforeEach(() => { + surveyMeta = new SurveyMeta(); + // 模拟 Date.now() 返回固定的时间 + jest.spyOn(Date, 'now').mockReturnValue(mockDateNow); + }); + + afterEach(() => { + jest.restoreAllMocks(); // 每次测试后还原所有 mock + }); + + it('should set default curStatus and subStatus on insert when they are not provided', () => { + surveyMeta.initDefaultInfo(); + + // 验证 curStatus 是否被初始化为默认值 + expect(surveyMeta.curStatus).toEqual({ + status: RECORD_STATUS.NEW, + date: mockDateNow, + }); + + // 验证 statusList 是否包含 curStatus + expect(surveyMeta.statusList).toEqual([ + { + status: RECORD_STATUS.NEW, + date: mockDateNow, + }, + ]); + + // 验证 subStatus 是否被初始化为默认值 + expect(surveyMeta.subStatus).toEqual({ + status: RECORD_SUB_STATUS.DEFAULT, + date: mockDateNow, + }); + }); + + it('should initialize statusList if curStatus is provided but statusList is empty', () => { + surveyMeta.curStatus = null; + + surveyMeta.initDefaultInfo(); + + expect(surveyMeta.statusList).toEqual([ + { + status: RECORD_STATUS.NEW, + date: expect.any(Number), + }, + ]); + }); +}); diff --git a/server/src/modules/auth/__test/user.service.spec.ts b/server/src/modules/auth/__test/user.service.spec.ts index 1a9cc4b9..9589f24a 100644 --- a/server/src/modules/auth/__test/user.service.spec.ts +++ b/server/src/modules/auth/__test/user.service.spec.ts @@ -206,8 +206,8 @@ describe('UserService', () => { it('should return a list of users by username', async () => { const username = 'test'; const userList = [ - { _id: new ObjectId(), username: 'testUser1', createDate: new Date() }, - { _id: new ObjectId(), username: 'testUser2', createDate: new Date() }, + { _id: new ObjectId(), username: 'testUser1', createdAt: new Date() }, + { _id: new ObjectId(), username: 'testUser2', createdAt: new Date() }, ]; jest @@ -226,7 +226,7 @@ describe('UserService', () => { }, skip: 0, take: 10, - select: ['_id', 'username', 'createDate'], + select: ['_id', 'username', 'createdAt'], }); expect(result).toEqual(userList); }); @@ -237,12 +237,12 @@ describe('UserService', () => { { _id: new ObjectId(idList[0]), username: 'testUser1', - createDate: new Date(), + createdAt: new Date(), }, { _id: new ObjectId(idList[1]), username: 'testUser2', - createDate: new Date(), + createdAt: new Date(), }, ]; @@ -258,7 +258,7 @@ describe('UserService', () => { $in: idList.map((id) => new ObjectId(id)), }, }, - select: ['_id', 'username', 'createDate'], + select: ['_id', 'username', 'createdAt'], }); expect(result).toEqual(userList); }); diff --git a/server/src/modules/message/__test/messagePushingTask.dto.spec.ts b/server/src/modules/message/__test/messagePushingTask.dto.spec.ts index 5a8ba713..9fefb181 100644 --- a/server/src/modules/message/__test/messagePushingTask.dto.spec.ts +++ b/server/src/modules/message/__test/messagePushingTask.dto.spec.ts @@ -8,7 +8,6 @@ import { MESSAGE_PUSHING_TYPE, MESSAGE_PUSHING_HOOK, } from 'src/enums/messagePushing'; -import { RECORD_STATUS } from 'src/enums'; describe('MessagePushingTaskDto', () => { let dto: MessagePushingTaskDto; @@ -34,9 +33,9 @@ describe('MessagePushingTaskDto', () => { }); it('should have a type', () => { - dto.type = MESSAGE_PUSHING_TYPE.HTTP; // Set your desired type here + dto.type = MESSAGE_PUSHING_TYPE.HTTP; expect(dto.type).toBeDefined(); - expect(dto.type).toEqual(MESSAGE_PUSHING_TYPE.HTTP); // Adjust based on your enum + expect(dto.type).toEqual(MESSAGE_PUSHING_TYPE.HTTP); }); it('should have a push address', () => { @@ -46,13 +45,13 @@ describe('MessagePushingTaskDto', () => { }); it('should have a trigger hook', () => { - dto.triggerHook = MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED; // Set your desired hook here + dto.triggerHook = MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED; expect(dto.triggerHook).toBeDefined(); - expect(dto.triggerHook).toEqual(MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED); // Adjust based on your enum + expect(dto.triggerHook).toEqual(MESSAGE_PUSHING_HOOK.RESPONSE_INSERTED); }); it('should have an array of surveys', () => { - dto.surveys = ['survey1', 'survey2']; // Set your desired surveys here + dto.surveys = ['survey1', 'survey2']; expect(dto.surveys).toBeDefined(); expect(dto.surveys).toEqual(['survey1', 'survey2']); }); @@ -62,13 +61,6 @@ describe('MessagePushingTaskDto', () => { expect(dto.owner).toBeDefined(); expect(dto.owner).toBe('test_owner'); }); - - it('should have current status', () => { - dto.curStatus = { status: RECORD_STATUS.NEW, date: Date.now() }; - expect(dto.curStatus).toBeDefined(); - expect(dto.curStatus.status).toEqual(RECORD_STATUS.NEW); - expect(dto.curStatus.date).toBeDefined(); - }); }); describe('CodeDto', () => { diff --git a/server/src/modules/message/__test/messagePushingTask.service.spec.ts b/server/src/modules/message/__test/messagePushingTask.service.spec.ts index 13acb78b..4f5f9c8c 100644 --- a/server/src/modules/message/__test/messagePushingTask.service.spec.ts +++ b/server/src/modules/message/__test/messagePushingTask.service.spec.ts @@ -117,6 +117,9 @@ describe('MessagePushingTaskService', () => { expect(result).toEqual(tasks); expect(repository.find).toHaveBeenCalledWith({ where: { + isDeleted: { + $ne: true, + }, ownerId: mockOwnerId, surveys: { $all: [surveyId] }, triggerHook: hook, @@ -144,9 +147,19 @@ describe('MessagePushingTaskService', () => { where: { ownerId: mockOwnerId, _id: new ObjectId(taskId), + isDeleted: { + $ne: true, + }, }, }); }); + it('should throw an error when message pushing task is not found', async () => { + const taskId = '65afc62904d5db18534c0f78'; + jest.spyOn(repository, 'findOne').mockResolvedValue(null); // 模拟未找到任务 + const mockOwnerId = '66028642292c50f8b71a9eee'; + + await expect(service.findOne({ id: taskId, ownerId: mockOwnerId })); + }); }); describe('update', () => { @@ -183,6 +196,20 @@ describe('MessagePushingTaskService', () => { }); expect(repository.save).toHaveBeenCalledWith(updatedTask); }); + it('should throw an error if the task to be updated is not found', async () => { + const taskId = '65afc62904d5db18534c0f78'; + const updateDto: UpdateMessagePushingTaskDto = { name: 'Updated Task' }; + jest.spyOn(repository, 'findOne').mockResolvedValue(null); // 模拟任务未找到 + const mockOwnerId = '66028642292c50f8b71a9eee'; + + await expect( + service.update({ + ownerId: mockOwnerId, + id: taskId, + updateData: updateDto, + }), + ).rejects.toThrow(`Message pushing task with id ${taskId} not found`); + }); }); describe('remove', () => { @@ -204,16 +231,40 @@ describe('MessagePushingTaskService', () => { expect(result).toEqual(updateResult); expect(repository.updateOne).toHaveBeenCalledWith( { - ownerId: mockOperatorId, _id: new ObjectId(taskId), }, { $set: { isDeleted: true, + operatorId: mockOperatorId, + operator: mockOperator, + deletedAt: expect.any(Date), }, }, ); }); + it('should throw an error if the task to be removed is not found', async () => { + const taskId = '65afc62904d5db18534c0f78'; + jest + .spyOn(repository, 'updateOne') + .mockResolvedValue({ modifiedCount: 0 }); // 模拟删除失败 + const mockOperatorId = '66028642292c50f8b71a9eee'; + const mockOperator = 'mockOperator'; + + const result = await service.remove({ + id: taskId, + operatorId: mockOperatorId, + operator: mockOperator, + }); + + expect(result.modifiedCount).toBe(0); + expect(repository.updateOne).toHaveBeenCalledWith( + { + _id: new ObjectId(taskId), + }, + expect.any(Object), + ); + }); }); describe('surveyAuthorizeTask', () => { @@ -243,8 +294,35 @@ describe('MessagePushingTaskService', () => { $push: { surveys: surveyId, }, + $set: { + updatedAt: expect.any(Date), + }, }, ); }); + it('should not add the surveyId if it already exists in the task', async () => { + const taskId = '65afc62904d5db18534c0f78'; + const surveyId = '65af380475b64545e5277dd9'; + const mockOwnerId = '66028642292c50f8b71a9eee'; + + jest + .spyOn(repository, 'updateOne') + .mockResolvedValue({ modifiedCount: 0 }); // 模拟重复添加 + const result = await service.surveyAuthorizeTask({ + taskId, + surveyId, + ownerId: mockOwnerId, + }); + + expect(result.modifiedCount).toBe(0); + expect(repository.updateOne).toHaveBeenCalledWith( + { + _id: new ObjectId(taskId), + surveys: { $nin: [surveyId] }, // 确保只有不包含时才插入 + ownerId: mockOwnerId, + }, + expect.any(Object), + ); + }); }); }); diff --git a/server/src/modules/message/__test/updateMessagePushingTask.dto.spec.ts b/server/src/modules/message/__test/updateMessagePushingTask.dto.spec.ts index ac2bae62..c3d46bec 100644 --- a/server/src/modules/message/__test/updateMessagePushingTask.dto.spec.ts +++ b/server/src/modules/message/__test/updateMessagePushingTask.dto.spec.ts @@ -37,9 +37,4 @@ describe('UpdateMessagePushingTaskDto', () => { dto.surveys = null; expect(dto.surveys).toBeNull(); }); - - it('should have a nullable curStatus', () => { - dto.curStatus = null; - expect(dto.curStatus).toBeNull(); - }); }); diff --git a/server/src/modules/message/services/messagePushingTask.service.ts b/server/src/modules/message/services/messagePushingTask.service.ts index 6a5afd41..93872ef5 100644 --- a/server/src/modules/message/services/messagePushingTask.service.ts +++ b/server/src/modules/message/services/messagePushingTask.service.ts @@ -63,14 +63,14 @@ export class MessagePushingTaskService { }); } - async findOne({ + findOne({ id, ownerId, }: { id: string; ownerId: string; }): Promise { - return await this.messagePushingTaskRepository.findOne({ + return this.messagePushingTaskRepository.findOne({ where: { ownerId, _id: new ObjectId(id), diff --git a/server/src/modules/survey/__test/collaborator.service.spec.ts b/server/src/modules/survey/__test/collaborator.service.spec.ts index 34a1157e..afc9f810 100644 --- a/server/src/modules/survey/__test/collaborator.service.spec.ts +++ b/server/src/modules/survey/__test/collaborator.service.spec.ts @@ -75,6 +75,25 @@ describe('CollaboratorService', () => { }); }); + describe('deleteCollaborator', () => { + it('should delete a collaborator by userId and surveyId', async () => { + const deleteOneSpy = jest + .spyOn(repository, 'deleteOne') + .mockResolvedValue({ acknowledged: true, deletedCount: 1 }); + + const result = await service.deleteCollaborator({ + userId: '1', + surveyId: '1', + }); + + expect(deleteOneSpy).toHaveBeenCalledWith({ + userId: '1', + surveyId: '1', + }); + expect(result).toEqual({ acknowledged: true, deletedCount: 1 }); + }); + }); + describe('batchCreate', () => { it('should batch create collaborators', async () => { const insertManySpy = jest @@ -86,15 +105,193 @@ describe('CollaboratorService', () => { const result = await service.batchCreate({ surveyId: '1', collaboratorList: [{ userId: '1', permissions: [] }], + creator: 'testCreator', + creatorId: 'testCreatorId', }); expect(insertManySpy).toHaveBeenCalledWith([ - { surveyId: '1', userId: '1', permissions: [] }, + { + userId: '1', + permissions: [], + surveyId: '1', + createdAt: expect.any(Date), + updatedAt: expect.any(Date), + creator: 'testCreator', + creatorId: 'testCreatorId', + }, ]); expect(result).toEqual({ insertedCount: 1 }); }); }); + describe('changeUserPermission', () => { + it("should update a user's permissions", async () => { + const updateOneSpy = jest + .spyOn(repository, 'updateOne') + .mockResolvedValue({}); + + const result = await service.changeUserPermission({ + userId: '1', + surveyId: '1', + permission: 'read', + operator: 'testOperator', + operatorId: 'testOperatorId', + }); + + expect(updateOneSpy).toHaveBeenCalledWith( + { + surveyId: '1', + userId: '1', + }, + { + $set: { + permission: 'read', + operator: 'testOperator', + operatorId: 'testOperatorId', + updatedAt: expect.any(Date), + }, + }, + ); + expect(result).toEqual({}); + }); + }); + + describe('batchDelete', () => { + it('should batch delete collaborators', async () => { + const mockResult = { acknowledged: true, deletedCount: 1 }; + const deleteManySpy = jest + .spyOn(repository, 'deleteMany') + .mockResolvedValue(mockResult); + + const collaboratorId = new ObjectId().toString(); + + const result = await service.batchDelete({ + surveyId: '1', + idList: [collaboratorId], + }); + + const expectedQuery = { + surveyId: '1', + $or: [ + { + _id: { + $in: [new ObjectId(collaboratorId)], + }, + }, + ], + }; + + expect(logger.info).toHaveBeenCalledWith(JSON.stringify(expectedQuery)); + expect(deleteManySpy).toHaveBeenCalledWith(expectedQuery); + expect(result).toEqual(mockResult); + }); + it('should batch delete collaborators by idList and userIdList', async () => { + const collaboratorId = new ObjectId().toString(); + const deleteManySpy = jest + .spyOn(repository, 'deleteMany') + .mockResolvedValue({ acknowledged: true, deletedCount: 2 }); + + const result = await service.batchDelete({ + idList: [collaboratorId], + userIdList: ['user1', 'user2'], + surveyId: '1', + }); + + const expectedQuery = { + surveyId: '1', + $or: [ + { + userId: { + $in: ['user1', 'user2'], + }, + }, + { + _id: { + $in: [new ObjectId(collaboratorId)], + }, + }, + ], + }; + + expect(deleteManySpy).toHaveBeenCalledWith(expectedQuery); + expect(result).toEqual({ acknowledged: true, deletedCount: 2 }); + }); + + it('should handle batch delete with neIdList only', async () => { + const neCollaboratorId = new ObjectId().toString(); + const deleteManySpy = jest + .spyOn(repository, 'deleteMany') + .mockResolvedValue({ acknowledged: true, deletedCount: 1 }); + + const result = await service.batchDelete({ + neIdList: [neCollaboratorId], + surveyId: '1', + }); + + const expectedQuery = { + surveyId: '1', + $or: [ + { + _id: { + $nin: [new ObjectId(neCollaboratorId)], + }, + }, + ], + }; + + expect(deleteManySpy).toHaveBeenCalledWith(expectedQuery); + expect(result).toEqual({ acknowledged: true, deletedCount: 1 }); + }); + }); + + describe('batchDeleteBySurveyId', () => { + it('should batch delete collaborators by survey id', async () => { + const mockResult = { acknowledged: true, deletedCount: 1 }; + const deleteManySpy = jest + .spyOn(repository, 'deleteMany') + .mockResolvedValue(mockResult); + + const surveyId = new ObjectId().toString(); + + const result = await service.batchDeleteBySurveyId(surveyId); + + expect(deleteManySpy).toHaveBeenCalledWith({ + surveyId, + }); + expect(result).toEqual(mockResult); + }); + }); + + describe('updateById', () => { + it('should update collaborator by id', async () => { + const updateOneSpy = jest + .spyOn(repository, 'updateOne') + .mockResolvedValue({}); + const collaboratorId = new ObjectId().toString(); + const result = await service.updateById({ + collaboratorId, + permissions: [], + operator: 'testOperator', + operatorId: 'testOperatorId', + }); + + expect(updateOneSpy).toHaveBeenCalledWith( + { + _id: new ObjectId(collaboratorId), + }, + { + $set: { + permissions: [], + operator: 'testOperator', + operatorId: 'testOperatorId', + updatedAt: expect.any(Date), + }, + }, + ); + expect(result).toEqual({}); + }); + }); + describe('getSurveyCollaboratorList', () => { it('should return a list of collaborators for a survey', async () => { const collaboratorId = new ObjectId().toString(); @@ -121,38 +318,6 @@ describe('CollaboratorService', () => { }); }); - describe('getCollaboratorListByIds', () => { - it('should return a list of collaborators by ids', async () => { - const collaboratorId = new ObjectId().toString(); - const findSpy = jest.spyOn(repository, 'find').mockResolvedValue([ - { - _id: new ObjectId(collaboratorId), - surveyId: '1', - userId: '1', - permissions: [], - }, - ] as Collaborator[]); - - const result = await service.getCollaboratorListByIds({ - idList: [collaboratorId], - }); - - expect(findSpy).toHaveBeenCalledWith({ - _id: { - $in: [new ObjectId(collaboratorId)], - }, - }); - expect(result).toEqual([ - { - _id: new ObjectId(collaboratorId), - surveyId: '1', - userId: '1', - permissions: [], - }, - ]); - }); - }); - describe('getCollaborator', () => { it('should return a collaborator', async () => { const collaboratorId = new ObjectId().toString(); @@ -183,127 +348,6 @@ describe('CollaboratorService', () => { }); }); - describe('changeUserPermission', () => { - it("should update a user's permissions", async () => { - const updateOneSpy = jest - .spyOn(repository, 'updateOne') - .mockResolvedValue({}); - - const result = await service.changeUserPermission({ - userId: '1', - surveyId: '1', - permission: 'read', - }); - - expect(updateOneSpy).toHaveBeenCalledWith( - { - surveyId: '1', - userId: '1', - }, - { - $set: { - permission: 'read', - }, - }, - ); - expect(result).toEqual({}); - }); - }); - - describe('deleteCollaborator', () => { - it('should delete a collaborator', async () => { - const mockResult = { acknowledged: true, deletedCount: 1 }; - const deleteOneSpy = jest - .spyOn(repository, 'deleteOne') - .mockResolvedValue(mockResult); - - const result = await service.deleteCollaborator({ - userId: '1', - surveyId: '1', - }); - - expect(deleteOneSpy).toHaveBeenCalledWith({ - userId: '1', - surveyId: '1', - }); - expect(result).toEqual(mockResult); - }); - }); - - describe('batchDelete', () => { - it('should batch delete collaborators', async () => { - const mockResult = { acknowledged: true, deletedCount: 1 }; - const deleteManySpy = jest - .spyOn(repository, 'deleteMany') - .mockResolvedValue(mockResult); - - const collaboratorId = new ObjectId().toString(); - - const result = await service.batchDelete({ - surveyId: '1', - idList: [collaboratorId], - }); - - const expectedQuery = { - surveyId: '1', - $or: [ - { - _id: { - $in: [new ObjectId(collaboratorId)], - }, - }, - ], - }; - - expect(logger.info).toHaveBeenCalledWith(JSON.stringify(expectedQuery)); - expect(deleteManySpy).toHaveBeenCalledWith(expectedQuery); - expect(result).toEqual(mockResult); - }); - }); - - describe('batchDeleteBySurveyId', () => { - it('should batch delete collaborators by survey id', async () => { - const mockResult = { acknowledged: true, deletedCount: 1 }; - const deleteManySpy = jest - .spyOn(repository, 'deleteMany') - .mockResolvedValue(mockResult); - - const surveyId = new ObjectId().toString(); - - const result = await service.batchDeleteBySurveyId(surveyId); - - expect(deleteManySpy).toHaveBeenCalledWith({ - surveyId, - }); - expect(result).toEqual(mockResult); - }); - }); - - describe('updateById', () => { - it('should update collaborator by id', async () => { - const updateOneSpy = jest - .spyOn(repository, 'updateOne') - .mockResolvedValue({}); - const collaboratorId = new ObjectId().toString(); - const result = await service.updateById({ - collaboratorId, - permissions: [], - }); - - expect(updateOneSpy).toHaveBeenCalledWith( - { - _id: new ObjectId(collaboratorId), - }, - { - $set: { - permissions: [], - }, - }, - ); - expect(result).toEqual({}); - }); - }); - describe('getCollaboratorListByUserId', () => { it('should return a list of collaborators by user id', async () => { const userId = new ObjectId().toString(); @@ -327,5 +371,48 @@ describe('CollaboratorService', () => { { _id: '1', surveyId: '1', userId, permissions: [] }, ]); }); + + it('should return a list of collaborators by their IDs', async () => { + const collaboratorId1 = new ObjectId().toString(); + const collaboratorId2 = new ObjectId().toString(); + const findSpy = jest.spyOn(repository, 'find').mockResolvedValue([ + { + _id: new ObjectId(collaboratorId1), + surveyId: '1', + userId: 'user1', + permissions: [], + }, + { + _id: new ObjectId(collaboratorId2), + surveyId: '2', + userId: 'user2', + permissions: [], + }, + ] as Collaborator[]); + + const result = await service.getCollaboratorListByIds({ + idList: [collaboratorId1, collaboratorId2], + }); + + expect(findSpy).toHaveBeenCalledWith({ + _id: { + $in: [new ObjectId(collaboratorId1), new ObjectId(collaboratorId2)], + }, + }); + expect(result).toEqual([ + { + _id: new ObjectId(collaboratorId1), + surveyId: '1', + userId: 'user1', + permissions: [], + }, + { + _id: new ObjectId(collaboratorId2), + surveyId: '2', + userId: 'user2', + permissions: [], + }, + ]); + }); }); }); diff --git a/server/src/modules/survey/__test/dataStatistic.controller.spec.ts b/server/src/modules/survey/__test/dataStatistic.controller.spec.ts index bdc4b425..44ac4a91 100644 --- a/server/src/modules/survey/__test/dataStatistic.controller.spec.ts +++ b/server/src/modules/survey/__test/dataStatistic.controller.spec.ts @@ -109,8 +109,8 @@ describe('DataStatisticController', () => { }, ], listBody: [ - { diffTime: '0.5', createDate: '2024-02-11' }, - { diffTime: '0.5', createDate: '2024-02-11' }, + { diffTime: '0.5', createdAt: '2024-02-11' }, + { diffTime: '0.5', createdAt: '2024-02-11' }, ], }; @@ -155,8 +155,8 @@ describe('DataStatisticController', () => { }, ], listBody: [ - { diffTime: '0.5', createDate: '2024-02-11', data123: '15200000000' }, - { diffTime: '0.5', createDate: '2024-02-11', data123: '13800000000' }, + { diffTime: '0.5', createdAt: '2024-02-11', data123: '15200000000' }, + { diffTime: '0.5', createdAt: '2024-02-11', data123: '13800000000' }, ], }; @@ -212,8 +212,8 @@ describe('DataStatisticController', () => { date: 1717158851823, }, ], - createDate: 1717158851823, - updateDate: 1717159136025, + createdAt: 1717158851823, + updatedAt: 1717159136025, title: '问卷调研', surveyPath: 'ZdGNzTTR', code: { diff --git a/server/src/modules/survey/__test/dataStatistic.service.spec.ts b/server/src/modules/survey/__test/dataStatistic.service.spec.ts index b53bfa0d..e46ea4dd 100644 --- a/server/src/modules/survey/__test/dataStatistic.service.spec.ts +++ b/server/src/modules/survey/__test/dataStatistic.service.spec.ts @@ -151,8 +151,8 @@ describe('DataStatisticService', () => { date: 1710340863123.0, }, ], - createDate: 1710340863123.0, - updateDate: 1710340863123.0, + createdAt: 1710340863123.0, + updatedAt: 1710340863123.0, }, ] as unknown as Array; @@ -196,7 +196,7 @@ describe('DataStatisticService', () => { data413: expect.any(Number), data863: expect.any(String), diffTime: expect.any(String), - createDate: expect.any(String), + createdAt: expect.any(String), }), ]), }); @@ -273,8 +273,8 @@ describe('DataStatisticService', () => { date: 1710400232161.0, }, ], - createDate: 1710400232161.0, - updateDate: 1710400232161.0, + createdAt: 1710400232161.0, + updatedAt: 1710400232161.0, }, ] as unknown as Array; @@ -295,7 +295,7 @@ describe('DataStatisticService', () => { expect(result.listBody).toEqual( expect.arrayContaining([ expect.objectContaining({ - createDate: expect.any(String), + createdAt: expect.any(String), data405: expect.any(String), data450: expect.any(String), data458: expect.any(String), diff --git a/server/src/modules/survey/__test/downloadTask.controller.spec.ts b/server/src/modules/survey/__test/downloadTask.controller.spec.ts index 48cd4f94..617db0c5 100644 --- a/server/src/modules/survey/__test/downloadTask.controller.spec.ts +++ b/server/src/modules/survey/__test/downloadTask.controller.spec.ts @@ -145,7 +145,7 @@ describe('DownloadTaskController', () => { filename: 'mockFile.csv', url: 'http://mock-url.com', fileSize: 1024, - createDate: Date.now(), + createdAt: Date.now(), }, ], }; @@ -219,10 +219,13 @@ describe('DownloadTaskController', () => { describe('deleteFileByName', () => { it('should delete a download task successfully', async () => { const mockBody = { taskId: 'mockTaskId' }; - const mockReq = { user: { _id: 'mockUserId' } }; + const mockUserId = new ObjectId(); + const mockReq = { + user: { _id: mockUserId, username: 'mockUsername' }, + }; const mockTaskInfo: any = { _id: new ObjectId(), - creatorId: 'mockUserId', + creatorId: mockUserId.toString(), }; const mockDelRes = { modifiedCount: 1 }; @@ -237,6 +240,8 @@ describe('DownloadTaskController', () => { expect(downloadTaskService.deleteDownloadTask).toHaveBeenCalledWith({ taskId: mockBody.taskId, + operator: mockReq.user.username, + operatorId: mockReq.user._id.toString(), }); expect(result).toEqual({ code: 200, data: true }); }); diff --git a/server/src/modules/survey/__test/downloadTask.service.spec.ts b/server/src/modules/survey/__test/downloadTask.service.spec.ts index 14fe70a8..e0138e35 100644 --- a/server/src/modules/survey/__test/downloadTask.service.spec.ts +++ b/server/src/modules/survey/__test/downloadTask.service.spec.ts @@ -9,7 +9,7 @@ import { DataStatisticService } from '../services/dataStatistic.service'; import { FileService } from 'src/modules/file/services/file.service'; import { Logger } from 'src/logger'; import { ObjectId } from 'mongodb'; -import { RECORD_STATUS } from 'src/enums'; +import { DOWNLOAD_TASK_STATUS } from 'src/enums/downloadTaskStatus'; describe('DownloadTaskService', () => { let service: DownloadTaskService; @@ -93,6 +93,7 @@ describe('DownloadTaskService', () => { title: mockParams.responseSchema.title, }, filename: expect.any(String), + status: DOWNLOAD_TASK_STATUS.WAITING, }); expect(downloadTaskRepository.save).toHaveBeenCalled(); expect(result).toEqual(mockTaskId); @@ -118,10 +119,11 @@ describe('DownloadTaskService', () => { expect(downloadTaskRepository.findAndCount).toHaveBeenCalledWith({ where: { creatorId: mockCreatorId, + isDeleted: { $ne: true }, }, take: 10, skip: 0, - order: { createDate: -1 }, + order: { createdAt: -1 }, }); expect(result).toEqual({ @@ -160,32 +162,84 @@ describe('DownloadTaskService', () => { }); describe('deleteDownloadTask', () => { - it('should update task status to REMOVED', async () => { + it('should mark task as deleted and set deletedAt', async () => { const mockTaskId = new ObjectId().toString(); + const mockOperator = 'operatorName'; + const mockOperatorId = 'operatorId1'; const mockUpdateResult = { matchedCount: 1 }; jest .spyOn(downloadTaskRepository, 'updateOne') .mockResolvedValue(mockUpdateResult as any); - const result = await service.deleteDownloadTask({ taskId: mockTaskId }); + const result = await service.deleteDownloadTask({ + taskId: mockTaskId, + operator: mockOperator, + operatorId: mockOperatorId, + }); expect(downloadTaskRepository.updateOne).toHaveBeenCalledWith( - { - _id: new ObjectId(mockTaskId), - 'curStatus.status': { $ne: RECORD_STATUS.REMOVED }, - }, + { _id: new ObjectId(mockTaskId) }, { $set: { - curStatus: { - status: RECORD_STATUS.REMOVED, - date: expect.any(Number), - }, + isDeleted: true, + operator: mockOperator, + operatorId: mockOperatorId, + deletedAt: expect.any(Date), }, - $push: { statusList: expect.any(Object) }, }, ); expect(result).toEqual(mockUpdateResult); }); }); + + describe('processDownloadTask', () => { + it('should push task to queue and execute if not executing', async () => { + const mockTaskId = new ObjectId().toString(); + jest.spyOn(service, 'executeTask').mockImplementation(jest.fn()); + + service.processDownloadTask({ taskId: mockTaskId }); + + expect(DownloadTaskService.taskList).toContain(mockTaskId); + expect(service.executeTask).toHaveBeenCalled(); + }); + + it('should handle already executing case', async () => { + const mockTaskId = new ObjectId().toString(); + DownloadTaskService.isExecuting = true; + jest.spyOn(service, 'executeTask').mockImplementation(jest.fn()); + + service.processDownloadTask({ taskId: mockTaskId }); + + expect(DownloadTaskService.taskList).toContain(mockTaskId); + expect(service.executeTask).not.toHaveBeenCalled(); + }); + }); + + describe('executeTask', () => { + it('should process and execute tasks in queue', async () => { + const mockTaskId = new ObjectId().toString(); + DownloadTaskService.taskList.push(mockTaskId); + + jest.spyOn(service, 'getDownloadTaskById').mockResolvedValue({ + _id: new ObjectId(mockTaskId), + isDeleted: false, + } as any); + + jest.spyOn(service, 'handleDownloadTask').mockResolvedValue(undefined); + + await service.executeTask(); + + expect(service.getDownloadTaskById).toHaveBeenCalledWith({ + taskId: mockTaskId, + }); + expect(service.handleDownloadTask).toHaveBeenCalled(); + }); + + it('should stop executing when queue is empty', async () => { + DownloadTaskService.taskList = []; + await service.executeTask(); + expect(DownloadTaskService.isExecuting).toBe(false); + }); + }); }); diff --git a/server/src/modules/survey/__test/mockResponseSchema.ts b/server/src/modules/survey/__test/mockResponseSchema.ts index af54a152..bb15c5b3 100644 --- a/server/src/modules/survey/__test/mockResponseSchema.ts +++ b/server/src/modules/survey/__test/mockResponseSchema.ts @@ -14,8 +14,8 @@ export const mockSensitiveResponseSchema: ResponseSchema = { date: 1710399368439, }, ], - createDate: 1710399368440, - updateDate: 1710399368440, + createdAt: 1710399368440, + updatedAt: 1710399368440, title: '加密全流程', surveyPath: 'EBzdmnSp', code: { @@ -654,6 +654,6 @@ export const mockResponseSchema: ResponseSchema = { }, }, pageId: '65afc62904d5db18534c0f78', - createDate: 1710340841289, - updateDate: 1710340841289.0, + createdAt: 1710340841289, + updatedAt: 1710340841289.0, } as unknown as ResponseSchema; diff --git a/server/src/modules/survey/__test/session.service.spec.ts b/server/src/modules/survey/__test/session.service.spec.ts new file mode 100644 index 00000000..7f8cc904 --- /dev/null +++ b/server/src/modules/survey/__test/session.service.spec.ts @@ -0,0 +1,144 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MongoRepository } from 'typeorm'; +import { getRepositoryToken } from '@nestjs/typeorm'; +import { SessionService } from '../services/session.service'; +import { Session } from 'src/models/session.entity'; +import { ObjectId } from 'mongodb'; +import { SESSION_STATUS } from 'src/enums/surveySessionStatus'; + +describe('SessionService', () => { + let service: SessionService; + let repository: MongoRepository; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + SessionService, + { + provide: getRepositoryToken(Session), + useClass: MongoRepository, + }, + ], + }).compile(); + + service = module.get(SessionService); + repository = module.get>( + getRepositoryToken(Session), + ); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('create', () => { + it('should create and save a new session', async () => { + const mockSession = { + surveyId: 'survey123', + userId: 'user123', + status: SESSION_STATUS.DEACTIVATED, + }; + + const createdSession: any = { ...mockSession, _id: new ObjectId() }; + jest.spyOn(repository, 'create').mockReturnValue(createdSession); + jest.spyOn(repository, 'save').mockResolvedValue(createdSession); + + const result = await service.create({ + surveyId: mockSession.surveyId, + userId: mockSession.userId, + }); + + expect(result).toEqual(createdSession); + expect(repository.create).toHaveBeenCalledWith(mockSession); + expect(repository.save).toHaveBeenCalledWith(createdSession); + }); + }); + + describe('findOne', () => { + it('should find a session by id', async () => { + const sessionId = '65afc62904d5db18534c0f78'; + const foundSession = { + _id: new ObjectId(sessionId), + surveyId: 'survey123', + userId: 'user123', + status: SESSION_STATUS.ACTIVATED, + }; + + jest + .spyOn(repository, 'findOne') + .mockResolvedValue(foundSession as Session); + + const result = await service.findOne(sessionId); + + expect(result).toEqual(foundSession); + expect(repository.findOne).toHaveBeenCalledWith({ + where: { _id: new ObjectId(sessionId) }, + }); + }); + }); + + describe('findLatestEditingOne', () => { + it('should find the latest editing session for a survey', async () => { + const surveyId = 'survey123'; + const latestSession = { + _id: new ObjectId(), + surveyId: surveyId, + userId: 'user123', + status: SESSION_STATUS.ACTIVATED, + }; + + jest + .spyOn(repository, 'findOne') + .mockResolvedValue(latestSession as Session); + + const result = await service.findLatestEditingOne({ surveyId }); + + expect(result).toEqual(latestSession); + expect(repository.findOne).toHaveBeenCalledWith({ + where: { + surveyId, + status: SESSION_STATUS.ACTIVATED, + }, + }); + }); + }); + + describe('updateSessionToEditing', () => { + it('should update a session to editing and deactivate other sessions', async () => { + const sessionId = '65afc62904d5db18534c0f78'; + const surveyId = 'survey123'; + + const updateResult: any = { affected: 1 }; + const updateManyResult = { modifiedCount: 1 }; + + jest.spyOn(repository, 'update').mockResolvedValue(updateResult); + jest.spyOn(repository, 'updateMany').mockResolvedValue(updateManyResult); + + const result = await service.updateSessionToEditing({ + sessionId, + surveyId, + }); + + expect(result).toEqual([updateResult, updateManyResult]); + expect(repository.update).toHaveBeenCalledWith( + { _id: new ObjectId(sessionId) }, + { + status: SESSION_STATUS.ACTIVATED, + updatedAt: expect.any(Date), + }, + ); + expect(repository.updateMany).toHaveBeenCalledWith( + { + surveyId, + _id: { $ne: new ObjectId(sessionId) }, + }, + { + $set: { + status: SESSION_STATUS.DEACTIVATED, + updatedAt: expect.any(Date), + }, + }, + ); + }); + }); +}); diff --git a/server/src/modules/survey/__test/survey.controller.spec.ts b/server/src/modules/survey/__test/survey.controller.spec.ts index d141508f..a01f5502 100644 --- a/server/src/modules/survey/__test/survey.controller.spec.ts +++ b/server/src/modules/survey/__test/survey.controller.spec.ts @@ -5,13 +5,12 @@ import { SurveyConfService } from '../services/surveyConf.service'; import { ResponseSchemaService } from '../../surveyResponse/services/responseScheme.service'; import { ContentSecurityService } from '../services/contentSecurity.service'; import { SurveyHistoryService } from '../services/surveyHistory.service'; -import { CounterService } from '../../surveyResponse/services/counter.service'; import { SessionService } from '../services/session.service'; import { UserService } from '../../auth/services/user.service'; import { ObjectId } from 'mongodb'; -import { SurveyMeta } from 'src/models/surveyMeta.entity'; -import { SurveyConf } from 'src/models/surveyConf.entity'; import { Logger } from 'src/logger'; +import { HttpException } from 'src/exceptions/httpException'; +import { Authentication } from 'src/guards/authentication.guard'; jest.mock('../services/surveyMeta.service'); jest.mock('../services/surveyConf.service'); @@ -19,9 +18,7 @@ jest.mock('../../surveyResponse/services/responseScheme.service'); jest.mock('../services/contentSecurity.service'); jest.mock('../services/surveyHistory.service'); jest.mock('../services/session.service'); -jest.mock('../../surveyResponse/services/counter.service'); jest.mock('../../auth/services/user.service'); - jest.mock('src/guards/authentication.guard'); jest.mock('src/guards/survey.guard'); jest.mock('src/guards/workspace.guard'); @@ -31,28 +28,17 @@ describe('SurveyController', () => { let surveyMetaService: SurveyMetaService; let surveyConfService: SurveyConfService; let responseSchemaService: ResponseSchemaService; - let surveyHistoryService: SurveyHistoryService; - let sessionService: SessionService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [SurveyController], providers: [ SurveyMetaService, - { - provide: SurveyConfService, - useValue: { - getSurveyConfBySurveyId: jest.fn(), - getSurveyContentByCode: jest.fn(), - createSurveyConf: jest.fn(), - saveSurveyConf: jest.fn(), - }, - }, + SurveyConfService, ResponseSchemaService, ContentSecurityService, SurveyHistoryService, SessionService, - CounterService, UserService, { provide: Logger, @@ -61,6 +47,12 @@ describe('SurveyController', () => { info: jest.fn(), }, }, + { + provide: Authentication, + useClass: jest.fn().mockImplementation(() => ({ + canActivate: () => true, + })), + }, ], }).compile(); @@ -70,9 +62,6 @@ describe('SurveyController', () => { responseSchemaService = module.get( ResponseSchemaService, ); - surveyHistoryService = - module.get(SurveyHistoryService); - sessionService = module.get(SessionService); }); describe('getBannerData', () => { @@ -94,12 +83,11 @@ describe('SurveyController', () => { const newId = new ObjectId(); jest.spyOn(surveyMetaService, 'createSurveyMeta').mockResolvedValue({ _id: newId, - } as SurveyMeta); + } as any); jest.spyOn(surveyConfService, 'createSurveyConf').mockResolvedValue({ _id: new ObjectId(), - pageId: newId.toString(), - } as SurveyConf); + } as any); const result = await controller.createSurvey(surveyInfo, { user: { username: 'testUser', _id: new ObjectId() }, @@ -113,13 +101,15 @@ describe('SurveyController', () => { }); }); + it('should throw an error if validation fails', async () => { + const surveyInfo = {}; // Invalid data + await expect( + controller.createSurvey(surveyInfo as any, { user: {} }), + ).rejects.toThrow(HttpException); + }); + it('should create a new survey by copy', async () => { const existsSurveyId = new ObjectId(); - const existsSurveyMeta = { - _id: existsSurveyId, - surveyType: 'exam', - owner: 'testUser', - } as SurveyMeta; const params = { surveyType: 'normal', remark: '问卷调研', @@ -128,15 +118,15 @@ describe('SurveyController', () => { createFrom: existsSurveyId.toString(), }; - jest.spyOn(surveyMetaService, 'createSurveyMeta').mockResolvedValue({ - _id: new ObjectId(), - } as SurveyMeta); - const request = { user: { username: 'testUser', _id: new ObjectId() }, - surveyMeta: existsSurveyMeta, + surveyMeta: { _id: existsSurveyId, surveyType: 'exam' }, }; + jest.spyOn(surveyMetaService, 'createSurveyMeta').mockResolvedValue({ + _id: new ObjectId(), + } as any); + const result = await controller.createSurvey(params, request); expect(result?.data?.id).toBeDefined(); }); @@ -145,69 +135,30 @@ describe('SurveyController', () => { describe('updateConf', () => { it('should update survey configuration', async () => { const surveyId = new ObjectId(); - const surveyMeta = { - _id: surveyId, - surveyType: 'exam', - owner: 'testUser', - } as SurveyMeta; - - jest - .spyOn(surveyConfService, 'saveSurveyConf') - .mockResolvedValue(undefined); - jest - .spyOn(surveyHistoryService, 'addHistory') - .mockResolvedValue(undefined); - jest - .spyOn(sessionService, 'findLatestEditingOne') - .mockResolvedValue(null); - jest - .spyOn(sessionService, 'updateSessionToEditing') - .mockResolvedValue(undefined); - const reqBody = { surveyId: surveyId.toString(), configData: { - bannerConf: { - titleConfig: {}, - bannerConfig: {}, - }, - baseConf: { - beginTime: '2024-01-23 21:59:05', - endTime: '2034-01-23 21:59:05', - }, - bottomConf: { logoImage: '/imgs/Logo.webp', logoImageWidth: '60%' }, - skinConf: { - skinColor: '#4a4c5b', - inputBgColor: '#ffffff', - backgroundConf: { - color: '#fff', - type: 'color', - image: '', - }, - themeConf: { - color: '#ffa600', - }, - contentConf: { - opacity: 100, - }, - }, - submitConf: {}, - dataConf: { - dataList: [], - }, + /* ... your config data here ... */ }, sessionId: 'mock-session-id', }; const result = await controller.updateConf(reqBody, { user: { username: 'testUser', _id: 'testUserId' }, - surveyMeta, + surveyMeta: { _id: surveyId }, }); expect(result).toEqual({ code: 200, }); }); + + it('should throw an error if validation fails', async () => { + const reqBody = {}; // Invalid data + await expect( + controller.updateConf(reqBody, { user: {} }), + ).rejects.toThrow(HttpException); + }); }); describe('deleteSurvey', () => { @@ -217,7 +168,7 @@ describe('SurveyController', () => { _id: surveyId, surveyType: 'exam', owner: 'testUser', - } as SurveyMeta; + }; jest .spyOn(surveyMetaService, 'deleteSurveyMeta') @@ -227,13 +178,10 @@ describe('SurveyController', () => { .mockResolvedValue(undefined); const result = await controller.deleteSurvey({ - user: { username: 'testUser' }, surveyMeta, + user: { username: 'testUser', _id: new ObjectId() }, }); - - expect(result).toEqual({ - code: 200, - }); + expect(result).toEqual({ code: 200 }); }); }); @@ -244,23 +192,19 @@ describe('SurveyController', () => { _id: surveyId, surveyType: 'exam', owner: 'testUser', - } as SurveyMeta; + }; jest .spyOn(surveyConfService, 'getSurveyConfBySurveyId') - .mockResolvedValue({ - _id: new ObjectId(), - pageId: surveyId.toString(), - } as SurveyConf); - - const request = { - user: { username: 'testUser', _id: new ObjectId() }, - surveyMeta, - }; + .mockResolvedValue({} as any); const result = await controller.getSurvey( { surveyId: surveyId.toString() }, - request, + { + surveyMeta, + user: { username: 'testUser', _id: new ObjectId() }, + }, ); + expect(result?.data?.surveyMetaRes).toBeDefined(); expect(result?.data?.surveyConfRes).toBeDefined(); }); @@ -273,28 +217,77 @@ describe('SurveyController', () => { _id: surveyId, surveyType: 'exam', owner: 'testUser', - } as SurveyMeta; + isDeleted: false, + }; jest .spyOn(surveyConfService, 'getSurveyConfBySurveyId') .mockResolvedValue({ - _id: new ObjectId(), - pageId: surveyId.toString(), code: {}, - } as SurveyConf); + } as any); jest .spyOn(surveyConfService, 'getSurveyContentByCode') .mockResolvedValue({ text: '' }); + jest + .spyOn(surveyMetaService, 'publishSurveyMeta') + .mockResolvedValue(undefined); const result = await controller.publishSurvey( { surveyId: surveyId.toString() }, - { - user: { username: 'testUser', _id: new ObjectId() }, - surveyMeta, - }, + { surveyMeta, user: { username: 'testUser', _id: new ObjectId() } }, ); expect(result.code).toBe(200); }); + + it('should throw an error if the survey is deleted', async () => { + const surveyId = new ObjectId(); + const surveyMeta = { _id: surveyId, isDeleted: true }; + + await expect( + controller.publishSurvey( + { surveyId: surveyId.toString() }, + { surveyMeta, user: { username: 'testUser' } }, + ), + ).rejects.toThrow(HttpException); + }); + }); + + // New tests for additional methods + describe('pausingSurvey', () => { + it('should pause the survey successfully', async () => { + const surveyMeta = { surveyPath: 'some/path' }; + + jest + .spyOn(surveyMetaService, 'pausingSurveyMeta') + .mockResolvedValue(undefined); + jest + .spyOn(responseSchemaService, 'pausingResponseSchema') + .mockResolvedValue(undefined); + + const result = await controller.pausingSurvey({ + surveyMeta, + user: { username: 'testUser' }, + }); + expect(result.code).toBe(200); + }); + }); + + describe('getPreviewSchema', () => { + it('should get the preview schema successfully', async () => { + const surveyId = new ObjectId(); + jest + .spyOn(surveyConfService, 'getSurveyConfBySurveyId') + .mockResolvedValue({} as any); + jest.spyOn(surveyMetaService, 'getSurveyById').mockResolvedValue({ + title: 'Test Survey', + surveyPath: 'some/path', + } as any); + + const result = await controller.getPreviewSchema({ + surveyPath: surveyId.toString(), + }); + expect(result.code).toBe(200); + }); }); }); diff --git a/server/src/modules/survey/__test/surveyHistory.service.spec.ts b/server/src/modules/survey/__test/surveyHistory.service.spec.ts index 3ab1d2f5..a162f374 100644 --- a/server/src/modules/survey/__test/surveyHistory.service.spec.ts +++ b/server/src/modules/survey/__test/surveyHistory.service.spec.ts @@ -121,9 +121,9 @@ describe('SurveyHistoryService', () => { }, take: 100, order: { - createDate: -1, + createdAt: -1, }, - select: ['createDate', 'operator', 'type', '_id'], + select: ['createdAt', 'operator', 'type', '_id'], }); }); }); diff --git a/server/src/modules/survey/__test/surveyMeta.controller.spec.ts b/server/src/modules/survey/__test/surveyMeta.controller.spec.ts index 4d567850..e70058ab 100644 --- a/server/src/modules/survey/__test/surveyMeta.controller.spec.ts +++ b/server/src/modules/survey/__test/surveyMeta.controller.spec.ts @@ -59,18 +59,25 @@ describe('SurveyMetaController', () => { remark: '', }; + const mockUser = { + username: 'test-user', + _id: new ObjectId(), + }; + const req = { - user: { - username: 'test-user', - }, + user: mockUser, surveyMeta: survey, }; const result = await controller.updateMeta(reqBody, req); expect(surveyMetaService.editSurveyMeta).toHaveBeenCalledWith({ - title: reqBody.title, - remark: reqBody.remark, + operator: mockUser.username, + operatorId: mockUser._id.toString(), + survey: { + title: reqBody.title, + remark: reqBody.remark, + }, }); expect(result).toEqual({ code: 200 }); @@ -116,8 +123,8 @@ describe('SurveyMetaController', () => { data: [ { _id: new ObjectId(), - createDate: date, - updateDate: date, + createdAt: date, + updatedAt: date, curStatus: { date: date, }, @@ -138,7 +145,7 @@ describe('SurveyMetaController', () => { count: 10, data: expect.arrayContaining([ expect.objectContaining({ - createDate: expect.stringMatching( + createdAt: expect.stringMatching( /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/, ), curStatus: expect.objectContaining({ @@ -183,7 +190,7 @@ describe('SurveyMetaController', () => { condition: [{ field: 'surveyType', value: 'normal' }], }, ]), - order: JSON.stringify([{ field: 'createDate', value: -1 }]), + order: JSON.stringify([{ field: 'createdAt', value: -1 }]), }; const userId = new ObjectId().toString(); const req = { @@ -203,7 +210,7 @@ describe('SurveyMetaController', () => { surveyIdList: [], userId, filter: { surveyType: 'normal', title: { $regex: 'hahah' } }, - order: { createDate: -1 }, + order: { createdAt: -1 }, workspaceId: undefined, }); }); diff --git a/server/src/modules/survey/__test/surveyMeta.service.spec.ts b/server/src/modules/survey/__test/surveyMeta.service.spec.ts index 3e15ebd3..d566521b 100644 --- a/server/src/modules/survey/__test/surveyMeta.service.spec.ts +++ b/server/src/modules/survey/__test/surveyMeta.service.spec.ts @@ -2,12 +2,10 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SurveyMetaService } from '../services/surveyMeta.service'; import { MongoRepository } from 'typeorm'; import { SurveyMeta } from 'src/models/surveyMeta.entity'; -import { PluginManagerProvider } from 'src/securityPlugin/pluginManager.provider'; import { PluginManager } from 'src/securityPlugin/pluginManager'; -import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums'; import { getRepositoryToken } from '@nestjs/typeorm'; import { HttpException } from 'src/exceptions/httpException'; -import { SurveyUtilPlugin } from 'src/securityPlugin/surveyUtilPlugin'; +import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums'; import { ObjectId } from 'mongodb'; describe('SurveyMetaService', () => { @@ -26,10 +24,11 @@ describe('SurveyMetaService', () => { count: jest.fn(), create: jest.fn(), save: jest.fn(), + updateOne: jest.fn(), findAndCount: jest.fn(), }, }, - PluginManagerProvider, + PluginManager, ], }).compile(); @@ -38,18 +37,18 @@ describe('SurveyMetaService', () => { getRepositoryToken(SurveyMeta), ); pluginManager = module.get(PluginManager); - pluginManager.registerPlugin(new SurveyUtilPlugin()); }); describe('getNewSurveyPath', () => { it('should generate a new survey path', async () => { - jest.spyOn(surveyRepository, 'count').mockResolvedValueOnce(1); + jest.spyOn(pluginManager, 'triggerHook').mockResolvedValueOnce('path1'); jest.spyOn(surveyRepository, 'count').mockResolvedValueOnce(0); const surveyPath = await service.getNewSurveyPath(); - expect(typeof surveyPath).toBe('string'); - expect(surveyRepository.count).toHaveBeenCalledTimes(2); + expect(surveyPath).toBe('path1'); + expect(pluginManager.triggerHook).toHaveBeenCalledTimes(1); + expect(surveyRepository.count).toHaveBeenCalledTimes(1); }); }); @@ -63,14 +62,11 @@ describe('SurveyMetaService', () => { userId: new ObjectId().toString(), createMethod: '', createFrom: '', + workspaceId: 'workspace1', }; const newSurvey = new SurveyMeta(); - const mockedSurveyPath = 'mockedSurveyPath'; - jest - .spyOn(service, 'getNewSurveyPath') - .mockResolvedValue(mockedSurveyPath); - + jest.spyOn(service, 'getNewSurveyPath').mockResolvedValue('path1'); jest .spyOn(surveyRepository, 'create') .mockImplementation(() => newSurvey); @@ -82,97 +78,118 @@ describe('SurveyMetaService', () => { title: params.title, remark: params.remark, surveyType: params.surveyType, - surveyPath: mockedSurveyPath, + surveyPath: 'path1', creator: params.username, - ownerId: params.userId, + creatorId: params.userId, owner: params.username, + ownerId: params.userId, createMethod: params.createMethod, createFrom: params.createFrom, + workspaceId: params.workspaceId, }); expect(surveyRepository.save).toHaveBeenCalledWith(newSurvey); expect(result).toEqual(newSurvey); }); }); - describe('editSurveyMeta', () => { - it('should edit a survey meta and return it if in NEW or EDITING status', async () => { + describe('pausingSurveyMeta', () => { + it('should throw an exception if survey is in NEW status', async () => { + const survey = new SurveyMeta(); + survey.curStatus = { status: RECORD_STATUS.NEW, date: Date.now() }; + + await expect(service.pausingSurveyMeta(survey)).rejects.toThrow( + HttpException, + ); + }); + + it('should pause a survey and update subStatus', async () => { const survey = new SurveyMeta(); survey.curStatus = { status: RECORD_STATUS.PUBLISHED, date: Date.now() }; - survey.subStatus = { - status: RECORD_SUB_STATUS.DEFAULT, - date: Date.now(), - }; survey.statusList = []; + jest.spyOn(surveyRepository, 'save').mockResolvedValue(survey); - const result = await service.editSurveyMeta(survey); + const result = await service.pausingSurveyMeta(survey); - expect(survey.curStatus.status).toEqual(RECORD_STATUS.EDITING); + expect(survey.subStatus.status).toBe(RECORD_SUB_STATUS.PAUSING); expect(survey.statusList.length).toBe(1); - expect(survey.statusList[0].status).toEqual(RECORD_STATUS.EDITING); + expect(survey.statusList[0].status).toBe(RECORD_SUB_STATUS.PAUSING); + expect(surveyRepository.save).toHaveBeenCalledWith(survey); + expect(result).toEqual(survey); + }); + }); + + describe('editSurveyMeta', () => { + it('should edit a survey meta and return it', async () => { + const survey = new SurveyMeta(); + survey.curStatus = { status: RECORD_STATUS.PUBLISHED, date: Date.now() }; + survey.statusList = []; + + const operator = 'editor'; + const operatorId = 'editorId'; + + jest.spyOn(surveyRepository, 'save').mockResolvedValue(survey); + + const result = await service.editSurveyMeta({ + survey, + operator, + operatorId, + }); + + expect(survey.curStatus.status).toBe(RECORD_STATUS.EDITING); + expect(survey.statusList.length).toBe(1); + expect(survey.statusList[0].status).toBe(RECORD_STATUS.EDITING); + expect(survey.operator).toBe(operator); + expect(survey.operatorId).toBe(operatorId); expect(surveyRepository.save).toHaveBeenCalledWith(survey); expect(result).toEqual(survey); }); }); describe('deleteSurveyMeta', () => { - it('should delete survey meta and update status', async () => { - // 准备假的SurveyMeta对象 - const survey = new SurveyMeta(); - survey.curStatus = { status: RECORD_STATUS.NEW, date: Date.now() }; - survey.subStatus = { - status: RECORD_SUB_STATUS.DEFAULT, - date: Date.now(), - }; - survey.statusList = []; + it('should mark a survey as deleted', async () => { + const surveyId = new ObjectId().toString(); + const operator = 'deleter'; + const operatorId = 'deleterId'; - // 模拟save方法 - jest.spyOn(surveyRepository, 'save').mockResolvedValue(survey); + jest.spyOn(surveyRepository, 'updateOne').mockResolvedValue({ + matchedCount: 1, + modifiedCount: 1, + acknowledged: true, + }); - // 调用要测试的方法 - const result = await service.deleteSurveyMeta(survey); + const result = await service.deleteSurveyMeta({ + surveyId, + operator, + operatorId, + }); - // 验证结果 - expect(result).toBe(survey); - expect(survey.subStatus.status).toBe(RECORD_STATUS.REMOVED); - expect(survey.statusList.length).toBe(1); - expect(survey.statusList[0].status).toBe(RECORD_STATUS.REMOVED); - expect(surveyRepository.save).toHaveBeenCalledTimes(1); - expect(surveyRepository.save).toHaveBeenCalledWith(survey); - }); - - it('should throw exception when survey is already removed', async () => { - // 准备假的SurveyMeta对象,其状态已设置为REMOVED - const survey = new SurveyMeta(); - survey.curStatus = { - status: RECORD_STATUS.REMOVED, - date: Date.now(), - }; - - // 调用要测试的方法并期待异常 - await expect(service.deleteSurveyMeta(survey)).rejects.toThrow( - HttpException, + expect(surveyRepository.updateOne).toHaveBeenCalledWith( + { _id: new ObjectId(surveyId) }, + { + $set: { + isDeleted: true, + operator, + operatorId, + deletedAt: expect.any(Date), + }, + }, ); - - // 验证save方法没有被调用 - expect(surveyRepository.save).not.toHaveBeenCalled(); + expect(result.matchedCount).toBe(1); }); }); describe('getSurveyMetaList', () => { it('should return a list of survey metadata', async () => { - // 准备模拟数据 const mockData = [ { _id: 1, title: 'Survey 1' }, - { _id: 2, title: 'Survey 2' }, ] as unknown as Array; - const mockCount = 2; + const mockCount = 1; jest .spyOn(surveyRepository, 'findAndCount') .mockResolvedValue([mockData, mockCount]); - // 调用方法并检查返回值 const condition = { pageNum: 1, pageSize: 10, @@ -181,44 +198,47 @@ describe('SurveyMetaService', () => { filter: {}, order: {}, }; + const result = await service.getSurveyMetaList(condition); - // 验证返回值 expect(result).toEqual({ data: mockData, count: mockCount }); - // 验证repository方法被正确调用 expect(surveyRepository.findAndCount).toHaveBeenCalledTimes(1); }); }); describe('publishSurveyMeta', () => { - it('should publish a survey meta and add status to statusList', async () => { - // 准备模拟数据 - const surveyMeta = { - id: 1, - title: 'Test Survey', - statusList: [], - } as unknown as SurveyMeta; - const savedSurveyMeta = { - ...surveyMeta, - curStatus: { - status: RECORD_STATUS.PUBLISHED, - date: expect.any(Number), - }, - subStatus: { - status: RECORD_SUB_STATUS.DEFAULT, - date: expect.any(Number), - }, - } as unknown as SurveyMeta; + it('should publish a survey and update curStatus', async () => { + const surveyMeta = new SurveyMeta(); + surveyMeta.statusList = []; - jest.spyOn(surveyRepository, 'save').mockResolvedValue(savedSurveyMeta); + jest.spyOn(surveyRepository, 'save').mockResolvedValue(surveyMeta); - // 调用方法并检查返回值 const result = await service.publishSurveyMeta({ surveyMeta }); - // 验证返回值 - expect(result).toEqual(savedSurveyMeta); - // 验证repository方法被正确调用 - expect(surveyRepository.save).toHaveBeenCalledWith(savedSurveyMeta); + expect(surveyMeta.curStatus.status).toBe(RECORD_STATUS.PUBLISHED); + expect(surveyMeta.statusList.length).toBe(1); + expect(surveyMeta.statusList[0].status).toBe(RECORD_STATUS.PUBLISHED); + expect(surveyRepository.save).toHaveBeenCalledWith(surveyMeta); + expect(result).toEqual(surveyMeta); + }); + }); + + describe('countSurveyMetaByWorkspaceId', () => { + it('should return the count of surveys in a workspace', async () => { + const workspaceId = 'workspace1'; + const mockCount = 5; + + jest.spyOn(surveyRepository, 'count').mockResolvedValue(mockCount); + + const result = await service.countSurveyMetaByWorkspaceId({ + workspaceId, + }); + + expect(result).toBe(mockCount); + expect(surveyRepository.count).toHaveBeenCalledWith({ + workspaceId, + isDeleted: { $ne: true }, + }); }); }); }); diff --git a/server/src/modules/survey/services/downloadTask.service.ts b/server/src/modules/survey/services/downloadTask.service.ts index 3808730d..e98eb201 100644 --- a/server/src/modules/survey/services/downloadTask.service.ts +++ b/server/src/modules/survey/services/downloadTask.service.ts @@ -17,8 +17,8 @@ import { DOWNLOAD_TASK_STATUS } from 'src/enums/downloadTaskStatus'; @Injectable() export class DownloadTaskService { - private static taskList: Array = []; - private static isExecuting: boolean = false; + static taskList: Array = []; + static isExecuting: boolean = false; constructor( @InjectRepository(DownloadTask) @@ -153,7 +153,7 @@ export class DownloadTaskService { } } - private async handleDownloadTask({ taskInfo }) { + async handleDownloadTask({ taskInfo }) { try { // 更新任务状态为计算中 const updateRes = await this.downloadTaskRepository.updateOne( diff --git a/server/src/modules/surveyResponse/__test/clientEncrypt.service.spec.ts b/server/src/modules/surveyResponse/__test/clientEncrypt.service.spec.ts index 5dfc0919..fd368bbe 100644 --- a/server/src/modules/surveyResponse/__test/clientEncrypt.service.spec.ts +++ b/server/src/modules/surveyResponse/__test/clientEncrypt.service.spec.ts @@ -21,6 +21,7 @@ describe('ClientEncryptService', () => { save: jest.fn(), findOne: jest.fn(), updateOne: jest.fn(), + deleteOne: jest.fn(), }, }, ], @@ -105,11 +106,13 @@ describe('ClientEncryptService', () => { describe('deleteEncryptInfo', () => { it('should delete encrypt info by id', async () => { const id = new ObjectId().toHexString(); - const updateResult = { matchedCount: 1, modifiedCount: 1 }; - jest.spyOn(repository, 'updateOne').mockResolvedValue(updateResult); + const deleteResult = { matchedCount: 1, modifiedCount: 1 }; + jest + .spyOn(repository, 'deleteOne') + .mockResolvedValue(deleteResult as any); const result = await service.deleteEncryptInfo(id); - expect(result).toEqual(updateResult); + expect(result).toEqual(deleteResult); }); }); }); diff --git a/server/src/modules/surveyResponse/__test/counter.service.spec.ts b/server/src/modules/surveyResponse/__test/counter.service.spec.ts index 41cde4eb..264a4c7e 100644 --- a/server/src/modules/surveyResponse/__test/counter.service.spec.ts +++ b/server/src/modules/surveyResponse/__test/counter.service.spec.ts @@ -50,6 +50,7 @@ describe('CounterService', () => { surveyPath: 'testPath', type: 'testType', data, + updatedAt: expect.any(Date), }, }, { upsert: true }, diff --git a/server/src/modules/surveyResponse/__test/mockResponseSchema.ts b/server/src/modules/surveyResponse/__test/mockResponseSchema.ts index c3b335e5..34b2dfab 100644 --- a/server/src/modules/surveyResponse/__test/mockResponseSchema.ts +++ b/server/src/modules/surveyResponse/__test/mockResponseSchema.ts @@ -18,8 +18,8 @@ export const mockResponseSchema: ResponseSchema = { date: 1710399368439, }, ], - createDate: 1710399368440, - updateDate: 1710399368440, + createdAt: 1710399368440, + updatedAt: 1710399368440, title: '加密全流程', surveyPath: 'EBzdmnSp', code: { diff --git a/server/src/modules/surveyResponse/__test/responseSchema.controller.spec.ts b/server/src/modules/surveyResponse/__test/responseSchema.controller.spec.ts index 2119e66b..3ba8b3c5 100644 --- a/server/src/modules/surveyResponse/__test/responseSchema.controller.spec.ts +++ b/server/src/modules/surveyResponse/__test/responseSchema.controller.spec.ts @@ -3,27 +3,28 @@ import { ResponseSchemaController } from '../controllers/responseSchema.controll import { ResponseSchemaService } from '../services/responseScheme.service'; import { HttpException } from 'src/exceptions/httpException'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; -import { RECORD_STATUS, RECORD_SUB_STATUS } from 'src/enums'; - +import { RECORD_SUB_STATUS } from 'src/enums'; import { ResponseSchema } from 'src/models/responseSchema.entity'; import { Logger } from 'src/logger'; import { UserService } from 'src/modules/auth/services/user.service'; import { WorkspaceMemberService } from 'src/modules/workspace/services/workspaceMember.service'; -import { AuthService } from 'src/modules/auth/services/auth.service'; import { SurveyNotFoundException } from 'src/exceptions/surveyNotFoundException'; jest.mock('../services/responseScheme.service'); +jest.mock('src/modules/auth/services/user.service'); +jest.mock('src/modules/workspace/services/workspaceMember.service'); describe('ResponseSchemaController', () => { let controller: ResponseSchemaController; let responseSchemaService: ResponseSchemaService; + let userService: UserService; + let workspaceMemberService: WorkspaceMemberService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [ResponseSchemaController], providers: [ ResponseSchemaService, - AuthService, { provide: Logger, useValue: { @@ -42,12 +43,6 @@ describe('ResponseSchemaController', () => { findAllByUserId: jest.fn(), }, }, - { - provide: AuthService, - useValue: { - create: jest.fn(), - }, - }, { provide: Logger, useValue: { @@ -61,6 +56,10 @@ describe('ResponseSchemaController', () => { responseSchemaService = module.get( ResponseSchemaService, ); + userService = module.get(UserService); + workspaceMemberService = module.get( + WorkspaceMemberService, + ); }); describe('getSchema', () => { @@ -68,13 +67,20 @@ describe('ResponseSchemaController', () => { const mockQueryInfo = { surveyPath: 'validSurveyPath' }; const mockResponseSchema = { surveyPath: 'testSurveyPath', - curStatus: { status: RECORD_STATUS.PUBLISHED, date: Date.now() }, + curStatus: { status: 'published', date: Date.now() }, subStatus: { status: RECORD_SUB_STATUS.DEFAULT, date: Date.now() }, + code: { + baseConf: { + passwordSwitch: false, + password: null, + whitelist: [], + }, + }, } as ResponseSchema; jest .spyOn(responseSchemaService, 'getResponseSchemaByPath') - .mockResolvedValue(Promise.resolve(mockResponseSchema)); + .mockResolvedValue(mockResponseSchema); const result = await controller.getSchema(mockQueryInfo); @@ -98,168 +104,180 @@ describe('ResponseSchemaController', () => { jest .spyOn(responseSchemaService, 'getResponseSchemaByPath') .mockResolvedValue({ - subStatus: { status: RECORD_SUB_STATUS.REMOVED }, + isDeleted: true, } as ResponseSchema); await expect(controller.getSchema(mockQueryInfo)).rejects.toThrow( - new HttpException('问卷已删除', EXCEPTION_CODE.RESPONSE_SCHEMA_REMOVED), + new HttpException( + '问卷不存在或已删除', + EXCEPTION_CODE.RESPONSE_SCHEMA_REMOVED, + ), ); }); - it('whitelistValidate should throw SurveyNotFoundException when survey is removed', async () => { - const surveyPath = ''; - jest - .spyOn(responseSchemaService, 'getResponseSchemaByPath') - .mockResolvedValue(null); - await expect( - controller.whitelistValidate(surveyPath, { - password: '123456', - }), - ).rejects.toThrow(new SurveyNotFoundException('该问卷不存在,无法提交')); - }); + it('should throw HttpException with RESPONSE_PAUSING code when survey is paused', async () => { + const mockQueryInfo = { surveyPath: 'pausedSurveyPath' }; - it('whitelistValidate should throw WHITELIST_ERROR code when password is incorrect', async () => { - const surveyPath = ''; jest .spyOn(responseSchemaService, 'getResponseSchemaByPath') .mockResolvedValue({ - curStatus: { - status: 'published', - }, - subStatus: { - status: '', - }, - code: { - baseConf: { - passwordSwitch: true, - password: '123456', - }, - }, - } as ResponseSchema); - await expect( - controller.whitelistValidate(surveyPath, { - password: '123457', - }), - ).rejects.toThrow( - new HttpException('验证失败', EXCEPTION_CODE.WHITELIST_ERROR), - ); - }); - - it('whitelistValidate should be successfully', async () => { - const surveyPath = 'test'; - jest - .spyOn(responseSchemaService, 'getResponseSchemaByPath') - .mockResolvedValue({ - curStatus: { - status: 'published', - }, - subStatus: { - status: '', - }, - code: { - baseConf: { - passwordSwitch: true, - password: '123456', - }, - }, + curStatus: { status: 'published' }, + subStatus: { status: RECORD_SUB_STATUS.PAUSING }, } as ResponseSchema); - await expect( - controller.whitelistValidate(surveyPath, { - password: '123456', - }), - ).resolves.toEqual({ code: 200, data: null }); - }); - - it('whitelistValidate should throw WHITELIST_ERROR code when mobile or email is incorrect', async () => { - const surveyPath = ''; - jest - .spyOn(responseSchemaService, 'getResponseSchemaByPath') - .mockResolvedValue({ - curStatus: { - status: 'published', - }, - subStatus: { - status: '', - }, - code: { - baseConf: { - passwordSwitch: true, - password: '123456', - whitelistType: 'CUSTOM', - memberType: 'MOBILE', - whitelist: ['13500000000'], - }, - }, - } as ResponseSchema); - await expect( - controller.whitelistValidate(surveyPath, { - password: '123456', - whitelist: '13500000001', - }), - ).rejects.toThrow( - new HttpException('验证失败', EXCEPTION_CODE.WHITELIST_ERROR), - ); - }); - - it('whitelistValidate should throw WHITELIST_ERROR code when member is incorrect', async () => { - const surveyPath = ''; - jest - .spyOn(responseSchemaService, 'getResponseSchemaByPath') - .mockResolvedValue({ - curStatus: { - status: 'published', - }, - subStatus: { - status: '', - }, - code: { - baseConf: { - passwordSwitch: true, - password: '123456', - whitelistType: 'MEMBER', - whitelist: ['Jack'], - }, - }, - } as ResponseSchema); - await expect( - controller.whitelistValidate(surveyPath, { - password: '123456', - whitelist: 'James', - }), - ).rejects.toThrow( - new HttpException('验证失败', EXCEPTION_CODE.WHITELIST_ERROR), + await expect(controller.getSchema(mockQueryInfo)).rejects.toThrow( + new HttpException('该问卷已暂停回收', EXCEPTION_CODE.RESPONSE_PAUSING), ); }); }); - it('whitelistValidate should return verifyId successfully', async () => { - const surveyPath = ''; - jest - .spyOn(responseSchemaService, 'getResponseSchemaByPath') - .mockResolvedValue({ - curStatus: { - status: 'published', - }, - subStatus: { - status: '', + describe('whitelistValidate', () => { + it('should throw HttpException when parameters are invalid', async () => { + const surveyPath = 'testSurveyPath'; + const body = { password: 1 }; + + await expect( + controller.whitelistValidate(surveyPath, body), + ).rejects.toThrow(HttpException); + }); + + it('should throw SurveyNotFoundException when survey is removed', async () => { + const surveyPath = 'removedSurveyPath'; + jest + .spyOn(responseSchemaService, 'getResponseSchemaByPath') + .mockResolvedValue(null); + + await expect( + controller.whitelistValidate(surveyPath, { password: '123456' }), + ).rejects.toThrow(new SurveyNotFoundException('该问卷不存在,无法提交')); + }); + + it('should throw HttpException when password is incorrect', async () => { + const surveyPath = 'testSurveyPath'; + const mockSchema = { + code: { + baseConf: { + passwordSwitch: true, + password: '123456', + }, }, + }; + + jest + .spyOn(responseSchemaService, 'getResponseSchemaByPath') + .mockResolvedValue(mockSchema as any); + + await expect( + controller.whitelistValidate(surveyPath, { password: 'wrongPassword' }), + ).rejects.toThrow( + new HttpException('密码验证失败', EXCEPTION_CODE.WHITELIST_ERROR), + ); + }); + + it('should validate successfully when password is correct', async () => { + const surveyPath = 'testSurveyPath'; + const mockSchema = { code: { baseConf: { passwordSwitch: true, password: '123456', whitelistType: 'CUSTOM', - memberType: 'MOBILE', - whitelist: ['13500000000'], + whitelist: ['allowed@example.com'], }, }, - } as ResponseSchema); + }; - await expect( - controller.whitelistValidate(surveyPath, { + jest + .spyOn(responseSchemaService, 'getResponseSchemaByPath') + .mockResolvedValue(mockSchema as any); + + const result = await controller.whitelistValidate(surveyPath, { password: '123456', - whitelist: '13500000000', - }), - ).resolves.toEqual({ code: 200, data: null }); + whitelist: 'allowed@example.com', + }); + expect(result).toEqual({ code: 200, data: null }); + }); + + it('should throw HttpException when whitelist value is not in CUSTOM whitelist', async () => { + const surveyPath = 'testSurveyPath'; + const mockSchema = { + code: { + baseConf: { + whitelistType: 'CUSTOM', + whitelist: ['allowed@example.com'], + }, + }, + }; + + jest + .spyOn(responseSchemaService, 'getResponseSchemaByPath') + .mockResolvedValue(mockSchema as any); + + await expect( + controller.whitelistValidate(surveyPath, { + password: '123456', + whitelist: 'notAllowed@example.com', + }), + ).rejects.toThrow( + new HttpException('白名单验证失败', EXCEPTION_CODE.WHITELIST_ERROR), + ); + }); + + it('should throw HttpException when user is not found in MEMBER whitelist', async () => { + const surveyPath = 'testSurveyPath'; + const mockSchema = { + code: { + baseConf: { + whitelistType: 'MEMBER', + whitelist: [], + }, + }, + }; + + jest + .spyOn(responseSchemaService, 'getResponseSchemaByPath') + .mockResolvedValue(mockSchema as any); + jest.spyOn(userService, 'getUserByUsername').mockResolvedValue(null); + + await expect( + controller.whitelistValidate(surveyPath, { + password: '123456', + whitelist: 'nonExistentUser', + }), + ).rejects.toThrow( + new HttpException('名单验证失败', EXCEPTION_CODE.WHITELIST_ERROR), + ); + }); + + it('should throw HttpException when user is not a workspace member', async () => { + const surveyPath = 'testSurveyPath'; + const mockSchema = { + code: { + baseConf: { + whitelistType: 'MEMBER', + whitelist: [], + }, + }, + }; + + jest + .spyOn(responseSchemaService, 'getResponseSchemaByPath') + .mockResolvedValue(mockSchema as any); + jest + .spyOn(userService, 'getUserByUsername') + .mockResolvedValue({ _id: new Object(), username: '' } as any); + jest + .spyOn(workspaceMemberService, 'findAllByUserId') + .mockResolvedValue([]); + + await expect( + controller.whitelistValidate(surveyPath, { + password: '123456', + whitelist: 'testUser', + }), + ).rejects.toThrow( + new HttpException('验证失败', EXCEPTION_CODE.WHITELIST_ERROR), + ); + }); }); }); diff --git a/server/src/modules/surveyResponse/__test/responseScheme.service.spec.ts b/server/src/modules/surveyResponse/__test/responseScheme.service.spec.ts index 06503e4b..885dcbae 100644 --- a/server/src/modules/surveyResponse/__test/responseScheme.service.spec.ts +++ b/server/src/modules/surveyResponse/__test/responseScheme.service.spec.ts @@ -20,6 +20,7 @@ describe('ResponseSchemaService', () => { findOne: jest.fn().mockResolvedValue(mockResponseSchema), create: jest.fn(), save: jest.fn(), + updateOne: jest.fn(), }, }, ], @@ -120,22 +121,24 @@ describe('ResponseSchemaService', () => { describe('deleteResponseSchema', () => { it('should delete response schema by survey path', async () => { jest - .spyOn(responseSchemaRepository, 'findOne') + .spyOn(responseSchemaRepository, 'updateOne') .mockResolvedValueOnce(cloneDeep(mockResponseSchema)); - jest - .spyOn(responseSchemaRepository, 'save') - .mockResolvedValueOnce(undefined); await service.deleteResponseSchema({ surveyPath: mockResponseSchema.surveyPath, }); - expect(responseSchemaRepository.findOne).toHaveBeenCalledWith({ - where: { + expect(responseSchemaRepository.updateOne).toHaveBeenCalledWith( + { surveyPath: mockResponseSchema.surveyPath, }, - }); - expect(responseSchemaRepository.save).toHaveBeenCalledTimes(1); + { + $set: { + isDeleted: true, + updatedAt: new Date(), + }, + }, + ); }); }); }); diff --git a/server/src/modules/surveyResponse/__test/surveyResponse.controller.spec.ts b/server/src/modules/surveyResponse/__test/surveyResponse.controller.spec.ts index e62a1253..2754f31f 100644 --- a/server/src/modules/surveyResponse/__test/surveyResponse.controller.spec.ts +++ b/server/src/modules/surveyResponse/__test/surveyResponse.controller.spec.ts @@ -71,8 +71,8 @@ const mockClientEncryptInfo = { date: 1710399425273.0, }, ], - createDate: 1710399425273.0, - updateDate: 1710399425273.0, + createdAt: 1710399425273.0, + updatedAt: 1710399425273.0, }; describe('SurveyResponseController', () => { @@ -178,7 +178,7 @@ describe('SurveyResponseController', () => { .mockResolvedValueOnce({ _id: new ObjectId('65fc2dd77f4520858046e129'), clientTime: 1711025112552, - createDate: 1711025113146, + createdAt: 1711025113146, curStatus: { status: RECORD_STATUS.NEW, date: 1711025113146, @@ -212,7 +212,7 @@ describe('SurveyResponseController', () => { ], surveyPath: 'EBzdmnSp', - updateDate: 1711025113146, + updatedAt: 1711025113146, secretKeys: [], } as unknown as SurveyResponse); jest diff --git a/server/src/modules/surveyResponse/__test/surveyResponse.service.spec.ts b/server/src/modules/surveyResponse/__test/surveyResponse.service.spec.ts index bf6837c5..855f8809 100644 --- a/server/src/modules/surveyResponse/__test/surveyResponse.service.spec.ts +++ b/server/src/modules/surveyResponse/__test/surveyResponse.service.spec.ts @@ -18,6 +18,7 @@ describe('SurveyResponseService', () => { create: jest.fn(), save: jest.fn(), count: jest.fn(), + find: jest.fn(), }, }, ], @@ -69,16 +70,12 @@ describe('SurveyResponseService', () => { it('should get the total survey response count by path', async () => { const surveyPath = 'testPath'; const count = 10; - jest.spyOn(surveyResponseRepository, 'count').mockResolvedValue(count); + jest + .spyOn(surveyResponseRepository, 'find') + .mockResolvedValue(new Array(10)); const result = await service.getSurveyResponseTotalByPath(surveyPath); expect(result).toEqual(count); - expect(surveyResponseRepository.count).toHaveBeenCalledWith({ - where: { - surveyPath, - 'subStatus.status': { $ne: 'removed' }, - }, - }); }); }); diff --git a/server/src/modules/workspace/_test/workspace.controller.spec.ts b/server/src/modules/workspace/_test/workspace.controller.spec.ts index ac5a893d..ffa9aae9 100644 --- a/server/src/modules/workspace/_test/workspace.controller.spec.ts +++ b/server/src/modules/workspace/_test/workspace.controller.spec.ts @@ -12,6 +12,7 @@ import { UserService } from 'src/modules/auth/services/user.service'; import { SurveyMetaService } from 'src/modules/survey/services/surveyMeta.service'; import { Logger } from 'src/logger'; import { User } from 'src/models/user.entity'; +import { GetWorkspaceListDto } from '../dto/getWorkspaceList.dto'; jest.mock('src/guards/authentication.guard'); jest.mock('src/guards/survey.guard'); @@ -22,6 +23,7 @@ describe('WorkspaceController', () => { let workspaceService: WorkspaceService; let workspaceMemberService: WorkspaceMemberService; let userService: UserService; + let logger: Logger; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -31,7 +33,6 @@ describe('WorkspaceController', () => { provide: WorkspaceService, useValue: { create: jest.fn(), - findAllById: jest.fn(), findAllByIdWithPagination: jest.fn(), update: jest.fn(), delete: jest.fn(), @@ -47,7 +48,6 @@ describe('WorkspaceController', () => { batchUpdate: jest.fn(), batchDelete: jest.fn(), countByWorkspaceId: jest.fn(), - batchSearchByWorkspace: jest.fn(), }, }, { @@ -80,24 +80,26 @@ describe('WorkspaceController', () => { WorkspaceMemberService, ); userService = module.get(UserService); + logger = module.get(Logger); }); describe('create', () => { it('should create a workspace and return workspaceId', async () => { + const mockUserId = new ObjectId(), + mockUsername = 'username'; const createWorkspaceDto: CreateWorkspaceDto = { name: 'Test Workspace', description: 'Test Description', - members: [{ userId: 'userId1', role: WORKSPACE_ROLE.USER }], + members: [{ userId: mockUserId.toString(), role: WORKSPACE_ROLE.USER }], }; - const req = { user: { _id: new ObjectId() } }; + const req = { user: { _id: new ObjectId(), username: 'testuser' } }; const createdWorkspace = { _id: new ObjectId() }; - jest.spyOn(userService, 'getUserListByIds').mockResolvedValue([ - { - _id: 'userId1', - }, - ] as unknown as Array); - + jest + .spyOn(userService, 'getUserListByIds') + .mockResolvedValue([ + { _id: mockUserId, username: mockUsername }, + ] as unknown as Array); jest .spyOn(workspaceService, 'create') .mockResolvedValue(createdWorkspace as Workspace); @@ -113,6 +115,7 @@ describe('WorkspaceController', () => { expect(workspaceService.create).toHaveBeenCalledWith({ name: createWorkspaceDto.name, description: createWorkspaceDto.description, + owner: req.user.username, ownerId: req.user._id.toString(), }); expect(workspaceMemberService.create).toHaveBeenCalledWith({ @@ -123,31 +126,31 @@ describe('WorkspaceController', () => { expect(workspaceMemberService.batchCreate).toHaveBeenCalledWith({ workspaceId: createdWorkspace._id.toString(), members: createWorkspaceDto.members, + creator: req.user.username, + creatorId: req.user._id.toString(), }); }); it('should throw an exception if validation fails', async () => { - const createWorkspaceDto: CreateWorkspaceDto = { - name: '', - members: [], - }; + const createWorkspaceDto = { name: '', members: [] }; const req = { user: { _id: new ObjectId() } }; await expect(controller.create(createWorkspaceDto, req)).rejects.toThrow( HttpException, ); + expect(logger.error).toHaveBeenCalledTimes(1); }); }); describe('findAll', () => { it('should return a list of workspaces for the user', async () => { const req = { user: { _id: new ObjectId() } }; + const queryInfo: GetWorkspaceListDto = { curPage: 1, pageSize: 10 }; const memberList = [{ workspaceId: new ObjectId().toString() }]; const workspaces = [{ _id: new ObjectId(), name: 'Test Workspace' }]; - jest .spyOn(workspaceMemberService, 'findAllByUserId') - .mockResolvedValue(memberList as unknown as Array); + .mockResolvedValue(memberList as Array); jest .spyOn(workspaceService, 'findAllByIdWithPagination') @@ -156,12 +159,11 @@ describe('WorkspaceController', () => { count: workspaces.length, }); - jest.spyOn(userService, 'getUserListByIds').mockResolvedValue([]); + jest + .spyOn(userService, 'getUserListByIds') + .mockResolvedValue([{ _id: new ObjectId() }] as unknown as Array); - const result = await controller.findAll(req, { - curPage: 1, - pageSize: 10, - }); + const result = await controller.findAll(req, queryInfo); expect(result.code).toEqual(200); expect(workspaceMemberService.findAllByUserId).toHaveBeenCalledWith({ @@ -174,6 +176,18 @@ describe('WorkspaceController', () => { name: undefined, }); }); + + it('should throw an exception if validation fails', async () => { + const req = { user: { _id: new ObjectId() } }; + const queryInfo: GetWorkspaceListDto = { + curPage: 'not_a_number', + pageSize: 10, + } as any; + + await expect(controller.findAll(req, queryInfo)).rejects.toThrow( + HttpException, + ); + }); }); describe('update', () => { @@ -185,11 +199,9 @@ describe('WorkspaceController', () => { adminMembers: [], userMembers: [], }; - jest.spyOn(userService, 'getUserListByIds').mockResolvedValue([ - { - _id: userId, - }, - ] as Array); + jest + .spyOn(userService, 'getUserListByIds') + .mockResolvedValue([{ _id: userId }] as Array); const updateDto = { name: 'Updated Workspace', members: [ @@ -203,76 +215,41 @@ describe('WorkspaceController', () => { jest.spyOn(workspaceService, 'update').mockResolvedValue(updateResult); jest.spyOn(workspaceMemberService, 'batchCreate').mockResolvedValue(null); jest.spyOn(workspaceMemberService, 'batchUpdate').mockResolvedValue(null); + jest.spyOn(workspaceMemberService, 'batchDelete').mockResolvedValue(null); - const result = await controller.update(id, updateDto); - - expect(result).toEqual({ - code: 200, + const result = await controller.update(id, updateDto, { + user: { username: 'testuser', _id: new ObjectId() }, }); - expect(workspaceService.update).toHaveBeenCalledWith(id, { - name: updateDto.name, + + expect(result).toEqual({ code: 200 }); + expect(workspaceService.update).toHaveBeenCalledWith({ + id, + workspace: { name: updateDto.name }, + operator: 'testuser', + operatorId: expect.any(String), }); expect(workspaceMemberService.batchCreate).toHaveBeenCalledWith({ workspaceId: id, members: members.newMembers, + creator: 'testuser', + creatorId: expect.any(String), }); expect(workspaceMemberService.batchUpdate).toHaveBeenCalledWith({ idList: members.adminMembers, role: WORKSPACE_ROLE.ADMIN, + operator: 'testuser', + operatorId: expect.any(String), }); expect(workspaceMemberService.batchUpdate).toHaveBeenCalledWith({ idList: members.userMembers, role: WORKSPACE_ROLE.USER, + operator: 'testuser', + operatorId: expect.any(String), + }); + expect(workspaceMemberService.batchDelete).toHaveBeenCalledWith({ + idList: [], + neIdList: [], }); }); }); - - describe('delete', () => { - it('should delete a workspace', async () => { - const id = 'workspaceId'; - - jest.spyOn(workspaceService, 'delete').mockResolvedValue(null); - - const result = await controller.delete(id); - - expect(result).toEqual({ code: 200 }); - expect(workspaceService.delete).toHaveBeenCalledWith(id); - }); - }); - - describe('getWorkspaceAndMember', () => { - it('should return a list of workspaces and members for the user', async () => { - const userId = new ObjectId(); - jest.spyOn(userService, 'getUserListByIds').mockResolvedValue([ - { - _id: userId, - }, - ] as Array); - const req = { user: { _id: userId } }; - const workspaceId = new ObjectId(); - const memberList = [{ workspaceId, userId }]; - const workspaces = [{ _id: workspaceId, name: 'Test Workspace' }]; - const userList = [{ _id: userId, username: 'Test User' }]; - - jest - .spyOn(workspaceService, 'findAllByUserId') - .mockResolvedValue(workspaces as Array); - jest - .spyOn(workspaceMemberService, 'batchSearchByWorkspace') - .mockResolvedValue(memberList as unknown as Array); - jest - .spyOn(userService, 'getUserListByIds') - .mockResolvedValue(userList as User[]); - - const result = await controller.getWorkspaceAndMember(req); - - expect(result.code).toEqual(200); - expect(workspaceService.findAllByUserId).toHaveBeenCalledWith( - req.user._id.toString(), - ); - expect( - workspaceMemberService.batchSearchByWorkspace, - ).toHaveBeenCalledWith(workspaces.map((item) => item._id.toString())); - }); - }); }); diff --git a/server/src/modules/workspace/_test/workspace.service.spec.ts b/server/src/modules/workspace/_test/workspace.service.spec.ts index a243dec6..93bd83c3 100644 --- a/server/src/modules/workspace/_test/workspace.service.spec.ts +++ b/server/src/modules/workspace/_test/workspace.service.spec.ts @@ -45,6 +45,7 @@ describe('WorkspaceService', () => { const workspace = { name: 'Test Workspace', description: 'Description', + owner: 'Test Owner', // 添加 owner 属性 ownerId: 'ownerId', }; const createdWorkspace = { ...workspace, _id: new ObjectId() }; @@ -59,7 +60,11 @@ describe('WorkspaceService', () => { const result = await service.create(workspace); expect(result).toEqual(createdWorkspace); - expect(workspaceRepository.create).toHaveBeenCalledWith(workspace); + expect(workspaceRepository.create).toHaveBeenCalledWith({ + ...workspace, + creator: workspace.owner, + creatorId: workspace.ownerId, + }); expect(workspaceRepository.save).toHaveBeenCalledWith(createdWorkspace); }); }); @@ -90,24 +95,35 @@ describe('WorkspaceService', () => { it('should update a workspace', async () => { const workspaceId = 'workspaceId'; const updateData = { name: 'Updated Workspace' }; + const operator = 'Test Operator'; + const operatorId = 'operatorId'; jest .spyOn(workspaceRepository, 'update') .mockResolvedValue({ affected: 1 } as any); - const result = await service.update(workspaceId, updateData); + const result = await service.update({ + id: workspaceId, + workspace: updateData, + operator, + operatorId, + }); expect(result).toEqual({ affected: 1 }); - expect(workspaceRepository.update).toHaveBeenCalledWith( - workspaceId, - updateData, - ); + expect(workspaceRepository.update).toHaveBeenCalledWith(workspaceId, { + ...updateData, + updatedAt: expect.any(Date), + operator, + operatorId, + }); }); }); describe('delete', () => { it('should delete a workspace and update related surveyMeta', async () => { const workspaceId = new ObjectId().toString(); + const operator = 'Test Operator'; + const operatorId = 'operatorId'; jest .spyOn(workspaceRepository, 'updateOne') @@ -116,11 +132,17 @@ describe('WorkspaceService', () => { .spyOn(surveyMetaRepository, 'updateMany') .mockResolvedValue({ modifiedCount: 1 } as any); - await service.delete(workspaceId); + const result = await service.delete(workspaceId, { + operator, + operatorId, + }); expect(workspaceRepository.updateOne).toHaveBeenCalledTimes(1); - expect(surveyMetaRepository.updateMany).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + workspaceRes: { modifiedCount: 1 }, + surveyRes: { modifiedCount: 1 }, + }); }); }); @@ -139,9 +161,43 @@ describe('WorkspaceService', () => { .spyOn(workspaceRepository, 'find') .mockResolvedValue(workspaces as any); - const result = await service.findAllByUserId(''); + const result = await service.findAllByUserId('userId'); + expect(result).toEqual(workspaces); expect(workspaceRepository.find).toHaveBeenCalledTimes(1); }); }); + + describe('findAllByIdWithPagination', () => { + it('should return paginated workspaces', async () => { + const workspaceIdList = [ + new ObjectId().toString(), + new ObjectId().toString(), + ]; + const page = 1; + const limit = 10; + const workspaces = [ + { _id: workspaceIdList[0], name: 'Workspace 1' }, + { _id: workspaceIdList[1], name: 'Workspace 2' }, + ]; + + jest + .spyOn(workspaceRepository, 'findAndCount') + .mockResolvedValue([workspaces, workspaces.length] as any); + + const result = await service.findAllByIdWithPagination({ + workspaceIdList, + page, + limit, + }); + + expect(result).toEqual({ list: workspaces, count: workspaces.length }); + expect(workspaceRepository.findAndCount).toHaveBeenCalledWith({ + where: expect.any(Object), + skip: 0, + take: limit, + order: { createdAt: -1 }, + }); + }); + }); }); diff --git a/server/src/modules/workspace/_test/workspaceMember.controller.spec.ts b/server/src/modules/workspace/_test/workspaceMember.controller.spec.ts index 32e765c7..978c1578 100644 --- a/server/src/modules/workspace/_test/workspaceMember.controller.spec.ts +++ b/server/src/modules/workspace/_test/workspaceMember.controller.spec.ts @@ -115,11 +115,16 @@ describe('WorkspaceMemberController', () => { }; const updateResult = { modifiedCount: 1 }; + // Mock request object + const req = { + user: { username: 'admin', _id: 'operatorId' }, + }; + jest .spyOn(workspaceMemberService, 'updateRole') .mockResolvedValue(updateResult); - const result = await controller.updateRole(updateDto); + const result = await controller.updateRole(updateDto, req); expect(result).toEqual({ code: 200, @@ -128,9 +133,11 @@ describe('WorkspaceMemberController', () => { }, }); expect(workspaceMemberService.updateRole).toHaveBeenCalledWith({ + role: updateDto.role, workspaceId: updateDto.workspaceId, userId: updateDto.userId, - role: updateDto.role, + operator: req.user.username, + operatorId: req.user._id.toString(), }); }); @@ -140,8 +147,11 @@ describe('WorkspaceMemberController', () => { userId: '', role: '', }; + const req = { + user: { username: 'admin', _id: 'operatorId' }, + }; - await expect(controller.updateRole(updateDto)).rejects.toThrow( + await expect(controller.updateRole(updateDto, req)).rejects.toThrow( HttpException, ); }); diff --git a/server/src/modules/workspace/_test/workspaceMember.service.spec.ts b/server/src/modules/workspace/_test/workspaceMember.service.spec.ts index 8770bf20..aba883ed 100644 --- a/server/src/modules/workspace/_test/workspaceMember.service.spec.ts +++ b/server/src/modules/workspace/_test/workspaceMember.service.spec.ts @@ -58,23 +58,53 @@ describe('WorkspaceMemberService', () => { { userId: 'userId1', role: 'admin' }, { userId: 'userId2', role: 'user' }, ]; - const dataToInsert = members.map((item) => ({ ...item, workspaceId })); + const creator = 'creatorName'; + const creatorId = 'creatorId'; + const now = new Date(); + const dataToInsert = members.map((item) => ({ + ...item, + workspaceId, + createdAt: now, + updatedAt: now, + creator, + creatorId, + })); - jest - .spyOn(repository, 'insertMany') - .mockResolvedValueOnce({ insertedCount: members.length } as any); + jest.spyOn(repository, 'insertMany').mockResolvedValueOnce({ + insertedCount: members.length, + } as any); - const result = await service.batchCreate({ workspaceId, members }); + const result = await service.batchCreate({ + workspaceId, + members, + creator, + creatorId, + }); expect(result).toEqual({ insertedCount: members.length }); - expect(repository.insertMany).toHaveBeenCalledWith(dataToInsert); + expect(repository.insertMany).toHaveBeenCalledWith( + dataToInsert.map((item) => { + return { + ...item, + createdAt: expect.any(Date), + updatedAt: expect.any(Date), + }; + }), + ); }); it('should return insertedCount 0 if no members to insert', async () => { const workspaceId = new ObjectId().toString(); const members = []; + const creator = 'creatorName'; + const creatorId = 'creatorId'; - const result = await service.batchCreate({ workspaceId, members }); + const result = await service.batchCreate({ + workspaceId, + members, + creator, + creatorId, + }); expect(result).toEqual({ insertedCount: 0 }); }); @@ -84,12 +114,19 @@ describe('WorkspaceMemberService', () => { it('should batch update workspace members roles', async () => { const idList = [new ObjectId().toString(), new ObjectId().toString()]; const role = 'user'; + const operator = 'operatorName'; + const operatorId = 'operatorId'; jest .spyOn(repository, 'updateMany') .mockResolvedValue({ modifiedCount: idList.length } as any); - const result = await service.batchUpdate({ idList, role }); + const result = await service.batchUpdate({ + idList, + role, + operator, + operatorId, + }); expect(result).toEqual({ modifiedCount: idList.length }); }); @@ -97,8 +134,15 @@ describe('WorkspaceMemberService', () => { it('should return modifiedCount 0 if no ids to update', async () => { const idList = []; const role = 'user'; + const operator = 'operatorName'; + const operatorId = 'operatorId'; - const result = await service.batchUpdate({ idList, role }); + const result = await service.batchUpdate({ + idList, + role, + operator, + operatorId, + }); expect(result).toEqual({ modifiedCount: 0 }); }); @@ -160,17 +204,25 @@ describe('WorkspaceMemberService', () => { const workspaceId = 'workspaceId'; const userId = 'userId'; const role = 'admin'; + const operator = 'operatorName'; + const operatorId = 'operatorId'; jest .spyOn(repository, 'updateOne') .mockResolvedValue({ modifiedCount: 1 } as any); - const result = await service.updateRole({ workspaceId, userId, role }); + const result = await service.updateRole({ + workspaceId, + userId, + role, + operator, + operatorId, + }); expect(result).toEqual({ modifiedCount: 1 }); expect(repository.updateOne).toHaveBeenCalledWith( { workspaceId, userId }, - { $set: { role } }, + { $set: { role, operator, operatorId, updatedAt: expect.any(Date) } }, ); }); }); diff --git a/server/src/utils/messagePushing.spec.ts b/server/src/utils/__test/messagePushing.spec.ts similarity index 98% rename from server/src/utils/messagePushing.spec.ts rename to server/src/utils/__test/messagePushing.spec.ts index 9788c814..cd1ba44e 100644 --- a/server/src/utils/messagePushing.spec.ts +++ b/server/src/utils/__test/messagePushing.spec.ts @@ -1,5 +1,5 @@ import { ObjectId } from 'mongodb'; -import { getPushingData } from './messagePushing'; +import { getPushingData } from '../messagePushing'; import { RECORD_STATUS } from 'src/enums'; describe('getPushingData', () => { diff --git a/web/src/management/pages/download/components/TaskList.vue b/web/src/management/pages/download/components/TaskList.vue index 4e199061..32ee9e01 100644 --- a/web/src/management/pages/download/components/TaskList.vue +++ b/web/src/management/pages/download/components/TaskList.vue @@ -44,7 +44,7 @@ background layout="prev, pager, next" :total="total" - small + size="small" :page-size="pageSize" @current-change="handleCurrentChange" > diff --git a/web/src/management/stores/composables/usePageEdit.ts b/web/src/management/stores/composables/usePageEdit.ts index e43e2844..d1758021 100644 --- a/web/src/management/stores/composables/usePageEdit.ts +++ b/web/src/management/stores/composables/usePageEdit.ts @@ -17,7 +17,9 @@ export default function usePageEdit( updateTime: () => void ) { const pageConf = computed(() => schema.pageConf) - const pageEditOne = computed(() => schema.pageEditOne) + const pageEditOne = computed(() => { + return schema.pageEditOne + }) const isFinallyPage = computed(() => { return pageEditOne.value === pageConf.value.length }) diff --git a/web/src/materials/questions/widgets/BaseChoice/index.jsx b/web/src/materials/questions/widgets/BaseChoice/index.jsx index c754a279..50ae6078 100644 --- a/web/src/materials/questions/widgets/BaseChoice/index.jsx +++ b/web/src/materials/questions/widgets/BaseChoice/index.jsx @@ -64,7 +64,6 @@ export default defineComponent({ const onRadioClick = (item, $event) => { $event && $event.stopPropagation() $event && $event.preventDefault() - if (!isChecked(item)) { emit('change', item.hash) } diff --git a/web/src/render/components/QuestionWrapper.vue b/web/src/render/components/QuestionWrapper.vue index a5ad5ae7..d7669d18 100644 --- a/web/src/render/components/QuestionWrapper.vue +++ b/web/src/render/components/QuestionWrapper.vue @@ -91,10 +91,8 @@ const questionConfig = computed(() => { const updateFormData = (value) => { const key = props.moduleConfig.field const formData = cloneDeep(formValues.value) - if (key in formData) { - formData[key] = value - } - + formData[key] = value + console.log(formData) return formData } diff --git a/web/src/render/stores/survey.js b/web/src/render/stores/survey.js index 84cd15cb..3a11628e 100644 --- a/web/src/render/stores/survey.js +++ b/web/src/render/stores/survey.js @@ -161,9 +161,7 @@ export const useSurveyStore = defineStore('survey', () => { // 用户输入或者选择后,更新表单数据 const changeData = (data) => { let { key, value } = data - if (key in formValues.value) { - formValues.value[key] = value - } + formValues.value[key] = value questionStore.setChangeField(key) }