import { BadRequestException } from "@nestjs/common"; import { ClaimCaseStatus } from "src/Types&Enums/claim-request-management/claim-case-status.enum"; import { DaghiOption } from "src/Types&Enums/claim-request-management/daghi-option.enum"; import { RoleEnum } from "src/Types&Enums/role.enum"; import { TypeOfDamage } from "src/Types&Enums/claim-request-management/type-of-damage.enum"; import { ExpertClaimService } from "./expert-claim.service"; const blankPricingReply = { description: "Damage assessment", parts: [ { partId: 201, typeOfDamage: TypeOfDamage.Repair, price: "", salary: "", totalPayment: "", factorNeeded: false, daghi: { option: DaghiOption.NO_VALUE }, }, ], }; function createService() { return new (ExpertClaimService as any)( ...new Array(21).fill(undefined), ) as ExpertClaimService; } describe("ExpertClaimService expert-reply pricing", () => { it("allows a repair line without daghi and removes a stray daghi payload", () => { const service = createService() as any; expect( service.validateAndNormalizeDaghiForExpertReplyV2([ { partId: 201, typeOfDamage: TypeOfDamage.Repair, price: "10000", salary: "0", totalPayment: "10000", daghi: { option: DaghiOption.NO_VALUE }, }, ]), ).toEqual([ { partId: 201, typeOfDamage: TypeOfDamage.Repair, price: "10000", salary: "0", totalPayment: "10000", }, ]); }); it("allows a deliver-damaged-part line without a branch", () => { const service = createService() as any; expect( service.validateAndNormalizeDaghiForExpertReplyV2([ { partId: 201, typeOfDamage: TypeOfDamage.Change, price: "100000", salary: "100000", totalPayment: "200000", daghi: { option: DaghiOption.DELIVER_DAMAGED_PART }, }, ]), ).toEqual([ { partId: 201, typeOfDamage: TypeOfDamage.Change, price: "100000", salary: "100000", totalPayment: "200000", daghi: { option: DaghiOption.DELIVER_DAMAGED_PART }, }, ]); }); it("rejects a blank pricing line on the legacy submit endpoint before changing status", async () => { const service = createService() as any; const findAndUpdate = jest.fn(); service.claimRequestManagementDbService = { findOne: jest.fn().mockResolvedValue({ _id: "legacy-claim", actorLocked: { actorId: "expert-1" }, unlockTime: new Date(Date.now() + 60_000), lockFile: true, }), findAndUpdate, }; await expect( service.submitReplyRequest("legacy-claim", blankPricingReply, { sub: "expert-1", }), ).rejects.toBeInstanceOf(BadRequestException); expect(findAndUpdate).not.toHaveBeenCalled(); }); it("rejects a blank pricing line on the V2 submit endpoint before completion", async () => { const service = createService() as any; const findByIdAndUpdate = jest.fn(); service.claimCaseDbService = { findById: jest.fn().mockResolvedValue({ status: ClaimCaseStatus.EXPERT_REVIEWING, workflow: { locked: true, lockedBy: { actorId: "expert-1" } }, }), findByIdAndUpdate, }; service.assertExpertActorOnClaim = jest.fn().mockResolvedValue(undefined); await expect( service.submitExpertReplyV2("v2-claim", blankPricingReply, { sub: "expert-1", role: RoleEnum.FIELD_EXPERT, }), ).rejects.toBeInstanceOf(BadRequestException); expect(findByIdAndUpdate).not.toHaveBeenCalled(); }); it("returns a Persian, field-specific error when a V2 price is below the minimum", async () => { const service = createService() as any; const findByIdAndUpdate = jest.fn(); service.claimCaseDbService = { findById: jest.fn().mockResolvedValue({ status: ClaimCaseStatus.EXPERT_REVIEWING, workflow: { locked: true, lockedBy: { actorId: "expert-1" } }, }), findByIdAndUpdate, }; service.assertExpertActorOnClaim = jest.fn().mockResolvedValue(undefined); await expect( service.submitExpertReplyV2( "v2-claim", { description: "Damage assessment", parts: [ { partId: 201, typeOfDamage: TypeOfDamage.Repair, price: "99,999", salary: "100000", totalPayment: "100000", factorNeeded: false, }, ], }, { sub: "expert-1", role: RoleEnum.FIELD_EXPERT }, ), ).rejects.toMatchObject({ response: { code: "EXPERT_REPLY_VALIDATION_ERROR", field: "parts[0].price", message: expect.stringContaining("حداقل"), }, }); expect(findByIdAndUpdate).not.toHaveBeenCalled(); }); });