const test = require('tape') const sinon = require('sinon') const axios = require('axios') const BN = require('../../lib/bn') // Mock the module before requiring the plugin const lnbits = require('../../lib/plugins/wallet/lnbits/lnbits') test('LNBits plugin - configuration validation', t => { t.plan(3) const validAccount = { endpoint: 'https://demo.lnbits.com', adminKey: 'test_admin_key_123' } const invalidAccount1 = { endpoint: 'https://demo.lnbits.com' // Missing adminKey } const invalidAccount2 = { adminKey: 'test_admin_key_123' // Missing endpoint } // Valid config should not throw t.doesNotThrow(() => { lnbits.balance(validAccount, 'LN').catch(() => {}) }, 'Valid configuration passes validation') // Missing adminKey should throw lnbits.balance(invalidAccount1, 'LN').catch(err => { t.ok(err.message.includes('LNBits configuration missing: adminKey'), 'Missing adminKey throws appropriate error') }) // Missing endpoint should throw lnbits.balance(invalidAccount2, 'LN').catch(err => { t.ok(err.message.includes('LNBits configuration missing: endpoint'), 'Missing endpoint throws appropriate error') }) }) test('LNBits plugin - crypto code validation', t => { t.plan(2) const account = { endpoint: 'https://demo.lnbits.com', adminKey: 'test_admin_key' } // Valid crypto code lnbits.balance(account, 'LN').catch(() => { // Expected to fail at API call, not validation }) t.pass('LN crypto code is accepted') // Invalid crypto code lnbits.balance(account, 'BTC').catch(err => { t.ok(err.message.includes('Unsupported crypto'), 'Invalid crypto code throws appropriate error') }) }) test('LNBits plugin - invoice detection', t => { t.plan(6) // Valid Lightning invoices t.ok(lnbits.isLnInvoice('lnbc100n1p0example'), 'Detects mainnet Lightning invoice') t.ok(lnbits.isLnInvoice('lntb100n1p0example'), 'Detects testnet Lightning invoice') t.ok(lnbits.isLnInvoice('lnbcrt100n1p0example'), 'Detects regtest Lightning invoice') // Invalid invoices t.notOk(lnbits.isLnInvoice('bc1qexample'), 'Rejects Bitcoin address') t.notOk(lnbits.isLnInvoice('lnurl1234'), 'Rejects LNURL') t.notOk(lnbits.isLnInvoice(''), 'Rejects empty string') }) test('LNBits plugin - LNURL detection', t => { t.plan(3) t.ok(lnbits.isLnurl('lnurl1dp68gurn8ghj7um9'), 'Detects LNURL') t.notOk(lnbits.isLnurl('lnbc100n1p0example'), 'Rejects Lightning invoice') t.notOk(lnbits.isLnurl(''), 'Rejects empty string') }) test('LNBits plugin - network detection', t => { t.plan(4) const mainnetAccount = { endpoint: 'https://lnbits.com', adminKey: 'test' } const testnetAccount = { endpoint: 'https://testnet.lnbits.com', adminKey: 'test' } const regtestAccount = { endpoint: 'http://localhost:5000', adminKey: 'test' } const testAccount = { endpoint: 'https://test.lnbits.com', adminKey: 'test' } t.equal(lnbits.cryptoNetwork(mainnetAccount, 'LN'), 'main', 'Detects mainnet') t.equal(lnbits.cryptoNetwork(testnetAccount, 'LN'), 'test', 'Detects testnet') t.equal(lnbits.cryptoNetwork(regtestAccount, 'LN'), 'regtest', 'Detects regtest/local') t.equal(lnbits.cryptoNetwork(testAccount, 'LN'), 'test', 'Detects test environment') }) test('LNBits plugin - newAddress creates invoice', async t => { t.plan(3) const account = { endpoint: 'https://demo.lnbits.com', adminKey: 'test_admin_key' } const tx = { cryptoAtoms: new BN('100000'), cryptoCode: 'LN' } const info = { cryptoCode: 'LN' } // Mock the axios request const stub = sinon.stub(axios, 'request') stub.resolves({ data: { payment_request: 'lnbc1000n1p0example', payment_hash: 'abc123' } }) try { const invoice = await lnbits.newAddress(account, info, tx) t.equal(invoice, 'lnbc1000n1p0example', 'Returns Lightning invoice') t.ok(stub.calledOnce, 'Makes one API request') const callArgs = stub.firstCall.args[0] t.equal(callArgs.data.amount, 100000, 'Sends correct amount in satoshis') } catch (err) { t.fail(`Unexpected error: ${err.message}`) } finally { stub.restore() } }) test('LNBits plugin - balance returns BN', async t => { t.plan(2) const account = { endpoint: 'https://demo.lnbits.com', adminKey: 'test_admin_key' } // Mock the axios request const stub = sinon.stub(axios, 'request') stub.resolves({ data: { balance: 500000000, // 500000 sats in millisats name: 'Test Wallet' } }) try { const balance = await lnbits.balance(account, 'LN') t.ok(balance instanceof BN, 'Returns BigNumber instance') t.equal(balance.toString(), '500000', 'Converts millisats to sats correctly') } catch (err) { t.fail(`Unexpected error: ${err.message}`) } finally { stub.restore() } }) test('LNBits plugin - getStatus checks payment', async t => { t.plan(3) const account = { endpoint: 'https://demo.lnbits.com', adminKey: 'test_admin_key' } const paidTx = { toAddress: 'lnbc1000n1p3q7vlpp5example', cryptoCode: 'LN' } // Mock bolt11 decode const bolt11 = require('bolt11') const decodeStub = sinon.stub(bolt11, 'decode') decodeStub.returns({ tagsObject: { payment_hash: 'abc123' } }) // Mock axios for paid invoice const axiosStub = sinon.stub(axios, 'request') axiosStub.onFirstCall().resolves({ data: { paid: true, amount: 1000 } }) try { const status = await lnbits.getStatus(account, paidTx) t.equal(status.status, 'confirmed', 'Returns confirmed for paid invoice') } catch (err) { t.fail(`Unexpected error: ${err.message}`) } // Test pending invoice axiosStub.onSecondCall().resolves({ data: { paid: false, pending: true } }) try { const status = await lnbits.getStatus(account, paidTx) t.equal(status.status, 'pending', 'Returns pending for unpaid but pending invoice') } catch (err) { t.fail(`Unexpected error: ${err.message}`) } // Test not found invoice axiosStub.onThirdCall().rejects({ response: { status: 404, data: { detail: 'Payment not found' } } }) try { const status = await lnbits.getStatus(account, paidTx) t.equal(status.status, 'notSeen', 'Returns notSeen for not found invoice') } catch (err) { t.fail(`Unexpected error: ${err.message}`) } finally { decodeStub.restore() axiosStub.restore() } })