forked from Yara724/api
509 lines
15 KiB
TypeScript
509 lines
15 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 {
|
|
assertPreviousPlateInquiryMatchesVin,
|
|
isMappedPolicyCurrent,
|
|
normalizeInquirySubmission,
|
|
participantForRole,
|
|
resolveInquiryParticipants,
|
|
resolveInquiryVehicle,
|
|
runPlateInquiryWithFallback,
|
|
vehiclePlateCandidates,
|
|
} 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("defaults an omitted registration state to CURRENT", () => {
|
|
expect(
|
|
resolveInquiryVehicle({
|
|
currentPlate: {
|
|
leftDigits: "44",
|
|
centerAlphabet: "ب",
|
|
centerDigits: "111",
|
|
ir: "22",
|
|
},
|
|
}).registrationState,
|
|
).toBe(VehicleRegistrationState.CURRENT);
|
|
});
|
|
|
|
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("orders the current plate before the previous-plate fallback", () => {
|
|
const currentPlate = {
|
|
leftDigits: "44",
|
|
centerAlphabet: "ب",
|
|
centerDigits: "111",
|
|
ir: "22",
|
|
};
|
|
const previousPlate = {
|
|
leftDigits: "55",
|
|
centerAlphabet: "ج",
|
|
centerDigits: "222",
|
|
ir: "33",
|
|
};
|
|
|
|
expect(
|
|
vehiclePlateCandidates({
|
|
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
|
currentPlate,
|
|
previousPlate,
|
|
vin: "NAAM01E15HK123456",
|
|
}),
|
|
).toEqual([
|
|
{ kind: "CURRENT", plate: currentPlate },
|
|
{ kind: "PREVIOUS", plate: previousPlate },
|
|
]);
|
|
});
|
|
|
|
it("rejects a previous-plate result for another chassis", () => {
|
|
expect(() =>
|
|
assertPreviousPlateInquiryMatchesVin("NAAM01E15HK123456", {
|
|
VinNumberField: "DIFFERENTVIN00001",
|
|
}),
|
|
).toThrow(BadRequestException);
|
|
});
|
|
|
|
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("falls back to the previous plate and accepts only a matching VIN", async () => {
|
|
const currentPlate = {
|
|
leftDigits: "44",
|
|
centerAlphabet: "ب",
|
|
centerDigits: "111",
|
|
ir: "22",
|
|
};
|
|
const previousPlate = {
|
|
leftDigits: "55",
|
|
centerAlphabet: "ج",
|
|
centerDigits: "222",
|
|
ir: "33",
|
|
};
|
|
const query = jest
|
|
.fn()
|
|
.mockRejectedValueOnce(new Error("not found"))
|
|
.mockResolvedValueOnce({
|
|
mapped: { VinNumberField: "NAAM01E15HK123456", CompanyName: "پارسیان" },
|
|
});
|
|
|
|
const result = await runPlateInquiryWithFallback<{
|
|
mapped: { VinNumberField?: string; CompanyName?: string };
|
|
}>({
|
|
vehicle: {
|
|
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
|
currentPlate,
|
|
previousPlate,
|
|
vin: "NAAM01E15HK123456",
|
|
},
|
|
fallbackCurrentPlate: currentPlate,
|
|
query,
|
|
isUsable: (value) => !!value.mapped.CompanyName,
|
|
mappedValue: (value) => value.mapped,
|
|
});
|
|
|
|
expect(query).toHaveBeenCalledTimes(2);
|
|
expect(result.plateKind).toBe("PREVIOUS");
|
|
expect(result.attempts).toMatchObject([
|
|
{ plateKind: "CURRENT", succeeded: false, error: "not found" },
|
|
{ plateKind: "PREVIOUS", succeeded: true, usable: true },
|
|
]);
|
|
expect(result.attempts[0]).toMatchObject({
|
|
plate: currentPlate,
|
|
vin: "NAAM01E15HK123456",
|
|
});
|
|
expect(result.attempts[1]).toMatchObject({
|
|
plate: previousPlate,
|
|
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("falls back from a stale current policy to a current previous-plate policy", async () => {
|
|
const currentPlate = {
|
|
leftDigits: "44",
|
|
centerAlphabet: "ب",
|
|
centerDigits: "111",
|
|
ir: "22",
|
|
};
|
|
const previousPlate = {
|
|
leftDigits: "55",
|
|
centerAlphabet: "ج",
|
|
centerDigits: "222",
|
|
ir: "33",
|
|
};
|
|
const query = jest
|
|
.fn()
|
|
.mockResolvedValueOnce({
|
|
mapped: { CompanyName: "پارسیان", EndDate: "1404/01/01" },
|
|
})
|
|
.mockResolvedValueOnce({
|
|
mapped: {
|
|
CompanyName: "پارسیان",
|
|
EndDate: "1406/01/01",
|
|
VinNumberField: "NAAM01E15HK123456",
|
|
},
|
|
});
|
|
|
|
const result = await runPlateInquiryWithFallback<{
|
|
mapped: Record<string, any>;
|
|
}>({
|
|
vehicle: resolveInquiryVehicle({
|
|
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
|
currentPlate,
|
|
previousPlate,
|
|
vin: "NAAM01E15HK123456",
|
|
}),
|
|
fallbackCurrentPlate: currentPlate,
|
|
query,
|
|
isUsable: (value) =>
|
|
!!value.mapped.CompanyName &&
|
|
isMappedPolicyCurrent(value.mapped, "2026-09-13"),
|
|
mappedValue: (value) => value.mapped,
|
|
});
|
|
|
|
expect(result.plateKind).toBe("PREVIOUS");
|
|
expect(result.attempts[0]).toMatchObject({
|
|
plateKind: "CURRENT",
|
|
succeeded: true,
|
|
usable: false,
|
|
});
|
|
});
|
|
|
|
it("rejects the result and retains audit attempts when every plate is unusable", async () => {
|
|
const currentPlate = {
|
|
leftDigits: "44",
|
|
centerAlphabet: "ب",
|
|
centerDigits: "111",
|
|
ir: "22",
|
|
};
|
|
const previousPlate = {
|
|
leftDigits: "55",
|
|
centerAlphabet: "ج",
|
|
centerDigits: "222",
|
|
ir: "33",
|
|
};
|
|
|
|
await expect(
|
|
runPlateInquiryWithFallback({
|
|
vehicle: resolveInquiryVehicle({
|
|
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
|
currentPlate,
|
|
previousPlate,
|
|
vin: "NAAM01E15HK123456",
|
|
}),
|
|
fallbackCurrentPlate: currentPlate,
|
|
query: async () => ({ mapped: { CompanyName: "پارسیان" } }),
|
|
isUsable: () => false,
|
|
mappedValue: (value) => value.mapped,
|
|
}),
|
|
).rejects.toMatchObject({
|
|
attempts: [
|
|
{ plateKind: "CURRENT", succeeded: true, usable: false },
|
|
{ plateKind: "PREVIOUS", succeeded: true, usable: false },
|
|
],
|
|
});
|
|
});
|
|
|
|
it("does not use the previous plate after a transport or 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(
|
|
runPlateInquiryWithFallback({
|
|
vehicle: resolveInquiryVehicle({
|
|
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
|
currentPlate,
|
|
previousPlate: {
|
|
leftDigits: "55",
|
|
centerAlphabet: "ج",
|
|
centerDigits: "222",
|
|
ir: "33",
|
|
},
|
|
vin: "NAAM01E15HK123456",
|
|
}),
|
|
fallbackCurrentPlate: currentPlate,
|
|
query,
|
|
isUsable: () => false,
|
|
mappedValue: () => ({}),
|
|
}),
|
|
).rejects.toThrow("upstream timeout");
|
|
expect(query).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("allows an explicitly unknown policyholder only for a damaged-party submission", () => {
|
|
const input = {
|
|
driver: {
|
|
nationalCode: "0012345678",
|
|
birthday: "1370/01/01",
|
|
hasDrivingLicense: false,
|
|
},
|
|
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
|
|
thirdPartyPolicyholder: { unknown: true },
|
|
};
|
|
|
|
expect(() =>
|
|
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, input),
|
|
).toThrow(BadRequestException);
|
|
|
|
const resolved = resolveInquiryParticipants(
|
|
BlameRequestType.THIRD_PARTY,
|
|
input,
|
|
{ allowUnknownThirdPartyPolicyholder: true },
|
|
);
|
|
expect(
|
|
participantForRole(
|
|
resolved,
|
|
InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER,
|
|
),
|
|
).toMatchObject({ unknown: true });
|
|
});
|
|
});
|