diff --git a/backend/routes/sessions.js b/backend/routes/sessions.js index 9f262df..48a3608 100644 --- a/backend/routes/sessions.js +++ b/backend/routes/sessions.js @@ -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 }); diff --git a/tests/api/session-archive.test.js b/tests/api/session-archive.test.js new file mode 100644 index 0000000..f1f205b --- /dev/null +++ b/tests/api/session-archive.test.js @@ -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); + }); +});