Files
jackboxpartypack-gamepicker/tests/api/votes-live-e2e.test.js

166 lines
5.4 KiB
JavaScript
Raw Permalink Normal View History

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);
});
});