Files
psf-memo/psf-memo-db/test/unit/use-cases/list-topics.unit.js
T
Chris Troutner e02bea5460 Implement topic recency ordering and pagination
- Indexer maintains topicSummaries and topicRecency for topic messages and
  follows, idempotently, moving each room's recency record to its newest post.
- DB adds the two index stores and serves GET /topics from them with
  limit/offset pagination, without iterating the rooms store. Adds
  util/room/backfill-topic-indexes.js to build the indexes idempotently.
- Client topics page loads 50 per page with Previous/Next.

By coder.
2026-09-17 07:24:45 -07:00

83 lines
2.2 KiB
JavaScript

import { assert } from 'chai'
import sinon from 'sinon'
import ListTopics from '../../../src/use-cases/list-topics.js'
describe('#ListTopics', () => {
let uut
let sandbox
let topicQuery
const adapterResult = {
topics: [
{ room: 'bitcoin', postCount: 2 },
{ room: 'cash', postCount: 1 }
],
pagination: { limit: 100, offset: 0, total: 2, hasMore: false }
}
beforeEach(() => {
sandbox = sinon.createSandbox()
topicQuery = {
listTopics: sandbox.stub().resolves(adapterResult)
}
uut = new ListTopics({
adapters: { topicQuery }
})
})
afterEach(() => sandbox.restore())
it('should throw when adapters are missing', () => {
try {
// eslint-disable-next-line no-new
new ListTopics({})
assert.fail('Expected error')
} catch (err) {
assert.include(err.message, 'Adapters required')
}
})
it('should throw when topicQuery adapter is missing', () => {
try {
// eslint-disable-next-line no-new
new ListTopics({ adapters: {} })
assert.fail('Expected error')
} catch (err) {
assert.include(err.message, 'topicQuery adapter required')
}
})
it('should return the adapter page with default limit and offset', async () => {
const result = await uut.execute()
assert.deepEqual(result, adapterResult)
assert.deepEqual(topicQuery.listTopics.firstCall.args[0], { limit: 100, offset: 0 })
})
it('should forward limit and offset to the adapter', async () => {
await uut.execute({ limit: 2, offset: 4 })
assert.deepEqual(topicQuery.listTopics.firstCall.args[0], { limit: 2, offset: 4 })
})
it('should reject a non-positive limit', async () => {
try {
await uut.execute({ limit: 0 })
assert.fail('Expected error')
} catch (err) {
assert.include(err.message, 'limit must be a positive integer')
assert.equal(err.status, 400)
}
})
it('should reject a negative offset', async () => {
try {
await uut.execute({ offset: -1 })
assert.fail('Expected error')
} catch (err) {
assert.include(err.message, 'offset must be a non-negative integer')
assert.equal(err.status, 400)
}
})
})