Skip to content

Commit 62e5cda

Browse files
authored
Merge pull request #1531 from input-output-hk/test/lw-11696-repurpose-blockfrost-e2e
test(e2e): repurpose blockfrost test suite for direct from client providers
2 parents dea40cc + 27cf4a4 commit 62e5cda

File tree

8 files changed

+99
-97
lines changed

8 files changed

+99
-97
lines changed

.github/workflows/continuous-integration-blockfrost-e2e.yaml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,29 @@ env:
1010
OGMIOS_URL: 'ws://localhost:1340/'
1111
STAKE_POOL_CONNECTION_STRING: 'postgresql://postgres:doNoUseThisSecret!@localhost:5435/stake_pool'
1212
STAKE_POOL_TEST_CONNECTION_STRING: 'postgresql://postgres:doNoUseThisSecret!@localhost:5435/stake_pool_test'
13-
TEST_CLIENT_ASSET_PROVIDER: 'http'
14-
TEST_CLIENT_ASSET_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4014/"}'
15-
TEST_CLIENT_CHAIN_HISTORY_PROVIDER: 'http'
16-
TEST_CLIENT_CHAIN_HISTORY_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4001/"}'
13+
TEST_CLIENT_ASSET_PROVIDER: 'blockfrost'
14+
TEST_CLIENT_ASSET_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:3015"}'
15+
TEST_CLIENT_CHAIN_HISTORY_PROVIDER: 'ws'
16+
TEST_CLIENT_CHAIN_HISTORY_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
1717
TEST_CLIENT_HANDLE_PROVIDER: 'http'
1818
TEST_CLIENT_HANDLE_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4011/"}'
1919
TEST_CLIENT_NETWORK_INFO_PROVIDER: 'ws'
2020
TEST_CLIENT_NETWORK_INFO_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
2121
TEST_CLIENT_REWARDS_PROVIDER: 'http'
22-
TEST_CLIENT_REWARDS_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4001/"}'
22+
TEST_CLIENT_REWARDS_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
2323
TEST_CLIENT_TX_SUBMIT_PROVIDER: 'http'
2424
TEST_CLIENT_TX_SUBMIT_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
25-
TEST_CLIENT_UTXO_PROVIDER: 'http'
26-
TEST_CLIENT_UTXO_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4001/"}'
25+
TEST_CLIENT_UTXO_PROVIDER: 'ws'
26+
TEST_CLIENT_UTXO_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
2727
TEST_CLIENT_STAKE_POOL_PROVIDER: 'http'
2828
TEST_CLIENT_STAKE_POOL_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
2929
WS_PROVIDER_URL: 'http://localhost:4100/ws'
3030

3131
on:
32-
workflow_dispatch:
32+
pull_request:
33+
push:
34+
branches: ['master']
35+
tags: ['*.*.*']
3336

3437
jobs:
3538
build_and_test:

packages/cardano-services-client/src/AssetInfoProvider/BlockfrostAssetProvider.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class BlockfrostAssetProvider extends BlockfrostProvider implements Asset
3333
return {
3434
mediaType: Asset.MediaType(mediaType),
3535
name: fileName,
36-
otherProperties: this.mapNftMetadataOtherProperties(file),
36+
otherProperties: this.mapFileMetadataOtherProperties(file),
3737
src: Asset.Uri(src)
3838
};
3939
} catch (error) {
@@ -88,15 +88,26 @@ export class BlockfrostAssetProvider extends BlockfrostProvider implements Asset
8888
return typeof metadata?.version === 'string' ? metadata.version : '1.0';
8989
}
9090

91+
private mapFileMetadataOtherProperties(
92+
metadata: Responses['asset']['onchain_metadata']
93+
): Map<string, Cardano.Metadatum> | undefined {
94+
return this.mapOtherProperties(metadata, ['name', 'mediaType', 'src']);
95+
}
96+
9197
private mapNftMetadataOtherProperties(
9298
metadata: Responses['asset']['onchain_metadata']
99+
): Map<string, Cardano.Metadatum> | undefined {
100+
return this.mapOtherProperties(metadata, ['name', 'image', 'description', 'mediaType', 'files', 'version']);
101+
}
102+
103+
private mapOtherProperties(
104+
metadata: Responses['asset']['onchain_metadata'],
105+
mainProperties: string[]
93106
): Map<string, Cardano.Metadatum> | undefined {
94107
if (!metadata) {
95108
return;
96109
}
97-
const otherProperties = Object.entries(
98-
omit(metadata, ['name', 'image', 'description', 'mediaType', 'files', 'version'])
99-
);
110+
const otherProperties = Object.entries(omit(metadata, mainProperties));
100111
if (otherProperties.length === 0) return;
101112
// eslint-disable-next-line consistent-return
102113
return new Map(otherProperties.map(([key, value]) => [key, this.objToMetadatum(value)]));

packages/cardano-services-client/test/AssetInfoProvider/BlockfrostAssetProvider.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ describe('BlockfrostAssetProvider', () => {
178178
...mockedAssetResponse,
179179
onchain_metadata: {
180180
...mockedAssetResponse.onchain_metadata,
181-
files: [{ mediaType: 'image/png', src: ['http://', 'some.png'] }]
181+
files: [{ image: 'should be in other properties', mediaType: 'image/png', src: ['http://', 'some.png'] }]
182182
}
183183
}
184184
]
@@ -190,6 +190,7 @@ describe('BlockfrostAssetProvider', () => {
190190
});
191191

192192
expect(response.nftMetadata!.files![0].src).toBe('http://some.png');
193+
expect(response.nftMetadata!.files![0].otherProperties?.get('image')).toBe('should be in other properties');
193194
});
194195

195196
test('version', async () => {

packages/e2e/.env.example

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,15 @@ STAKE_POOL_PROJECTOR_URL='http://localhost:4002/'
6161
NETWORK_SPEED=fast
6262

6363
# to run tests against local blockfrost
64-
# Blockfrost secrets
65-
#BLOCKFROST_CUSTOM_BACKEND_URL='http://blockfrost-ryo:3000'
66-
#ASSET_PROVIDER: 'blockfrost'
67-
#UTXO_PROVIDER: 'blockfrost'
68-
#CHAIN_HISTORY_PROVIDER: 'blockfrost'
69-
#REWARDS_PROVIDER: 'blockfrost'
70-
#NETWORK_INFO_PROVIDER: 'blockfrost'
71-
#TX_SUBMIT_PROVIDER: 'blockfrost'
64+
TEST_CLIENT_ASSET_PROVIDER='blockfrost'
65+
TEST_CLIENT_ASSET_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'
66+
#TEST_CLIENT_UTXO_PROVIDER='blockfrost'
67+
#TEST_UTXO_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'
68+
#TEST_CLIENT_CHAIN_HISTORY_PROVIDER='blockfrost'
69+
#TEST_CLIENT_CHAIN_HISTORY_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'
70+
#TEST_CLIENT_REWARDS_PROVIDER='blockfrost'
71+
#TEST_REWARDS_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'
72+
#TEST_CLIENT_NETWORK_INFO_PROVIDER='blockfrost'
73+
#TEST_NETWORK_INFO_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'
74+
#TEST_CLIENT_TX_SUBMIT_PROVIDER='blockfrost'
75+
#TEST_TX_SUBMIT_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'

packages/e2e/src/factories.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import {
3636
util
3737
} from '@cardano-sdk/key-management';
3838
import {
39+
BlockfrostAssetProvider,
40+
BlockfrostClient,
3941
CardanoWsClient,
4042
assetInfoHttpProvider,
4143
chainHistoryHttpProvider,
@@ -66,6 +68,7 @@ const HTTP_PROVIDER = 'http';
6668
const OGMIOS_PROVIDER = 'ogmios';
6769
const STUB_PROVIDER = 'stub';
6870
const WS_PROVIDER = 'ws';
71+
const BLOCKFROST_PROVIDER = 'blockfrost';
6972

7073
const MISSING_URL_PARAM = 'Missing URL';
7174

@@ -130,6 +133,19 @@ assetProviderFactory.register(HTTP_PROVIDER, async (params: any, logger: Logger)
130133
});
131134
});
132135

136+
assetProviderFactory.register(BLOCKFROST_PROVIDER, async (params: any, logger): Promise<AssetProvider> => {
137+
if (params.baseUrl === undefined) throw new Error(`${BlockfrostAssetProvider.name}: ${MISSING_URL_PARAM}`);
138+
139+
return new Promise<AssetProvider>(async (resolve) => {
140+
resolve(
141+
new BlockfrostAssetProvider(
142+
new BlockfrostClient({ baseUrl: params.baseUrl }, { rateLimiter: { schedule: (task) => task() } }),
143+
logger
144+
)
145+
);
146+
});
147+
});
148+
133149
chainHistoryProviderFactory.register(
134150
HTTP_PROVIDER,
135151
async (params: any, logger: Logger): Promise<ChainHistoryProvider> => {

packages/e2e/test/blockfrost-providers/networkInfo.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { logger } from '@cardano-sdk/util-dev';
55
import { networkInfoHttpProvider } from '@cardano-sdk/cardano-services-client';
66
import { toSerializableObject } from '@cardano-sdk/util';
77

8-
// LW-11697 to enable this
8+
// LW-11858 to enable this
99
describe.skip('Web Socket', () => {
1010
const legacyProvider = networkInfoHttpProvider({ baseUrl: 'http://localhost:4000/', logger });
1111
const provider = networkInfoHttpProvider({ baseUrl: 'http://localhost:4001/', logger });

packages/e2e/test/blockfrost/getAsset.test.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1-
import { BlockfrostAssetProvider } from '@cardano-sdk/cardano-services-client';
21
import { Cardano } from '@cardano-sdk/core';
2+
import { assetProviderFactory, getEnv, walletVariables } from '../../src';
33
import { logger } from '@cardano-sdk/util-dev';
4-
import { util } from '@cardano-sdk/cardano-services';
4+
5+
const env = getEnv(walletVariables);
56

67
describe('BlockfrostAssetProvider', () => {
8+
beforeAll(() => {
9+
if (env.TEST_CLIENT_ASSET_PROVIDER !== 'blockfrost')
10+
throw new Error('TEST_CLIENT_ASSET_PROVIDER must be "blockfrost" to run these tests');
11+
});
12+
713
test('getAsset', async () => {
8-
const assetProvider = new BlockfrostAssetProvider(util.getBlockfrostClient(), logger);
14+
const assetProvider = await assetProviderFactory.create(
15+
'blockfrost',
16+
env.TEST_CLIENT_ASSET_PROVIDER_PARAMS,
17+
logger
18+
);
919
const asset = await assetProvider.getAsset({
1020
assetId: Cardano.AssetId(
1121
'b27160f0c50a9cf168bf945dcbfcabbfbee5c7a801e7b467093b41534d6574616c4d6f6e7374657230303036'

packages/e2e/test/wallet_epoch_0/PersonalWallet/nft.test.ts

Lines changed: 29 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ describe('PersonalWallet.assets/nft', () => {
4646
let policyId: Cardano.PolicyId;
4747
let policyScript: Cardano.NativeScript;
4848
let assetIds: Cardano.AssetId[];
49-
let fingerprints: Cardano.AssetFingerprint[];
5049
const assetNames = ['4e46542d66696c6573', '4e46542d303031', '4e46542d303032'];
5150
let walletAddress: Cardano.PaymentAddress;
5251
const coins = 10_000_000n; // number of coins to use in each transaction
@@ -101,12 +100,6 @@ describe('PersonalWallet.assets/nft', () => {
101100
[assetIds[TOKEN_BURN_INDEX], 1n]
102101
]);
103102

104-
fingerprints = [
105-
Cardano.AssetFingerprint.fromParts(policyId, Cardano.AssetName(assetNames[TOKEN_METADATA_1_INDEX])),
106-
Cardano.AssetFingerprint.fromParts(policyId, Cardano.AssetName(assetNames[TOKEN_METADATA_2_INDEX])),
107-
Cardano.AssetFingerprint.fromParts(policyId, Cardano.AssetName(assetNames[TOKEN_BURN_INDEX]))
108-
];
109-
110103
walletAddress = (await firstValueFrom(wallet.addresses$))[0].address;
111104

112105
const txMetadatum = metadatum.jsonToMetadatum({
@@ -199,23 +192,13 @@ describe('PersonalWallet.assets/nft', () => {
199192
// Check balance here because asset info will not be re-fetched when balance changes due to minting and burning
200193
expect(walletAssetBalance?.get(assetIds[TOKEN_METADATA_2_INDEX])).toBe(1n);
201194

202-
expect(nfts.find((nft) => nft.assetId === assetIds[TOKEN_METADATA_2_INDEX])).toMatchObject({
203-
assetId: assetIds[TOKEN_METADATA_2_INDEX],
204-
fingerprint: fingerprints[TOKEN_METADATA_2_INDEX],
205-
name: assetNames[TOKEN_METADATA_2_INDEX],
206-
nftMetadata: {
207-
image: 'ipfs://some_hash1',
208-
name: 'One',
209-
otherProperties: new Map([['version', '1.0']]),
210-
version: '1.0'
211-
},
212-
policyId,
213-
// in case of repeated tests on the same network, total asset supply is not updated due to
214-
// the limitation that asset info is not refreshed on wallet balance changes
215-
quantity: expect.anything(),
216-
supply: expect.anything(),
217-
tokenMetadata: null
195+
const secondTokenMetadata = nfts.find((nft) => nft.assetId === assetIds[TOKEN_METADATA_2_INDEX])?.nftMetadata;
196+
expect(secondTokenMetadata).toMatchObject({
197+
image: 'ipfs://some_hash1',
198+
name: 'One',
199+
version: '1.0'
218200
});
201+
219202
expect(nfts.find((nft) => nft.assetId === assetIds[TOKEN_METADATA_1_INDEX])).toBeDefined();
220203
});
221204

@@ -225,37 +208,20 @@ describe('PersonalWallet.assets/nft', () => {
225208
// Check balance here because asset info will not be re-fetched when balance changes due to minting and burning
226209
expect(walletAssetBalance?.get(assetIds[TOKEN_METADATA_1_INDEX])).toBe(1n);
227210

228-
expect(nfts.find((nft) => nft.assetId === assetIds[TOKEN_METADATA_1_INDEX])).toMatchObject({
229-
assetId: assetIds[TOKEN_METADATA_1_INDEX],
230-
fingerprint: fingerprints[TOKEN_METADATA_1_INDEX],
231-
name: assetNames[TOKEN_METADATA_1_INDEX],
232-
nftMetadata: {
233-
description: 'NFT with different types of files',
234-
files: [
235-
{
236-
mediaType: 'video/mp4',
237-
name: 'some name',
238-
src: 'ipfs://Qmb78QQ4RXxKQrteRn4X3WaMXXfmi2BU2dLjfWxuJoF2N5'
239-
},
240-
{
241-
mediaType: 'audio/mpeg',
242-
name: 'some name',
243-
src: 'ipfs://Qmb78QQ4RXxKQrteRn4X3WaMXXfmi2BU2dLjfWxuJoF2Ny'
244-
}
245-
],
246-
image: 'ipfs://somehash',
247-
mediaType: 'image/png',
248-
name: 'NFT with files',
249-
otherProperties: new Map([
250-
['id', '1'],
251-
['version', '1.0']
252-
]),
253-
version: '1.0'
254-
} as Asset.NftMetadata,
255-
policyId,
256-
supply: expect.anything(),
257-
tokenMetadata: null
258-
});
211+
const nftMetadata = nfts.find((nft) => nft.assetId === assetIds[TOKEN_METADATA_1_INDEX])?.nftMetadata;
212+
expect(nftMetadata?.otherProperties?.get('id')).toBe('1');
213+
expect(nftMetadata?.files).toEqual([
214+
expect.objectContaining({
215+
mediaType: 'video/mp4',
216+
name: 'some name',
217+
src: 'ipfs://Qmb78QQ4RXxKQrteRn4X3WaMXXfmi2BU2dLjfWxuJoF2N5'
218+
}),
219+
expect.objectContaining({
220+
mediaType: 'audio/mpeg',
221+
name: 'some name',
222+
src: 'ipfs://Qmb78QQ4RXxKQrteRn4X3WaMXXfmi2BU2dLjfWxuJoF2Ny'
223+
})
224+
]);
259225
});
260226

261227
it('supports burning tokens', async () => {
@@ -313,7 +279,6 @@ describe('PersonalWallet.assets/nft', () => {
313279

314280
const assetNameHex = Buffer.from(assetName).toString('hex');
315281
const assetId = Cardano.AssetId(`${policyId}${assetNameHex}`);
316-
const fingerprint = Cardano.AssetFingerprint.fromParts(policyId, Cardano.AssetName(assetNameHex));
317282
const tokens = new Map([[assetId, 1n]]);
318283

319284
const txDataMetadatum = new Map([
@@ -325,7 +290,7 @@ describe('PersonalWallet.assets/nft', () => {
325290
metadatum.jsonToMetadatum({
326291
image: ['ipfs://some_hash1'],
327292
name: assetName,
328-
version: '1.0'
293+
version
329294
})
330295
]
331296
])
@@ -369,26 +334,18 @@ describe('PersonalWallet.assets/nft', () => {
369334

370335
// try remove the asset.nftMetadata filter
371336
const [, nfts] = await firstValueFromTimed(walletBalanceAssetsAndNfts(wallet));
337+
const nftMetadata = nfts.find((nft) => nft.assetId === assetId)?.nftMetadata;
372338

373-
expect(nfts.find((nft) => nft.assetId === assetId)).toMatchObject({
374-
assetId,
375-
fingerprint,
376-
name: assetNameHex,
377-
nftMetadata: {
378-
image: 'ipfs://some_hash1',
379-
name: assetName,
380-
otherProperties: new Map([['version', '1.0']]),
381-
version: '1.0'
382-
},
383-
policyId,
384-
quantity: expect.anything(),
385-
supply: expect.anything(),
386-
tokenMetadata: null
387-
});
339+
expect(nftMetadata?.image).toBe('ipfs://some_hash1');
340+
expect(nftMetadata?.name).toBe(assetName);
388341
});
389342

390343
CIP0025Test('supports CIP-25 v1, assetName hex encoded', 'CIP-0025-v1-hex', 1, 'hex');
391344
CIP0025Test('supports CIP-25 v1, assetName utf8 encoded', 'CIP-0025-v1-utf8', 1, 'utf8');
392-
CIP0025Test('supports CIP-25 v2', 'CIP-0025-v2', 2);
345+
346+
// https://input-output-rnd.slack.com/archives/C06J663L2A2/p1731505470694659
347+
env.TEST_CLIENT_ASSET_PROVIDER !== 'blockfrost'
348+
? CIP0025Test('supports CIP-25 v2', 'CIP-0025-v2', 2)
349+
: test.todo('"supports CIP-25 v2" test is disabled when running with Blockfrost asset provider');
393350
});
394351
});

0 commit comments

Comments
 (0)