Files
psf-memo/psf-memo-client/test/unit/memo-poll-option.test.js
T
Chris Troutner 2e2db86263 Implement multi-push encoding for Memo multi-field actions
Reply, topic message, add-poll-option, poll-vote, and create-poll now
broadcast each protocol field as its own OP_RETURN script push instead of
combining them into one push. The indexer requires three pushes for reply,
topic message, and poll children, and accepts four for create-poll; the
combined form was rejected or misparsed.

hex.js builds the txid and text as separate pushes, and a new
memo-multipush adapter teaches minimal-slp-wallet's single-push sendOpReturn
to expand an array of fields into separate pushes. Acceptance step handlers
assert the individual pushes, and unit/property tests cover the new shape.

By coder.
2026-09-16 20:37:59 -07:00

117 lines
3.5 KiB
JavaScript

/*
Unit tests for the Memo add-poll-option behavior.
An add-poll-option transaction carries the Memo add-poll-option protocol
prefix (0x6d13) followed by the poll's 32-byte txid and the option text.
The option text is limited to 184 bytes.
*/
'use strict'
const test = require('node:test')
const assert = require('node:assert/strict')
const MemoPollOption = require('../../src/services/memo-poll-option')
const MY_ADDRESS = 'bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d'
// A non-palindromic txid so a missing byte reversal is observable.
const POLL_TXID = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
function makeWallet (address = MY_ADDRESS) {
return {
walletInfo: { cashAddress: address },
broadcasts: [],
async getUtxos () {
return []
},
async sendOpReturn (msg, prefix) {
this.broadcasts.push({ msg, prefix })
return 'aa'.repeat(32)
}
}
}
test('add broadcasts the poll txid and option as separate pushes', async () => {
const wallet = makeWallet()
const memoPollOption = new MemoPollOption({ wallet, pollTxid: POLL_TXID })
await memoPollOption.add('yes')
assert.equal(wallet.broadcasts.length, 1)
assert.equal(wallet.broadcasts[0].prefix, MemoPollOption.MEMO_ADD_POLL_OPTION_PREFIX)
const pushes = wallet.broadcasts[0].msg
assert.ok(Array.isArray(pushes), 'expected separate pushes')
assert.equal(pushes.length, 2)
assert.equal(Buffer.from(pushes[0]).reverse().toString('hex'), POLL_TXID)
assert.equal(Buffer.from(pushes[1]).toString('utf8'), 'yes')
})
test('add reflects the new option on the injected poll store', async () => {
const wallet = makeWallet()
const added = []
const polls = {
addOption (option) {
added.push(option)
}
}
const memoPollOption = new MemoPollOption({ wallet, pollTxid: POLL_TXID, polls })
await memoPollOption.add('definitely')
assert.equal(added.length, 1)
assert.equal(added[0].option, 'definitely')
assert.equal(added[0].pollTxid, POLL_TXID)
assert.equal(added[0].address, wallet.walletInfo.cashAddress)
})
test('add rejects an empty option', async () => {
const wallet = makeWallet()
const memoPollOption = new MemoPollOption({ wallet, pollTxid: POLL_TXID })
await assert.rejects(
() => memoPollOption.add(''),
{ code: 'poll_option_validation', message: /must not be empty/ }
)
assert.equal(wallet.broadcasts.length, 0)
})
test('add rejects an option that exceeds the byte limit', async () => {
const wallet = makeWallet()
const memoPollOption = new MemoPollOption({ wallet, pollTxid: POLL_TXID })
const option = 'a'.repeat(185)
await assert.rejects(
() => memoPollOption.add(option),
{ code: 'poll_option_length', message: /too long/ }
)
assert.equal(wallet.broadcasts.length, 0)
})
test('add accepts an option at the byte limit', async () => {
const wallet = makeWallet()
const memoPollOption = new MemoPollOption({ wallet, pollTxid: POLL_TXID })
const option = 'a'.repeat(MemoPollOption.MAX_OPTION_BYTES)
await memoPollOption.add(option)
assert.equal(wallet.broadcasts.length, 1)
})
test('add requires a wallet', async () => {
const memoPollOption = new MemoPollOption({ pollTxid: POLL_TXID })
await assert.rejects(
() => memoPollOption.add('yes'),
/requires a wallet/
)
})
test('add requires a poll txid', async () => {
const wallet = makeWallet()
const memoPollOption = new MemoPollOption({ wallet })
await assert.rejects(
() => memoPollOption.add('yes'),
{ code: 'poll_option_validation', message: /Poll txid is required/ }
)
})