// TODO: We want to create a uniform set of tests for our BaseBlockchain interface. // We should repeat the tests for each provider that we have. // The below is a **VERY ROUGH** example of how we might be able to do this. // However, Chaingraph backend is currently broken for many queries, so let's not do this yet. import { beforeAll, describe, it, expect } from 'vitest'; import { BaseBlockchain, BlockchainElectrum } from '../../src/index.js'; const adapters: Record< string, () => Promise> > = { // Commenting out since the chaingraph endopoints we are hitting are currently broken. // demo won't allow non-browser clients // Pat's server isn't responding to block height requests correctly (or we are using an old version, which the demo server is also running). // chaingraph: async () => { // return await BlockchainChaingraph.from(); // }, electrum: async () => { return await BlockchainElectrum.from(); }, }; const testAddress = 'bitcoincash:qq974d8zzqxmkzk0xxlm6ryx3nqyq53u25w6mwm0rm'; const testLockscript = '76a91454eaf78abae543d9d8c1cf87109cb261101714ea88ac'; const testTxId = '7b63d5923f76be4e49820c41ed3557a55d5ce33e429519c6b9773b3214be5cb6'; const testBlockHeight = 800000; describe.each(Object.entries(adapters))('%s Adapter', (name, createAdapter) => { let blockchain: BaseBlockchain; beforeAll(async () => { blockchain = await createAdapter(); }); it(`${name}.fetchUnspents()`, async () => { const result = await blockchain.fetchUnspents(testAddress); expect(Array.isArray(result.toArray())).toBeTruthy(); if (result.size > 0) { const firstUTXO = result.toArray()[0]; expect(firstUTXO.utxo.outpoint).toHaveProperty('outpointTransactionHash'); expect(firstUTXO.utxo.outpoint).toHaveProperty('outpointIndex'); expect(firstUTXO.utxo.output).toHaveProperty('valueSatoshis'); } }); it(`${name}.fetchTransaction() `, async () => { const result = await blockchain.fetchTransaction(testTxId); expect(result).toBeDefined(); expect(Array.isArray(result.getInputs())).toBeTruthy(); expect(result.getInputs()[0]).toHaveProperty('outpointTransactionHash'); expect(Array.isArray(result.getOutputs())).toBeTruthy(); expect(result.getOutputs()[0]).toHaveProperty('valueSatoshis'); }); it(`${name}.fetchBlockHeader() `, async () => { const result = await blockchain.fetchBlockHeader(1); expect(result).toBeDefined(); expect(result.toHex()).toBeTypeOf('string'); expect(result.getTimestamp()).toBeTypeOf('number'); }); it(`${name}.fetchChainTip() `, async () => { const result = await blockchain.fetchChainTip(); expect(result).toBeDefined(); expect(result.height).toBeTypeOf('number'); expect(result.blockHeader.getHashHex()).toBeTypeOf('string'); }); it(`${name}.subscribeLockscript()`, async () => { const subscriptionPromise = new Promise((resolve, reject) => { blockchain .subscribeLockscript(testLockscript, () => {}) .then((unsubscribeCallback) => resolve(unsubscribeCallback)) .catch((error) => reject(error)); }); // Should return and unsuscription callback after a successful suscription. await expect(subscriptionPromise).resolves.toBeTypeOf('function'); }); it(`${name}.subscribeAddress(), `, async () => { const subscriptionPromise = new Promise((resolve, reject) => { blockchain .subscribeAddress(testAddress, () => {}) .then((unsubscribeCallback) => resolve(unsubscribeCallback)) .catch((error) => reject(error)); }); // Should return and unsuscription callback after a successful suscription. await expect(subscriptionPromise).resolves.toBeTypeOf('function'); }); it.sequential(`${name}.subscribeTransaction()`, async () => { const subscriptionPromise = new Promise((resolve, reject) => { blockchain .subscribeTransaction(testTxId, () => {}) .then((unsubscribeCallback) => resolve(unsubscribeCallback)) .catch((error) => reject(error)); }); // Should return and unsuscription callback after a successful suscription. await expect(subscriptionPromise).resolves.toBeTypeOf('function'); }); it(`${name}.unsubscribeLockscript()`, async () => { const unsubscriptionPromise = new Promise((resolve, reject) => { blockchain .unsubscribeLockscript(testLockscript, () => {}) .then((result) => resolve(result)) .catch((error) => reject(error)); }); // Should return true after a successful unsubscription. await expect(unsubscriptionPromise).resolves.toBeTypeOf('boolean'); }); it(`${name}.unsubscribeAddress()`, async () => { const unsubscriptionPromise = new Promise((resolve, reject) => { blockchain .unsubscribeAddress(testAddress, () => {}) .then((result) => resolve(result)) .catch((error) => reject(error)); }); // Should return true after a successful unsubscription. await expect(unsubscriptionPromise).resolves.toBeTypeOf('boolean'); }); it.sequential(`${name}.unsubscribeTransaction()`, async () => { const unsubscriptionPromise = new Promise((resolve, reject) => { blockchain .unsubscribeTransaction(testTxId, () => {}) .then((result) => resolve(result)) .catch((error) => reject(error)); }); // Should return true after a successful unsubscription. await expect(unsubscriptionPromise).resolves.toBeTypeOf('boolean'); }); });