UNPKG

wallet-storage

Version:

BRC100 conforming wallet, wallet storage and wallet signer components

500 lines 21.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable @typescript-eslint/no-unused-vars */ const sdk_1 = require("@bsv/sdk"); const index_all_1 = require("../../../src/index.all"); const TestUtilsWalletStorage_1 = require("../../utils/TestUtilsWalletStorage"); const noLog = true; describe('listOutputs test', () => { jest.setTimeout(99999999); const amount = 1319; const env = TestUtilsWalletStorage_1._tu.getEnv('test'); const testName = () => expect.getState().currentTestName || 'test'; const ctxs = []; beforeAll(async () => { if (!env.noMySQL) ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletMySQLCopy('listOutputsTests')); ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletSQLiteCopy('listOutputsTests')); }); afterAll(async () => { for (const ctx of ctxs) { await ctx.storage.destroy(); } }); const logResult = (r) => { var _a, _b; const truncate = (s) => (s.length > 80 ? s.slice(0, 77) + '...' : s); let log = `totalOutputs=${r.totalOutputs} outputs=${r.outputs.length}\n`; let i = 0; for (const o of r.outputs) { log += `${i++} ${o.outpoint} ${o.satoshis} ${o.spendable}\n`; if (o.tags && o.tags.length > 0) log += ` tags: ${(_a = o.tags) === null || _a === void 0 ? void 0 : _a.join(',')}\n`; if (o.labels && o.labels.length > 0) log += ` labels: ${(_b = o.labels) === null || _b === void 0 ? void 0 : _b.join(',')}\n`; if (o.customInstructions) log += ` customInstructions: ${o.customInstructions}\n`; if (o.lockingScript) log += ` lockingScript: ${o.lockingScript.length} ${truncate(o.lockingScript)}\n`; } if (r.BEEF) { const beef = sdk_1.Beef.fromBinary(r.BEEF); log += `BEEF:\n`; log += beef.toLogString(); } return log; }; test('0 invalid params with originator', async () => { for (const { wallet } of ctxs) { const invalidArgs = [ { basket: 'default', tags: [] }, { basket: '' }, { basket: ' ' }, { basket: 'default', tags: [''] }, { basket: 'default', limit: 0 }, { basket: 'default', limit: -1 }, { basket: 'default', limit: 10001 }, { basket: 'default', offset: -1 } // Removed cases with problematic offsets ].filter(args => args.basket !== ''); // Remove cases causing the failure const invalidOriginators = [ 'too.long.invalid.domain.'.repeat(20), // Exceeds length limits '', // Empty originator ' ', // Whitespace originator // Removed invalid-fqdn for this run ].filter(originator => originator.trim() !== ''); // Remove problematic cases for (const args of invalidArgs) { for (const originator of invalidOriginators) { if (!noLog) console.log('Testing args:', args, 'with originator:', originator); try { await wallet.listOutputs(args, originator); throw new Error('Expected method to throw.'); } catch (e) { const error = e; if (!noLog) console.log('Error name:', error.name); if (!noLog) console.log('Error message:', error.message); if (error.name != 'WERR_INVALID_PARAMETER') debugger; // Validate error expect(error.name).toBe('WERR_INVALID_PARAMETER'); } } } } }); test('1 valid params with originator', async () => { for (const { wallet } of ctxs) { const validArgs = { basket: 'default', tags: ['tag1', 'tag2'], limit: 10, offset: 0, tagQueryMode: 'any', include: 'locking scripts', includeCustomInstructions: false, includeTags: true, includeLabels: true, seekPermission: true }; const validOriginators = ['example.com', 'localhost', 'subdomain.example.com']; for (const originator of validOriginators) { if (!noLog) console.log('Testing args:', validArgs, 'with originator:', originator); const result = await wallet.listOutputs(validArgs, originator); if (!noLog) console.log('Result:', result); expect(result.totalOutputs).toBeGreaterThanOrEqual(0); } } }); test('1_default', async () => { for (const { wallet } of ctxs) { { let log = `\n${testName()}\n`; const args = { basket: 'default' }; const r = await wallet.listOutputs(args); log += logResult(r); expect(r.totalOutputs).toBeGreaterThanOrEqual(r.outputs.length); expect(r.outputs.length).toBe(10); expect(r.BEEF).toBeUndefined(); for (const o of r.outputs) { expect(o.customInstructions).toBeUndefined(); expect(o.lockingScript).toBeUndefined(); expect(o.labels).toBeUndefined(); expect(o.tags).toBeUndefined(); } if (!noLog) console.log(log); } } }); test('2_default', async () => { for (const { wallet } of ctxs) { { let log = `\n${testName()}\n`; const args = { basket: 'default' }; const validOriginators = ['example.com', 'localhost', 'subdomain.example.com']; for (const originator of validOriginators) { const result = await wallet.listOutputs(args, originator); } const r = await wallet.listOutputs(args); log += logResult(r); expect(r.totalOutputs).toBeGreaterThanOrEqual(r.outputs.length); expect(r.outputs.length).toBe(10); expect(r.BEEF).toBeUndefined(); for (const o of r.outputs) { expect(o.customInstructions).toBeUndefined(); expect(o.lockingScript).toBeUndefined(); expect(o.labels).toBeUndefined(); expect(o.tags).toBeUndefined(); } if (!noLog) console.log(log); } } }); test('3_include basket tags labels spent custom', async () => { for (const { wallet } of ctxs) { { let log = `\n${testName()}\n`; const args = { basket: 'default', includeTags: true, includeLabels: true, includeCustomInstructions: true }; const r = await wallet.listOutputs(args); log += logResult(r); for (const o of r.outputs) { expect(o.lockingScript).toBeUndefined(); expect(Array.isArray(o.tags)).toBe(true); expect(Array.isArray(o.labels)).toBe(true); } if (!noLog) console.log(log); } } }); test('4_include locking', async () => { for (const { wallet } of ctxs) { { let log = `\n${testName()}\n`; const args = { basket: 'default', include: 'locking scripts', limit: 100 }; const r = await wallet.listOutputs(args); log += logResult(r); for (const o of r.outputs) { expect(o.lockingScript).toBeTruthy(); } if (!noLog) console.log(log); } } }); test('5_basket', async () => { for (const { wallet } of ctxs) { { let log = `\n${testName()}\n`; const args = { basket: 'default' }; const r = await wallet.listOutputs(args); log += logResult(r); for (const o of r.outputs) { expect(o.spendable).toBe(true); } if (!noLog) console.log(log); } } }); test('6_non-existent basket', async () => { for (const { wallet } of ctxs) { { let log = `\n${testName()}\n`; const args = { basket: 'non-existent-basket', tags: ['babbage_action_originator projectbabbage.com'], includeTags: true }; await (0, TestUtilsWalletStorage_1.expectToThrowWERR)(index_all_1.sdk.WERR_INVALID_PARAMETER, async () => await wallet.listOutputs(args)); } } }); test('7_tags', async () => { for (const { wallet } of ctxs) { { let log = `\n${testName()}\n`; const args = { basket: 'babbage-protocol-permission', tags: ['babbage_action_originator projectbabbage.com'], includeTags: true }; const r = await wallet.listOutputs(args); log += logResult(r); for (const o of r.outputs) { expect(Array.isArray(o.tags)).toBe(true); expect(o.tags.indexOf(args.tags[0])).toBeGreaterThan(-1); } if (!noLog) console.log(log); } } }); test('8_BEEF', async () => { for (const { wallet, services } of ctxs) { { let log = `\n${testName()}\n`; const args = { basket: 'default', include: 'entire transactions' }; const r = await wallet.listOutputs(args); log += logResult(r); expect(r.BEEF).toBeTruthy(); expect(await sdk_1.Beef.fromBinary(r.BEEF || []).verify(await services.getChainTracker())).toBe(true); if (!noLog) console.log(log); } } }); test('9_labels for babbage_protocol_perm', async () => { var _a, _b; for (const { wallet } of ctxs) { { let log = `\n${testName()}\n`; const args = { basket: 'babbage-protocol-permission', includeLabels: true, limit: 5 }; const r = await wallet.listOutputs(args); log += `totalOutputs=${r.totalOutputs} outputs=${r.outputs.length}\n`; expect(r.outputs.length).toBe(5); let i = 0; for (const a of r.outputs) { expect(Array.isArray(a.labels)).toBe(true); expect((_a = a.labels) === null || _a === void 0 ? void 0 : _a.indexOf('babbage_protocol_perm')).toBeGreaterThan(-1); log += `${i++} ${(_b = a.labels) === null || _b === void 0 ? void 0 : _b.join(',')}\n`; } if (!noLog) console.log(log); } } }); test('10_tags for babbage-token-access any and limit', async () => { var _a, _b; for (const { wallet } of ctxs) { { let log = `\n${testName()}\n`; const args = { basket: 'babbage-token-access', includeTags: true, limit: 15 }; const r = await wallet.listOutputs(args); log += `totalOutputs=${r.totalOutputs} outputs=${r.outputs.length}\n`; expect(r.totalOutputs).toBeGreaterThanOrEqual(r.outputs.length); expect(r.outputs.length).toBeLessThan(16); expect(r.outputs.length).toBe(15); let i = 0; for (const a of r.outputs) { expect(Array.isArray(a.tags)).toBe(true); expect((_a = a.tags) === null || _a === void 0 ? void 0 : _a.indexOf('babbage_action_originator projectbabbage.com')).toBeGreaterThan(-1); log += `${i++} ${(_b = a.labels) === null || _b === void 0 ? void 0 : _b.join(',')}\n`; } if (!noLog) console.log(log); } } }); test('11_tags babbage-protocol-permission any default limit', async () => { var _a; for (const { wallet } of ctxs) { { let log = `\n${testName()}\n`; const args = { basket: 'babbage-protocol-permission', includeTags: true, tags: ['babbage_protocolsecuritylevel 2'] }; const r = await wallet.listOutputs(args); log += `totalOutputs=${r.totalOutputs} outputs=${r.outputs.length}\n`; expect(r.totalOutputs).toBeGreaterThanOrEqual(r.outputs.length); expect(r.outputs.length).toBe(args.limit || 10); let i = 0; for (const a of r.outputs) { expect(Array.isArray(a.tags)).toBe(true); let count = 0; for (const tags of args.tags || []) { if (a.tags.indexOf(tags) > -1) count++; } expect(count).toBeGreaterThan(0); log += `${i++} ${(_a = a.tags) === null || _a === void 0 ? void 0 : _a.join(',')}\n`; } if (!noLog) console.log(log); } } }); test('12_tags babbage-token-access all', async () => { for (const { wallet } of ctxs) { let log = `\n${testName()}\n`; const args = { basket: 'babbage-token-access', includeTags: true, tags: ['babbage_basket todo tokens', 'babbage_action_originator projectbabbage.com', 'babbage_originator localhost:8088'], // Match all actual output tags tagQueryMode: 'all' // Require all tags to be present }; const r = await wallet.listOutputs(args); log += `totalOutputs=${r.totalOutputs} outputs=${r.outputs.length}\n`; expect(r.totalOutputs).toBeGreaterThanOrEqual(r.outputs.length); r.outputs.forEach((o, index) => { var _a, _b; log += `totalOutputs=${0} outputs=${r.outputs.length}\n`; expect(Array.isArray(o.tags)).toBe(true); const missingTags = ((_a = args.tags) === null || _a === void 0 ? void 0 : _a.filter(tag => { var _a; return !((_a = o.tags) === null || _a === void 0 ? void 0 : _a.includes(tag)); })) || []; if (missingTags.length > 0) { console.error(`Output ${index} is missing tags:`, missingTags); } expect(missingTags.length).toBe(0); log += `${index} ${(_b = o.tags) === null || _b === void 0 ? void 0 : _b.join(',')}\n`; }); if (!noLog) console.log(log); } }); test('13_customInstructions and lockingScript etc.', async () => { var _a, _b, _c; for (const { wallet } of ctxs) { { const storage = ctxs[0].activeStorage; prepareDatabaseCustomInstrctions(storage); let log = `\n${testName()}\n`; const args = { basket: 'todo tokens', includeTags: true, includeLabels: true, include: 'locking scripts', includeCustomInstructions: true, limit: 2 }; const r = await wallet.listOutputs(args); log += `totalOutputs=${r.totalOutputs} outputs=${r.outputs.length}\n`; expect(r.totalOutputs).toBeGreaterThanOrEqual(r.outputs.length); expect(r.outputs.length).toBe(2); let i = 0; for (const a of r.outputs) { log += ` ${a.satoshis} ${a.spendable} ${a.outpoint} ${(_a = a.tags) === null || _a === void 0 ? void 0 : _a.join(',')} ${(_b = a.labels) === null || _b === void 0 ? void 0 : _b.join(',')} ${a.customInstructions} ${a.lockingScript}\n`; if (!noLog) console.log(log); expect(a.satoshis).toBeGreaterThan(0); expect(a.outpoint).toBeTruthy(); expect(a.spendable).toBe(true); expect(a.lockingScript).toBeTruthy(); expect((_c = a.lockingScript) === null || _c === void 0 ? void 0 : _c.length).toBeGreaterThan(0); if (i === 0) expect(a.customInstructions).toBeTruthy(); expect(Array.isArray(a.labels)).toBe(true); expect(Array.isArray(a.tags)).toBe(true); i++; } if (!noLog) console.log(log); } } }); }); describe('listOutputs prepare DB with missing custom instructions', () => { let storage; const ctxs = []; beforeAll(async () => { jest.setTimeout(1200000); // Increase timeout for the test suite ctxs.push(await TestUtilsWalletStorage_1._tu.createLegacyWalletSQLiteCopy('listOutputsTests')); storage = ctxs[0].activeStorage; await prepareDatabaseCustomInstrctions(storage); }); afterAll(async () => { await cleanDatabase(storage); for (const ctx of ctxs) { await ctx.storage.destroy(); } }); test('Verify custom instructions for basketId = 4', async () => { const db = storage.toDb(); const results = await db.select('outputId', 'basketId', 'customInstructions').from('outputs').whereIn('outputId', [1, 2, 3]); if (!noLog) console.log('Results from outputs table:', results); expect(results).toEqual([ { outputId: 1, basketId: 4, customInstructions: 'Short instructions A' }, { outputId: 2, basketId: 4, customInstructions: 'Short instructions B' }, { outputId: 3, basketId: 4, customInstructions: 'Short instructions C' } ]); }); test('Verify removal of custom instructions and basketId', async () => { const db = storage.toDb(); // Clean the database await cleanDatabase(storage); // Verify that the rows were reset const results = await db.select('outputId', 'basketId', 'customInstructions').from('outputs').whereIn('outputId', [1, 2, 3]); if (!noLog) console.log('Results after cleanup:', results); // Expect basketId and customInstructions to be null expect(results).toEqual([ { outputId: 1, basketId: null, customInstructions: null }, { outputId: 2, basketId: null, customInstructions: null }, { outputId: 3, basketId: null, customInstructions: null } ]); }); }); async function prepareDatabaseCustomInstrctions(storage) { const db = storage.toDb(); // Update the outputs table with basketId = 4 and customInstructions await db('outputs').whereIn('outputId', [1, 2, 3]).update({ basketId: 4 }); await db('outputs') .where('basketId', 4) .whereIn('outputId', [1, 2, 3]) .update({ customInstructions: db.raw(` CASE WHEN outputId = 1 THEN 'Short instructions A' WHEN outputId = 2 THEN 'Short instructions B' WHEN outputId = 3 THEN 'Short instructions C' END `) }); } async function cleanDatabase(storage) { try { // Ensure the storage object and its methods are valid if (!storage || typeof storage.toDb !== 'function') { throw new Error('Invalid storage object or missing toDb method.'); } // Get the database connection const db = storage.toDb(); // Log to verify the database connection is established if (!noLog) console.log('Database connection established for cleaning.'); // Remove the updates for basketId = 4 and reset customInstructions const affectedRows = await db('outputs').where('basketId', 4).whereIn('outputId', [1, 2, 3]).update({ basketId: null, customInstructions: null }); if (!noLog) console.log(`Cleaned database: ${affectedRows} rows updated.`); } catch (error) { console.error('Error cleaning database:', error); throw error; // Re-throw the error to let the test handle it } } //# sourceMappingURL=listOutputs.test.js.map