import { Types } from "mongoose"; import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum"; import { WorkflowStep } from "src/Types&Enums/blame-request-management/blameWorkflow-steps.enum"; import { RoleEnum } from "src/Types&Enums/role.enum"; import { CreationMethod } from "./entities/schema/request-management.schema"; import { PartyRole } from "./entities/schema/partyRole.enum"; import { RequestManagementService } from "./request-management.service"; describe("RequestManagementService V4 FileMaker workflow", () => { it("persists FIRST_INITIAL_FORM after the first party OTP is verified", async () => { const fileMakerId = new Types.ObjectId(); const userId = new Types.ObjectId(); const request = { _id: new Types.ObjectId(), publicId: "BL-V4-001", type: BlameRequestType.THIRD_PARTY, expertInitiated: true, isMadeByFileMaker: true, requiresFileMakerApproval: false, initiatedByFieldExpertId: fileMakerId, creationMethod: CreationMethod.IN_PERSON, parties: [{ role: PartyRole.FIRST, person: {} }], workflow: { currentStep: WorkflowStep.CREATED, nextStep: WorkflowStep.FIRST_BLAME_CONFESSION, completedSteps: [WorkflowStep.CREATED], }, history: [], save: jest.fn().mockResolvedValue(undefined), }; const service = new (RequestManagementService as any)() as RequestManagementService; (service as any).blameRequestDbService = { findById: jest.fn().mockResolvedValue(request), }; (service as any).userDbService = { findOne: jest.fn().mockResolvedValue({ _id: userId, otp: "hashed-otp", otpExpire: Date.now() + 60_000, }), }; (service as any).hashService = { compare: jest.fn().mockResolvedValue(true), }; (service as any).claimCaseDbService = { findOne: jest.fn().mockResolvedValue(null), }; (service as any).workflowStepDbService = { findByStepNumber: jest.fn().mockImplementation((stepNumber: number) => { if (stepNumber === 2) { return { stepNumber: 2, stepKey: WorkflowStep.FIRST_BLAME_CONFESSION, nextPossibleSteps: [WorkflowStep.FIRST_VIDEO], }; } if (stepNumber === 3) { return { stepNumber: 3, stepKey: WorkflowStep.FIRST_VIDEO, nextPossibleSteps: [WorkflowStep.FIRST_INITIAL_FORM], }; } return null; }), findByStepKey: jest.fn().mockResolvedValue(null), }; await service.verifyPartyOtpV2( { sub: String(fileMakerId), role: RoleEnum.FILE_MAKER, firstName: "File", lastName: "Maker", }, String(request._id), { phoneNumber: "09121234567", otp: "12345", partyRole: "FIRST", }, ); expect(request.workflow.currentStep).toBe(WorkflowStep.FIRST_INITIAL_FORM); expect(request.workflow.currentStep).not.toBe(WorkflowStep.FIRST_VIDEO); expect(request.workflow.nextStep).toBe(WorkflowStep.FIRST_VOICE); expect(request.workflow.completedSteps).not.toContain( WorkflowStep.FIRST_VIDEO, ); expect(request.save).toHaveBeenCalled(); const reopened = await service.getMyFileMakerFileDetail( { sub: String(fileMakerId), role: RoleEnum.FILE_MAKER }, String(request._id), ); expect(reopened.workflow.currentStep).toBe(WorkflowStep.FIRST_INITIAL_FORM); expect(reopened.workflow.currentStep).not.toBe(WorkflowStep.FIRST_VIDEO); }); it("keeps FIRST_VIDEO out of the FileMaker car-body workflow", async () => { const fileMakerId = new Types.ObjectId(); const request = { _id: new Types.ObjectId(), publicId: "BL-V4-002", type: BlameRequestType.CAR_BODY, expertInitiated: true, isMadeByFileMaker: true, requiresFileMakerApproval: false, initiatedByFieldExpertId: fileMakerId, creationMethod: CreationMethod.IN_PERSON, parties: [{ role: PartyRole.FIRST, person: {} }], workflow: { currentStep: WorkflowStep.CREATED, nextStep: WorkflowStep.CAR_BODY_ACCIDENT_TYPE, completedSteps: [WorkflowStep.CREATED], }, history: [], save: jest.fn().mockResolvedValue(undefined), }; const service = new (RequestManagementService as any)() as RequestManagementService; (service as any).blameRequestDbService = { findById: jest.fn().mockResolvedValue(request), }; (service as any).userDbService = { findOne: jest.fn().mockResolvedValue({ _id: new Types.ObjectId(), otp: "hashed-otp", otpExpire: Date.now() + 60_000, }), }; (service as any).hashService = { compare: jest.fn().mockResolvedValue(true), }; (service as any).workflowStepDbService = { findByStepNumber: jest.fn().mockResolvedValue(null), findByStepKey: jest.fn().mockResolvedValue({ stepNumber: 2, stepKey: WorkflowStep.CAR_BODY_ACCIDENT_TYPE, nextPossibleSteps: [WorkflowStep.FIRST_VIDEO], }), }; const actor = { sub: String(fileMakerId), role: RoleEnum.FILE_MAKER, firstName: "File", lastName: "Maker", }; await service.verifyPartyOtpV2(actor, String(request._id), { phoneNumber: "09121234567", otp: "12345", partyRole: "FIRST", }); expect(request.workflow.currentStep).toBe( WorkflowStep.CAR_BODY_ACCIDENT_TYPE, ); expect(request.workflow.nextStep).toBe(WorkflowStep.FIRST_INITIAL_FORM); await service.carBodyAccidentTypeFormV3( String(request._id), { car: true, object: false }, actor, ); expect(request.workflow.currentStep).toBe(WorkflowStep.FIRST_INITIAL_FORM); expect(request.workflow.nextStep).toBe(WorkflowStep.FIRST_VOICE); expect(request.workflow.completedSteps).not.toContain( WorkflowStep.FIRST_VIDEO, ); }); it("repairs legacy stuck workflow values when a V4 file is reopened", async () => { const fileMakerId = new Types.ObjectId(); const request = { _id: new Types.ObjectId(), publicId: "BL-V4-LEGACY", type: BlameRequestType.THIRD_PARTY, isMadeByFileMaker: true, initiatedByFieldExpertId: fileMakerId, workflow: { currentStep: WorkflowStep.FIRST_VIDEO, nextStep: WorkflowStep.FIRST_INITIAL_FORM, completedSteps: [ WorkflowStep.CREATED, WorkflowStep.FIRST_BLAME_CONFESSION, ], }, parties: [ { role: PartyRole.FIRST, person: { userId: new Types.ObjectId() } }, ], }; const service = new (RequestManagementService as any)() as RequestManagementService; (service as any).blameRequestDbService = { findById: jest.fn().mockResolvedValue(request), }; (service as any).claimCaseDbService = { findOne: jest.fn().mockResolvedValue(null), }; const reopened = await service.getMyFileMakerFileDetail( { sub: String(fileMakerId), role: RoleEnum.FILE_MAKER }, String(request._id), ); expect(reopened.workflow).toEqual( expect.objectContaining({ currentStep: WorkflowStep.FIRST_INITIAL_FORM, nextStep: WorkflowStep.FIRST_VOICE, }), ); expect(reopened.workflow.completedSteps).not.toContain( WorkflowStep.FIRST_VIDEO, ); }); });