quantum-migration.test.cjs (4167B)
1 const { test } = require('node:test'); 2 const assert = require('node:assert/strict'); 3 const { readFileSync } = require('node:fs'); 4 const vm = require('node:vm'); 5 6 function app() { 7 const context = { 8 window: {}, 9 localStorage: { getItem: () => null }, 10 setTimeout, 11 clearTimeout, 12 URLSearchParams, 13 }; 14 vm.runInNewContext(readFileSync(require.resolve('../www/assets/iuna-ui.js'), 'utf8'), context); 15 const ui = context.window.iunaApp(); 16 ui.status = { 17 wallet_locked: false, 18 quantum_migration: { active: true, legacy_balance: 1_000_000, hybrid_balance: 0 }, 19 }; 20 ui.refresh = async () => {}; 21 ui.showFlash = () => {}; 22 return ui; 23 } 24 25 test('migration preview does not submit funds', async () => { 26 const ui = app(); 27 const paths = []; 28 ui.submitForm = async (path, fields) => { 29 paths.push(path); 30 assert.equal(fields.fee_per_byte, 1); 31 return { 32 preview: { 33 transaction_id: 'ab'.repeat(32), 34 input_count: 2, 35 remaining_legacy_utxos: 3, 36 bytes: 500, 37 fee: 500, 38 amount: 999_500, 39 }, 40 }; 41 }; 42 43 await ui.previewQuantumMigration(); 44 45 assert.deepEqual(paths, ['/api/wallet/quantum-migration/preview']); 46 assert.equal(ui.quantumMigrationPreview.remaining_legacy_utxos, 3); 47 }); 48 49 test('confirmed migration submits the exact preview once', async () => { 50 const ui = app(); 51 ui.quantumMigrationPreview = { 52 transaction_id: 'cd'.repeat(32), 53 rate: 2, 54 fee: 1_000, 55 remaining_legacy_utxos: 4, 56 }; 57 let submissions = 0; 58 ui.submitForm = async (path, fields) => { 59 submissions += 1; 60 assert.equal(path, '/api/wallet/quantum-migration/submit'); 61 assert.equal(fields.transaction_id, 'cd'.repeat(32)); 62 assert.equal(fields.max_fee, 1_000); 63 return { transaction_id: fields.transaction_id, remaining_legacy_utxos: 4 }; 64 }; 65 66 await ui.submitQuantumMigration(); 67 await ui.submitQuantumMigration(); 68 69 assert.equal(submissions, 1); 70 assert.equal(ui.quantumMigrationPreview, null); 71 }); 72 73 test('uncertain migration submission requires a fresh preview', async () => { 74 const ui = app(); 75 ui.quantumMigrationPreview = { 76 transaction_id: 'ef'.repeat(32), 77 rate: 1, 78 fee: 500, 79 }; 80 ui.submitForm = async () => { throw new Error('timeout'); }; 81 82 await ui.submitQuantumMigration(); 83 84 assert.equal(ui.quantumMigrationPreview, null); 85 assert.match(ui.quantumMigrationError, /Check wallet activity/); 86 }); 87 88 test('durability failure warns without inviting a duplicate migration', async () => { 89 const ui = app(); 90 ui.quantumMigrationPreview = { 91 transaction_id: '34'.repeat(32), 92 rate: 1, 93 fee: 500, 94 }; 95 ui.submitForm = async () => ({ 96 transaction_id: '34'.repeat(32), 97 remaining_legacy_utxos: 0, 98 persistence_error: 'disk full', 99 }); 100 101 await ui.submitQuantumMigration(); 102 103 assert.equal(ui.quantumMigrationPreview, null); 104 assert.match(ui.quantumMigrationError, /durable recovery failed/); 105 }); 106 107 test('broadcast failure explains that the saved migration will retry', async () => { 108 const ui = app(); 109 ui.quantumMigrationPreview = { 110 transaction_id: '56'.repeat(32), 111 rate: 1, 112 fee: 500, 113 }; 114 ui.submitForm = async () => ({ 115 transaction_id: '56'.repeat(32), 116 remaining_legacy_utxos: 0, 117 broadcast_error: 'no peers', 118 }); 119 120 await ui.submitQuantumMigration(); 121 122 assert.match(ui.quantumMigrationError, /rebroadcast automatically/); 123 }); 124 125 test('hybrid recipients never reuse selected legacy UTXOs', () => { 126 const ui = app(); 127 ui.transferTo = `iuna1p${'q'.repeat(58)}`; 128 ui.selectedTransferUtxos = ['legacy:0']; 129 ui.selectedTransferUtxoAmounts = { 'legacy:0': 10 }; 130 ui.showSendAdvanced = true; 131 132 ui.transferRecipientChanged(); 133 134 assert.equal(ui.hybridTransferRecipient(), true); 135 assert.equal(ui.selectedTransferUtxos.length, 0); 136 assert.equal(ui.showSendAdvanced, false); 137 }); 138 139 test('migration status can identify the exact pending v2 transaction', () => { 140 const ui = app(); 141 ui.status.quantum_migration.migration_pending = true; 142 ui.status.quantum_migration.pending_transaction_id = '12'.repeat(32); 143 144 assert.equal(ui.status.quantum_migration.pending_transaction_id, '12'.repeat(32)); 145 });