Refactor topic recency indexing: cut CRAP, harden property coverage

- Extract roomFromEntry/applyPost from backfill collectSummaries so its
  cyclomatic complexity drops from 7 to 4 (CRAP 7.0 -> 4.0), and extract
  the shared roomRange key bounds from getTopicPostTxids/listRoomFollowers.
- Add indexer property tests over arbitrary post/follow/unfollow
  interleavings: postCount conservation, newest-height recency shape,
  replay idempotence, and inverted-key ordering.
- Add DB property tests for the topic index backfill: conservation,
  recency shape, stale-record removal, and inverted-key ordering.
- Cover the topic action error paths (invalid push counts, oversized
  topic message), the topic-query room fallbacks, and the client
  openTopic/topicFeedPath navigation.

By refactorer.
This commit is contained in:
Chris Troutner
2026-09-17 08:07:24 -07:00
parent e02bea5460
commit 92ce290873
9 changed files with 568 additions and 11 deletions
@@ -110,6 +110,29 @@ test('getTopic returns null when the topic is not loaded', async () => {
assert.equal(page.getTopic('bitcoin'), null)
})
test('openTopic navigates to the encoded topic feed path', () => {
const calls = []
const page = new TopicDiscoveryPage({
memoDb: makeMemoDb({ topics: [] }),
navigate: (path) => calls.push(path)
})
const result = page.openTopic('space room')
assert.deepEqual(result, { path: '/topics/space%20room' })
assert.deepEqual(calls, ['/topics/space%20room'])
})
test('topicFeedPath percent-encodes the room name', () => {
assert.equal(TopicDiscoveryPage.topicFeedPath('a/b'), '/topics/a%2Fb')
})
test('openTopic uses a no-op navigate by default', () => {
const page = new TopicDiscoveryPage({ memoDb: makeMemoDb({ topics: [] }) })
assert.deepEqual(page.openTopic('bitcoin'), { path: '/topics/bitcoin' })
})
test('exposes the topics page path', () => {
assert.equal(TopicDiscoveryPage.TOPICS_PATH, '/topics')
})