const request = require('supertest'); const { app } = require('../../backend/server'); const { getAuthHeader, cleanDb, seedGame, seedSession, seedSessionGame, db } = require('../helpers/test-utils'); describe('POST /api/votes/live -> read-back (end-to-end)', () => { let game1, game2, session; const baseTime = '2026-03-15T20:00:00.000Z'; const game2Time = '2026-03-15T20:30:00.000Z'; beforeEach(() => { cleanDb(); game1 = seedGame({ title: 'Quiplash 3', pack_name: 'Party Pack 7' }); game2 = seedGame({ title: 'Drawful 2', pack_name: 'Party Pack 3' }); session = seedSession({ is_active: 1 }); seedSessionGame(session.id, game1.id, { status: 'played', played_at: baseTime }); seedSessionGame(session.id, game2.id, { status: 'playing', played_at: game2Time }); }); test('vote via POST populates live_votes table (direct DB check)', async () => { const res = await request(app) .post('/api/votes/live') .set('Authorization', getAuthHeader()) .set('Content-Type', 'application/json') .send({ username: 'viewer1', vote: 'up', timestamp: '2026-03-15T20:35:00.000Z', }); expect(res.status).toBe(200); const row = db.prepare( 'SELECT * FROM live_votes WHERE session_id = ? AND game_id = ? AND username = ?' ).get(session.id, game2.id, 'viewer1'); expect(row).toBeDefined(); expect(row.session_id).toBe(session.id); expect(row.game_id).toBe(game2.id); expect(row.username).toBe('viewer1'); expect(row.vote_type).toBe(1); expect(row.timestamp).toBe('2026-03-15T20:35:00.000Z'); }); test('vote via POST is visible in GET /api/sessions/:id/votes', async () => { await request(app) .post('/api/votes/live') .set('Authorization', getAuthHeader()) .set('Content-Type', 'application/json') .send({ username: 'viewer1', vote: 'up', timestamp: '2026-03-15T20:35:00.000Z', }); await request(app) .post('/api/votes/live') .set('Authorization', getAuthHeader()) .set('Content-Type', 'application/json') .send({ username: 'viewer2', vote: 'down', timestamp: '2026-03-15T20:36:00.000Z', }); const res = await request(app).get(`/api/sessions/${session.id}/votes`); expect(res.status).toBe(200); expect(res.body.session_id).toBe(session.id); expect(res.body.votes).toHaveLength(1); const gameVotes = res.body.votes[0]; expect(gameVotes.game_id).toBe(game2.id); expect(gameVotes.title).toBe('Drawful 2'); expect(gameVotes.upvotes).toBe(1); expect(gameVotes.downvotes).toBe(1); expect(gameVotes.net_score).toBe(0); expect(gameVotes.total_votes).toBe(2); }); test('vote via POST is visible in GET /api/votes', async () => { await request(app) .post('/api/votes/live') .set('Authorization', getAuthHeader()) .set('Content-Type', 'application/json') .send({ username: 'viewer1', vote: 'up', timestamp: '2026-03-15T20:35:00.000Z', }); const res = await request(app).get(`/api/votes?session_id=${session.id}`); expect(res.status).toBe(200); expect(res.body.votes).toHaveLength(1); expect(res.body.votes[0].username).toBe('viewer1'); expect(res.body.votes[0].vote_type).toBe('up'); expect(res.body.votes[0].game_id).toBe(game2.id); expect(res.body.votes[0].game_title).toBe('Drawful 2'); expect(res.body.votes[0].session_id).toBe(session.id); }); test('votes for different games in same session are tracked separately', async () => { await request(app) .post('/api/votes/live') .set('Authorization', getAuthHeader()) .set('Content-Type', 'application/json') .send({ username: 'viewer1', vote: 'up', timestamp: '2026-03-15T20:10:00.000Z', }); await request(app) .post('/api/votes/live') .set('Authorization', getAuthHeader()) .set('Content-Type', 'application/json') .send({ username: 'viewer2', vote: 'down', timestamp: '2026-03-15T20:35:00.000Z', }); const res = await request(app).get(`/api/sessions/${session.id}/votes`); expect(res.status).toBe(200); expect(res.body.votes).toHaveLength(2); const q3 = res.body.votes.find((v) => v.game_id === game1.id); expect(q3.upvotes).toBe(1); expect(q3.downvotes).toBe(0); const d2 = res.body.votes.find((v) => v.game_id === game2.id); expect(d2.upvotes).toBe(0); expect(d2.downvotes).toBe(1); }); test('live_votes row count matches number of accepted votes', async () => { const timestamps = [ '2026-03-15T20:35:00.000Z', '2026-03-15T20:36:05.000Z', '2026-03-15T20:37:10.000Z', ]; for (let i = 0; i < timestamps.length; i++) { const res = await request(app) .post('/api/votes/live') .set('Authorization', getAuthHeader()) .set('Content-Type', 'application/json') .send({ username: `viewer${i}`, vote: i % 2 === 0 ? 'up' : 'down', timestamp: timestamps[i], }); expect(res.status).toBe(200); } const count = db.prepare( 'SELECT COUNT(*) as cnt FROM live_votes WHERE session_id = ?' ).get(session.id); expect(count.cnt).toBe(3); const sessionVotes = await request(app).get(`/api/sessions/${session.id}/votes`); expect(sessionVotes.body.votes[0].total_votes).toBe(3); }); });