Skip to content

Commit 38194f5

Browse files
fixup! feat(core): add timeout and logger to the tokenTransferInspector and transactionSummaryInspector
update unit tests
1 parent ce2f3a9 commit 38194f5

File tree

4 files changed

+279
-59
lines changed

4 files changed

+279
-59
lines changed

packages/core/test/util/mocks.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import * as Cardano from '../../src/Cardano';
2+
import { Asset, AssetProvider, HealthCheckResponse } from '../../src';
3+
4+
export const createMockInputResolver = (
5+
historicalTxs: Cardano.HydratedTx[],
6+
resolveDelay = 0
7+
): Cardano.InputResolver => ({
8+
async resolveInput(input: Cardano.TxIn) {
9+
const tx = historicalTxs.find((historicalTx) => historicalTx.id === input.txId);
10+
11+
if (!tx || tx.body.outputs.length <= input.index) return Promise.resolve(null);
12+
13+
return await new Promise((resolve) => {
14+
setTimeout(() => {
15+
resolve(tx.body.outputs[input.index]);
16+
}, resolveDelay);
17+
});
18+
}
19+
});
20+
21+
export const createMockAssetProvider = (assets: Asset.AssetInfo[]): AssetProvider => ({
22+
getAsset: async ({ assetId }) =>
23+
assets.find((asset) => asset.assetId === assetId) ?? Promise.reject('Asset not found'),
24+
getAssets: async ({ assetIds }) => assets.filter((asset) => assetIds.includes(asset.assetId)),
25+
healthCheck: async () => Promise.resolve({} as HealthCheckResponse)
26+
});

packages/core/test/util/tokenTransferInspector.test.ts

Lines changed: 152 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ import * as Cardano from '../../src/Cardano';
44
import {
55
Asset,
66
AssetInfoWithAmount,
7-
AssetProvider,
8-
HealthCheckResponse,
97
Milliseconds,
108
TokenTransferValue,
119
createTxInspector,
1210
tokenTransferInspector
1311
} from '../../src';
1412
import { Ed25519KeyHashHex, Ed25519PublicKeyHex, Ed25519SignatureHex } from '@cardano-sdk/crypto';
13+
import { createMockAssetProvider, createMockInputResolver } from './mocks';
1514
import { jsonToMetadatum } from '../../src/util/metadatum';
1615
import { logger } from '@cardano-sdk/util-dev';
1716

@@ -22,27 +21,6 @@ const buildTokenTransferValue = (coins: bigint, assets: Array<[Asset.AssetInfo,
2221
coins
2322
});
2423

25-
const createMockInputResolver = (historicalTxs: Cardano.HydratedTx[], resolveDelay = 0): Cardano.InputResolver => ({
26-
async resolveInput(input: Cardano.TxIn) {
27-
const tx = historicalTxs.find((historicalTx) => historicalTx.id === input.txId);
28-
29-
if (!tx || tx.body.outputs.length <= input.index) return Promise.resolve(null);
30-
31-
return await new Promise((resolve) => {
32-
setTimeout(() => {
33-
resolve(tx.body.outputs[input.index]);
34-
}, resolveDelay);
35-
});
36-
}
37-
});
38-
39-
const createMockAssetProvider = (assets: Asset.AssetInfo[]): AssetProvider => ({
40-
getAsset: async ({ assetId }) =>
41-
assets.find((asset) => asset.assetId === assetId) ?? Promise.reject('Asset not found'),
42-
getAssets: async ({ assetIds }) => assets.filter((asset) => assetIds.includes(asset.assetId)),
43-
healthCheck: async () => Promise.resolve({} as HealthCheckResponse)
44-
});
45-
4624
// eslint-disable-next-line max-statements
4725
describe('txInspector', () => {
4826
const sendingAddress = Cardano.PaymentAddress(
@@ -1074,7 +1052,7 @@ describe('txInspector', () => {
10741052
);
10751053
});
10761054

1077-
it('has no resolved fromAddresses on timeout', async () => {
1055+
it('has no resolved fromAddresses on inputResolver timeout', async () => {
10781056
const resolveDelay = Milliseconds(5);
10791057
const timeoutAfter = Milliseconds(1);
10801058

@@ -1143,5 +1121,155 @@ describe('txInspector', () => {
11431121
// Assert
11441122
expect(tokenTransfer.fromAddress).toEqual(new Map([]));
11451123
});
1124+
1125+
it('should have fromAddress and toAddress without metadata on AssetProvider.getAssets error', async () => {
1126+
const failingAssetProvider = {
1127+
...assetProvider,
1128+
getAssets: () => Promise.reject('Failed to get assets')
1129+
};
1130+
1131+
const tx = buildMockTx({
1132+
inputs: [
1133+
{
1134+
address: addresses[1],
1135+
index: 0,
1136+
txId: Cardano.TransactionId('bb217abaca60fc0ca68c1555eca6a96d2478547818ae76ce6836133f3cc546e0')
1137+
}
1138+
],
1139+
outputs: [
1140+
{
1141+
address: addresses[0],
1142+
value: {
1143+
assets: new Map([
1144+
[AssetIds.TSLA, 100n],
1145+
[AssetIds.PXL, 50n],
1146+
[AssetIds.Unit, 30n]
1147+
]),
1148+
coins: 3_000_000n
1149+
}
1150+
}
1151+
]
1152+
});
1153+
1154+
const histTx: Cardano.HydratedTx[] = [
1155+
{
1156+
body: {
1157+
outputs: [
1158+
{
1159+
address: addresses[1],
1160+
value: {
1161+
assets: new Map([
1162+
[AssetIds.TSLA, 25n],
1163+
[AssetIds.PXL, 10n],
1164+
[AssetIds.Unit, 5n]
1165+
]),
1166+
coins: 3_000_000n
1167+
}
1168+
}
1169+
]
1170+
},
1171+
id: Cardano.TransactionId('bb217abaca60fc0ca68c1555eca6a96d2478547818ae76ce6836133f3cc546e0')
1172+
} as unknown as Cardano.HydratedTx
1173+
];
1174+
1175+
const inspectTx = createTxInspector({
1176+
tokenTransfer: tokenTransferInspector({
1177+
fromAddressAssetProvider: failingAssetProvider,
1178+
inputResolver: createMockInputResolver(histTx),
1179+
logger,
1180+
timeout,
1181+
toAddressAssetProvider: failingAssetProvider
1182+
})
1183+
});
1184+
1185+
// Act
1186+
const { tokenTransfer } = await inspectTx(tx);
1187+
1188+
// Assert
1189+
expect(tokenTransfer.fromAddress).toMatchInlineSnapshot(`
1190+
Map {
1191+
"addr1z8phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gten0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs9yc0hh" => Object {
1192+
"assets": Map {
1193+
"659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41" => Object {
1194+
"amount": -25n,
1195+
"assetInfo": Object {
1196+
"assetId": "659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41",
1197+
"fingerprint": "asset1rqluyux4nxv6kjashz626c8usp8g88unmqwnyh",
1198+
"name": "54534c41",
1199+
"policyId": "659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba82",
1200+
"quantity": 0n,
1201+
"supply": 0n,
1202+
},
1203+
},
1204+
"1ec85dcee27f2d90ec1f9a1e4ce74a667dc9be8b184463223f9c960150584c" => Object {
1205+
"amount": -10n,
1206+
"assetInfo": Object {
1207+
"assetId": "1ec85dcee27f2d90ec1f9a1e4ce74a667dc9be8b184463223f9c960150584c",
1208+
"fingerprint": "asset1376eat26n9qnucrkkcy4pjps8d536nh5gds0gt",
1209+
"name": "50584c",
1210+
"policyId": "1ec85dcee27f2d90ec1f9a1e4ce74a667dc9be8b184463223f9c9601",
1211+
"quantity": 0n,
1212+
"supply": 0n,
1213+
},
1214+
},
1215+
"a5425bd7bc4182325188af2340415827a73f845846c165d9e14c5aed556e6974" => Object {
1216+
"amount": -5n,
1217+
"assetInfo": Object {
1218+
"assetId": "a5425bd7bc4182325188af2340415827a73f845846c165d9e14c5aed556e6974",
1219+
"fingerprint": "asset1947w6qxce5w35ur5q4s62z9fml2np9st3v20d9",
1220+
"name": "556e6974",
1221+
"policyId": "a5425bd7bc4182325188af2340415827a73f845846c165d9e14c5aed",
1222+
"quantity": 0n,
1223+
"supply": 0n,
1224+
},
1225+
},
1226+
},
1227+
"coins": -3000000n,
1228+
},
1229+
}
1230+
`);
1231+
expect(tokenTransfer.toAddress).toMatchInlineSnapshot(`
1232+
Map {
1233+
"addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgse35a3x" => Object {
1234+
"assets": Map {
1235+
"659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41" => Object {
1236+
"amount": 100n,
1237+
"assetInfo": Object {
1238+
"assetId": "659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41",
1239+
"fingerprint": "asset1rqluyux4nxv6kjashz626c8usp8g88unmqwnyh",
1240+
"name": "54534c41",
1241+
"policyId": "659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba82",
1242+
"quantity": 0n,
1243+
"supply": 0n,
1244+
},
1245+
},
1246+
"1ec85dcee27f2d90ec1f9a1e4ce74a667dc9be8b184463223f9c960150584c" => Object {
1247+
"amount": 50n,
1248+
"assetInfo": Object {
1249+
"assetId": "1ec85dcee27f2d90ec1f9a1e4ce74a667dc9be8b184463223f9c960150584c",
1250+
"fingerprint": "asset1376eat26n9qnucrkkcy4pjps8d536nh5gds0gt",
1251+
"name": "50584c",
1252+
"policyId": "1ec85dcee27f2d90ec1f9a1e4ce74a667dc9be8b184463223f9c9601",
1253+
"quantity": 0n,
1254+
"supply": 0n,
1255+
},
1256+
},
1257+
"a5425bd7bc4182325188af2340415827a73f845846c165d9e14c5aed556e6974" => Object {
1258+
"amount": 30n,
1259+
"assetInfo": Object {
1260+
"assetId": "a5425bd7bc4182325188af2340415827a73f845846c165d9e14c5aed556e6974",
1261+
"fingerprint": "asset1947w6qxce5w35ur5q4s62z9fml2np9st3v20d9",
1262+
"name": "556e6974",
1263+
"policyId": "a5425bd7bc4182325188af2340415827a73f845846c165d9e14c5aed",
1264+
"quantity": 0n,
1265+
"supply": 0n,
1266+
},
1267+
},
1268+
},
1269+
"coins": 3000000n,
1270+
},
1271+
}
1272+
`);
1273+
});
11461274
});
11471275
});

packages/core/test/util/transactionSummaryInspector.test.ts

Lines changed: 100 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
Asset,
66
AssetInfoWithAmount,
77
AssetProvider,
8-
HealthCheckResponse,
98
Milliseconds,
109
createTxInspector,
1110
transactionSummaryInspector
@@ -18,29 +17,10 @@ import {
1817
createStakeRegistrationCert
1918
} from '../../src/Cardano';
2019
import { Ed25519KeyHashHex, Ed25519PublicKeyHex, Ed25519SignatureHex, Hash28ByteBase16 } from '@cardano-sdk/crypto';
20+
import { createMockAssetProvider, createMockInputResolver } from './mocks';
2121
import { jsonToMetadatum } from '../../src/util/metadatum';
2222
import { logger } from '@cardano-sdk/util-dev';
23-
24-
const createMockInputResolver = (historicalTxs: Cardano.HydratedTx[], resolveDelay = 0): Cardano.InputResolver => ({
25-
async resolveInput(input: Cardano.TxIn) {
26-
const tx = historicalTxs.find((historicalTx) => historicalTx.id === input.txId);
27-
28-
if (!tx || tx.body.outputs.length <= input.index) return Promise.resolve(null);
29-
30-
return await new Promise((resolve) => {
31-
setTimeout(() => {
32-
resolve(tx.body.outputs[input.index]);
33-
}, resolveDelay);
34-
});
35-
}
36-
});
37-
38-
const createMockAssetProvider = (assets: Asset.AssetInfo[]): AssetProvider => ({
39-
getAsset: async ({ assetId }) =>
40-
assets.find((asset) => asset.assetId === assetId) ?? Promise.reject('Asset not found'),
41-
getAssets: async ({ assetIds }) => assets.filter((asset) => assetIds.includes(asset.assetId)),
42-
healthCheck: async () => Promise.resolve({} as HealthCheckResponse)
43-
});
23+
import delay from 'delay';
4424

4525
const buildAssetInfoWithAmount = (
4626
assets: Array<[Asset.AssetInfo, bigint]>
@@ -1044,7 +1024,7 @@ describe('Transaction Summary Inspector', () => {
10441024
});
10451025
});
10461026

1047-
it('displays unresolved inputs on timeout', async () => {
1027+
it('displays unresolved inputs on inputResolver timeout', async () => {
10481028
const resolveDelay = Milliseconds(5);
10491029
const timeoutAfter = Milliseconds(1);
10501030

@@ -1120,4 +1100,101 @@ describe('Transaction Summary Inspector', () => {
11201100
}
11211101
});
11221102
});
1103+
1104+
it('has no ntfMetadata nor tokenMetadata on AssetProvider timeout', async () => {
1105+
const resolveDelay = Milliseconds(5);
1106+
const timeoutAfter = Milliseconds(1);
1107+
1108+
const timeoutAssetProvider: AssetProvider = {
1109+
...assetProvider,
1110+
getAssets: async (...args) => {
1111+
await delay(resolveDelay);
1112+
return assetProvider.getAssets(...args);
1113+
}
1114+
};
1115+
const outputAssets = new Map([[AssetIds.TSLA, 5n]]);
1116+
1117+
const tx = buildMockTx({
1118+
certificates: [],
1119+
inputs: [
1120+
{
1121+
address: addresses[1],
1122+
index: 0,
1123+
txId: Cardano.TransactionId('bb217abaca60fc0ca68c1555eca6a96d2478547818ae76ce6836133f3cc546e0')
1124+
}
1125+
],
1126+
outputs: [
1127+
{
1128+
address: Cardano.PaymentAddress(
1129+
'addr_test1qpfhhfy2qgls50r9u4yh0l7z67xpg0a5rrhkmvzcuqrd0znuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q9gw0lz'
1130+
),
1131+
value: {
1132+
assets: outputAssets,
1133+
coins: 3_000_000n - fee // In this TX the fee is coming out of one of our own addresses.
1134+
}
1135+
}
1136+
]
1137+
});
1138+
1139+
const histTx: Cardano.HydratedTx[] = [
1140+
{
1141+
body: {
1142+
outputs: [
1143+
{
1144+
address: addresses[1],
1145+
value: {
1146+
assets: new Map([[AssetIds.TSLA, 5n]]),
1147+
coins: 3_000_000n
1148+
}
1149+
}
1150+
]
1151+
},
1152+
id: Cardano.TransactionId('bb217abaca60fc0ca68c1555eca6a96d2478547818ae76ce6836133f3cc546e0')
1153+
} as unknown as Cardano.HydratedTx
1154+
];
1155+
1156+
const inspectTx = createTxInspector({
1157+
summary: transactionSummaryInspector({
1158+
addresses,
1159+
assetProvider: timeoutAssetProvider,
1160+
inputResolver: createMockInputResolver(histTx),
1161+
logger,
1162+
protocolParameters,
1163+
rewardAccounts,
1164+
timeout: timeoutAfter
1165+
})
1166+
});
1167+
1168+
const { summary } = await inspectTx(tx);
1169+
1170+
expect(summary).toMatchInlineSnapshot(`
1171+
Object {
1172+
"assets": Map {
1173+
"659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41" => Object {
1174+
"amount": -5n,
1175+
"assetInfo": Object {
1176+
"assetId": "659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41",
1177+
"fingerprint": "asset1rqluyux4nxv6kjashz626c8usp8g88unmqwnyh",
1178+
"name": "54534c41",
1179+
"policyId": "659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba82",
1180+
"quantity": 0n,
1181+
"supply": 0n,
1182+
},
1183+
},
1184+
},
1185+
"coins": -3000000n,
1186+
"collateral": 0n,
1187+
"deposit": 0n,
1188+
"fee": 170000n,
1189+
"returnedDeposit": 0n,
1190+
"unresolved": Object {
1191+
"inputs": Array [],
1192+
"value": Object {
1193+
"assets": Map {},
1194+
"coins": 0n,
1195+
},
1196+
},
1197+
}
1198+
`);
1199+
});
11231200
});

0 commit comments

Comments
 (0)