feat: add filter, limit, and X-Total-Count to session list endpoint
Made-with: Cursor
This commit is contained in:
@@ -21,18 +21,45 @@ function createMessageHash(username, message, timestamp) {
|
||||
// Get all sessions
|
||||
router.get('/', (req, res) => {
|
||||
try {
|
||||
const filter = req.query.filter || 'default';
|
||||
const limitParam = req.query.limit || 'all';
|
||||
|
||||
let whereClause = '';
|
||||
if (filter === 'default') {
|
||||
whereClause = 'WHERE s.archived = 0';
|
||||
} else if (filter === 'archived') {
|
||||
whereClause = 'WHERE s.archived = 1';
|
||||
}
|
||||
|
||||
const countRow = db.prepare(`
|
||||
SELECT COUNT(DISTINCT s.id) as total
|
||||
FROM sessions s
|
||||
${whereClause}
|
||||
`).get();
|
||||
|
||||
let limitClause = '';
|
||||
if (limitParam !== 'all') {
|
||||
const limitNum = parseInt(limitParam, 10);
|
||||
if (!isNaN(limitNum) && limitNum > 0) {
|
||||
limitClause = `LIMIT ${limitNum}`;
|
||||
}
|
||||
}
|
||||
|
||||
const sessions = db.prepare(`
|
||||
SELECT
|
||||
s.id,
|
||||
s.created_at,
|
||||
s.closed_at,
|
||||
s.is_active,
|
||||
s.archived,
|
||||
s.notes,
|
||||
COUNT(sg.id) as games_played
|
||||
FROM sessions s
|
||||
LEFT JOIN session_games sg ON s.id = sg.session_id
|
||||
${whereClause}
|
||||
GROUP BY s.id
|
||||
ORDER BY s.created_at DESC
|
||||
${limitClause}
|
||||
`).all();
|
||||
|
||||
const result = sessions.map(({ notes, ...session }) => {
|
||||
@@ -40,6 +67,7 @@ router.get('/', (req, res) => {
|
||||
return { ...session, has_notes, notes_preview };
|
||||
});
|
||||
|
||||
res.set('X-Total-Count', String(countRow.total));
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
|
||||
95
tests/api/session-archive.test.js
Normal file
95
tests/api/session-archive.test.js
Normal file
@@ -0,0 +1,95 @@
|
||||
const request = require('supertest');
|
||||
const { app } = require('../../backend/server');
|
||||
const { cleanDb, getAuthHeader, seedSession } = require('../helpers/test-utils');
|
||||
|
||||
describe('GET /api/sessions — filter and limit', () => {
|
||||
beforeEach(() => {
|
||||
cleanDb();
|
||||
});
|
||||
|
||||
test('default filter excludes archived sessions', async () => {
|
||||
seedSession({ is_active: 0, notes: null });
|
||||
const archived = seedSession({ is_active: 0, notes: null });
|
||||
require('../helpers/test-utils').db.prepare(
|
||||
'UPDATE sessions SET archived = 1 WHERE id = ?'
|
||||
).run(archived.id);
|
||||
|
||||
const res = await request(app).get('/api/sessions');
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toHaveLength(1);
|
||||
expect(res.body[0].archived).toBe(0);
|
||||
});
|
||||
|
||||
test('filter=archived returns only archived sessions', async () => {
|
||||
seedSession({ is_active: 0, notes: null });
|
||||
const archived = seedSession({ is_active: 0, notes: null });
|
||||
require('../helpers/test-utils').db.prepare(
|
||||
'UPDATE sessions SET archived = 1 WHERE id = ?'
|
||||
).run(archived.id);
|
||||
|
||||
const res = await request(app).get('/api/sessions?filter=archived');
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toHaveLength(1);
|
||||
expect(res.body[0].archived).toBe(1);
|
||||
});
|
||||
|
||||
test('filter=all returns all sessions', async () => {
|
||||
seedSession({ is_active: 0, notes: null });
|
||||
const archived = seedSession({ is_active: 0, notes: null });
|
||||
require('../helpers/test-utils').db.prepare(
|
||||
'UPDATE sessions SET archived = 1 WHERE id = ?'
|
||||
).run(archived.id);
|
||||
|
||||
const res = await request(app).get('/api/sessions?filter=all');
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toHaveLength(2);
|
||||
});
|
||||
|
||||
test('limit restricts number of sessions returned', async () => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
seedSession({ is_active: 0, notes: null });
|
||||
}
|
||||
|
||||
const res = await request(app).get('/api/sessions?filter=all&limit=3');
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toHaveLength(3);
|
||||
});
|
||||
|
||||
test('limit=all returns all sessions', async () => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
seedSession({ is_active: 0, notes: null });
|
||||
}
|
||||
|
||||
const res = await request(app).get('/api/sessions?filter=all&limit=all');
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toHaveLength(10);
|
||||
});
|
||||
|
||||
test('X-Total-Count header reflects total matching sessions before limit', async () => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
seedSession({ is_active: 0, notes: null });
|
||||
}
|
||||
|
||||
const res = await request(app).get('/api/sessions?filter=all&limit=3');
|
||||
expect(res.headers['x-total-count']).toBe('10');
|
||||
expect(res.body).toHaveLength(3);
|
||||
});
|
||||
|
||||
test('response includes archived field on each session', async () => {
|
||||
seedSession({ is_active: 0, notes: null });
|
||||
|
||||
const res = await request(app).get('/api/sessions?filter=all');
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body[0]).toHaveProperty('archived', 0);
|
||||
});
|
||||
|
||||
test('default limit is all when no limit param provided', async () => {
|
||||
for (let i = 0; i < 8; i++) {
|
||||
seedSession({ is_active: 0, notes: null });
|
||||
}
|
||||
|
||||
const res = await request(app).get('/api/sessions?filter=all');
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toHaveLength(8);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user