mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo.git
synced 2026-09-21 16:52:01 -07:00
Review txid wire encoding: harden mutation coverage and DRY test doubles
Refresh the mutate4javascript and Gherkin acceptance-mutation manifests, kill the memo-like/memo-reply boundary survivors, and extract the shared FakeDb/makeLevel double used by the txid repair unit and property tests. By architect.
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
# acceptance-mutation-manifest-begin
|
||||
# {"version":1,"tested_at":"2026-09-16T22:22:12.675245296Z","feature_name":"Txid wire encoding","feature_path":"/home/trout/work/psf-memo/.worktrees/architect/psf-memo-client/specs/txid-wire-encoding.feature","background_hash":"e1d5f81f1ed083ac6934c429ca3cb4a0f8d4dac44c2eaa45c0960920bde2c017","implementation_hash":"unknown","scenarios":[{"index":0,"name":"Txid wire encoding - 1 a like broadcasts the post txid in little-endian wire order","scenario_hash":"42f1932ce013d74c217b8becad4e7f496e84e5236a2728dcc2418b57d67bd6e6","mutation_count":2,"result":{"Total":2,"Killed":2,"Survived":0,"Errors":0},"tested_at":"2026-09-16T22:22:12.675245296Z"}]}
|
||||
# acceptance-mutation-manifest-end
|
||||
|
||||
# Scenarios: Txid wire encoding - 1, Txid wire encoding - 2, Txid wire encoding - 3, Txid wire encoding - 4
|
||||
#
|
||||
# Memo actions that reference a parent transaction embed that txid as 32 raw
|
||||
|
||||
@@ -48,5 +48,5 @@ function buildTxidTextPayload (txid, text, label = 'Poll txid') {
|
||||
module.exports = { hexToBytes, txidToWireBytes, buildTxidTextPayload }
|
||||
|
||||
// mutate4javascript-manifest-begin
|
||||
// {"version":1,"tested_at":"2026-08-28T22:43:42.484Z","module_hash":"7f3445b7df4d792113f2736232f69faee044f71f6b4a5985224615b36909607b","functions":[{"id":"func/hexToBytes","name":"hexToBytes","line":12,"end_line":26,"hash":"dbf7e0a434598f85365f5a60a0ca227a17a1bcd6718a78bb5aaf7afb8eb2487b"},{"id":"func/buildTxidTextPayload","name":"buildTxidTextPayload","line":30,"end_line":37,"hash":"542a47a13d30c845b7ac9a04079d7a131ece3368a604840ffef75adf97b11f7d"}]}
|
||||
// {"version":1,"tested_at":"2026-09-16T22:01:27.280Z","module_hash":"454b45f3ce08c5ea6346c113db6384f7a44dddbd21e2fb0a35c41ce6701e62c8","functions":[{"id":"func/hexToBytes","name":"hexToBytes","line":12,"end_line":26,"hash":"dbf7e0a434598f85365f5a60a0ca227a17a1bcd6718a78bb5aaf7afb8eb2487b"},{"id":"func/txidToWireBytes","name":"txidToWireBytes","line":32,"end_line":34,"hash":"ac25753fecb32c929134b6e6379cae0bfddd4aa6f7ae438a08da650450e9ea1a"},{"id":"func/buildTxidTextPayload","name":"buildTxidTextPayload","line":39,"end_line":46,"hash":"068504ea13a037adabbbfeab122fb5ec6113625a9e0b009cbbe8b46b6bebdaee"}]}
|
||||
// mutate4javascript-manifest-end
|
||||
|
||||
@@ -198,5 +198,5 @@ MemoLike.MAX_TIP_SATS = MAX_TIP_SATS
|
||||
module.exports = MemoLike
|
||||
|
||||
// mutate4javascript-manifest-begin
|
||||
// {"version":1,"tested_at":"2026-08-26T13:36:49.209Z","module_hash":"f27440db63274125b6b03bafd66222b31966670d8956b865e0bf2c695aacb54b","functions":[{"id":"func/MemoLike.constructor","name":"MemoLike.constructor","line":42,"end_line":47,"hash":"6ebbf231548843e23238988fde00cca861fd89513abba00febba964f3ef32365"},{"id":"func/MemoLike.validate","name":"MemoLike.validate","line":51,"end_line":60,"hash":"d9267f14ac52c7aa3afc07333b621c5faa751efa217bf02ae7695bb640827d44"},{"id":"func/MemoLike.validateTip","name":"MemoLike.validateTip","line":64,"end_line":74,"hash":"504b41cfa8db9754d31b0698550f9fe791d005c5b44bd30ec380202f1df11d6f"},{"id":"func/MemoLike._validateTipAmount","name":"MemoLike._validateTipAmount","line":77,"end_line":95,"hash":"98483bcf6212c2e0a2400d4da1b667b9f1c017a06ee6e4f282ccdf71cb8e397a"},{"id":"func/MemoLike.getSpendableSats","name":"MemoLike.getSpendableSats","line":99,"end_line":106,"hash":"7790154b7489758e6254cc7be00802fde237a33e0bb1440a2da33d303a2bdbe7"},{"id":"func/MemoLike.like","name":"MemoLike.like","line":112,"end_line":139,"hash":"5a748442d77446643448f1835b5e546569524af642af6be7c338e33ca4901051"},{"id":"func/MemoLike.reflect","name":"MemoLike.reflect","line":142,"end_line":145,"hash":"b159b912ba5b88468e5bfb7458385edc932850fe43948f3028e8ed9fea7269e9"},{"id":"func/MemoLike._requireTipAddress","name":"MemoLike._requireTipAddress","line":148,"end_line":154,"hash":"3b838a583b14112523e2cbe856a63896b1045a1ad91dac500e38693332b7c890"},{"id":"func/MemoLike._buildTipOutput","name":"MemoLike._buildTipOutput","line":157,"end_line":161,"hash":"e2a602c0afa9730dee67497869c4d18ed75708f6503d68fb1f0c0ba596826b5a"},{"id":"func/MemoLike._notifyFeed","name":"MemoLike._notifyFeed","line":164,"end_line":173,"hash":"bfc0e888ee106e21ee73146e92ebd3e4ce41d2ed8595a1539f823dc6c8f31d06"},{"id":"func/MemoLike._incrementPostCount","name":"MemoLike._incrementPostCount","line":176,"end_line":182,"hash":"61ae383f836b64a7aeabaf0d04e50ac9a24b87f726d5d6afac5f5585cdc6031c"}]}
|
||||
// {"version":1,"tested_at":"2026-09-16T22:10:37.562Z","module_hash":"62f705760252a76903f00c4f95b1b8f8a8608ad5c257c3c4a9e8b37f2b9e6cdd","functions":[{"id":"func/MemoLike.constructor","name":"MemoLike.constructor","line":44,"end_line":50,"hash":"058496fa3f123b3876e8312ed6effcc78dd2b11fb3e02f815d552dddf5c9bc22"},{"id":"func/MemoLike.validate","name":"MemoLike.validate","line":54,"end_line":63,"hash":"d9267f14ac52c7aa3afc07333b621c5faa751efa217bf02ae7695bb640827d44"},{"id":"func/MemoLike.validateTip","name":"MemoLike.validateTip","line":67,"end_line":77,"hash":"504b41cfa8db9754d31b0698550f9fe791d005c5b44bd30ec380202f1df11d6f"},{"id":"func/MemoLike._validateTipAmount","name":"MemoLike._validateTipAmount","line":80,"end_line":98,"hash":"b0f8a9d775f83c92aec63f0c4bf5b83a035648909cc9e3e4880903ea20c6a6d7"},{"id":"func/MemoLike.getSpendableSats","name":"MemoLike.getSpendableSats","line":102,"end_line":114,"hash":"e9ba4b8f5013276261487d4d5890f27d8724a34f35878416bbbd0aaa2ef221af"},{"id":"func/MemoLike.like","name":"MemoLike.like","line":120,"end_line":147,"hash":"109c1e6dbf56a76166b17cae013fbd4a5ac79d64958d6e28003174139d015a2e"},{"id":"func/MemoLike.reflect","name":"MemoLike.reflect","line":150,"end_line":153,"hash":"b159b912ba5b88468e5bfb7458385edc932850fe43948f3028e8ed9fea7269e9"},{"id":"func/MemoLike._requireTipAddress","name":"MemoLike._requireTipAddress","line":156,"end_line":162,"hash":"3b838a583b14112523e2cbe856a63896b1045a1ad91dac500e38693332b7c890"},{"id":"func/MemoLike._buildTipOutput","name":"MemoLike._buildTipOutput","line":165,"end_line":169,"hash":"e2a602c0afa9730dee67497869c4d18ed75708f6503d68fb1f0c0ba596826b5a"},{"id":"func/MemoLike._notifyFeed","name":"MemoLike._notifyFeed","line":172,"end_line":181,"hash":"bfc0e888ee106e21ee73146e92ebd3e4ce41d2ed8595a1539f823dc6c8f31d06"},{"id":"func/MemoLike._incrementPostCount","name":"MemoLike._incrementPostCount","line":184,"end_line":190,"hash":"61ae383f836b64a7aeabaf0d04e50ac9a24b87f726d5d6afac5f5585cdc6031c"}]}
|
||||
// mutate4javascript-manifest-end
|
||||
|
||||
@@ -85,5 +85,5 @@ MemoReply.MAX_REPLY_BYTES = MAX_REPLY_BYTES
|
||||
module.exports = MemoReply
|
||||
|
||||
// mutate4javascript-manifest-begin
|
||||
// {"version":1,"tested_at":"2026-08-26T12:18:17.625Z","module_hash":"3eb068f9f90f27c5d7acf2bb5a6c517d1085bcb379123ca2d7134d4fb48a092b","functions":[{"id":"func/MemoReply.constructor","name":"MemoReply.constructor","line":36,"end_line":39,"hash":"23091c1b8f7847199bab3b54d8d81e9d8432c6da96138d72ac03fcf9426d542c"},{"id":"func/MemoReply.isTooLong","name":"MemoReply.isTooLong","line":42,"end_line":44,"hash":"2e867501d184010313ba9b27a6bb1e446df8f093514ee231b90ce77699ecbaf2"},{"id":"func/MemoReply.reply","name":"MemoReply.reply","line":48,"end_line":67,"hash":"2d7b425e350b640caf6ca821146065e9c21fe56f1e8a9d8b1f6cab5504a523c1"},{"id":"func/MemoReply.reflect","name":"MemoReply.reflect","line":70,"end_line":79,"hash":"344e1bf304a4dfd475b02824b7ddbf555da0f3ec89f73b4f6009cf0bf097fb02"},{"id":"func/buildReplyPayload","name":"buildReplyPayload","line":84,"end_line":91,"hash":"ef9ee77938593f1dbf2d168c20ea4f8dee0300f64f0fdb4f06a4ba647bb782d5"},{"id":"func/hexToBytes","name":"hexToBytes","line":94,"end_line":107,"hash":"29b401020452eabcb1b54634029d8015758b77b536be3d1ed508e9d560ac93b1"}]}
|
||||
// {"version":1,"tested_at":"2026-09-16T22:14:19.213Z","module_hash":"6c515a91afe94a6be8671a1541302446bb6b1015d03ce0e278644508ebd20090","functions":[{"id":"func/MemoReply.constructor","name":"MemoReply.constructor","line":36,"end_line":39,"hash":"23091c1b8f7847199bab3b54d8d81e9d8432c6da96138d72ac03fcf9426d542c"},{"id":"func/MemoReply.isTooLong","name":"MemoReply.isTooLong","line":42,"end_line":44,"hash":"2e867501d184010313ba9b27a6bb1e446df8f093514ee231b90ce77699ecbaf2"},{"id":"func/MemoReply.reply","name":"MemoReply.reply","line":48,"end_line":67,"hash":"00fb3598d5c3bdc3c6744d1316ad552364574fed105438af4aa011347f3306ce"},{"id":"func/MemoReply.reflect","name":"MemoReply.reflect","line":70,"end_line":79,"hash":"344e1bf304a4dfd475b02824b7ddbf555da0f3ec89f73b4f6009cf0bf097fb02"}]}
|
||||
// mutate4javascript-manifest-end
|
||||
|
||||
@@ -175,3 +175,60 @@ test('_buildTipOutput omits the output for a zero tip', () => {
|
||||
|
||||
assert.deepEqual(memoLike._buildTipOutput(0, MY_ADDRESS), [])
|
||||
})
|
||||
|
||||
test('validate reports ok for a valid post txid', () => {
|
||||
const memoLike = new MemoLike()
|
||||
|
||||
assert.deepEqual(memoLike.validate(POST_TXID), { ok: true })
|
||||
})
|
||||
|
||||
test('validateTip reports ok when the tip exactly matches the spendable balance', () => {
|
||||
const memoLike = new MemoLike()
|
||||
|
||||
assert.deepEqual(memoLike.validateTip(3000, 3000), { ok: true })
|
||||
})
|
||||
|
||||
test('_validateTipAmount accepts a tip exactly at the maximum', () => {
|
||||
const memoLike = new MemoLike()
|
||||
|
||||
assert.equal(memoLike._validateTipAmount(MemoLike.MAX_TIP_SATS), undefined)
|
||||
})
|
||||
|
||||
test('getSpendableSats treats a utxo without a value field as zero', () => {
|
||||
const wallet = { utxos: [{ satoshis: 1000 }, { txid: 'no-value' }] }
|
||||
const memoLike = new MemoLike({ wallet })
|
||||
|
||||
assert.equal(memoLike.getSpendableSats(), 1000)
|
||||
})
|
||||
|
||||
test('_requireTipAddress requires an address for a positive tip', () => {
|
||||
const memoLike = new MemoLike()
|
||||
|
||||
assert.throws(
|
||||
() => memoLike._requireTipAddress(1, ''),
|
||||
(err) => err.code === 'like_validation'
|
||||
)
|
||||
})
|
||||
|
||||
test('_requireTipAddress accepts a one-character address', () => {
|
||||
const memoLike = new MemoLike()
|
||||
|
||||
assert.equal(memoLike._requireTipAddress(1000, 'a'), undefined)
|
||||
})
|
||||
|
||||
test('_buildTipOutput builds the output for a positive tip', () => {
|
||||
const memoLike = new MemoLike()
|
||||
|
||||
assert.deepEqual(memoLike._buildTipOutput(1, MY_ADDRESS), [
|
||||
{ address: MY_ADDRESS, amountSat: 1 }
|
||||
])
|
||||
})
|
||||
|
||||
test('_incrementPostCount defaults a missing likeCount to zero', () => {
|
||||
const feed = { posts: [{ txid: POST_TXID }] }
|
||||
const memoLike = new MemoLike({ feed })
|
||||
|
||||
memoLike._incrementPostCount(POST_TXID)
|
||||
|
||||
assert.equal(feed.posts[0].likeCount, 1)
|
||||
})
|
||||
|
||||
@@ -44,6 +44,18 @@ test('reply broadcasts the parent txid in little-endian wire order', async () =>
|
||||
assert.equal(buf.slice(32).toString('utf8'), 'hello memo')
|
||||
})
|
||||
|
||||
test('isTooLong accepts a message exactly at the byte limit', () => {
|
||||
const memoReply = new MemoReply()
|
||||
|
||||
assert.equal(memoReply.isTooLong('a'.repeat(MemoReply.MAX_REPLY_BYTES)), false)
|
||||
})
|
||||
|
||||
test('isTooLong rejects a message over the byte limit', () => {
|
||||
const memoReply = new MemoReply()
|
||||
|
||||
assert.equal(memoReply.isTooLong('a'.repeat(MemoReply.MAX_REPLY_BYTES + 1)), true)
|
||||
})
|
||||
|
||||
test('reply reflects the parent txid in display order on the thread store', async () => {
|
||||
const wallet = makeWallet()
|
||||
const added = []
|
||||
|
||||
Reference in New Issue
Block a user