forked from Yara724/api
575 lines
17 KiB
TypeScript
575 lines
17 KiB
TypeScript
import { BadRequestException } from "@nestjs/common";
|
||
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
||
import {
|
||
InquiryParticipantRole,
|
||
VehicleRegistrationState,
|
||
} from "src/common/dto/inquiry-participants.dto";
|
||
import {
|
||
isMappedPolicyCurrent,
|
||
normalizeInquirySubmission,
|
||
participantForRole,
|
||
participantForStoredPartyRole,
|
||
resolveInquiryParticipants,
|
||
resolveInquirySubjects,
|
||
resolveInquiryVehicle,
|
||
runCurrentPlateInquiry,
|
||
sanitizeStoredInquiryParticipants,
|
||
} from "./inquiry-participant-resolver";
|
||
|
||
describe("inquiry participant resolver", () => {
|
||
it("links several roles to one explicitly shared person", () => {
|
||
const resolved = resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
||
driver: {
|
||
nationalCode: "0012345678",
|
||
birthday: "1370/01/01",
|
||
hasDrivingLicense: true,
|
||
licenseNumber: "123456789",
|
||
licenseType: "1",
|
||
},
|
||
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
|
||
thirdPartyPolicyholder: {
|
||
sameAs: InquiryParticipantRole.VEHICLE_OWNER,
|
||
},
|
||
});
|
||
|
||
expect(resolved.participants).toHaveLength(1);
|
||
expect(resolved.roles).toEqual({
|
||
driver: "DRIVER",
|
||
vehicleOwner: "DRIVER",
|
||
thirdPartyPolicyholder: "DRIVER",
|
||
});
|
||
});
|
||
|
||
it("requires both previous plate and VIN for a recent transfer", () => {
|
||
expect(() =>
|
||
resolveInquiryVehicle({
|
||
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
||
currentPlate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
},
|
||
}),
|
||
).toThrow(BadRequestException);
|
||
});
|
||
|
||
it("requires the previous policyholder national code for a recent transfer", () => {
|
||
expect(() =>
|
||
resolveInquiryVehicle({
|
||
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
||
currentPlate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
},
|
||
previousPlate: {
|
||
leftDigits: "55",
|
||
centerAlphabet: "ج",
|
||
centerDigits: "222",
|
||
ir: "33",
|
||
},
|
||
vin: "NAAM01E15HK123456",
|
||
}),
|
||
).toThrow("کد ملی بیمهگذار قبلی");
|
||
});
|
||
|
||
it("defaults an omitted registration state to CURRENT", () => {
|
||
expect(
|
||
resolveInquiryVehicle({
|
||
currentPlate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
},
|
||
}).registrationState,
|
||
).toBe(VehicleRegistrationState.CURRENT);
|
||
});
|
||
|
||
it("requires an exact 17-character VIN whenever VIN is provided", () => {
|
||
expect(() =>
|
||
resolveInquiryVehicle({
|
||
registrationState: VehicleRegistrationState.CURRENT,
|
||
currentPlate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
},
|
||
vin: "TOO-SHORT",
|
||
}),
|
||
).toThrow("شماره شاسی (VIN) باید دقیقاً ۱۷ کاراکتر باشد.");
|
||
});
|
||
|
||
it("rejects an incomplete current plate", () => {
|
||
expect(() =>
|
||
resolveInquiryVehicle({
|
||
currentPlate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "",
|
||
ir: "22",
|
||
},
|
||
}),
|
||
).toThrow("سه رقم میانی پلاک در پلاک فعلی الزامی است.");
|
||
});
|
||
|
||
it("rejects invalid vehicle choice values", () => {
|
||
expect(() =>
|
||
resolveInquiryVehicle({
|
||
registrationState: "SOLD" as any,
|
||
currentPlate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
},
|
||
}),
|
||
).toThrow("وضعیت پلاک خودرو باید «فعلی» یا «تازه تعویضشده» باشد.");
|
||
|
||
expect(() =>
|
||
resolveInquiryVehicle({
|
||
currentPlate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
},
|
||
isNewCar: "false" as any,
|
||
}),
|
||
).toThrow("وضعیت صفر بودن خودرو نامعتبر است.");
|
||
});
|
||
|
||
it("rejects a previous policyholder national code for a current registration", () => {
|
||
expect(() =>
|
||
resolveInquiryVehicle({
|
||
registrationState: VehicleRegistrationState.CURRENT,
|
||
currentPlate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
},
|
||
previousPolicyholderNationalCode: "0098765432",
|
||
}),
|
||
).toThrow(BadRequestException);
|
||
});
|
||
|
||
it("rejects the removed flat inquiry contract", () => {
|
||
expect(() =>
|
||
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
||
nationalCodeOfDriver: "0012345678",
|
||
driverBirthday: "1370/01/01",
|
||
driverLicense: "123456789",
|
||
nationalCodeOfInsurer: "0098765432",
|
||
insurerBirthday: "1360/02/02",
|
||
driverIsInsurer: false,
|
||
} as any),
|
||
).toThrow(BadRequestException);
|
||
});
|
||
|
||
it("rejects phone numbers in participant inquiry identity", () => {
|
||
expect(() =>
|
||
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
||
driver: {
|
||
nationalCode: "0012345678",
|
||
birthday: "1370/01/01",
|
||
hasDrivingLicense: false,
|
||
phoneNumber: "09120000000",
|
||
},
|
||
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
|
||
thirdPartyPolicyholder: {
|
||
sameAs: InquiryParticipantRole.DRIVER,
|
||
},
|
||
} as any),
|
||
).toThrow(BadRequestException);
|
||
});
|
||
|
||
it("keeps third-party and car-body policyholders distinct", () => {
|
||
const resolved = resolveInquiryParticipants(BlameRequestType.CAR_BODY, {
|
||
driver: {
|
||
nationalCode: "0012345678",
|
||
birthday: "1370/01/01",
|
||
hasDrivingLicense: false,
|
||
},
|
||
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
|
||
thirdPartyPolicyholder: {
|
||
nationalCode: "0023456789",
|
||
birthday: "1360/02/02",
|
||
},
|
||
carBodyPolicyholder: {
|
||
nationalCode: "0034567890",
|
||
birthday: "1350/03/03",
|
||
},
|
||
});
|
||
|
||
expect(
|
||
participantForRole(
|
||
resolved,
|
||
InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER,
|
||
)?.nationalCode,
|
||
).toBe("0023456789");
|
||
expect(
|
||
participantForRole(resolved, InquiryParticipantRole.CAR_BODY_POLICYHOLDER)
|
||
?.nationalCode,
|
||
).toBe("0034567890");
|
||
});
|
||
|
||
it("projects role-complete input onto the existing inquiry fields", () => {
|
||
const normalized = normalizeInquirySubmission(BlameRequestType.CAR_BODY, {
|
||
vehicle: {
|
||
currentPlate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
},
|
||
},
|
||
driver: {
|
||
nationalCode: "0012345678",
|
||
birthday: "1370/01/01",
|
||
hasDrivingLicense: true,
|
||
licenseNumber: "D-1",
|
||
licenseType: "1",
|
||
},
|
||
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
|
||
thirdPartyPolicyholder: {
|
||
nationalCode: "0023456789",
|
||
birthday: "1360/02/02",
|
||
},
|
||
carBodyPolicyholder: {
|
||
nationalCode: "0034567890",
|
||
birthday: "1350/03/03",
|
||
},
|
||
});
|
||
|
||
expect(normalized.dto.nationalCodeOfInsurer).toBe("0023456789");
|
||
expect(normalized.dto.nationalCodeOfDriver).toBe("0012345678");
|
||
expect(normalized.carBodyPolicyholder?.nationalCode).toBe("0034567890");
|
||
expect(normalized.vehicleOwner?.nationalCode).toBe("0012345678");
|
||
});
|
||
|
||
it("routes each inquiry to its domain participant", () => {
|
||
const normalized = normalizeInquirySubmission(BlameRequestType.CAR_BODY, {
|
||
vehicle: {
|
||
currentPlate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
},
|
||
},
|
||
driver: {
|
||
nationalCode: "0011111111",
|
||
birthday: "1370/01/01",
|
||
hasDrivingLicense: false,
|
||
},
|
||
vehicleOwner: {
|
||
nationalCode: "0022222222",
|
||
birthday: "1365/02/02",
|
||
},
|
||
thirdPartyPolicyholder: {
|
||
nationalCode: "0033333333",
|
||
birthday: "1360/03/03",
|
||
},
|
||
carBodyPolicyholder: {
|
||
nationalCode: "0044444444",
|
||
birthday: "1355/04/04",
|
||
},
|
||
});
|
||
|
||
expect(resolveInquirySubjects(normalized)).toEqual({
|
||
thirdPartyPolicyNationalCode: "0033333333",
|
||
carBodyPolicyNationalCode: "0044444444",
|
||
shebaNationalCode: "0022222222",
|
||
driverNationalCode: "0011111111",
|
||
});
|
||
});
|
||
|
||
it("resolves a persisted vehicle owner for later Sheba validation", () => {
|
||
const party = {
|
||
participants: [
|
||
{ participantId: "DRIVER", nationalCode: "0011111111" },
|
||
{ participantId: "VEHICLE_OWNER", nationalCode: "0022222222" },
|
||
],
|
||
participantRoles: {
|
||
driver: "DRIVER",
|
||
vehicleOwner: "VEHICLE_OWNER",
|
||
thirdPartyPolicyholder: "DRIVER",
|
||
},
|
||
};
|
||
|
||
expect(
|
||
participantForStoredPartyRole(party, InquiryParticipantRole.VEHICLE_OWNER)
|
||
?.nationalCode,
|
||
).toBe("0022222222");
|
||
});
|
||
|
||
it("requires the driver's licence status in the new contract", () => {
|
||
expect(() =>
|
||
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
||
driver: { nationalCode: "0012345678", birthday: "1370/01/01" },
|
||
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
|
||
thirdPartyPolicyholder: {
|
||
sameAs: InquiryParticipantRole.VEHICLE_OWNER,
|
||
},
|
||
}),
|
||
).toThrow(BadRequestException);
|
||
});
|
||
|
||
it("runs only one inquiry for the current plate", async () => {
|
||
const currentPlate = {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
};
|
||
const query = jest.fn().mockResolvedValue({
|
||
mapped: { CompanyName: "پارسیان" },
|
||
});
|
||
|
||
const result = await runCurrentPlateInquiry<{
|
||
mapped: { CompanyName?: string };
|
||
}>({
|
||
currentPlate,
|
||
vin: "NAAM01E15HK123456",
|
||
query,
|
||
isUsable: (value) => !!value.mapped.CompanyName,
|
||
});
|
||
|
||
expect(query).toHaveBeenCalledTimes(1);
|
||
expect(query).toHaveBeenCalledWith(currentPlate);
|
||
expect(result.plateKind).toBe("CURRENT");
|
||
expect(result.attempts).toMatchObject([
|
||
{ plateKind: "CURRENT", succeeded: true, usable: true },
|
||
]);
|
||
expect(result.attempts[0]).toMatchObject({
|
||
plate: currentPlate,
|
||
vin: "NAAM01E15HK123456",
|
||
});
|
||
});
|
||
|
||
it("rejects a car-body policyholder on a third-party case", () => {
|
||
expect(() =>
|
||
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
||
driver: {
|
||
nationalCode: "0012345678",
|
||
birthday: "1370/01/01",
|
||
hasDrivingLicense: false,
|
||
},
|
||
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
|
||
thirdPartyPolicyholder: {
|
||
sameAs: InquiryParticipantRole.VEHICLE_OWNER,
|
||
},
|
||
carBodyPolicyholder: {
|
||
nationalCode: "0034567890",
|
||
birthday: "1350/03/03",
|
||
},
|
||
}),
|
||
).toThrow(BadRequestException);
|
||
});
|
||
|
||
it("does not accept a previous plate unless recent transfer is declared", () => {
|
||
expect(() =>
|
||
resolveInquiryVehicle({
|
||
registrationState: VehicleRegistrationState.CURRENT,
|
||
currentPlate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
},
|
||
previousPlate: {
|
||
leftDigits: "55",
|
||
centerAlphabet: "ج",
|
||
centerDigits: "222",
|
||
ir: "33",
|
||
},
|
||
}),
|
||
).toThrow(BadRequestException);
|
||
});
|
||
|
||
it("rejects sameAs together with any person-specific fields", () => {
|
||
expect(() =>
|
||
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
||
driver: {
|
||
nationalCode: "0012345678",
|
||
birthday: "1370/01/01",
|
||
hasDrivingLicense: false,
|
||
},
|
||
vehicleOwner: {
|
||
sameAs: InquiryParticipantRole.DRIVER,
|
||
fullName: "Duplicate details must not be silently discarded",
|
||
},
|
||
thirdPartyPolicyholder: {
|
||
sameAs: InquiryParticipantRole.DRIVER,
|
||
},
|
||
}),
|
||
).toThrow(BadRequestException);
|
||
});
|
||
|
||
it("rejects conflicting legacy and structured current plates", () => {
|
||
expect(() =>
|
||
normalizeInquirySubmission(BlameRequestType.THIRD_PARTY, {
|
||
plate: {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
},
|
||
vehicle: {
|
||
registrationState: VehicleRegistrationState.CURRENT,
|
||
currentPlate: {
|
||
leftDigits: "55",
|
||
centerAlphabet: "ج",
|
||
centerDigits: "222",
|
||
ir: "33",
|
||
},
|
||
},
|
||
driver: {
|
||
nationalCode: "0012345678",
|
||
birthday: "1370/01/01",
|
||
hasDrivingLicense: false,
|
||
},
|
||
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
|
||
thirdPartyPolicyholder: {
|
||
sameAs: InquiryParticipantRole.DRIVER,
|
||
},
|
||
}),
|
||
).toThrow(BadRequestException);
|
||
});
|
||
|
||
it("treats an expired policy result as stale", () => {
|
||
expect(isMappedPolicyCurrent({ EndDate: "1404/01/01" }, "2026-09-13")).toBe(
|
||
false,
|
||
);
|
||
expect(isMappedPolicyCurrent({ EndDate: "1406/01/01" }, "2026-09-13")).toBe(
|
||
true,
|
||
);
|
||
});
|
||
|
||
it("rejects a stale current policy without trying another plate", async () => {
|
||
const currentPlate = {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
};
|
||
const query = jest.fn().mockResolvedValue({
|
||
mapped: { CompanyName: "پارسیان", EndDate: "1404/01/01" },
|
||
});
|
||
|
||
await expect(
|
||
runCurrentPlateInquiry<{ mapped: Record<string, any> }>({
|
||
currentPlate,
|
||
query,
|
||
isUsable: (value) =>
|
||
!!value.mapped.CompanyName &&
|
||
isMappedPolicyCurrent(value.mapped, "2026-09-13"),
|
||
}),
|
||
).rejects.toMatchObject({
|
||
attempts: [{ plateKind: "CURRENT", succeeded: true, usable: false }],
|
||
});
|
||
expect(query).toHaveBeenCalledTimes(1);
|
||
});
|
||
|
||
it("retains the current-plate audit attempt when the result is unusable", async () => {
|
||
const currentPlate = {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
};
|
||
|
||
await expect(
|
||
runCurrentPlateInquiry({
|
||
currentPlate,
|
||
vin: "NAAM01E15HK123456",
|
||
query: async () => ({ mapped: { CompanyName: "پارسیان" } }),
|
||
isUsable: () => false,
|
||
}),
|
||
).rejects.toMatchObject({
|
||
attempts: [{ plateKind: "CURRENT", succeeded: true, usable: false }],
|
||
});
|
||
});
|
||
|
||
it("preserves a mapped provider message when the current result is unusable", async () => {
|
||
const currentPlate = {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
};
|
||
|
||
await expect(
|
||
runCurrentPlateInquiry({
|
||
currentPlate,
|
||
query: async () => ({
|
||
mapped: { Error: { Message: "رکوردی یافت نشد" } },
|
||
}),
|
||
isUsable: () => false,
|
||
errorMessage: (value) => value.mapped.Error.Message,
|
||
}),
|
||
).rejects.toThrow("رکوردی یافت نشد");
|
||
});
|
||
|
||
it("retains one failed current-plate attempt after a provider outage", async () => {
|
||
const currentPlate = {
|
||
leftDigits: "44",
|
||
centerAlphabet: "ب",
|
||
centerDigits: "111",
|
||
ir: "22",
|
||
};
|
||
const query = jest.fn().mockRejectedValue(
|
||
Object.assign(new Error("upstream timeout"), {
|
||
code: "ETIMEDOUT",
|
||
}),
|
||
);
|
||
|
||
await expect(
|
||
runCurrentPlateInquiry({
|
||
currentPlate,
|
||
vin: "NAAM01E15HK123456",
|
||
query,
|
||
isUsable: () => false,
|
||
}),
|
||
).rejects.toThrow("upstream timeout");
|
||
expect(query).toHaveBeenCalledTimes(1);
|
||
});
|
||
|
||
it("rejects the removed unknown policyholder option", () => {
|
||
const input = {
|
||
driver: {
|
||
nationalCode: "0012345678",
|
||
birthday: "1370/01/01",
|
||
hasDrivingLicense: false,
|
||
},
|
||
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
|
||
thirdPartyPolicyholder: { unknown: true },
|
||
};
|
||
|
||
expect(() =>
|
||
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, input as any),
|
||
).toThrow("ثبت بیمهگذار شخص ثالث بهصورت نامشخص امکانپذیر نیست.");
|
||
});
|
||
|
||
it("strips the removed unknown field from historical participant output", () => {
|
||
expect(
|
||
sanitizeStoredInquiryParticipants([
|
||
{
|
||
participantId: "THIRD_PARTY_POLICYHOLDER",
|
||
nationalCode: "0012345678",
|
||
unknown: true,
|
||
},
|
||
]),
|
||
).toEqual([
|
||
{
|
||
participantId: "THIRD_PARTY_POLICYHOLDER",
|
||
nationalCode: "0012345678",
|
||
},
|
||
]);
|
||
});
|
||
});
|