Skip to content

Commit

Permalink
Merge pull request #1531 from input-output-hk/test/lw-11696-repurpose…
Browse files Browse the repository at this point in the history
…-blockfrost-e2e

test(e2e): repurpose blockfrost test suite for direct from client providers
  • Loading branch information
mkazlauskas authored Nov 15, 2024
2 parents dea40cc + 27cf4a4 commit 62e5cda
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 97 deletions.
19 changes: 11 additions & 8 deletions .github/workflows/continuous-integration-blockfrost-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,29 @@ env:
OGMIOS_URL: 'ws://localhost:1340/'
STAKE_POOL_CONNECTION_STRING: 'postgresql://postgres:doNoUseThisSecret!@localhost:5435/stake_pool'
STAKE_POOL_TEST_CONNECTION_STRING: 'postgresql://postgres:doNoUseThisSecret!@localhost:5435/stake_pool_test'
TEST_CLIENT_ASSET_PROVIDER: 'http'
TEST_CLIENT_ASSET_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4014/"}'
TEST_CLIENT_CHAIN_HISTORY_PROVIDER: 'http'
TEST_CLIENT_CHAIN_HISTORY_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4001/"}'
TEST_CLIENT_ASSET_PROVIDER: 'blockfrost'
TEST_CLIENT_ASSET_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:3015"}'
TEST_CLIENT_CHAIN_HISTORY_PROVIDER: 'ws'
TEST_CLIENT_CHAIN_HISTORY_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
TEST_CLIENT_HANDLE_PROVIDER: 'http'
TEST_CLIENT_HANDLE_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4011/"}'
TEST_CLIENT_NETWORK_INFO_PROVIDER: 'ws'
TEST_CLIENT_NETWORK_INFO_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
TEST_CLIENT_REWARDS_PROVIDER: 'http'
TEST_CLIENT_REWARDS_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4001/"}'
TEST_CLIENT_REWARDS_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
TEST_CLIENT_TX_SUBMIT_PROVIDER: 'http'
TEST_CLIENT_TX_SUBMIT_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
TEST_CLIENT_UTXO_PROVIDER: 'http'
TEST_CLIENT_UTXO_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4001/"}'
TEST_CLIENT_UTXO_PROVIDER: 'ws'
TEST_CLIENT_UTXO_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
TEST_CLIENT_STAKE_POOL_PROVIDER: 'http'
TEST_CLIENT_STAKE_POOL_PROVIDER_PARAMS: '{"baseUrl":"http://localhost:4000/"}'
WS_PROVIDER_URL: 'http://localhost:4100/ws'

on:
workflow_dispatch:
pull_request:
push:
branches: ['master']
tags: ['*.*.*']

jobs:
build_and_test:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class BlockfrostAssetProvider extends BlockfrostProvider implements Asset
return {
mediaType: Asset.MediaType(mediaType),
name: fileName,
otherProperties: this.mapNftMetadataOtherProperties(file),
otherProperties: this.mapFileMetadataOtherProperties(file),
src: Asset.Uri(src)
};
} catch (error) {
Expand Down Expand Up @@ -88,15 +88,26 @@ export class BlockfrostAssetProvider extends BlockfrostProvider implements Asset
return typeof metadata?.version === 'string' ? metadata.version : '1.0';
}

private mapFileMetadataOtherProperties(
metadata: Responses['asset']['onchain_metadata']
): Map<string, Cardano.Metadatum> | undefined {
return this.mapOtherProperties(metadata, ['name', 'mediaType', 'src']);
}

private mapNftMetadataOtherProperties(
metadata: Responses['asset']['onchain_metadata']
): Map<string, Cardano.Metadatum> | undefined {
return this.mapOtherProperties(metadata, ['name', 'image', 'description', 'mediaType', 'files', 'version']);
}

private mapOtherProperties(
metadata: Responses['asset']['onchain_metadata'],
mainProperties: string[]
): Map<string, Cardano.Metadatum> | undefined {
if (!metadata) {
return;
}
const otherProperties = Object.entries(
omit(metadata, ['name', 'image', 'description', 'mediaType', 'files', 'version'])
);
const otherProperties = Object.entries(omit(metadata, mainProperties));
if (otherProperties.length === 0) return;
// eslint-disable-next-line consistent-return
return new Map(otherProperties.map(([key, value]) => [key, this.objToMetadatum(value)]));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ describe('BlockfrostAssetProvider', () => {
...mockedAssetResponse,
onchain_metadata: {
...mockedAssetResponse.onchain_metadata,
files: [{ mediaType: 'image/png', src: ['http://', 'some.png'] }]
files: [{ image: 'should be in other properties', mediaType: 'image/png', src: ['http://', 'some.png'] }]
}
}
]
Expand All @@ -190,6 +190,7 @@ describe('BlockfrostAssetProvider', () => {
});

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

test('version', async () => {
Expand Down
20 changes: 12 additions & 8 deletions packages/e2e/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,15 @@ STAKE_POOL_PROJECTOR_URL='http://localhost:4002/'
NETWORK_SPEED=fast

# to run tests against local blockfrost
# Blockfrost secrets
#BLOCKFROST_CUSTOM_BACKEND_URL='http://blockfrost-ryo:3000'
#ASSET_PROVIDER: 'blockfrost'
#UTXO_PROVIDER: 'blockfrost'
#CHAIN_HISTORY_PROVIDER: 'blockfrost'
#REWARDS_PROVIDER: 'blockfrost'
#NETWORK_INFO_PROVIDER: 'blockfrost'
#TX_SUBMIT_PROVIDER: 'blockfrost'
TEST_CLIENT_ASSET_PROVIDER='blockfrost'
TEST_CLIENT_ASSET_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'
#TEST_CLIENT_UTXO_PROVIDER='blockfrost'
#TEST_UTXO_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'
#TEST_CLIENT_CHAIN_HISTORY_PROVIDER='blockfrost'
#TEST_CLIENT_CHAIN_HISTORY_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'
#TEST_CLIENT_REWARDS_PROVIDER='blockfrost'
#TEST_REWARDS_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'
#TEST_CLIENT_NETWORK_INFO_PROVIDER='blockfrost'
#TEST_NETWORK_INFO_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'
#TEST_CLIENT_TX_SUBMIT_PROVIDER='blockfrost'
#TEST_TX_SUBMIT_PROVIDER_PARAMS='{"baseUrl":"http://localhost:3015"}'
16 changes: 16 additions & 0 deletions packages/e2e/src/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import {
util
} from '@cardano-sdk/key-management';
import {
BlockfrostAssetProvider,
BlockfrostClient,
CardanoWsClient,
assetInfoHttpProvider,
chainHistoryHttpProvider,
Expand Down Expand Up @@ -66,6 +68,7 @@ const HTTP_PROVIDER = 'http';
const OGMIOS_PROVIDER = 'ogmios';
const STUB_PROVIDER = 'stub';
const WS_PROVIDER = 'ws';
const BLOCKFROST_PROVIDER = 'blockfrost';

const MISSING_URL_PARAM = 'Missing URL';

Expand Down Expand Up @@ -130,6 +133,19 @@ assetProviderFactory.register(HTTP_PROVIDER, async (params: any, logger: Logger)
});
});

assetProviderFactory.register(BLOCKFROST_PROVIDER, async (params: any, logger): Promise<AssetProvider> => {
if (params.baseUrl === undefined) throw new Error(`${BlockfrostAssetProvider.name}: ${MISSING_URL_PARAM}`);

return new Promise<AssetProvider>(async (resolve) => {
resolve(
new BlockfrostAssetProvider(
new BlockfrostClient({ baseUrl: params.baseUrl }, { rateLimiter: { schedule: (task) => task() } }),
logger
)
);
});
});

chainHistoryProviderFactory.register(
HTTP_PROVIDER,
async (params: any, logger: Logger): Promise<ChainHistoryProvider> => {
Expand Down
2 changes: 1 addition & 1 deletion packages/e2e/test/blockfrost-providers/networkInfo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { logger } from '@cardano-sdk/util-dev';
import { networkInfoHttpProvider } from '@cardano-sdk/cardano-services-client';
import { toSerializableObject } from '@cardano-sdk/util';

// LW-11697 to enable this
// LW-11858 to enable this
describe.skip('Web Socket', () => {
const legacyProvider = networkInfoHttpProvider({ baseUrl: 'http://localhost:4000/', logger });
const provider = networkInfoHttpProvider({ baseUrl: 'http://localhost:4001/', logger });
Expand Down
16 changes: 13 additions & 3 deletions packages/e2e/test/blockfrost/getAsset.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { BlockfrostAssetProvider } from '@cardano-sdk/cardano-services-client';
import { Cardano } from '@cardano-sdk/core';
import { assetProviderFactory, getEnv, walletVariables } from '../../src';
import { logger } from '@cardano-sdk/util-dev';
import { util } from '@cardano-sdk/cardano-services';

const env = getEnv(walletVariables);

describe('BlockfrostAssetProvider', () => {
beforeAll(() => {
if (env.TEST_CLIENT_ASSET_PROVIDER !== 'blockfrost')
throw new Error('TEST_CLIENT_ASSET_PROVIDER must be "blockfrost" to run these tests');
});

test('getAsset', async () => {
const assetProvider = new BlockfrostAssetProvider(util.getBlockfrostClient(), logger);
const assetProvider = await assetProviderFactory.create(
'blockfrost',
env.TEST_CLIENT_ASSET_PROVIDER_PARAMS,
logger
);
const asset = await assetProvider.getAsset({
assetId: Cardano.AssetId(
'b27160f0c50a9cf168bf945dcbfcabbfbee5c7a801e7b467093b41534d6574616c4d6f6e7374657230303036'
Expand Down
101 changes: 29 additions & 72 deletions packages/e2e/test/wallet_epoch_0/PersonalWallet/nft.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ describe('PersonalWallet.assets/nft', () => {
let policyId: Cardano.PolicyId;
let policyScript: Cardano.NativeScript;
let assetIds: Cardano.AssetId[];
let fingerprints: Cardano.AssetFingerprint[];
const assetNames = ['4e46542d66696c6573', '4e46542d303031', '4e46542d303032'];
let walletAddress: Cardano.PaymentAddress;
const coins = 10_000_000n; // number of coins to use in each transaction
Expand Down Expand Up @@ -101,12 +100,6 @@ describe('PersonalWallet.assets/nft', () => {
[assetIds[TOKEN_BURN_INDEX], 1n]
]);

fingerprints = [
Cardano.AssetFingerprint.fromParts(policyId, Cardano.AssetName(assetNames[TOKEN_METADATA_1_INDEX])),
Cardano.AssetFingerprint.fromParts(policyId, Cardano.AssetName(assetNames[TOKEN_METADATA_2_INDEX])),
Cardano.AssetFingerprint.fromParts(policyId, Cardano.AssetName(assetNames[TOKEN_BURN_INDEX]))
];

walletAddress = (await firstValueFrom(wallet.addresses$))[0].address;

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

expect(nfts.find((nft) => nft.assetId === assetIds[TOKEN_METADATA_2_INDEX])).toMatchObject({
assetId: assetIds[TOKEN_METADATA_2_INDEX],
fingerprint: fingerprints[TOKEN_METADATA_2_INDEX],
name: assetNames[TOKEN_METADATA_2_INDEX],
nftMetadata: {
image: 'ipfs://some_hash1',
name: 'One',
otherProperties: new Map([['version', '1.0']]),
version: '1.0'
},
policyId,
// in case of repeated tests on the same network, total asset supply is not updated due to
// the limitation that asset info is not refreshed on wallet balance changes
quantity: expect.anything(),
supply: expect.anything(),
tokenMetadata: null
const secondTokenMetadata = nfts.find((nft) => nft.assetId === assetIds[TOKEN_METADATA_2_INDEX])?.nftMetadata;
expect(secondTokenMetadata).toMatchObject({
image: 'ipfs://some_hash1',
name: 'One',
version: '1.0'
});

expect(nfts.find((nft) => nft.assetId === assetIds[TOKEN_METADATA_1_INDEX])).toBeDefined();
});

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

expect(nfts.find((nft) => nft.assetId === assetIds[TOKEN_METADATA_1_INDEX])).toMatchObject({
assetId: assetIds[TOKEN_METADATA_1_INDEX],
fingerprint: fingerprints[TOKEN_METADATA_1_INDEX],
name: assetNames[TOKEN_METADATA_1_INDEX],
nftMetadata: {
description: 'NFT with different types of files',
files: [
{
mediaType: 'video/mp4',
name: 'some name',
src: 'ipfs://Qmb78QQ4RXxKQrteRn4X3WaMXXfmi2BU2dLjfWxuJoF2N5'
},
{
mediaType: 'audio/mpeg',
name: 'some name',
src: 'ipfs://Qmb78QQ4RXxKQrteRn4X3WaMXXfmi2BU2dLjfWxuJoF2Ny'
}
],
image: 'ipfs://somehash',
mediaType: 'image/png',
name: 'NFT with files',
otherProperties: new Map([
['id', '1'],
['version', '1.0']
]),
version: '1.0'
} as Asset.NftMetadata,
policyId,
supply: expect.anything(),
tokenMetadata: null
});
const nftMetadata = nfts.find((nft) => nft.assetId === assetIds[TOKEN_METADATA_1_INDEX])?.nftMetadata;
expect(nftMetadata?.otherProperties?.get('id')).toBe('1');
expect(nftMetadata?.files).toEqual([
expect.objectContaining({
mediaType: 'video/mp4',
name: 'some name',
src: 'ipfs://Qmb78QQ4RXxKQrteRn4X3WaMXXfmi2BU2dLjfWxuJoF2N5'
}),
expect.objectContaining({
mediaType: 'audio/mpeg',
name: 'some name',
src: 'ipfs://Qmb78QQ4RXxKQrteRn4X3WaMXXfmi2BU2dLjfWxuJoF2Ny'
})
]);
});

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

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

const txDataMetadatum = new Map([
Expand All @@ -325,7 +290,7 @@ describe('PersonalWallet.assets/nft', () => {
metadatum.jsonToMetadatum({
image: ['ipfs://some_hash1'],
name: assetName,
version: '1.0'
version
})
]
])
Expand Down Expand Up @@ -369,26 +334,18 @@ describe('PersonalWallet.assets/nft', () => {

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

expect(nfts.find((nft) => nft.assetId === assetId)).toMatchObject({
assetId,
fingerprint,
name: assetNameHex,
nftMetadata: {
image: 'ipfs://some_hash1',
name: assetName,
otherProperties: new Map([['version', '1.0']]),
version: '1.0'
},
policyId,
quantity: expect.anything(),
supply: expect.anything(),
tokenMetadata: null
});
expect(nftMetadata?.image).toBe('ipfs://some_hash1');
expect(nftMetadata?.name).toBe(assetName);
});

CIP0025Test('supports CIP-25 v1, assetName hex encoded', 'CIP-0025-v1-hex', 1, 'hex');
CIP0025Test('supports CIP-25 v1, assetName utf8 encoded', 'CIP-0025-v1-utf8', 1, 'utf8');
CIP0025Test('supports CIP-25 v2', 'CIP-0025-v2', 2);

// https://input-output-rnd.slack.com/archives/C06J663L2A2/p1731505470694659
env.TEST_CLIENT_ASSET_PROVIDER !== 'blockfrost'
? CIP0025Test('supports CIP-25 v2', 'CIP-0025-v2', 2)
: test.todo('"supports CIP-25 v2" test is disabled when running with Blockfrost asset provider');
});
});

0 comments on commit 62e5cda

Please sign in to comment.