@fiado/type-kit 3.626.0 → 3.628.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_test_/unit/collection/collectionCallbackRequests.test.ts +96 -0
- package/_test_/unit/emailVerification/dtos/EmailSetTrustedRequest.test.ts +23 -0
- package/_test_/unit/loanCollectorAssignment/collectorAssignmentRequests.test.ts +65 -0
- package/_test_/unit/loanConfig/parameterWriteRequests.test.ts +62 -0
- package/_test_/unit/loanCredit/dtos/loanCreditInputLimits.test.ts +168 -0
- package/_test_/unit/loanCredit/signLoanCreditRequest.test.ts +61 -2
- package/_test_/unit/loanOfferings/offeringsInputRules.test.ts +254 -0
- package/_test_/unit/loanOfferings/promotionRequests.test.ts +1 -1
- package/_test_/unit/loanRetailAudit/auditInputLimits.test.ts +114 -0
- package/_test_/unit/loanScoring/evaluateSciRequest.test.ts +49 -0
- package/bin/collection/constants/CollectionResultLimits.d.ts +16 -0
- package/bin/collection/constants/CollectionResultLimits.js +19 -0
- package/bin/collection/dtos/ApplyCollectionResultRequest.d.ts +5 -0
- package/bin/collection/dtos/ApplyCollectionResultRequest.js +22 -0
- package/bin/collection/dtos/ResolvePendingChargesRequest.d.ts +2 -0
- package/bin/collection/dtos/ResolvePendingChargesRequest.js +9 -0
- package/bin/collection/index.d.ts +2 -0
- package/bin/collection/index.js +3 -0
- package/bin/collection/validators/IsCentPrecision.d.ts +11 -0
- package/bin/collection/validators/IsCentPrecision.js +32 -0
- package/bin/emailVerification/dtos/EmailSetTrustedRequest.d.ts +2 -0
- package/bin/emailVerification/dtos/EmailSetTrustedRequest.js +6 -0
- package/bin/loanCollectorAssignment/constants/CollectorAssignmentLimits.d.ts +12 -0
- package/bin/loanCollectorAssignment/constants/CollectorAssignmentLimits.js +15 -0
- package/bin/loanCollectorAssignment/dtos/requests/AutoAssignRequest.d.ts +1 -0
- package/bin/loanCollectorAssignment/dtos/requests/AutoAssignRequest.js +8 -0
- package/bin/loanCollectorAssignment/dtos/requests/ListCollectorAssignmentsRequest.d.ts +14 -0
- package/bin/loanCollectorAssignment/dtos/requests/ListCollectorAssignmentsRequest.js +62 -0
- package/bin/loanCollectorAssignment/dtos/requests/ReassignRequest.d.ts +1 -0
- package/bin/loanCollectorAssignment/dtos/requests/ReassignRequest.js +4 -0
- package/bin/loanCollectorAssignment/dtos/requests/RedistributeRequest.d.ts +1 -0
- package/bin/loanCollectorAssignment/dtos/requests/RedistributeRequest.js +4 -0
- package/bin/loanCollectorAssignment/dtos/responses/ListCollectorAssignmentsResponse.d.ts +9 -0
- package/bin/loanCollectorAssignment/dtos/responses/ListCollectorAssignmentsResponse.js +2 -0
- package/bin/loanCollectorAssignment/index.d.ts +3 -0
- package/bin/loanCollectorAssignment/index.js +4 -0
- package/bin/loanConfig/constants/ParameterRequestLimits.d.ts +8 -0
- package/bin/loanConfig/constants/ParameterRequestLimits.js +11 -0
- package/bin/loanConfig/dtos/requests/BulkGetParametersRequest.d.ts +1 -1
- package/bin/loanConfig/dtos/requests/BulkGetParametersRequest.js +3 -1
- package/bin/loanConfig/dtos/requests/BulkUpdateParametersRequest.d.ts +5 -2
- package/bin/loanConfig/dtos/requests/BulkUpdateParametersRequest.js +8 -4
- package/bin/loanConfig/dtos/requests/UpdateParameterRequest.d.ts +6 -3
- package/bin/loanConfig/dtos/requests/UpdateParameterRequest.js +10 -5
- package/bin/loanConfig/index.d.ts +1 -0
- package/bin/loanConfig/index.js +1 -0
- package/bin/loanCredit/constants/LoanInputLimits.d.ts +24 -0
- package/bin/loanCredit/constants/LoanInputLimits.js +27 -0
- package/bin/loanCredit/dtos/LoanBeneficiary.d.ts +1 -0
- package/bin/loanCredit/dtos/LoanBeneficiary.js +9 -0
- package/bin/loanCredit/dtos/LoanContractDetails.d.ts +18 -1
- package/bin/loanCredit/dtos/LoanContractDetails.js +2 -1
- package/bin/loanCredit/dtos/LoanDeviceNotifyItem.d.ts +5 -0
- package/bin/loanCredit/dtos/LoanDeviceNotifyItem.js +12 -3
- package/bin/loanCredit/dtos/RestructureTerms.d.ts +1 -1
- package/bin/loanCredit/dtos/RestructureTerms.js +2 -0
- package/bin/loanCredit/dtos/SignatureContext.d.ts +5 -1
- package/bin/loanCredit/dtos/requests/ActivationCheckRequest.d.ts +1 -1
- package/bin/loanCredit/dtos/requests/ActivationCheckRequest.js +4 -0
- package/bin/loanCredit/dtos/requests/BackofficeApproveRestructureRequest.js +2 -0
- package/bin/loanCredit/dtos/requests/BackofficeProposeRestructureRequest.js +2 -0
- package/bin/loanCredit/dtos/requests/BackofficeRejectRestructureRequest.js +2 -0
- package/bin/loanCredit/dtos/requests/BackofficeWriteOffCreditRequest.js +2 -0
- package/bin/loanCredit/dtos/requests/CreateCustomerPaymentRequest.d.ts +5 -0
- package/bin/loanCredit/dtos/requests/CreateCustomerPaymentRequest.js +6 -0
- package/bin/loanCredit/dtos/requests/CreateSellerPaymentRequest.d.ts +5 -0
- package/bin/loanCredit/dtos/requests/CreateSellerPaymentRequest.js +6 -0
- package/bin/loanCredit/dtos/requests/OriginateLoanCreditRequest.d.ts +1 -0
- package/bin/loanCredit/dtos/requests/OriginateLoanCreditRequest.js +7 -1
- package/bin/loanCredit/dtos/requests/SegmentFactsBatchRequest.d.ts +1 -1
- package/bin/loanCredit/dtos/requests/SegmentFactsBatchRequest.js +3 -1
- package/bin/loanCredit/dtos/requests/SignLoanCreditRequest.d.ts +2 -0
- package/bin/loanCredit/dtos/requests/SignLoanCreditRequest.js +3 -0
- package/bin/loanCredit/dtos/requests/UpsertLoanBorrowerRequest.d.ts +1 -0
- package/bin/loanCredit/dtos/requests/UpsertLoanBorrowerRequest.js +2 -0
- package/bin/loanCredit/index.d.ts +2 -0
- package/bin/loanCredit/index.js +2 -0
- package/bin/loanCredit/validators/IsContractBirthDate.d.ts +11 -0
- package/bin/loanCredit/validators/IsContractBirthDate.js +41 -0
- package/bin/loanOfferings/constants/offeringsInputLimits.d.ts +35 -0
- package/bin/loanOfferings/constants/offeringsInputLimits.js +38 -0
- package/bin/loanOfferings/dtos/requests/ChangeCreditPlanStatusRequest.d.ts +2 -0
- package/bin/loanOfferings/dtos/requests/ChangeCreditPlanStatusRequest.js +8 -0
- package/bin/loanOfferings/dtos/requests/ChangePromotionStatusRequest.js +2 -0
- package/bin/loanOfferings/dtos/requests/ComputeAmortizationScheduleRequest.d.ts +4 -1
- package/bin/loanOfferings/dtos/requests/ComputeAmortizationScheduleRequest.js +3 -1
- package/bin/loanOfferings/dtos/requests/ConsumePromotionRequest.d.ts +1 -0
- package/bin/loanOfferings/dtos/requests/ConsumePromotionRequest.js +3 -0
- package/bin/loanOfferings/dtos/requests/CreateCreditPlanRequest.d.ts +3 -0
- package/bin/loanOfferings/dtos/requests/CreateCreditPlanRequest.js +11 -2
- package/bin/loanOfferings/dtos/requests/CreateFinancierRequest.d.ts +5 -0
- package/bin/loanOfferings/dtos/requests/CreateFinancierRequest.js +11 -0
- package/bin/loanOfferings/dtos/requests/CreatePromotionRequest.d.ts +2 -2
- package/bin/loanOfferings/dtos/requests/CreatePromotionRequest.js +9 -0
- package/bin/loanOfferings/dtos/requests/CreateSegmentRequest.d.ts +1 -1
- package/bin/loanOfferings/dtos/requests/CreateSegmentRequest.js +3 -0
- package/bin/loanOfferings/dtos/requests/CreateStorePolicyRequest.d.ts +1 -1
- package/bin/loanOfferings/dtos/requests/CreateStorePolicyRequest.js +3 -0
- package/bin/loanOfferings/dtos/requests/ReleasePromotionRequest.d.ts +8 -0
- package/bin/loanOfferings/dtos/requests/ReleasePromotionRequest.js +30 -0
- package/bin/loanOfferings/dtos/requests/ReservePromotionBonusRequest.d.ts +1 -0
- package/bin/loanOfferings/dtos/requests/ReservePromotionBonusRequest.js +3 -0
- package/bin/loanOfferings/dtos/requests/SettlePromotionBonusRequest.d.ts +1 -0
- package/bin/loanOfferings/dtos/requests/SettlePromotionBonusRequest.js +3 -0
- package/bin/loanOfferings/dtos/requests/SimulateCreditPlanRequest.d.ts +1 -0
- package/bin/loanOfferings/dtos/requests/SimulateCreditPlanRequest.js +3 -0
- package/bin/loanOfferings/dtos/requests/UpdateCohortRequest.d.ts +3 -1
- package/bin/loanOfferings/dtos/requests/UpdateCohortRequest.js +5 -2
- package/bin/loanOfferings/dtos/requests/UpdateCreditPlanRequest.d.ts +8 -0
- package/bin/loanOfferings/dtos/requests/UpdateCreditPlanRequest.js +30 -12
- package/bin/loanOfferings/dtos/requests/UpdateFinancierRequest.d.ts +6 -0
- package/bin/loanOfferings/dtos/requests/UpdateFinancierRequest.js +13 -2
- package/bin/loanOfferings/dtos/requests/UpdatePromotionRequest.d.ts +7 -1
- package/bin/loanOfferings/dtos/requests/UpdatePromotionRequest.js +25 -5
- package/bin/loanOfferings/dtos/requests/UpdateSegmentRequest.d.ts +5 -2
- package/bin/loanOfferings/dtos/requests/UpdateSegmentRequest.js +12 -5
- package/bin/loanOfferings/dtos/requests/UpdateStorePolicyRequest.d.ts +18 -9
- package/bin/loanOfferings/dtos/requests/UpdateStorePolicyRequest.js +12 -4
- package/bin/loanOfferings/dtos/responses/ReleasePromotionResponse.d.ts +11 -0
- package/bin/loanOfferings/dtos/responses/ReleasePromotionResponse.js +2 -0
- package/bin/loanOfferings/index.d.ts +5 -0
- package/bin/loanOfferings/index.js +6 -0
- package/bin/loanOfferings/validators/IsOptionalNonNull.d.ts +9 -0
- package/bin/loanOfferings/validators/IsOptionalNonNull.js +14 -0
- package/bin/loanOfferings/validators/MaxJsonBytes.d.ts +5 -0
- package/bin/loanOfferings/validators/MaxJsonBytes.js +28 -0
- package/bin/loanRetailAudit/constants/auditInputLimits.d.ts +16 -0
- package/bin/loanRetailAudit/constants/auditInputLimits.js +19 -0
- package/bin/loanRetailAudit/dtos/AuditTrailChange.js +4 -0
- package/bin/loanRetailAudit/dtos/AuditTrailContextEntry.js +3 -0
- package/bin/loanRetailAudit/dtos/requests/CreatePiiAccessLogRequest.d.ts +7 -1
- package/bin/loanRetailAudit/dtos/requests/CreatePiiAccessLogRequest.js +8 -1
- package/bin/loanRetailAudit/events/AuditTrailRecordedV1.d.ts +1 -0
- package/bin/loanRetailAudit/events/AuditTrailRecordedV1.js +5 -1
- package/bin/loanRetailAudit/index.d.ts +2 -0
- package/bin/loanRetailAudit/index.js +3 -0
- package/bin/loanRetailAudit/validators/IsIsoDateTime.d.ts +13 -0
- package/bin/loanRetailAudit/validators/IsIsoDateTime.js +29 -0
- package/bin/loanScoring/dtos/ScoringOffer.d.ts +8 -6
- package/bin/loanScoring/dtos/requests/EvaluateSciRequest.d.ts +14 -3
- package/bin/loanScoring/dtos/requests/EvaluateSciRequest.js +15 -0
- package/package.json +1 -1
- package/src/collection/constants/CollectionResultLimits.ts +18 -0
- package/src/collection/dtos/ApplyCollectionResultRequest.ts +15 -8
- package/src/collection/dtos/ResolvePendingChargesRequest.ts +7 -4
- package/src/collection/index.ts +4 -0
- package/src/collection/validators/IsCentPrecision.ts +33 -0
- package/src/emailVerification/dtos/EmailSetTrustedRequest.ts +6 -0
- package/src/loanCollectorAssignment/constants/CollectorAssignmentLimits.ts +16 -0
- package/src/loanCollectorAssignment/dtos/requests/AutoAssignRequest.ts +13 -1
- package/src/loanCollectorAssignment/dtos/requests/ListCollectorAssignmentsRequest.ts +52 -0
- package/src/loanCollectorAssignment/dtos/requests/ReassignRequest.ts +8 -1
- package/src/loanCollectorAssignment/dtos/requests/RedistributeRequest.ts +8 -1
- package/src/loanCollectorAssignment/dtos/responses/ListCollectorAssignmentsResponse.ts +10 -0
- package/src/loanCollectorAssignment/index.ts +5 -0
- package/src/loanConfig/constants/ParameterRequestLimits.ts +11 -0
- package/src/loanConfig/dtos/requests/BulkGetParametersRequest.ts +4 -2
- package/src/loanConfig/dtos/requests/BulkUpdateParametersRequest.ts +26 -6
- package/src/loanConfig/dtos/requests/UpdateParameterRequest.ts +15 -7
- package/src/loanConfig/index.ts +1 -0
- package/src/loanCredit/constants/LoanInputLimits.ts +29 -0
- package/src/loanCredit/dtos/LoanBeneficiary.ts +11 -1
- package/src/loanCredit/dtos/LoanContractDetails.ts +26 -3
- package/src/loanCredit/dtos/LoanDeviceNotifyItem.ts +17 -3
- package/src/loanCredit/dtos/RestructureTerms.ts +4 -2
- package/src/loanCredit/dtos/SignatureContext.ts +5 -1
- package/src/loanCredit/dtos/requests/ActivationCheckRequest.ts +12 -2
- package/src/loanCredit/dtos/requests/BackofficeApproveRestructureRequest.ts +3 -1
- package/src/loanCredit/dtos/requests/BackofficeProposeRestructureRequest.ts +3 -1
- package/src/loanCredit/dtos/requests/BackofficeRejectRestructureRequest.ts +3 -1
- package/src/loanCredit/dtos/requests/BackofficeWriteOffCreditRequest.ts +3 -1
- package/src/loanCredit/dtos/requests/CreateCustomerPaymentRequest.ts +10 -1
- package/src/loanCredit/dtos/requests/CreateSellerPaymentRequest.ts +10 -1
- package/src/loanCredit/dtos/requests/OriginateLoanCreditRequest.ts +10 -1
- package/src/loanCredit/dtos/requests/SegmentFactsBatchRequest.ts +4 -2
- package/src/loanCredit/dtos/requests/SignLoanCreditRequest.ts +6 -1
- package/src/loanCredit/dtos/requests/UpsertLoanBorrowerRequest.ts +4 -1
- package/src/loanCredit/index.ts +2 -0
- package/src/loanCredit/validators/IsContractBirthDate.ts +41 -0
- package/src/loanOfferings/constants/offeringsInputLimits.ts +47 -0
- package/src/loanOfferings/dtos/requests/ChangeCreditPlanStatusRequest.ts +9 -1
- package/src/loanOfferings/dtos/requests/ChangePromotionStatusRequest.ts +3 -1
- package/src/loanOfferings/dtos/requests/ComputeAmortizationScheduleRequest.ts +7 -2
- package/src/loanOfferings/dtos/requests/ConsumePromotionRequest.ts +5 -1
- package/src/loanOfferings/dtos/requests/CreateCreditPlanRequest.ts +24 -2
- package/src/loanOfferings/dtos/requests/CreateFinancierRequest.ts +25 -0
- package/src/loanOfferings/dtos/requests/CreatePromotionRequest.ts +17 -2
- package/src/loanOfferings/dtos/requests/CreateSegmentRequest.ts +4 -1
- package/src/loanOfferings/dtos/requests/CreateStorePolicyRequest.ts +6 -1
- package/src/loanOfferings/dtos/requests/ReleasePromotionRequest.ts +16 -0
- package/src/loanOfferings/dtos/requests/ReservePromotionBonusRequest.ts +5 -1
- package/src/loanOfferings/dtos/requests/SettlePromotionBonusRequest.ts +5 -1
- package/src/loanOfferings/dtos/requests/SimulateCreditPlanRequest.ts +5 -1
- package/src/loanOfferings/dtos/requests/UpdateCohortRequest.ts +6 -3
- package/src/loanOfferings/dtos/requests/UpdateCreditPlanRequest.ts +43 -12
- package/src/loanOfferings/dtos/requests/UpdateFinancierRequest.ts +26 -2
- package/src/loanOfferings/dtos/requests/UpdatePromotionRequest.ts +34 -6
- package/src/loanOfferings/dtos/requests/UpdateSegmentRequest.ts +14 -7
- package/src/loanOfferings/dtos/requests/UpdateStorePolicyRequest.ts +28 -13
- package/src/loanOfferings/dtos/responses/ReleasePromotionResponse.ts +11 -0
- package/src/loanOfferings/index.ts +7 -0
- package/src/loanOfferings/validators/IsOptionalNonNull.ts +12 -0
- package/src/loanOfferings/validators/MaxJsonBytes.ts +28 -0
- package/src/loanRetailAudit/constants/auditInputLimits.ts +20 -0
- package/src/loanRetailAudit/dtos/AuditTrailChange.ts +5 -1
- package/src/loanRetailAudit/dtos/AuditTrailContextEntry.ts +4 -1
- package/src/loanRetailAudit/dtos/requests/CreatePiiAccessLogRequest.ts +20 -3
- package/src/loanRetailAudit/events/AuditTrailRecordedV1.ts +7 -2
- package/src/loanRetailAudit/index.ts +4 -0
- package/src/loanRetailAudit/validators/IsIsoDateTime.ts +33 -0
- package/src/loanScoring/dtos/ScoringOffer.ts +8 -6
- package/src/loanScoring/dtos/requests/EvaluateSciRequest.ts +26 -4
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { ClassConstructor, plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validate } from 'class-validator';
|
|
4
|
+
import {
|
|
5
|
+
ApplyCollectionResultRequest,
|
|
6
|
+
COLLECTION_AMOUNT_MAX,
|
|
7
|
+
hasCentPrecision,
|
|
8
|
+
ResolvePendingChargesRequest,
|
|
9
|
+
} from '../../../src/collection/index';
|
|
10
|
+
|
|
11
|
+
const errorProperties = async <T extends object>(dtoClass: ClassConstructor<T>, body: object): Promise<string[]> =>
|
|
12
|
+
(await validate(plainToInstance(dtoClass, body), { whitelist: true })).map((error) => error.property).sort();
|
|
13
|
+
|
|
14
|
+
/** Como lo arma el motor: ids compuestos, montos en pesos. */
|
|
15
|
+
const RESULT = {
|
|
16
|
+
attemptId: 'surekeep-retail#01KXPB422AFGZHQKHVM7E5YDBR#INSTALLMENT#003#1#PAGO_CONFIADO#MXN_DIRECT#1',
|
|
17
|
+
intentId: 'surekeep-retail#01KXPB422AFGZHQKHVM7E5YDBR#INSTALLMENT#003#1',
|
|
18
|
+
chargeId: '01KXPB422AFGZHQKHVM7E5YDBR#INSTALLMENT#003#1',
|
|
19
|
+
domain: 'surekeep-retail',
|
|
20
|
+
tenantId: 'surekeep',
|
|
21
|
+
result: 'FULL',
|
|
22
|
+
amountCollected: 1148.35,
|
|
23
|
+
amountReserved: 0,
|
|
24
|
+
mechanism: 'MXN_DIRECT',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
describe('ApplyCollectionResultRequest — ids no vacíos y con tope (L-005)', () => {
|
|
28
|
+
it('acepta el resultado tal como lo manda el motor', async () => {
|
|
29
|
+
expect(await errorProperties(ApplyCollectionResultRequest, RESULT)).toEqual([]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('rechaza attemptId, intentId o chargeId vacíos', async () => {
|
|
33
|
+
const body = { ...RESULT, attemptId: '', intentId: '', chargeId: '' };
|
|
34
|
+
expect(await errorProperties(ApplyCollectionResultRequest, body)).toEqual(['attemptId', 'chargeId', 'intentId']);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('acepta ids de 256 y rechaza los de 257', async () => {
|
|
38
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, attemptId: 'a'.repeat(256) })).toEqual([]);
|
|
39
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, attemptId: 'a'.repeat(257) })).toEqual(['attemptId']);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('holdTxId es opcional, pero si viaja no va vacío', async () => {
|
|
43
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, holdTxId: 'TX-1' })).toEqual([]);
|
|
44
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, holdTxId: '' })).toEqual(['holdTxId']);
|
|
45
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, holdTxId: 'a'.repeat(257) })).toEqual(['holdTxId']);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('ApplyCollectionResultRequest — montos en centavos, sin negativos y con techo (L-022)', () => {
|
|
50
|
+
it('acepta el ruido de una resta en double: 648.2299999999999 es 648.23', async () => {
|
|
51
|
+
const remaining = 1148.35 - 500.12;
|
|
52
|
+
expect(remaining).not.toBe(648.23);
|
|
53
|
+
const body = { ...RESULT, amountCollected: remaining, walletAmount: 350.3 - 100.1, walletCurrency: 'MXN' };
|
|
54
|
+
expect(await errorProperties(ApplyCollectionResultRequest, body)).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('rechaza fracciones de centavo reales', async () => {
|
|
58
|
+
for (const amountCollected of [10.005, 0.001, 1148.351]) {
|
|
59
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, amountCollected })).toEqual(['amountCollected']);
|
|
60
|
+
}
|
|
61
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, walletAmount: 1.239 })).toEqual(['walletAmount']);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('rechaza negativos, montos sobre el techo e Infinity/NaN', async () => {
|
|
65
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, amountReserved: -0.01 })).toEqual(['amountReserved']);
|
|
66
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, amountCollected: COLLECTION_AMOUNT_MAX })).toEqual([]);
|
|
67
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, amountCollected: COLLECTION_AMOUNT_MAX + 1 })).toEqual([
|
|
68
|
+
'amountCollected',
|
|
69
|
+
]);
|
|
70
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, amountCollected: Infinity })).toEqual(['amountCollected']);
|
|
71
|
+
expect(await errorProperties(ApplyCollectionResultRequest, { ...RESULT, amountCollected: NaN })).toEqual(['amountCollected']);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('hasCentPrecision solo acepta números finitos', () => {
|
|
75
|
+
expect(hasCentPrecision(999_999_999.99)).toBe(true);
|
|
76
|
+
expect(hasCentPrecision('10.00')).toBe(false);
|
|
77
|
+
expect(hasCentPrecision(Number.POSITIVE_INFINITY)).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('ResolvePendingChargesRequest — llaves no vacías y con tope (L-064)', () => {
|
|
82
|
+
const body = { domain: 'surekeep-retail', directoryId: '6f1c2a34-9b1e-4f1d-8a3e-2f4b5c6d7e8f', tenantId: 'surekeep' };
|
|
83
|
+
|
|
84
|
+
it('acepta la consulta del motor', async () => {
|
|
85
|
+
expect(await errorProperties(ResolvePendingChargesRequest, body)).toEqual([]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('rechaza vacíos y largos de más de 128', async () => {
|
|
89
|
+
expect(await errorProperties(ResolvePendingChargesRequest, { domain: '', directoryId: '', tenantId: '' })).toEqual([
|
|
90
|
+
'directoryId',
|
|
91
|
+
'domain',
|
|
92
|
+
'tenantId',
|
|
93
|
+
]);
|
|
94
|
+
expect(await errorProperties(ResolvePendingChargesRequest, { ...body, directoryId: 'd'.repeat(129) })).toEqual(['directoryId']);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -66,4 +66,27 @@ describe('EmailSetTrustedRequest', () => {
|
|
|
66
66
|
const errors = await validate(dto);
|
|
67
67
|
expect(errors.some(e => e.property === 'isPrincipal')).toBe(true);
|
|
68
68
|
});
|
|
69
|
+
|
|
70
|
+
it('acepta dedup en true', async () => {
|
|
71
|
+
const dto = plainToInstance(EmailSetTrustedRequest, { ...valid, dedup: true });
|
|
72
|
+
const errors = await validate(dto);
|
|
73
|
+
expect(errors).toEqual([]);
|
|
74
|
+
expect(dto.dedup).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('acepta la ausencia de dedup', async () => {
|
|
78
|
+
const dto = plainToInstance(EmailSetTrustedRequest, valid);
|
|
79
|
+
const errors = await validate(dto);
|
|
80
|
+
expect(errors).toEqual([]);
|
|
81
|
+
expect(dto.dedup).toBeUndefined();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('falla si dedup no es booleano', async () => {
|
|
85
|
+
const dto = plainToInstance(EmailSetTrustedRequest, {
|
|
86
|
+
...valid,
|
|
87
|
+
dedup: 'si' as unknown as boolean,
|
|
88
|
+
});
|
|
89
|
+
const errors = await validate(dto);
|
|
90
|
+
expect(errors.some(e => e.property === 'dedup')).toBe(true);
|
|
91
|
+
});
|
|
69
92
|
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { ClassConstructor, plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validate } from 'class-validator';
|
|
4
|
+
import {
|
|
5
|
+
AssignmentStatusEnum,
|
|
6
|
+
AutoAssignRequest,
|
|
7
|
+
ListCollectorAssignmentsRequest,
|
|
8
|
+
ReassignRequest,
|
|
9
|
+
RedistributeRequest,
|
|
10
|
+
} from '../../../src/loanCollectorAssignment/index';
|
|
11
|
+
|
|
12
|
+
const errorProperties = async <T extends object>(dtoClass: ClassConstructor<T>, body: object): Promise<string[]> =>
|
|
13
|
+
(await validate(plainToInstance(dtoClass, body), { whitelist: true })).map((error) => error.property).sort();
|
|
14
|
+
|
|
15
|
+
const ULID = '01KXPB422AFGZHQKHVM7E5YDBR';
|
|
16
|
+
|
|
17
|
+
describe('Motivos de reasignación y redistribución (L-171)', () => {
|
|
18
|
+
it.each([
|
|
19
|
+
['ReassignRequest', ReassignRequest],
|
|
20
|
+
['RedistributeRequest', RedistributeRequest],
|
|
21
|
+
] as const)('%s exige un motivo no en blanco de hasta 500', async (_name, dtoClass) => {
|
|
22
|
+
expect(await errorProperties(dtoClass, { reason: 'Cobrador dado de baja' })).toEqual([]);
|
|
23
|
+
expect(await errorProperties(dtoClass, { reason: ' ' })).toEqual(['reason']);
|
|
24
|
+
expect(await errorProperties(dtoClass, { reason: '' })).toEqual(['reason']);
|
|
25
|
+
expect(await errorProperties(dtoClass, { reason: 'x'.repeat(501) })).toEqual(['reason']);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('AutoAssignRequest — ids con forma de id (L-171)', () => {
|
|
30
|
+
it('acepta los ULID de retail-org y loan-credit', async () => {
|
|
31
|
+
expect(await errorProperties(AutoAssignRequest, { retailerId: ULID, creditId: ULID, storeId: 'VL-001' })).toEqual([]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('rechaza ids con separadores de llave o de más de 64', async () => {
|
|
35
|
+
const body = { retailerId: 'RET#1', creditId: 'a b', storeId: 'x'.repeat(65) };
|
|
36
|
+
expect(await errorProperties(AutoAssignRequest, body)).toEqual(['creditId', 'retailerId', 'storeId']);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('ListCollectorAssignmentsRequest — filtros validados y paginación (L-161, L-170)', () => {
|
|
41
|
+
it('acepta la consulta sin filtros y con todos', async () => {
|
|
42
|
+
expect(await errorProperties(ListCollectorAssignmentsRequest, {})).toEqual([]);
|
|
43
|
+
const query = { status: 'ACTIVE', storeId: ULID, collectorUserId: ULID, limit: '50', nextToken: 'eyJwayI6IkEifQ' };
|
|
44
|
+
const dto = plainToInstance(ListCollectorAssignmentsRequest, query);
|
|
45
|
+
expect(await validate(dto, { whitelist: true })).toEqual([]);
|
|
46
|
+
expect(dto.limit).toBe(50);
|
|
47
|
+
expect(dto.status).toBe(AssignmentStatusEnum.ACTIVE);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('rechaza un estado fuera del enum y filtros crudos', async () => {
|
|
51
|
+
expect(await errorProperties(ListCollectorAssignmentsRequest, { status: 'activo' })).toEqual(['status']);
|
|
52
|
+
expect(await errorProperties(ListCollectorAssignmentsRequest, { storeId: 'x'.repeat(65), collectorUserId: 'a,b' })).toEqual([
|
|
53
|
+
'collectorUserId',
|
|
54
|
+
'storeId',
|
|
55
|
+
]);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('limit de 1 a 100 y cursor no vacío de hasta 1024', async () => {
|
|
59
|
+
expect(await errorProperties(ListCollectorAssignmentsRequest, { limit: '0' })).toEqual(['limit']);
|
|
60
|
+
expect(await errorProperties(ListCollectorAssignmentsRequest, { limit: '101' })).toEqual(['limit']);
|
|
61
|
+
expect(await errorProperties(ListCollectorAssignmentsRequest, { limit: 'diez' })).toEqual(['limit']);
|
|
62
|
+
expect(await errorProperties(ListCollectorAssignmentsRequest, { nextToken: '' })).toEqual(['nextToken']);
|
|
63
|
+
expect(await errorProperties(ListCollectorAssignmentsRequest, { nextToken: 'x'.repeat(1025) })).toEqual(['nextToken']);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { ClassConstructor, plainToInstance } from 'class-transformer';
|
|
3
|
+
import { ValidationError, validate } from 'class-validator';
|
|
4
|
+
import {
|
|
5
|
+
BULK_GET_PARAMETERS_MAX_ITEMS,
|
|
6
|
+
BULK_UPDATE_PARAMETERS_MAX_ITEMS,
|
|
7
|
+
BulkGetParametersRequest,
|
|
8
|
+
BulkUpdateParametersRequest,
|
|
9
|
+
UpdateParameterRequest,
|
|
10
|
+
} from '../../../src/loanConfig/index';
|
|
11
|
+
|
|
12
|
+
const errorPaths = async <T extends object>(dtoClass: ClassConstructor<T>, body: object): Promise<string[]> => {
|
|
13
|
+
const pathsOf = (errors: ValidationError[], parent: string): string[] =>
|
|
14
|
+
errors.flatMap((error) => {
|
|
15
|
+
const path = parent ? `${parent}.${error.property}` : error.property;
|
|
16
|
+
return [...(error.constraints ? [path] : []), ...pathsOf(error.children ?? [], path)];
|
|
17
|
+
});
|
|
18
|
+
return pathsOf(await validate(plainToInstance(dtoClass, body), { whitelist: true }), '').sort();
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
describe('UpdateParameterRequest — value presente y reason obligatorio (L-139, L-140)', () => {
|
|
22
|
+
it('acepta un valor con motivo, y null para dejar el parámetro pendiente', async () => {
|
|
23
|
+
expect(await errorPaths(UpdateParameterRequest, { value: '30', reason: 'Ajuste de mora' })).toEqual([]);
|
|
24
|
+
expect(await errorPaths(UpdateParameterRequest, { value: null, reason: 'Queda pendiente' })).toEqual([]);
|
|
25
|
+
expect(await errorPaths(UpdateParameterRequest, { value: '', reason: 'Vacía el texto' })).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('rechaza omitir value: antes borraba el valor y respondía 200', async () => {
|
|
29
|
+
expect(await errorPaths(UpdateParameterRequest, { reason: 'Sin valor' })).toEqual(['value']);
|
|
30
|
+
expect(await errorPaths(UpdateParameterRequest, { value: 30, reason: 'Número crudo' })).toEqual(['value']);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('rechaza reason ausente, en blanco o de más de 500', async () => {
|
|
34
|
+
expect(await errorPaths(UpdateParameterRequest, { value: '30' })).toEqual(['reason']);
|
|
35
|
+
expect(await errorPaths(UpdateParameterRequest, { value: '30', reason: '' })).toEqual(['reason']);
|
|
36
|
+
expect(await errorPaths(UpdateParameterRequest, { value: '30', reason: ' ' })).toEqual(['reason']);
|
|
37
|
+
expect(await errorPaths(UpdateParameterRequest, { value: '30', reason: 'x'.repeat(501) })).toEqual(['reason']);
|
|
38
|
+
expect(await errorPaths(UpdateParameterRequest, { value: '30', reason: 'x'.repeat(500) })).toEqual([]);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('Lotes de parámetros (L-146, L-148, L-155)', () => {
|
|
43
|
+
const item = { paramType: 'CREDIT', key: 'grace_days', value: '3', reason: 'Ajuste' };
|
|
44
|
+
const key = { paramType: 'CREDIT', key: 'grace_days' };
|
|
45
|
+
|
|
46
|
+
it('bulk-update hasta 50, con las mismas reglas por ítem', async () => {
|
|
47
|
+
const at = Array.from({ length: BULK_UPDATE_PARAMETERS_MAX_ITEMS }, () => item);
|
|
48
|
+
expect(await errorPaths(BulkUpdateParametersRequest, { parameters: at })).toEqual([]);
|
|
49
|
+
expect(await errorPaths(BulkUpdateParametersRequest, { parameters: [...at, item] })).toEqual(['parameters']);
|
|
50
|
+
expect(await errorPaths(BulkUpdateParametersRequest, { parameters: [{ ...item, value: undefined, reason: ' ' }] })).toEqual([
|
|
51
|
+
'parameters.0.reason',
|
|
52
|
+
'parameters.0.value',
|
|
53
|
+
]);
|
|
54
|
+
expect(await errorPaths(BulkUpdateParametersRequest, { parameters: [{ ...item, value: null }] })).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('bulk-get hasta 100 claves', async () => {
|
|
58
|
+
const at = Array.from({ length: BULK_GET_PARAMETERS_MAX_ITEMS }, () => key);
|
|
59
|
+
expect(await errorPaths(BulkGetParametersRequest, { items: at })).toEqual([]);
|
|
60
|
+
expect(await errorPaths(BulkGetParametersRequest, { items: [...at, key] })).toEqual(['items']);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { ClassConstructor, plainToInstance } from 'class-transformer';
|
|
3
|
+
import { ValidationError, validate } from 'class-validator';
|
|
4
|
+
import {
|
|
5
|
+
ActivationCheckRequest,
|
|
6
|
+
BackofficeApproveRestructureRequest,
|
|
7
|
+
BackofficeProposeRestructureRequest,
|
|
8
|
+
BackofficeRejectRestructureRequest,
|
|
9
|
+
BackofficeWriteOffCreditRequest,
|
|
10
|
+
CreateCustomerPaymentRequest,
|
|
11
|
+
CreateSellerPaymentRequest,
|
|
12
|
+
LOAN_AMOUNT_MAX_CENTS,
|
|
13
|
+
LoanDeviceNotifyItem,
|
|
14
|
+
OriginateLoanCreditRequest,
|
|
15
|
+
SegmentFactsBatchRequest,
|
|
16
|
+
UpsertLoanBorrowerRequest,
|
|
17
|
+
} from '../../../../src/loanCredit/index';
|
|
18
|
+
|
|
19
|
+
/** Rutas que fallan, con los anidados como `beneficiary.phone`; valida como el motor (whitelist). */
|
|
20
|
+
const errorPaths = async <T extends object>(dtoClass: ClassConstructor<T>, body: object): Promise<string[]> => {
|
|
21
|
+
const pathsOf = (errors: ValidationError[], parent: string): string[] =>
|
|
22
|
+
errors.flatMap((error) => {
|
|
23
|
+
const path = parent ? `${parent}.${error.property}` : error.property;
|
|
24
|
+
return [...(error.constraints ? [path] : []), ...pathsOf(error.children ?? [], path)];
|
|
25
|
+
});
|
|
26
|
+
const dto = plainToInstance(dtoClass, body);
|
|
27
|
+
return pathsOf(await validate(dto, { whitelist: true, forbidNonWhitelisted: false }), '').sort();
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const CREDIT_ID = '01KXPB422AFGZHQKHVM7E5YDBR';
|
|
31
|
+
const ULID = '01KXPB422AFGZHQKHVM7E5YDBS';
|
|
32
|
+
|
|
33
|
+
describe('LoanDeviceNotifyItem — cada ítem trae un aviso (L-023)', () => {
|
|
34
|
+
const base = { creditId: CREDIT_ID, intensity: 'BANNER' };
|
|
35
|
+
|
|
36
|
+
it('acepta noticeType, title + content o notificationCode', async () => {
|
|
37
|
+
expect(await errorPaths(LoanDeviceNotifyItem, { ...base, noticeType: 'DUE_TODAY' })).toEqual([]);
|
|
38
|
+
expect(await errorPaths(LoanDeviceNotifyItem, { ...base, title: 'Pago', content: 'Tu pago vence hoy' })).toEqual([]);
|
|
39
|
+
expect(await errorPaths(LoanDeviceNotifyItem, { ...base, notificationCode: 'DC-001' })).toEqual([]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('rechaza el ítem sin aviso, o con título sin cuerpo', async () => {
|
|
43
|
+
expect(await errorPaths(LoanDeviceNotifyItem, base)).toEqual(['noticeType']);
|
|
44
|
+
expect(await errorPaths(LoanDeviceNotifyItem, { ...base, title: 'Pago' })).toEqual(['noticeType']);
|
|
45
|
+
expect(await errorPaths(LoanDeviceNotifyItem, { ...base, content: 'Tu pago vence hoy' })).toEqual(['noticeType']);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('sigue rechazando un noticeType fuera del enum aunque traiga texto legacy', async () => {
|
|
49
|
+
const body = { ...base, noticeType: 'OTRO', title: 'Pago', content: 'Tu pago vence hoy' };
|
|
50
|
+
expect(await errorPaths(LoanDeviceNotifyItem, body)).toEqual(['noticeType']);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('el mensaje dice qué falta', async () => {
|
|
54
|
+
const [error] = await validate(plainToInstance(LoanDeviceNotifyItem, base));
|
|
55
|
+
expect(Object.values(error.constraints ?? {})).toEqual([
|
|
56
|
+
'El aviso debe traer noticeType, o title y content, o notificationCode',
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('Reestructura y castigo (L-025, L-026)', () => {
|
|
62
|
+
const terms = { newTermWeeks: 52, newTnaAnnual: 2.8 };
|
|
63
|
+
|
|
64
|
+
it('el plazo nuevo va de 1 a 104 semanas; la TNA de la reestructura no tiene techo de plan', async () => {
|
|
65
|
+
const body = (newTermWeeks: number) => ({ justification: 'Atraso por desempleo', terms: { ...terms, newTermWeeks } });
|
|
66
|
+
expect(await errorPaths(BackofficeProposeRestructureRequest, body(104))).toEqual([]);
|
|
67
|
+
expect(await errorPaths(BackofficeProposeRestructureRequest, body(105))).toEqual(['terms.newTermWeeks']);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('justificación y motivos hasta 500 caracteres', async () => {
|
|
71
|
+
const ok = 'x'.repeat(500);
|
|
72
|
+
const long = 'x'.repeat(501);
|
|
73
|
+
expect(await errorPaths(BackofficeProposeRestructureRequest, { justification: ok, terms })).toEqual([]);
|
|
74
|
+
expect(await errorPaths(BackofficeProposeRestructureRequest, { justification: long, terms })).toEqual(['justification']);
|
|
75
|
+
expect(await errorPaths(BackofficeApproveRestructureRequest, { reason: long })).toEqual(['reason']);
|
|
76
|
+
expect(await errorPaths(BackofficeRejectRestructureRequest, { reason: long })).toEqual(['reason']);
|
|
77
|
+
expect(await errorPaths(BackofficeWriteOffCreditRequest, { reason: long })).toEqual(['reason']);
|
|
78
|
+
expect(await errorPaths(BackofficeWriteOffCreditRequest, {})).toEqual([]);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('OriginateLoanCreditRequest — techo de montos y SKU (L-027)', () => {
|
|
83
|
+
const base = {
|
|
84
|
+
wizardSessionId: 'WS-1',
|
|
85
|
+
borrowerId: 'B-1',
|
|
86
|
+
planId: 'PLAN-1',
|
|
87
|
+
termWeeks: 12,
|
|
88
|
+
equipmentPriceCents: 500000,
|
|
89
|
+
downPaymentCents: 100000,
|
|
90
|
+
storeId: 'ST-1',
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
it('acepta montos hasta el techo técnico y lo rechaza por encima', async () => {
|
|
94
|
+
const atCeiling = { ...base, equipmentPriceCents: LOAN_AMOUNT_MAX_CENTS, downPaymentCents: LOAN_AMOUNT_MAX_CENTS };
|
|
95
|
+
expect(await errorPaths(OriginateLoanCreditRequest, atCeiling)).toEqual([]);
|
|
96
|
+
const over = { ...base, equipmentPriceCents: LOAN_AMOUNT_MAX_CENTS + 1, downPaymentCents: Number.MAX_SAFE_INTEGER };
|
|
97
|
+
expect(await errorPaths(OriginateLoanCreditRequest, over)).toEqual(['downPaymentCents', 'equipmentPriceCents']);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('el SKU sigue el patrón del catálogo', async () => {
|
|
101
|
+
expect(await errorPaths(OriginateLoanCreditRequest, { ...base, productSku: 'SKU-MOTO G54 128' })).toEqual([]);
|
|
102
|
+
for (const productSku of ['', 'SKU_MOTO', 'SKU/1', ' SKU', 'A'.repeat(65)]) {
|
|
103
|
+
expect(await errorPaths(OriginateLoanCreditRequest, { ...base, productSku })).toEqual(['productSku']);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('ActivationCheckRequest — SKU e IMEI con formato (L-029)', () => {
|
|
109
|
+
it('acepta IMEI de 15 dígitos y SKU del catálogo', async () => {
|
|
110
|
+
expect(await errorPaths(ActivationCheckRequest, { imei: '351756051523999', productSku: 'SKU-MOTO-G54-128' })).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('rechaza IMEI con separadores o de otro largo, y SKU vacío o con otros caracteres', async () => {
|
|
114
|
+
for (const imei of ['35175605152399', '3517560515239990', '351756-05152399', '']) {
|
|
115
|
+
expect(await errorPaths(ActivationCheckRequest, { imei })).toEqual(['imei']);
|
|
116
|
+
}
|
|
117
|
+
for (const productSku of ['', 'SKU.1', 'A'.repeat(65)]) {
|
|
118
|
+
expect(await errorPaths(ActivationCheckRequest, { productSku })).toEqual(['productSku']);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('UpsertLoanBorrowerRequest y beneficiario (L-031)', () => {
|
|
124
|
+
it('acepta un ULID de retail-customer con beneficiario completo', async () => {
|
|
125
|
+
const body = { retailCustomerId: ULID, beneficiary: { fullName: 'Ana López', relationship: 'Hermana', phone: '5512345678' } };
|
|
126
|
+
expect(await errorPaths(UpsertLoanBorrowerRequest, body)).toEqual([]);
|
|
127
|
+
expect(await errorPaths(UpsertLoanBorrowerRequest, { ...body, beneficiary: { ...body.beneficiary, phone: '+525512345678' } })).toEqual([]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('rechaza un retailCustomerId con símbolos o de más de 64', async () => {
|
|
131
|
+
for (const retailCustomerId of ['RC 1', 'RC#1', 'a,b', 'A'.repeat(65), '']) {
|
|
132
|
+
expect(await errorPaths(UpsertLoanBorrowerRequest, { retailCustomerId })).toEqual(['retailCustomerId']);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('acota el beneficiario: nombre 300, parentesco 120 y teléfono MX', async () => {
|
|
137
|
+
const beneficiary = { fullName: 'x'.repeat(301), relationship: 'x'.repeat(121), phone: '55 1234 5678' };
|
|
138
|
+
expect(await errorPaths(UpsertLoanBorrowerRequest, { retailCustomerId: ULID, beneficiary })).toEqual([
|
|
139
|
+
'beneficiary.fullName',
|
|
140
|
+
'beneficiary.phone',
|
|
141
|
+
'beneficiary.relationship',
|
|
142
|
+
]);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('SegmentFactsBatchRequest — ids con forma de id (L-067)', () => {
|
|
147
|
+
it('acepta ULID y rechaza ids con espacios o símbolos', async () => {
|
|
148
|
+
expect(await errorPaths(SegmentFactsBatchRequest, { retailCustomerIds: [ULID, 'rc_legacy-1'] })).toEqual([]);
|
|
149
|
+
expect(await errorPaths(SegmentFactsBatchRequest, { retailCustomerIds: [ULID, 'rc 1'] })).toEqual(['retailCustomerIds']);
|
|
150
|
+
expect(await errorPaths(SegmentFactsBatchRequest, { retailCustomerIds: ['x'.repeat(65)] })).toEqual(['retailCustomerIds']);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('confirmDuplicate en los pagos (L-057, L-092)', () => {
|
|
155
|
+
const seller = { type: 'NEXT_INSTALLMENT', expectedAmountCents: 48_700, method: 'CASH', idempotencyKey: '7b0e3a52-6a0f-4f1f-9f43-6b4d5c1e2a10' };
|
|
156
|
+
const customer = { type: 'NEXT_INSTALLMENT', expectedAmountCents: 48_700, idempotencyKey: '7b0e3a52-6a0f-4f1f-9f43-6b4d5c1e2a10' };
|
|
157
|
+
|
|
158
|
+
it('es opcional y booleano, y sobrevive al whitelist', async () => {
|
|
159
|
+
expect(await errorPaths(CreateSellerPaymentRequest, seller)).toEqual([]);
|
|
160
|
+
expect(await errorPaths(CreateCustomerPaymentRequest, customer)).toEqual([]);
|
|
161
|
+
expect(await errorPaths(CreateSellerPaymentRequest, { ...seller, confirmDuplicate: 'si' })).toEqual(['confirmDuplicate']);
|
|
162
|
+
expect(await errorPaths(CreateCustomerPaymentRequest, { ...customer, confirmDuplicate: 1 })).toEqual(['confirmDuplicate']);
|
|
163
|
+
|
|
164
|
+
const dto = plainToInstance(CreateCustomerPaymentRequest, { ...customer, confirmDuplicate: true });
|
|
165
|
+
await validate(dto, { whitelist: true });
|
|
166
|
+
expect(dto.confirmDuplicate).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import { plainToInstance } from 'class-transformer';
|
|
3
3
|
import { ValidationError, validate } from 'class-validator';
|
|
4
|
-
import { SignLoanCreditRequest } from '../../../src/loanCredit/index';
|
|
4
|
+
import { isContractBirthDate, SignLoanCreditRequest } from '../../../src/loanCredit/index';
|
|
5
5
|
|
|
6
6
|
/** Rutas de las propiedades que fallan, con los anidados como `contractDetails.borrower.fullName`. */
|
|
7
7
|
const pathsOf = (errors: ValidationError[], prefix = ''): string[] =>
|
|
@@ -71,11 +71,16 @@ describe('SignLoanCreditRequest', () => {
|
|
|
71
71
|
expect(await errorsOf({ contractDetails: DETAILS })).toEqual([]);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
it('acepta la CLABE tal como la tiene la cuenta, aunque no sean 18 dígitos', async () => {
|
|
74
|
+
it('acepta la CLABE tal como la tiene la cuenta, aunque no sean 18 dígitos: D9 la aplica el dominio', async () => {
|
|
75
75
|
const details = { ...DETAILS, pcfAccount: { clabe: 'dummy' } };
|
|
76
76
|
expect(await errorsOf({ contractDetails: details })).toEqual([]);
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
+
it('rechaza una CLABE de más de 120 caracteres', async () => {
|
|
80
|
+
const details = { ...DETAILS, pcfAccount: { clabe: '6'.repeat(121) } };
|
|
81
|
+
expect(await errorsOf({ contractDetails: details })).toEqual(['contractDetails.pcfAccount.clabe']);
|
|
82
|
+
});
|
|
83
|
+
|
|
79
84
|
it('rechaza una CLABE vacía', async () => {
|
|
80
85
|
const details = { ...DETAILS, pcfAccount: { clabe: '' } };
|
|
81
86
|
expect(await errorsOf({ contractDetails: details })).toEqual(['contractDetails.pcfAccount.clabe']);
|
|
@@ -123,3 +128,57 @@ describe('SignLoanCreditRequest.signatureContext', () => {
|
|
|
123
128
|
expect(await errorsOf({ signatureContext: { ...CONTEXT, termsAccepted: 'si' } })).toEqual(['signatureContext.termsAccepted']);
|
|
124
129
|
});
|
|
125
130
|
});
|
|
131
|
+
|
|
132
|
+
describe('SignLoanCreditRequest — reglas de la auditoría (L-030, L-069)', () => {
|
|
133
|
+
const withBorrower = (borrower: Record<string, unknown>): Record<string, unknown> => ({
|
|
134
|
+
contractDetails: { ...DETAILS, borrower: { ...DETAILS.borrower, ...borrower } },
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('acepta un CURP sin el formato oficial (lo revisa el dominio) y rechaza uno de más de 120', async () => {
|
|
138
|
+
for (const curp of ['LOHM900215MMCPRR0', 'lohm900215mmcprr09', 'X'.repeat(120)]) {
|
|
139
|
+
expect(await errorsOf(withBorrower({ curp }))).toEqual([]);
|
|
140
|
+
}
|
|
141
|
+
expect(await errorsOf(withBorrower({ curp: 'X'.repeat(121) }))).toEqual(['contractDetails.borrower.curp']);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('acepta correo válido y rechaza uno sin dominio o de más de 254', async () => {
|
|
145
|
+
expect(await errorsOf(withBorrower({ email: 'maria@example.com' }))).toEqual([]);
|
|
146
|
+
expect(await errorsOf(withBorrower({ email: 'maria@' }))).toEqual(['contractDetails.borrower.email']);
|
|
147
|
+
expect(await errorsOf(withBorrower({ email: `${'a'.repeat(250)}@b.mx` }))).toEqual(['contractDetails.borrower.email']);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('la fecha de nacimiento solo tiene tope de largo: acepta lo que manda el wizard tal cual', async () => {
|
|
151
|
+
for (const birthDate of ['15/02/1990', '1990-02-15', '31/02/1990']) {
|
|
152
|
+
expect(await errorsOf(withBorrower({ birthDate }))).toEqual([]);
|
|
153
|
+
}
|
|
154
|
+
expect(await errorsOf(withBorrower({ birthDate: '1'.repeat(121) }))).toEqual(['contractDetails.borrower.birthDate']);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('isContractBirthDate queda para el dominio: DD/MM/AAAA o AAAA-MM-DD que existan', () => {
|
|
158
|
+
for (const value of ['15/02/1990', '1990-02-15', '29/02/2000']) {
|
|
159
|
+
expect(isContractBirthDate(value)).toBe(true);
|
|
160
|
+
}
|
|
161
|
+
for (const value of ['31/02/1990', '29/02/1990', '1990-13-01', '15 de febrero de 1990', '1990-02-15T00:00:00Z', '', 19900215]) {
|
|
162
|
+
expect(isContractBirthDate(value)).toBe(false);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('el RFC solo tiene tope de largo (el formato lo revisa el dominio)', async () => {
|
|
167
|
+
expect(await errorsOf(withBorrower({ rfc: 'lohm900215' }))).toEqual([]);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('la IP solo tiene tope de largo: acepta el "desconocida" de respaldo del wizard', async () => {
|
|
171
|
+
for (const ip of ['201.141.10.20', '2806:2f0:9020::1', 'desconocida']) {
|
|
172
|
+
expect(await errorsOf({ signatureContext: { ...CONTEXT, ip } })).toEqual([]);
|
|
173
|
+
}
|
|
174
|
+
expect(await errorsOf({ signatureContext: { ...CONTEXT, ip: '1'.repeat(65) } })).toEqual(['signatureContext.ip']);
|
|
175
|
+
expect(await errorsOf({ signatureContext: { ...CONTEXT, ip: '' } })).toEqual(['signatureContext.ip']);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('el destino del OTP va en E.164', async () => {
|
|
179
|
+
expect(await errorsOf({ otpDestination: '+525512345678' })).toEqual([]);
|
|
180
|
+
for (const otpDestination of ['5512345678', '+52 55 1234 5678', '+0525512345678', '+52551234567890123']) {
|
|
181
|
+
expect(await errorsOf({ otpDestination })).toEqual(['otpDestination']);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|