mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo.git
synced 2026-09-21 16:52:01 -07:00
Refactor topic feed: reduce CRAP, DRY, and add property coverage
- Add topics controller error-handling unit tests to bring handleError CRAP from 14.7 to 4.0. - Consolidate duplicate getTopicPosts/getPostsByAddr HTTP page fetches into a shared getPage helper in memo-db (DRY). - Add TopicQuery property tests: listTopics conservation/ordering, getTopicPostTxids newest-first pagination, and room/txid key round-trips. - Add client topic-services property tests for getTopic/getPost lookup completeness. By refactorer.
This commit is contained in:
@@ -52,17 +52,8 @@ class MemoDb {
|
||||
return this.getRecent('/topics', 'getTopics', {})
|
||||
}
|
||||
|
||||
async getTopicPosts (room, { limit = 100, offset = 0 } = {}) {
|
||||
try {
|
||||
const result = await this.axios.get(
|
||||
`${config.backend}/topics/${encodeURIComponent(room)}/posts`,
|
||||
{ params: { limit, offset } }
|
||||
)
|
||||
return result.data
|
||||
} catch (err) {
|
||||
console.error('Error in getTopicPosts()')
|
||||
throw err
|
||||
}
|
||||
async getTopicPosts (room, opts = {}) {
|
||||
return this.getPage(`/topics/${encodeURIComponent(room)}/posts`, 'getTopicPosts', opts)
|
||||
}
|
||||
|
||||
// GET a paginated 'recent' listing endpoint.
|
||||
@@ -79,6 +70,20 @@ class MemoDb {
|
||||
}
|
||||
}
|
||||
|
||||
// GET a paginated resource page at a full path.
|
||||
async getPage (path, name, { limit = 100, offset = 0 } = {}) {
|
||||
try {
|
||||
const result = await this.axios.get(`${config.backend}${path}`, {
|
||||
params: { limit, offset }
|
||||
})
|
||||
|
||||
return result.data
|
||||
} catch (err) {
|
||||
console.error(`Error in ${name}()`)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// GET a level endpoint that resolves an address. Returns null on 404.
|
||||
async getLevelResource (endpoint, addr, name) {
|
||||
try {
|
||||
@@ -95,18 +100,8 @@ class MemoDb {
|
||||
}
|
||||
}
|
||||
|
||||
async getPostsByAddr (addr, { limit = 100, offset = 0 } = {}) {
|
||||
try {
|
||||
const result = await this.axios.get(
|
||||
`${config.backend}/posts/by/${encodeURIComponent(addr)}`,
|
||||
{ params: { limit, offset } }
|
||||
)
|
||||
|
||||
return result.data
|
||||
} catch (err) {
|
||||
console.error('Error in getPostsByAddr()')
|
||||
throw err
|
||||
}
|
||||
async getPostsByAddr (addr, opts = {}) {
|
||||
return this.getPage(`/posts/by/${encodeURIComponent(addr)}`, 'getPostsByAddr', opts)
|
||||
}
|
||||
|
||||
async getPostThread (txid) {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
Property tests for the topic discovery and topic feed page controllers.
|
||||
|
||||
The unit tests probe getTopic / getPost at a few fixed fixtures. These
|
||||
properties pin down the lookup invariants over broad random inputs:
|
||||
|
||||
- TopicDiscoveryPage.getTopic finds any topic that was loaded.
|
||||
- TopicFeedPage.getPost finds any post that was loaded.
|
||||
- Lookups are stable: a loaded item is always found, and an unknown key
|
||||
returns null.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const test = require('node:test')
|
||||
const { seededRandom, forAll, intGen } = require('./harness')
|
||||
const TopicDiscoveryPage = require('../../src/services/topic-discovery-page')
|
||||
const TopicFeedPage = require('../../src/services/topic-feed-page')
|
||||
|
||||
const rng = seededRandom(20260828)
|
||||
|
||||
function randomRoom () {
|
||||
return 'room-' + Math.floor(rng() * 1e6)
|
||||
}
|
||||
|
||||
function randomTxid () {
|
||||
return 't' + rng().toString(36).slice(2, 10) + Math.floor(rng() * 1e6)
|
||||
}
|
||||
|
||||
test('TopicDiscoveryPage.getTopic finds any loaded topic', async () => {
|
||||
await forAll(
|
||||
(i) => {
|
||||
const n = intGen(rng, 0, 20)()
|
||||
const topics = []
|
||||
for (let j = 0; j < n; j++) {
|
||||
topics.push({ room: randomRoom(), postCount: intGen(rng, 0, 1000)() })
|
||||
}
|
||||
return topics
|
||||
},
|
||||
async (topics) => {
|
||||
const memoDb = { async getTopics () { return { topics } } }
|
||||
const page = new TopicDiscoveryPage({ memoDb })
|
||||
await page.load()
|
||||
|
||||
for (const topic of topics) {
|
||||
const found = page.getTopic(topic.room)
|
||||
if (!found || found.room !== topic.room) return false
|
||||
}
|
||||
if (page.getTopic('does-not-exist') !== null) return false
|
||||
return true
|
||||
},
|
||||
{ label: 'topic discovery lookup completeness' }
|
||||
)
|
||||
})
|
||||
|
||||
test('TopicFeedPage.getPost finds any loaded post', async () => {
|
||||
await forAll(
|
||||
(i) => {
|
||||
const n = intGen(rng, 0, 20)()
|
||||
const posts = []
|
||||
for (let j = 0; j < n; j++) {
|
||||
posts.push({ txid: randomTxid(), text: 'post ' + j })
|
||||
}
|
||||
return posts
|
||||
},
|
||||
async (posts) => {
|
||||
const memoDb = { async getTopicPosts () { return { posts, pagination: { total: posts.length } } } }
|
||||
const page = new TopicFeedPage({ memoDb, room: 'bitcoin' })
|
||||
await page.load()
|
||||
|
||||
for (const post of posts) {
|
||||
const found = page.getPost(post.txid)
|
||||
if (!found || found.txid !== post.txid) return false
|
||||
}
|
||||
if (page.getPost('does-not-exist') !== null) return false
|
||||
return true
|
||||
},
|
||||
{ label: 'topic feed lookup completeness' }
|
||||
)
|
||||
})
|
||||
Reference in New Issue
Block a user