@mojaloop/central-ledger 19.14.0 → 20.1.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/.grype.yaml +3 -0
- package/.license-scanner.json +16 -0
- package/.ncurc.yaml +19 -13
- package/.nvmrc +1 -1
- package/.nycrc.yml +14 -2
- package/CHANGELOG.md +51 -0
- package/Dockerfile +13 -10
- package/README.md +39 -89
- package/audit-ci.jsonc +1 -1
- package/config/default.json +60 -3
- package/config/deprecated.default-settlement.json +160 -0
- package/docker/ml-api-adapter/default.json +28 -0
- package/docker-compose.yml +6 -0
- package/nodemon.json +6 -0
- package/package.json +128 -64
- package/{sbom-v19.13.2.csv → sbom-v20.0.0.csv} +886 -736
- package/scripts/_wait4_all.js +5 -5
- package/scripts/transferSettlementTemp/interchangeFeeCalculation.js +51 -0
- package/src/api/index.ts +30 -0
- package/src/api/interface/swagger.json +2249 -2054
- package/src/api/participants/routes.js +1 -1
- package/src/api/root/handler.int.ts +55 -0
- package/src/api/routes.js +7 -6
- package/src/api/server.ts +27 -0
- package/src/api/server.unit.ts +16 -0
- package/src/api/settlementModels/routes.js +4 -4
- package/src/domain/participant/index.d.ts +47 -0
- package/src/domain/participant/index.int.ts +724 -0
- package/src/domain/participant/index.js +4 -5
- package/src/domain/position/fx-prepare.js +1 -1
- package/src/domain/position/fx-timeout-reserved.js +6 -0
- package/src/domain/position/prepare.js +1 -1
- package/src/handlers/admin/handler.js +1 -1
- package/src/handlers/index.js +30 -0
- package/src/handlers/positions/handler.js +0 -2
- package/src/handlers/positions/handlerBatch.js +5 -5
- package/src/handlers/register.js +44 -29
- package/src/handlers/timeouts/handler.js +1 -4
- package/src/handlers/transfers/fx-abort.int.ts +209 -0
- package/src/handlers/transfers/fx-handlers.int.ts +275 -0
- package/src/handlers/transfers/fx-timeout.int.ts +284 -0
- package/src/handlers/transfers/handler.js +1 -3
- package/src/handlers/transfers/handlers.int.ts +538 -0
- package/src/handlers/transfers/prepare.int.ts +1283 -0
- package/src/handlers/transfers/prepare.js +2 -2
- package/src/knexfile.js +19 -0
- package/src/lib/cache.js +1 -1
- package/src/lib/config/index.ts +60 -0
- package/src/lib/config/resolver.ts +231 -0
- package/src/lib/config/resolver.unit.ts +72 -0
- package/src/lib/config/types.ts +372 -0
- package/src/lib/config/util.ts +523 -0
- package/src/lib/config/util.unit.ts +825 -0
- package/src/lib/migrator.js +3 -3
- package/src/lib/proxyCache.js +3 -3
- package/{migrations → src/migrations}/100100_event.js +1 -1
- package/{migrations → src/migrations}/110100_contactType.js +1 -1
- package/{migrations → src/migrations}/110200_currency.js +1 -1
- package/{migrations → src/migrations}/110201_currency-scale.js +1 -1
- package/{migrations → src/migrations}/110300_endpointType.js +1 -1
- package/{migrations → src/migrations}/110400_ledgerEntryType.js +1 -1
- package/{migrations → src/migrations}/110450_ledgerAccountType.js +1 -1
- package/{migrations → src/migrations}/110500_participantLimitType.js +1 -1
- package/{migrations → src/migrations}/110600_transferParticipantRoleType.js +1 -1
- package/{migrations → src/migrations}/110700_transferState.js +1 -1
- package/{migrations → src/migrations}/110900_settlementState.js +1 -1
- package/{migrations → src/migrations}/112000_bulkTransferState.js +1 -1
- package/{migrations → src/migrations}/112100_bulkProcessingState.js +1 -1
- package/{migrations → src/migrations}/200100_participant.js +1 -1
- package/{migrations → src/migrations}/200200_participantContact.js +1 -1
- package/{migrations → src/migrations}/200300_participantEndpoint.js +1 -1
- package/{migrations → src/migrations}/200400_participantParty.js +1 -1
- package/{migrations → src/migrations}/200600_token.js +1 -1
- package/{migrations → src/migrations}/200601_token-indexes.js +1 -1
- package/{migrations → src/migrations}/300100_transferDuplicateCheck.js +2 -2
- package/{migrations → src/migrations}/300150_bulkTransferDuplicateCheck.js +2 -2
- package/{migrations → src/migrations}/300200_transfer.js +2 -2
- package/{migrations → src/migrations}/300202_transfer-decimal184.js +1 -1
- package/{migrations → src/migrations}/300250_bulkTransfer.js +2 -3
- package/{migrations → src/migrations}/300275_bulkTransferAssociation.js +2 -3
- package/{migrations → src/migrations}/300300_ilpPacket.js +1 -1
- package/{migrations → src/migrations}/300400_transferStateChange.js +1 -1
- package/{migrations → src/migrations}/300450_bulkTransferStateChange.js +1 -1
- package/{migrations → src/migrations}/300500_segment.js +1 -1
- package/{migrations → src/migrations}/310100_participantCurrency.js +1 -1
- package/{migrations → src/migrations}/310200_transferParticipant.js +1 -1
- package/{migrations → src/migrations}/310202_transferParticipant-decimal184.js +1 -1
- package/{migrations → src/migrations}/310204_transferParticipant-participantId.js +2 -2
- package/{migrations → src/migrations}/310300_participantPosition.js +1 -1
- package/{migrations → src/migrations}/310302_participantPosition-decimal184.js +1 -1
- package/{migrations → src/migrations}/310400_participantPositionChange.js +1 -1
- package/{migrations → src/migrations}/310402_participantPositionChange-decimal184.js +1 -1
- package/{migrations → src/migrations}/310403_participantPositionChange-participantCurrencyId.js +2 -2
- package/{migrations → src/migrations}/310404_participantPositionChange-change.js +2 -2
- package/{migrations → src/migrations}/310500_participantLimit.js +1 -1
- package/{migrations → src/migrations}/310502_participantLimit-decimal184.js +1 -1
- package/{migrations → src/migrations}/400100_settlementWindow.js +1 -1
- package/{migrations → src/migrations}/400200_settlement.js +1 -1
- package/{migrations → src/migrations}/400300_settlementSettlementWindow.js +1 -1
- package/{migrations → src/migrations}/400301_settlementSettlementWindow-indexes.js +0 -1
- package/{migrations → src/migrations}/400400_settlementWindowStateChange.js +1 -1
- package/{migrations → src/migrations}/400500_settlementTransferParticipant.js +1 -1
- package/{migrations → src/migrations}/400502_settlementTransferParticipant-decimal184.js +1 -1
- package/{migrations → src/migrations}/400600_settlementParticipantCurrency.js +1 -1
- package/{migrations → src/migrations}/400602_settlementParticipantCurrency-decimal184.js +1 -1
- package/{migrations → src/migrations}/400700_settlementParticipantCurrencyStateChange.js +1 -1
- package/{migrations → src/migrations}/400800_settlementStateChange.js +1 -1
- package/{migrations → src/migrations}/400900_settlementWindowContent.js +1 -1
- package/{migrations → src/migrations}/401000_settlementWindowContentStateChange.js +1 -1
- package/{migrations → src/migrations}/401001_settlementWindowContentStateChange-indexes.js +6 -6
- package/{migrations → src/migrations}/401200_settlementContentAggregation.js +1 -1
- package/{migrations → src/migrations}/401300_settlementModel.js +1 -1
- package/{migrations → src/migrations}/410100_transferFulfilmentDuplicateCheck.js +1 -1
- package/{migrations → src/migrations}/410102_transferFulfilmentDuplicateCheck-dropForeignTransferId.js +1 -1
- package/{migrations → src/migrations}/410103_transferFulfilmentDuplicateCheck-hashNullable.js +1 -1
- package/{migrations → src/migrations}/410200_transferFulfilment.js +1 -1
- package/{migrations → src/migrations}/410202_transferFulfilment-ilpFulfilment.js +1 -1
- package/{migrations → src/migrations}/410203_transferFulfilment-foreignTransferFulfilmentId.js +1 -1
- package/{migrations → src/migrations}/410240_bulkTransferFulfilmentDuplicateCheck.js +1 -1
- package/{migrations → src/migrations}/410250_bulkTransferFulfilment.js +1 -1
- package/{migrations → src/migrations}/410300_transferExtension.js +1 -1
- package/{migrations → src/migrations}/410302_transferExtension-transferErrorId.js +1 -1
- package/{migrations → src/migrations}/410350_bulkTransferExtension.js +1 -1
- package/{migrations → src/migrations}/410400_transferTimeout.js +1 -1
- package/{migrations → src/migrations}/410450_transferErrorDuplicateCheck.js +1 -1
- package/{migrations → src/migrations}/410500_transferError.js +1 -1
- package/{migrations → src/migrations}/410502_transferError-foreignTransferErrorDuplicateCheckId.js +1 -1
- package/{migrations → src/migrations}/410550_bulkTransferError.js +1 -1
- package/{migrations → src/migrations}/410600_expiringTransfer.js +1 -1
- package/{migrations → src/migrations}/500401_quote-hotfix-2719.js +8 -8
- package/{migrations → src/migrations}/500501_feature-fixSubIdRef.js +12 -12
- package/{migrations → src/migrations}/500601_party-2480.js +3 -3
- package/{migrations → src/migrations}/501001_quoteExtension-quote-service-174.js +1 -2
- package/{migrations → src/migrations}/501002_quoteExtension-2522.js +3 -3
- package/{migrations → src/migrations}/600010_fxTransferType.js +1 -1
- package/{migrations → src/migrations}/600012_fxParticipantCurrencyType.js +1 -1
- package/{migrations → src/migrations}/600100_fxTransferDuplicateCheck.js +2 -2
- package/{migrations → src/migrations}/600110_fxTransferErrorDuplicateCheck.js.js +1 -1
- package/{migrations → src/migrations}/600200_fxTransfer.js +2 -2
- package/{migrations → src/migrations}/600400_fxTransferStateChange.js +1 -1
- package/{migrations → src/migrations}/600501_fxWatchList.js +2 -2
- package/{migrations → src/migrations}/600600_fxTransferFulfilmentDuplicateCheck.js +1 -1
- package/{migrations → src/migrations}/600700_fxTransferFulfilment.js +1 -1
- package/{migrations → src/migrations}/600800_fxTransferExtension.js +1 -1
- package/{migrations → src/migrations}/601400_fxTransferTimeout.js +1 -1
- package/{migrations → src/migrations}/601500_fxTransferError.js +1 -1
- package/{migrations → src/migrations}/610200_fxTransferParticipant.js +1 -1
- package/{migrations → src/migrations}/610202_fxTransferParticipant-participantId.js +2 -2
- package/{migrations → src/migrations}/610403_participantPositionChange-fxTransfer.js +1 -1
- package/{migrations → src/migrations}/800100_quotePartyView.js +1 -1
- package/{migrations → src/migrations}/800101_feature-fixSubIdRef.js +1 -1
- package/{migrations → src/migrations}/800200_quoteView.js +1 -1
- package/{migrations → src/migrations}/800300_quoteResponseView.js +1 -1
- package/{migrations → src/migrations}/900100_foreign-keys.js +18 -18
- package/{migrations → src/migrations}/900101_foreign-keys.js +6 -6
- package/{migrations → src/migrations}/910100_feature904DDL.js +7 -7
- package/{migrations → src/migrations}/910101_feature904DataMigration.js +1 -1
- package/{migrations → src/migrations}/910102_feature949DataMigration.js +1 -1
- package/{migrations → src/migrations}/910103_feature1093SettlementByCurrency.js +3 -3
- package/{migrations → src/migrations}/940101_quotePartyIdInfoExtension-2522.js +25 -25
- package/{migrations → src/migrations}/950101_transferParticipantStateChange.js +2 -2
- package/{migrations → src/migrations}/950102_settlementModel-adjustPosition.js +2 -2
- package/{migrations → src/migrations}/950103_dropTransferParticipantStateChange.js +3 -3
- package/{migrations → src/migrations}/950104_settlementModel-settlementAccountTypeId.js +3 -3
- package/src/migrations/950107_settlementWindowContent_foreign_settlementModel.js +63 -0
- package/{migrations → src/migrations}/950108_participantProxy.js +1 -2
- package/{migrations → src/migrations}/950120_fxQuoteConversionTerms_sourceAmount_nullable.js +1 -1
- package/{migrations → src/migrations}/960100_create_externalParticipant.js +2 -2
- package/{migrations → src/migrations}/960110_alter_transferParticipant__addFiled_externalParticipantId.js +2 -2
- package/{migrations → src/migrations}/961001_transferForwarded.js +1 -1
- package/{migrations → src/migrations}/961002_fxTransferForwarded.js +1 -1
- package/src/models/participant/externalParticipant.int.ts +85 -0
- package/src/models/participant/facade.js +4 -2
- package/src/models/participant/participantCurrencyCached.js +1 -1
- package/src/models/position/facade.js +8 -2
- package/src/models/transfer/facade.int.ts +139 -0
- package/src/models/transfer/facade.js +8 -3
- package/src/models/transfer/transferDuplicateCheck.js +2 -2
- package/src/models/transfer/transferError.int.ts +112 -0
- package/src/models/transfer/transferExtension.int.ts +137 -0
- package/src/models/transfer/transferStateChange.int.ts +117 -0
- package/{seeds → src/seeds}/participant.js +1 -1
- package/{seeds → src/seeds}/settlementInterchange.js +1 -0
- package/src/settlement/api/handlerRoutes.js +71 -0
- package/src/settlement/api/handlers/health.js +10 -0
- package/src/settlement/api/handlers/index.js +57 -0
- package/src/settlement/api/handlers/settlementWindows/{id}.js +103 -0
- package/src/settlement/api/handlers/settlementWindows.js +77 -0
- package/src/settlement/api/handlers/settlements/{id}.js +133 -0
- package/src/settlement/api/handlers/settlements/{sid}/participants/{pid}/accounts/{aid}.js +127 -0
- package/src/settlement/api/handlers/settlements/{sid}/participants/{pid}.js +127 -0
- package/src/settlement/api/handlers/settlements.js +114 -0
- package/src/settlement/api/openapiRouting.js +87 -0
- package/src/settlement/api/routes.js +176 -0
- package/src/settlement/domain/rules/index.js +46 -0
- package/src/settlement/domain/settlement/index.js +405 -0
- package/src/settlement/domain/settlementWindow/index.js +117 -0
- package/src/settlement/domain/transactions/index.js +83 -0
- package/src/settlement/domain/transferSettlement/index.js +57 -0
- package/src/settlement/handlers/deferredSettlement/handler.js +180 -0
- package/src/settlement/handlers/grossSettlement/handler.js +180 -0
- package/src/settlement/handlers/index.js +101 -0
- package/src/settlement/handlers/register.js +92 -0
- package/src/settlement/handlers/rules/handler.js +164 -0
- package/src/settlement/interface/swagger-handler.json +34 -0
- package/src/settlement/interface/swagger.json +1560 -0
- package/src/settlement/lib/db.js +32 -0
- package/src/settlement/lib/kafka/index.js +41 -0
- package/src/settlement/lib/kafka/producer.js +142 -0
- package/src/settlement/lib/scriptEngine.js +99 -0
- package/src/settlement/lib/scriptsLoader.js +175 -0
- package/src/settlement/lib/utility.js +243 -0
- package/src/settlement/models/ilpPackets/ilpPacket.js +42 -0
- package/src/settlement/models/lib/enums.js +192 -0
- package/src/settlement/models/misc/migrationLock.js +53 -0
- package/src/settlement/models/settlement/facade.js +1652 -0
- package/src/settlement/models/settlement/index.js +55 -0
- package/src/settlement/models/settlement/participantCurrency.js +48 -0
- package/src/settlement/models/settlement/settlement.js +49 -0
- package/src/settlement/models/settlement/settlementModel.js +44 -0
- package/src/settlement/models/settlement/settlementParticipantCurrency.js +61 -0
- package/src/settlement/models/settlement/settlementStateChange.js +44 -0
- package/src/settlement/models/settlement/settlementTransferParticipant.js +46 -0
- package/src/settlement/models/settlementWindow/facade.js +345 -0
- package/src/settlement/models/settlementWindow/index.js +47 -0
- package/src/settlement/models/settlementWindow/settlementWindowStateChange.js +55 -0
- package/src/settlement/models/settlementWindowContent/facade.js +79 -0
- package/src/settlement/models/settlementWindowContent/index.js +40 -0
- package/src/settlement/models/settlementWindowContent/settlementWindowContentStateChange.js +53 -0
- package/src/settlement/models/transferSettlement/facade.js +363 -0
- package/src/settlement/models/transferSettlement/index.js +42 -0
- package/src/settlement/shared/logger/index.js +7 -0
- package/src/settlement/shared/plugins.js +88 -0
- package/src/settlement/shared/setup.js +224 -0
- package/src/settlement/utils/truthyProperty.js +37 -0
- package/src/shared/legacyUtilShim.js +20 -0
- package/src/shared/plugins.js +3 -3
- package/src/shared/results.ts +22 -0
- package/src/shared/results.unit.ts +38 -0
- package/src/shared/setup.js +14 -1
- package/src/testing/api-helpers.ts +1222 -0
- package/src/testing/exec-async.ts +67 -0
- package/src/testing/harness.snapshot.sql +3895 -0
- package/src/testing/harness.test.ts +15 -0
- package/src/testing/harness.ts +1160 -0
- package/src/testing/kafka.ts +221 -0
- package/src/testing/prng.ts +82 -0
- package/src/testing/rdkafka.test.ts +263 -0
- package/src/testing/run.ts +559 -0
- package/src/testing/snapshot.test.ts +92 -0
- package/src/testing/snapshot.ts +549 -0
- package/src/testing/tap-stream.ts +164 -0
- package/src/testing/types.ts +95 -0
- package/src/testing/util.ts +333 -0
- package/src/types/central-services-error-handling.d.ts +72 -0
- package/src/types/central-services-stream.d.ts +148 -0
- package/test-integration.Dockerfile +0 -2
- package/test.Dockerfile +0 -2
- package/tsconfig.json +40 -0
- package/config/knexfile.js +0 -19
- package/migrations/950107_settlementWindowContent_foreign_settlementModel.js +0 -63
- package/src/api/index.js +0 -16
- package/src/lib/config.js +0 -81
- /package/{migrations → src/migrations}/110101_contactType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110301_endpointType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110401_ledgerEntryType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110451_ledgerAccountType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110501_participantLimitType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110601_transferParticipantRoleType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110800_settlementWindowState.js +0 -0
- /package/{migrations → src/migrations}/111000_amountType.js +0 -0
- /package/{migrations → src/migrations}/111001_amountType-indexes.js +0 -0
- /package/{migrations → src/migrations}/111100_balanceOfPayments.js +0 -0
- /package/{migrations → src/migrations}/111101_balanceOfPayments-indexes.js +0 -0
- /package/{migrations → src/migrations}/111200_partyIdentifierType.js +0 -0
- /package/{migrations → src/migrations}/111201_partyIdentifierType-indexes.js +0 -0
- /package/{migrations → src/migrations}/111300_partyType.js +0 -0
- /package/{migrations → src/migrations}/111301_partyType-indexes.js +0 -0
- /package/{migrations → src/migrations}/111400_quoteDuplicateCheck.js +0 -0
- /package/{migrations → src/migrations}/111500_transactionInitiator.js +0 -0
- /package/{migrations → src/migrations}/111501_transactionInitiator-indexes.js +0 -0
- /package/{migrations → src/migrations}/111600_transactionInitiatorType.js +0 -0
- /package/{migrations → src/migrations}/111601_transactionInitiatorType-indexes.js +0 -0
- /package/{migrations → src/migrations}/111700_settlementDelay.js +0 -0
- /package/{migrations → src/migrations}/111701_settlementDelay-indexes.js +0 -0
- /package/{migrations → src/migrations}/111800_settlementInterchange.js +0 -0
- /package/{migrations → src/migrations}/111801_settlementInterchange-indexes.js +0 -0
- /package/{migrations → src/migrations}/111900_settlementGranularity.js +0 -0
- /package/{migrations → src/migrations}/111901_settlementGranularity-indexes.js +0 -0
- /package/{migrations → src/migrations}/112101_bulkProcessingState-indexes.js +0 -0
- /package/{migrations → src/migrations}/200101_participant-indexes.js +0 -0
- /package/{migrations → src/migrations}/200201_participantContact-indexes.js +0 -0
- /package/{migrations → src/migrations}/200301_participantEndpoint-indexes.js +0 -0
- /package/{migrations → src/migrations}/200401_participantParty-indexes.js +0 -0
- /package/{migrations → src/migrations}/300201_transfer-indexes.js +0 -0
- /package/{migrations → src/migrations}/300251_bulkTransfer-indexes.js +0 -0
- /package/{migrations → src/migrations}/300276_bulkTransferAssociation-indexes.js +0 -0
- /package/{migrations → src/migrations}/300401_transferStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/300451_bulkTransferStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/300501_segment-indexes.js +0 -0
- /package/{migrations → src/migrations}/310101_participantCurrency-indexes.js +0 -0
- /package/{migrations → src/migrations}/310201_transferParticipant-indexes.js +0 -0
- /package/{migrations → src/migrations}/310203_transferParticipant-indexes.js +0 -0
- /package/{migrations → src/migrations}/310301_participantPosition-indexes.js +0 -0
- /package/{migrations → src/migrations}/310401_participantPositionChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/310405_participantPositionChange-positionChange.js +0 -0
- /package/{migrations → src/migrations}/310406_participantPositionChange-positionChange-revert.js +0 -0
- /package/{migrations → src/migrations}/310501_participantLimit-indexes.js +0 -0
- /package/{migrations → src/migrations}/310503_participantLimit-indexes-composite.js +0 -0
- /package/{migrations → src/migrations}/400401_settlmentWindowStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/400501_settlementTransferParticipant-indexes.js +0 -0
- /package/{migrations → src/migrations}/400601_settlementParticipantCurrency-indexes.js +0 -0
- /package/{migrations → src/migrations}/400701_settlementParticipantCurrencyStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/400801_settlementStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/401002_settlementWindowContent-indexes.js +0 -0
- /package/{migrations → src/migrations}/401201_settlementContentAggregation-indexes.js +0 -0
- /package/{migrations → src/migrations}/401301_settlementModel-indexes.js +0 -0
- /package/{migrations → src/migrations}/410101_transferFulfilmentDuplicateCheck-indexes.js +0 -0
- /package/{migrations → src/migrations}/410201_transferFulfilment-indexes.js +0 -0
- /package/{migrations → src/migrations}/410301_transferExtension-indexes.js +0 -0
- /package/{migrations → src/migrations}/410351_bulkTransferExtension-indexes.js +0 -0
- /package/{migrations → src/migrations}/410401_transferTimeout-indexes.js +0 -0
- /package/{migrations → src/migrations}/410501_transferError-indexes.js +0 -0
- /package/{migrations → src/migrations}/410551_bulkTransferError-indexes.js +0 -0
- /package/{migrations → src/migrations}/410601_expiringTransfer-indexes.js +0 -0
- /package/{migrations → src/migrations}/500100_transactionReference.js +0 -0
- /package/{migrations → src/migrations}/500101_transactionReference-indexes.js +0 -0
- /package/{migrations → src/migrations}/500200_transactionScenario.js +0 -0
- /package/{migrations → src/migrations}/500201_transactionScenario-indexes.js +0 -0
- /package/{migrations → src/migrations}/500300_transactionSubScenario.js +0 -0
- /package/{migrations → src/migrations}/500400_quote.js +0 -0
- /package/{migrations → src/migrations}/500500_quoteParty.js +0 -0
- /package/{migrations → src/migrations}/500600_party.js +0 -0
- /package/{migrations → src/migrations}/500700_quoteResponse.js +0 -0
- /package/{migrations → src/migrations}/500800_quoteResponseIlpPacket.js +0 -0
- /package/{migrations → src/migrations}/500900_geoCode.js +0 -0
- /package/{migrations → src/migrations}/501000_quoteExtension.js +0 -0
- /package/{migrations → src/migrations}/501100_quoteResponseDuplicateCheck.js +0 -0
- /package/{migrations → src/migrations}/501200_quoteError.js +0 -0
- /package/{migrations → src/migrations}/501300_transferRules.js +0 -0
- /package/{migrations → src/migrations}/600011_fxTransferType-indexes.js +0 -0
- /package/{migrations → src/migrations}/600013_fxParticipantCurrencyType-indexes.js +0 -0
- /package/{migrations → src/migrations}/600201_fxTransfer-indexes.js +0 -0
- /package/{migrations → src/migrations}/600401_fxTransferStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/600502_fxWatchList-indexes.js +0 -0
- /package/{migrations → src/migrations}/600601_fxTransferFulfilmentDuplicateCheck-indexes.js +0 -0
- /package/{migrations → src/migrations}/600701_fxTransferFulfilment-indexes.js +0 -0
- /package/{migrations → src/migrations}/601401_fxTransferTimeout-indexes.js +0 -0
- /package/{migrations → src/migrations}/601501_fxTransferError-indexes.js +0 -0
- /package/{migrations → src/migrations}/610201_fxTransferParticipant-indexes.js +0 -0
- /package/{migrations → src/migrations}/910102_hotfix954.js +0 -0
- /package/{migrations → src/migrations}/940100_quotePartyIdInfoExtension.js +0 -0
- /package/{migrations → src/migrations}/950100_settlementContentAggregation-decimal184.js +0 -0
- /package/{migrations → src/migrations}/950106_unique_settlement_model_ledger_account_currency.js +0 -0
- /package/{migrations → src/migrations}/950109_fxQuote.js +0 -0
- /package/{migrations → src/migrations}/950110_fxQuoteResponse.js +0 -0
- /package/{migrations → src/migrations}/950111_fxQuoteError.js +0 -0
- /package/{migrations → src/migrations}/950113_fxQuoteDuplicateCheck.js +0 -0
- /package/{migrations → src/migrations}/950114_fxQuoteResponseDuplicateCheck.js +0 -0
- /package/{migrations → src/migrations}/950115_fxQuoteConversionTerms.js +0 -0
- /package/{migrations → src/migrations}/950116_fxQuoteConversionTermsExtension.js +0 -0
- /package/{migrations → src/migrations}/950117_fxQuoteResponseConversionTerms.js +0 -0
- /package/{migrations → src/migrations}/950118_fxQuoteResponseConversionTermsExtension.js +0 -0
- /package/{migrations → src/migrations}/950119_fxCharge.js +0 -0
- /package/{migrations → src/migrations}/960111_alter_fxTransferParticipant__addFiled_externalParticipantId.js +0 -0
- /package/{migrations → src/migrations}/960112_fxTransfer-indexes2.js +0 -0
- /package/{migrations → src/migrations}/960113_fxTransferExtension-indexes.js +0 -0
- /package/{migrations → src/migrations}/960114_fxTransferTimeout-indexes2.js +0 -0
- /package/{migrations → src/migrations}/961000_participantEndpoint_unique.js +0 -0
- /package/{migrations → src/migrations}/migration.template +0 -0
- /package/{seeds → src/seeds}/amountType.js +0 -0
- /package/{seeds → src/seeds}/balanceOfPayments.js +0 -0
- /package/{seeds → src/seeds}/bulkProcessingState.js +0 -0
- /package/{seeds → src/seeds}/bulkTransferState.js +0 -0
- /package/{seeds → src/seeds}/currency.js +0 -0
- /package/{seeds → src/seeds}/endpointType.js +0 -0
- /package/{seeds → src/seeds}/fxParticipantCurrencyType.js +0 -0
- /package/{seeds → src/seeds}/fxTransferType.js +0 -0
- /package/{seeds → src/seeds}/ledgerAccountType.js +0 -0
- /package/{seeds → src/seeds}/ledgerEntryType.js +0 -0
- /package/{seeds → src/seeds}/participantLimitType.js +0 -0
- /package/{seeds → src/seeds}/partyIdentifierType.js +0 -0
- /package/{seeds → src/seeds}/partyType.js +0 -0
- /package/{seeds → src/seeds}/settlementDelay.js +0 -0
- /package/{seeds → src/seeds}/settlementGranularity.js +0 -0
- /package/{seeds → src/seeds}/settlementState.js +0 -0
- /package/{seeds → src/seeds}/settlementWindow1State.js +0 -0
- /package/{seeds → src/seeds}/settlementWindow2Open.js +0 -0
- /package/{seeds → src/seeds}/transactionInitiator.js +0 -0
- /package/{seeds → src/seeds}/transactionInitiatorType.js +0 -0
- /package/{seeds → src/seeds}/transactionScenario.js +0 -0
- /package/{seeds → src/seeds}/transferParticipantRoleType.js +0 -0
- /package/{seeds → src/seeds}/transferState.js +0 -0
- /package/{seeds → src/seeds}/z1000_settlementModel-deprecated.js- +0 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
import assert from "node:assert"
|
|
2
|
+
import fs from "node:fs"
|
|
3
|
+
|
|
4
|
+
const BG_YELLOW = '\x1b[43m'
|
|
5
|
+
const BLACK = '\x1b[30m'
|
|
6
|
+
const RESET = '\x1b[0m'
|
|
7
|
+
const BG_RED = '\x1b[41m'
|
|
8
|
+
|
|
9
|
+
export enum SnapshotResultType {
|
|
10
|
+
MATCH = 'MATCH',
|
|
11
|
+
MISMATCH = 'MISMATCH',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SnapshotMatch<T> {
|
|
15
|
+
type: SnapshotResultType.MATCH
|
|
16
|
+
actual: T
|
|
17
|
+
snapshot: T
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SnapshotMismatch<T> {
|
|
21
|
+
type: SnapshotResultType.MISMATCH
|
|
22
|
+
actual: T
|
|
23
|
+
snapshot: T
|
|
24
|
+
diff: string
|
|
25
|
+
/**
|
|
26
|
+
* The updated string to use for updating the inline snapshots.
|
|
27
|
+
*/
|
|
28
|
+
update: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SnapshotRange {
|
|
32
|
+
start: number,
|
|
33
|
+
end: number,
|
|
34
|
+
indent: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type SnapshotResult<T> = SnapshotMatch<T> | SnapshotMismatch<T>
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @class Snapshot
|
|
41
|
+
* @description A simple inline snapshot testing library, inspired by
|
|
42
|
+
* https://tigerbeetle.com/blog/2024-05-14-snapshot-testing-for-the-masses/
|
|
43
|
+
*
|
|
44
|
+
* Motivation:
|
|
45
|
+
* 1. The built in nodejs snapshot features store the files separately, which makes tests harder to
|
|
46
|
+
* read and the snapshots more brittle. It also doesn't support features such as ":ignore", which
|
|
47
|
+
* we have implemented here.
|
|
48
|
+
*
|
|
49
|
+
* 2. Jest-based property matchers are very tricky to write, and are _almost_ snapshots but harder
|
|
50
|
+
* to update and reason about. Sometimes we just want to _see_ exactly what the response from
|
|
51
|
+
* some API was.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
*
|
|
55
|
+
* import { describe, it } from "node:test";
|
|
56
|
+
* import { getCallerLocation, Snapshot } from "./snapshot";
|
|
57
|
+
*
|
|
58
|
+
* describe('snapshot smoke test', () => {
|
|
59
|
+
* it('updates the snapshot', () => {
|
|
60
|
+
* const result = {
|
|
61
|
+
* a: true,
|
|
62
|
+
* b: {
|
|
63
|
+
* icecream: 'chocolate'
|
|
64
|
+
* },
|
|
65
|
+
* c: new Date()
|
|
66
|
+
* }
|
|
67
|
+
*
|
|
68
|
+
* Snapshot.from(getCallerLocation(), `{
|
|
69
|
+
* "a": true,
|
|
70
|
+
* "b": {
|
|
71
|
+
* "icecream": "vanilla"
|
|
72
|
+
* },
|
|
73
|
+
* "c": ":ignore"
|
|
74
|
+
* }`).checkStringUnwrap(JSON.stringify(result, null, 2))
|
|
75
|
+
* })
|
|
76
|
+
* })
|
|
77
|
+
*
|
|
78
|
+
* This test ^^ will fail, as the line with "icecream" doesn't match.
|
|
79
|
+
* You can rerun the tests with `SNAP_UPDATE=1` and this library will try and auto-update the
|
|
80
|
+
* snapshot.
|
|
81
|
+
*/
|
|
82
|
+
export class Snapshot {
|
|
83
|
+
sourceLocation: CallerLocation
|
|
84
|
+
text: any
|
|
85
|
+
updateable: boolean
|
|
86
|
+
|
|
87
|
+
private constructor(sourceLocation: CallerLocation, text: string, updateable: boolean) {
|
|
88
|
+
this.sourceLocation = sourceLocation
|
|
89
|
+
this.text = text
|
|
90
|
+
this.updateable = updateable
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public static from(
|
|
94
|
+
text: string,
|
|
95
|
+
options: {
|
|
96
|
+
stripWhitespace: boolean, // Whether or not we should display the update hint. Default: true.
|
|
97
|
+
updateable: boolean // Whether or not we should display the update hint. Default: true.
|
|
98
|
+
} = {
|
|
99
|
+
stripWhitespace: true,
|
|
100
|
+
updateable: true,
|
|
101
|
+
}) {
|
|
102
|
+
const sourceLocation = getCallerLocation(2)
|
|
103
|
+
if (options.stripWhitespace) {
|
|
104
|
+
text = this.stripLeadingWhitespace(text)
|
|
105
|
+
}
|
|
106
|
+
return new Snapshot(sourceLocation, text, options.updateable)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @description Format the text to remove indents, so we can write prettier inline snapshots:
|
|
111
|
+
*
|
|
112
|
+
* E.g. if we are indented 2 layers deep, this:
|
|
113
|
+
* const snap = Snapshot.from(getCallerLocation(), `{
|
|
114
|
+
* "a": true,
|
|
115
|
+
* }
|
|
116
|
+
* `)
|
|
117
|
+
*
|
|
118
|
+
* is much nicer to write than this:
|
|
119
|
+
* const snap = Snapshot.from(getCallerLocation(), `{
|
|
120
|
+
* "a": true,
|
|
121
|
+
* }
|
|
122
|
+
* `)
|
|
123
|
+
*
|
|
124
|
+
* @param text
|
|
125
|
+
*/
|
|
126
|
+
private static stripLeadingWhitespace(text: string): string {
|
|
127
|
+
const lines = text.split('\n')
|
|
128
|
+
// Check the last line for indented whitespace, otherwise the penultimate line.
|
|
129
|
+
let lastLine = lines.pop()
|
|
130
|
+
assert(lastLine !== undefined, 'stripLeadingWhitespace(), text was empty.')
|
|
131
|
+
if (lastLine.length === 0) {
|
|
132
|
+
lastLine = lines.pop()
|
|
133
|
+
}
|
|
134
|
+
assert(lastLine)
|
|
135
|
+
|
|
136
|
+
const indent = lastLine.match(/^\s+/)
|
|
137
|
+
if (!indent) {
|
|
138
|
+
return text
|
|
139
|
+
}
|
|
140
|
+
assert(indent[0].length % 2 === 0, 'Malformatted string. Expected indent to be a multiple of 2.')
|
|
141
|
+
const leadingSpaceRe = new RegExp(String.raw`^\s{${indent[0].length}}`, "gm");
|
|
142
|
+
return text.replaceAll(leadingSpaceRe, '')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public checkString(have: string): SnapshotResult<string> {
|
|
146
|
+
return checkSnapshotString(have, this.text)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Check a `have` of any against the snapshot string.
|
|
151
|
+
*/
|
|
152
|
+
public checkUnwrap(have: any): void {
|
|
153
|
+
const haveString = JSON.stringify(have, null, 2);
|
|
154
|
+
return this.checkStringUnwrap(haveString)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public checkStringUnwrap(have: string): void {
|
|
158
|
+
const result = checkSnapshotString(have, this.text)
|
|
159
|
+
|
|
160
|
+
if (result.type === SnapshotResultType.MATCH) {
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log(`Snapshot mismatch:\n${result.diff}`)
|
|
165
|
+
|
|
166
|
+
if (!this.updateable) {
|
|
167
|
+
throw new Error(`Snapshot mismatch.`)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (process.env.SNAP_UPDATE === undefined) {
|
|
171
|
+
throw new Error(`Snapshot mismatch. Rerun with SNAP_UPDATE=1 to update the snapshot.`)
|
|
172
|
+
}
|
|
173
|
+
const updated = this._replaceSnapshot('`' + result.update + '`')
|
|
174
|
+
fs.writeFileSync(this.sourceLocation.file, updated)
|
|
175
|
+
throw new Error(`Snapshot updated.`)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Read the current file, replace the old snapshot with the new snapshot.
|
|
180
|
+
*/
|
|
181
|
+
public _replaceSnapshot(update: string): string {
|
|
182
|
+
assert(update[0] === '`', 'Replacement should start with a tick.')
|
|
183
|
+
assert(update.endsWith('`'), 'Replacement should end with a tick.')
|
|
184
|
+
const contents = fs.readFileSync(this.sourceLocation.file).toString('utf-8')
|
|
185
|
+
const range = Snapshot._snapshotRange(contents, this.sourceLocation.line)
|
|
186
|
+
return Snapshot._replaceSnapshot(contents, range, update)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Replace the old snapshot in the file with a new snapshot.
|
|
191
|
+
*/
|
|
192
|
+
public static _replaceSnapshot(contents: string, range: SnapshotRange, update: string): string {
|
|
193
|
+
let newFile = contents.slice(0, range.start - 1)
|
|
194
|
+
const updateLines = update.split('\n')
|
|
195
|
+
updateLines.forEach((line, idx) => {
|
|
196
|
+
// Don't indent the first line
|
|
197
|
+
if (idx === 0) {
|
|
198
|
+
newFile += line + '\n'
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
// Skip newline on last line.
|
|
202
|
+
if (idx === updateLines.length - 1) {
|
|
203
|
+
newFile += ' '.repeat(range.indent) + line
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
newFile += ' '.repeat(range.indent) + line + '\n'
|
|
207
|
+
})
|
|
208
|
+
newFile += contents.slice(range.end)
|
|
209
|
+
|
|
210
|
+
return newFile;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Find the snapshot string from ``, inclusive of start and end ticks to be updated.
|
|
215
|
+
*/
|
|
216
|
+
public static _snapshotRange(contents: string, lineNum: number, ): SnapshotRange {
|
|
217
|
+
assert(lineNum > 0)
|
|
218
|
+
|
|
219
|
+
const lines = contents.split('\n')
|
|
220
|
+
const lineIdx = lineNum - 1
|
|
221
|
+
let start = 0
|
|
222
|
+
let range = 0
|
|
223
|
+
let indent = 0
|
|
224
|
+
lines.forEach((line, idx) => {
|
|
225
|
+
if (idx < lineIdx) {
|
|
226
|
+
// Don't forget newlines!
|
|
227
|
+
start += line.length + 1
|
|
228
|
+
}
|
|
229
|
+
if (idx === lineIdx) {
|
|
230
|
+
const openingTickIdx = line.indexOf('`')
|
|
231
|
+
if (openingTickIdx === -1) {
|
|
232
|
+
throw new Error(`Couldn't find opening '\`' in snapshot string.`)
|
|
233
|
+
}
|
|
234
|
+
start += openingTickIdx + 1
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
const tail = contents.slice(start)
|
|
239
|
+
const tailLines = tail.split('\n')
|
|
240
|
+
let foundEnd: boolean = false
|
|
241
|
+
let lastLine = ''
|
|
242
|
+
tailLines.forEach((line, idx) => {
|
|
243
|
+
if (foundEnd) {
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const closingTickIdx = line.indexOf('\`')
|
|
248
|
+
if (closingTickIdx === -1) {
|
|
249
|
+
range += line.length + 1
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
range += closingTickIdx + 1
|
|
253
|
+
lastLine = line
|
|
254
|
+
foundEnd = true
|
|
255
|
+
})
|
|
256
|
+
if (!foundEnd) {
|
|
257
|
+
throw new Error(`Snapshot end not found. Searched string:\n${tail}`)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// We can find the indent based on the last line before the `.
|
|
261
|
+
// If there are spaces before `
|
|
262
|
+
const indentMatch = lastLine.match(/^\s*/)
|
|
263
|
+
if (indentMatch) {
|
|
264
|
+
indent = indentMatch[0].length
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
start,
|
|
269
|
+
end: start + range,
|
|
270
|
+
indent
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function unwrapSnapshot<T>(result: SnapshotResult<T>): void {
|
|
276
|
+
if (result.type === SnapshotResultType.MATCH) {
|
|
277
|
+
return
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
console.log(`snapshot mismatch:\n${result.diff}`)
|
|
281
|
+
|
|
282
|
+
if (process.env.SNAP_UPDATE === undefined) {
|
|
283
|
+
throw new Error(`Snapshot mismatch. Rerun with SNAP_UPDATE=1 to update the snapshot.`)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
throw new Error(`Snapshot updated.`)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
enum SpecialToken {
|
|
290
|
+
IGNORE = 'IGNORE',
|
|
291
|
+
MATCH_STRING = 'MATCH_STRING',
|
|
292
|
+
MATCH_INTEGER = 'MATCH_INTEGER',
|
|
293
|
+
MATCH_DATE = 'MATCH_DATE',
|
|
294
|
+
MATCH_BIGINT = 'MATCH_BIGINT',
|
|
295
|
+
NONE = 'NONE',
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Search the line for special tokens.
|
|
300
|
+
*/
|
|
301
|
+
const matchSpecialToken = (line: string): {
|
|
302
|
+
token: SpecialToken,
|
|
303
|
+
index: number,
|
|
304
|
+
literal: string
|
|
305
|
+
} => {
|
|
306
|
+
|
|
307
|
+
const matchers: Array<[string, SpecialToken]> = [
|
|
308
|
+
['\":ignore', SpecialToken.IGNORE],
|
|
309
|
+
['\':ignore', SpecialToken.IGNORE],
|
|
310
|
+
[':ignore', SpecialToken.IGNORE],
|
|
311
|
+
['\":string', SpecialToken.MATCH_STRING],
|
|
312
|
+
['\':string', SpecialToken.MATCH_STRING],
|
|
313
|
+
[':string', SpecialToken.MATCH_STRING],
|
|
314
|
+
['":integer', SpecialToken.MATCH_INTEGER],
|
|
315
|
+
['\':integer', SpecialToken.MATCH_INTEGER],
|
|
316
|
+
[':integer', SpecialToken.MATCH_INTEGER],
|
|
317
|
+
['":int', SpecialToken.MATCH_INTEGER],
|
|
318
|
+
['\':int', SpecialToken.MATCH_INTEGER],
|
|
319
|
+
[':int', SpecialToken.MATCH_INTEGER],
|
|
320
|
+
['":date', SpecialToken.MATCH_DATE],
|
|
321
|
+
['\':date', SpecialToken.MATCH_DATE],
|
|
322
|
+
[':date', SpecialToken.MATCH_DATE],
|
|
323
|
+
[':bigint', SpecialToken.MATCH_BIGINT],
|
|
324
|
+
]
|
|
325
|
+
|
|
326
|
+
for (const matcher of matchers) {
|
|
327
|
+
let index = line.indexOf(matcher[0])
|
|
328
|
+
if (index > -1) {
|
|
329
|
+
return {
|
|
330
|
+
token: matcher[1],
|
|
331
|
+
index,
|
|
332
|
+
literal: line.slice(index)
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// No match.
|
|
338
|
+
return {
|
|
339
|
+
token: SpecialToken.NONE,
|
|
340
|
+
index: 0,
|
|
341
|
+
literal: ''
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function checkSnapshotString(actual: string, snapshot: string): SnapshotResult<string> {
|
|
346
|
+
assert(actual)
|
|
347
|
+
assert(typeof actual === 'string')
|
|
348
|
+
assert(snapshot)
|
|
349
|
+
assert(typeof snapshot === 'string')
|
|
350
|
+
|
|
351
|
+
const actualLines = actual.split('\n')
|
|
352
|
+
const snapshotLines = snapshot.split('\n')
|
|
353
|
+
const maxLines = Math.max(actualLines.length, snapshotLines.length)
|
|
354
|
+
const mismatchedLines: Array<number> = []
|
|
355
|
+
// Start with a reasonable number.
|
|
356
|
+
let maxColumnLengthLeft = 45
|
|
357
|
+
|
|
358
|
+
const specialTokens: Array<{ token: SpecialToken, line: number, col: number, literal: string }> = []
|
|
359
|
+
|
|
360
|
+
for (let lineIdx = 0; lineIdx < maxLines; lineIdx++) {
|
|
361
|
+
const left = actualLines[lineIdx] || ''
|
|
362
|
+
const right = snapshotLines[lineIdx] || ''
|
|
363
|
+
if (left.length > maxColumnLengthLeft) {
|
|
364
|
+
maxColumnLengthLeft = left.length
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if ((!left && right) || (left && !right)) {
|
|
368
|
+
mismatchedLines.push(lineIdx)
|
|
369
|
+
continue
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const specialToken = matchSpecialToken(right)
|
|
373
|
+
specialTokens.push({ token: specialToken.token, line: lineIdx, col: specialToken.index, literal: specialToken.literal })
|
|
374
|
+
switch (specialToken.token) {
|
|
375
|
+
/**
|
|
376
|
+
* We ignore the rest of the line after the index of the `:ignore`
|
|
377
|
+
*/
|
|
378
|
+
case SpecialToken.IGNORE: {
|
|
379
|
+
// if we found a skip token, then only match the line up to the token
|
|
380
|
+
if (left.length < specialToken.index) {
|
|
381
|
+
mismatchedLines.push(lineIdx)
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const leftTruncated = left.substring(0, specialToken.index)
|
|
386
|
+
const rightTruncated = right.substring(0, specialToken.index)
|
|
387
|
+
assert(leftTruncated.length === rightTruncated.length)
|
|
388
|
+
if (leftTruncated !== rightTruncated) {
|
|
389
|
+
mismatchedLines.push(lineIdx)
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
// We expect the left side to be a string
|
|
395
|
+
case SpecialToken.MATCH_STRING: {
|
|
396
|
+
if (left.length < specialToken.index) {
|
|
397
|
+
mismatchedLines.push(lineIdx)
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Ensure the left side is a string.
|
|
402
|
+
const leftCandidate = left.substring(specialToken.index)
|
|
403
|
+
.replace(',', '') // Workaround for trailing commas
|
|
404
|
+
if (!leftCandidate || leftCandidate.length === 0) {
|
|
405
|
+
mismatchedLines.push(lineIdx)
|
|
406
|
+
continue
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Parse and see what it might be.
|
|
410
|
+
try {
|
|
411
|
+
const leftParsed = JSON.parse(leftCandidate)
|
|
412
|
+
if (typeof leftParsed !== 'string') {
|
|
413
|
+
mismatchedLines.push(lineIdx)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
} catch (err) {
|
|
417
|
+
mismatchedLines.push(lineIdx)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// We expect the left side to be an integer.
|
|
424
|
+
case SpecialToken.MATCH_INTEGER: {
|
|
425
|
+
if (left.length < specialToken.index) {
|
|
426
|
+
mismatchedLines.push(lineIdx)
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const leftCandidate = left.substring(specialToken.index)
|
|
431
|
+
.replace(',', '') // Strip off trailing commas, a little hacky but it works!
|
|
432
|
+
if (!leftCandidate || leftCandidate.length === 0) {
|
|
433
|
+
mismatchedLines.push(lineIdx)
|
|
434
|
+
continue
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Parse and see what it might be.
|
|
438
|
+
try {
|
|
439
|
+
const leftParsed = JSON.parse(leftCandidate)
|
|
440
|
+
if (typeof leftParsed !== 'number') {
|
|
441
|
+
mismatchedLines.push(lineIdx)
|
|
442
|
+
}
|
|
443
|
+
} catch (err) {
|
|
444
|
+
mismatchedLines.push(lineIdx)
|
|
445
|
+
}
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
case SpecialToken.NONE: {
|
|
449
|
+
if (left !== right) {
|
|
450
|
+
mismatchedLines.push(lineIdx)
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
default: {
|
|
456
|
+
throw new Error(`${specialToken.token} not yet implemented!`)
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (mismatchedLines.length === 0) {
|
|
462
|
+
return {
|
|
463
|
+
type: SnapshotResultType.MATCH,
|
|
464
|
+
actual: actual,
|
|
465
|
+
snapshot: snapshot
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
let diff = `${RESET}\n`
|
|
470
|
+
maxColumnLengthLeft = Math.min(85, maxColumnLengthLeft)
|
|
471
|
+
diff += `${'Actual:'.padEnd(maxColumnLengthLeft)} | Snapshot:\n`
|
|
472
|
+
for (let index = 0; index < maxLines; index++) {
|
|
473
|
+
let left = actualLines[index] || ''
|
|
474
|
+
let right = snapshotLines[index] || ''
|
|
475
|
+
// Truncate very long strings. Doesn't affect the matching.
|
|
476
|
+
if (left.length > maxColumnLengthLeft) {
|
|
477
|
+
left = left.slice(0, maxColumnLengthLeft - 20) + '...(truncated)'
|
|
478
|
+
}
|
|
479
|
+
if (right.length > maxColumnLengthLeft) {
|
|
480
|
+
right = right.slice(0, maxColumnLengthLeft - 20) + '...(truncated)'
|
|
481
|
+
}
|
|
482
|
+
if (mismatchedLines.indexOf(index) < 0) {
|
|
483
|
+
diff += `${left.padEnd(maxColumnLengthLeft)} | ${right}\n`
|
|
484
|
+
continue
|
|
485
|
+
}
|
|
486
|
+
diff += `${BG_YELLOW}${left.padEnd(maxColumnLengthLeft)} | ${right}${RESET}\n`
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return {
|
|
490
|
+
type: SnapshotResultType.MISMATCH,
|
|
491
|
+
actual: actual,
|
|
492
|
+
snapshot: snapshot,
|
|
493
|
+
diff,
|
|
494
|
+
update: updateStringForActualAndTokens(actual, specialTokens)
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* @description Take the actual string, and interpolate the special tokens.
|
|
500
|
+
*/
|
|
501
|
+
function updateStringForActualAndTokens(
|
|
502
|
+
input: string,
|
|
503
|
+
tokens: Array<{ token: SpecialToken, line: number, col: number, literal: string }>
|
|
504
|
+
): string {
|
|
505
|
+
let out = ""
|
|
506
|
+
const lines = input.split('\n')
|
|
507
|
+
if (lines.length !== tokens.length) {
|
|
508
|
+
console.warn(`Couldn't update snapshot, as the number of lines don't match.`)
|
|
509
|
+
return input
|
|
510
|
+
}
|
|
511
|
+
lines.forEach((line, idx) => {
|
|
512
|
+
const token = tokens[idx]
|
|
513
|
+
if (token.token === SpecialToken.NONE) {
|
|
514
|
+
out += line + (idx !== lines.length -1 ? '\n' : '')
|
|
515
|
+
return
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const mergedLine = line.slice(0, token.col) + token.literal
|
|
519
|
+
out += mergedLine + (idx !== lines.length -1 ? '\n' : '')
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
return out
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
type CallerLocation = {
|
|
526
|
+
file: string,
|
|
527
|
+
line: number,
|
|
528
|
+
col: number,
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* @description Gets the source file location of the caller.
|
|
533
|
+
*/
|
|
534
|
+
export function getCallerLocation(depth: number = 1): CallerLocation {
|
|
535
|
+
const error = new Error()
|
|
536
|
+
assert(error.stack)
|
|
537
|
+
const line = error.stack?.split('\n')[1 + depth]
|
|
538
|
+
if (!line) {
|
|
539
|
+
throw new Error(`getCallerLocation() - line was undefined`);
|
|
540
|
+
}
|
|
541
|
+
const match = line.match(/\((.+):(\d+):(\d+)\)/) || line.match(/at (.+):(\d+):(\d+)/)
|
|
542
|
+
assert(match !== null)
|
|
543
|
+
assert(match!.length === 4)
|
|
544
|
+
return {
|
|
545
|
+
file: match![1],
|
|
546
|
+
line: Number.parseInt(match![2]),
|
|
547
|
+
col: Number.parseInt(match![3])
|
|
548
|
+
}
|
|
549
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/*****
|
|
2
|
+
License
|
|
3
|
+
--------------
|
|
4
|
+
Copyright © 2020-2026 Mojaloop Foundation
|
|
5
|
+
The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
10
|
+
|
|
11
|
+
Contributors
|
|
12
|
+
--------------
|
|
13
|
+
This is the official list of the Mojaloop project contributors for this file.
|
|
14
|
+
Names of the original copyright holders (individuals or organizations)
|
|
15
|
+
should be listed with a '*' in the first column. People who have
|
|
16
|
+
contributed from an organization can be listed under the organization
|
|
17
|
+
that actually holds the copyright for their contributions (see the
|
|
18
|
+
Mojaloop Foundation for an example). Those individuals should have
|
|
19
|
+
their names indented and be marked with a '-'. Email address can be added
|
|
20
|
+
optionally within square brackets <email>.
|
|
21
|
+
|
|
22
|
+
* TigerBeetle
|
|
23
|
+
- Lewis Daly <lewis@tigerbeetle.com>
|
|
24
|
+
--------------
|
|
25
|
+
|
|
26
|
+
******/
|
|
27
|
+
|
|
28
|
+
import { ParsedTap } from './types'
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @function parseTestResult
|
|
32
|
+
* @description Parse a test result line and update counters.
|
|
33
|
+
*/
|
|
34
|
+
function parseTestResult(testMatch: RegExpMatchArray, result: ParsedTap): void {
|
|
35
|
+
const ok = testMatch[1] === 'ok'
|
|
36
|
+
const number = Number.parseInt(testMatch[2], 10)
|
|
37
|
+
let description = (testMatch[3] || '').replace(/^ - |^ /, '')
|
|
38
|
+
let directive: string | undefined
|
|
39
|
+
|
|
40
|
+
// Check for directives.
|
|
41
|
+
const directiveMatch = description.match(/# ?(SKIP|TODO)\b/i)
|
|
42
|
+
if (directiveMatch) {
|
|
43
|
+
directive = directiveMatch[1].toUpperCase()
|
|
44
|
+
description = description.replace(directiveMatch[0], '').trim()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
result.tests.push({ number, ok, description, directive })
|
|
48
|
+
result.total++
|
|
49
|
+
|
|
50
|
+
if (directive === 'SKIP') {
|
|
51
|
+
result.skip++
|
|
52
|
+
} else if (directive === 'TODO') {
|
|
53
|
+
result.todo++
|
|
54
|
+
} else if (ok) {
|
|
55
|
+
result.pass++
|
|
56
|
+
} else {
|
|
57
|
+
result.fail++
|
|
58
|
+
result.ok = false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @function parseTap
|
|
64
|
+
* @description Parse a TAP output string into a structured format.
|
|
65
|
+
*/
|
|
66
|
+
export function parseTap(tap: string): ParsedTap {
|
|
67
|
+
const lines = tap.split('\n')
|
|
68
|
+
const result: ParsedTap = {
|
|
69
|
+
version: undefined,
|
|
70
|
+
plan: null,
|
|
71
|
+
tests: [],
|
|
72
|
+
ok: true,
|
|
73
|
+
total: 0,
|
|
74
|
+
pass: 0,
|
|
75
|
+
fail: 0,
|
|
76
|
+
skip: 0,
|
|
77
|
+
todo: 0
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
for (const line of lines) {
|
|
81
|
+
// Parse TAP version.
|
|
82
|
+
const versionMatch = line.match(/^TAP version (\d+)/)
|
|
83
|
+
if (versionMatch) {
|
|
84
|
+
result.version = Number.parseInt(versionMatch[1], 10)
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Parse plan (1..N).
|
|
89
|
+
const planMatch = line.match(/^(\d+)\.\.(\d+)/)
|
|
90
|
+
if (planMatch) {
|
|
91
|
+
result.plan = {
|
|
92
|
+
start: Number.parseInt(planMatch[1], 10),
|
|
93
|
+
end: Number.parseInt(planMatch[2], 10)
|
|
94
|
+
}
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Parse test result (ok/not ok).
|
|
99
|
+
const testMatch = line.match(/^(ok|not ok) +(\d+)\b(.*)$/)
|
|
100
|
+
if (testMatch) {
|
|
101
|
+
parseTestResult(testMatch, result)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return result
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @function mergeTapStreams
|
|
110
|
+
* @description Merge two TAP streams into a single TAP output. Tests from the second stream are
|
|
111
|
+
* renumbered to follow the first.
|
|
112
|
+
*/
|
|
113
|
+
export function mergeTapStreams(legacy: string, native: string): string {
|
|
114
|
+
const legacyParsed = parseTap(legacy)
|
|
115
|
+
const nativeParsed = parseTap(native)
|
|
116
|
+
|
|
117
|
+
const legacyCount = legacyParsed.total
|
|
118
|
+
const totalCount = legacyCount + nativeParsed.total
|
|
119
|
+
|
|
120
|
+
const lines: string[] = []
|
|
121
|
+
|
|
122
|
+
lines.push('TAP version 13', `1..${totalCount}`)
|
|
123
|
+
|
|
124
|
+
// Add legacy tests (keep original numbering).
|
|
125
|
+
for (const test of legacyParsed.tests) {
|
|
126
|
+
const status = test.ok ? 'ok' : 'not ok'
|
|
127
|
+
let line = `${status} ${test.number} - ${test.description}`
|
|
128
|
+
if (test.directive) {
|
|
129
|
+
line += ` # ${test.directive}`
|
|
130
|
+
}
|
|
131
|
+
lines.push(line)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Add native tests (renumber starting after legacy).
|
|
135
|
+
for (const test of nativeParsed.tests) {
|
|
136
|
+
const newNumber = legacyCount + test.number
|
|
137
|
+
const status = test.ok ? 'ok' : 'not ok'
|
|
138
|
+
let line = `${status} ${newNumber} - ${test.description}`
|
|
139
|
+
if (test.directive) {
|
|
140
|
+
line += ` # ${test.directive}`
|
|
141
|
+
}
|
|
142
|
+
lines.push(line)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Add summary comments.
|
|
146
|
+
const totalPass = legacyParsed.pass + nativeParsed.pass
|
|
147
|
+
const totalFail = legacyParsed.fail + nativeParsed.fail
|
|
148
|
+
const totalSkip = legacyParsed.skip + nativeParsed.skip
|
|
149
|
+
|
|
150
|
+
lines.push('',
|
|
151
|
+
`# tests ${totalCount}`,
|
|
152
|
+
`# pass ${totalPass}`,
|
|
153
|
+
`# fail ${totalFail}`,
|
|
154
|
+
)
|
|
155
|
+
if (totalSkip > 0) {
|
|
156
|
+
lines.push(`# skip ${totalSkip}`)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (totalFail === 0) {
|
|
160
|
+
lines.push('', '# ok')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return lines.join('\n')
|
|
164
|
+
}
|