wallet-storage
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
500 lines • 21.8 kB
JavaScript
;
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