test: regression tests for POST /api/votes/live
Made-with: Cursor
This commit is contained in:
199
tests/api/regression-votes-live.test.js
Normal file
199
tests/api/regression-votes-live.test.js
Normal file
@@ -0,0 +1,199 @@
|
||||
const request = require('supertest');
|
||||
const { app } = require('../../backend/server');
|
||||
const { getAuthHeader, cleanDb, seedGame, seedSession, seedSessionGame } = require('../helpers/test-utils');
|
||||
|
||||
describe('POST /api/votes/live (regression)', () => {
|
||||
let game, session, sessionGame;
|
||||
const baseTime = '2026-03-15T20:00:00.000Z';
|
||||
|
||||
beforeEach(() => {
|
||||
cleanDb();
|
||||
game = seedGame({ title: 'Quiplash 3', pack_name: 'Party Pack 7' });
|
||||
session = seedSession({ is_active: 1 });
|
||||
sessionGame = seedSessionGame(session.id, game.id, {
|
||||
status: 'playing',
|
||||
played_at: baseTime,
|
||||
});
|
||||
});
|
||||
|
||||
test('returns 200 with correct response shape for upvote', 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:05:00.000Z',
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body.success).toBe(true);
|
||||
expect(res.body.session).toEqual(
|
||||
expect.objectContaining({ id: session.id })
|
||||
);
|
||||
expect(res.body.game).toEqual(
|
||||
expect.objectContaining({
|
||||
id: game.id,
|
||||
title: 'Quiplash 3',
|
||||
upvotes: 1,
|
||||
downvotes: 0,
|
||||
popularity_score: 1,
|
||||
})
|
||||
);
|
||||
expect(res.body.vote).toEqual({
|
||||
username: 'viewer1',
|
||||
type: 'up',
|
||||
timestamp: '2026-03-15T20:05:00.000Z',
|
||||
});
|
||||
});
|
||||
|
||||
test('increments downvotes and decrements popularity_score for downvote', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/votes/live')
|
||||
.set('Authorization', getAuthHeader())
|
||||
.set('Content-Type', 'application/json')
|
||||
.send({
|
||||
username: 'viewer1',
|
||||
vote: 'down',
|
||||
timestamp: '2026-03-15T20:05:00.000Z',
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body.game.downvotes).toBe(1);
|
||||
expect(res.body.game.popularity_score).toBe(-1);
|
||||
});
|
||||
|
||||
test('returns 400 for missing username', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/votes/live')
|
||||
.set('Authorization', getAuthHeader())
|
||||
.set('Content-Type', 'application/json')
|
||||
.send({ vote: 'up', timestamp: '2026-03-15T20:05:00.000Z' });
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body.error).toMatch(/Missing required fields/);
|
||||
});
|
||||
|
||||
test('returns 400 for missing vote', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/votes/live')
|
||||
.set('Authorization', getAuthHeader())
|
||||
.set('Content-Type', 'application/json')
|
||||
.send({ username: 'viewer1', timestamp: '2026-03-15T20:05:00.000Z' });
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
|
||||
test('returns 400 for missing timestamp', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/votes/live')
|
||||
.set('Authorization', getAuthHeader())
|
||||
.set('Content-Type', 'application/json')
|
||||
.send({ username: 'viewer1', vote: 'up' });
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
|
||||
test('returns 400 for invalid vote value', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/votes/live')
|
||||
.set('Authorization', getAuthHeader())
|
||||
.set('Content-Type', 'application/json')
|
||||
.send({
|
||||
username: 'viewer1',
|
||||
vote: 'maybe',
|
||||
timestamp: '2026-03-15T20:05:00.000Z',
|
||||
});
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body.error).toMatch(/vote must be either/);
|
||||
});
|
||||
|
||||
test('returns 400 for invalid timestamp format', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/votes/live')
|
||||
.set('Authorization', getAuthHeader())
|
||||
.set('Content-Type', 'application/json')
|
||||
.send({
|
||||
username: 'viewer1',
|
||||
vote: 'up',
|
||||
timestamp: 'not-a-date',
|
||||
});
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body.error).toMatch(/Invalid timestamp/);
|
||||
});
|
||||
|
||||
test('returns 404 when no active session', async () => {
|
||||
cleanDb();
|
||||
seedGame({ title: 'Unused' });
|
||||
seedSession({ is_active: 0 });
|
||||
|
||||
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:05:00.000Z',
|
||||
});
|
||||
|
||||
expect(res.status).toBe(404);
|
||||
expect(res.body.error).toMatch(/No active session/);
|
||||
});
|
||||
|
||||
test('returns 404 when vote timestamp does not match any game', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/votes/live')
|
||||
.set('Authorization', getAuthHeader())
|
||||
.set('Content-Type', 'application/json')
|
||||
.send({
|
||||
username: 'viewer1',
|
||||
vote: 'up',
|
||||
timestamp: '2020-01-01T00:00:00.000Z',
|
||||
});
|
||||
|
||||
expect(res.status).toBe(404);
|
||||
expect(res.body.error).toMatch(/does not match any game/);
|
||||
});
|
||||
|
||||
test('returns 409 for duplicate vote within 1 second', 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:05:00.000Z',
|
||||
});
|
||||
|
||||
const res = await request(app)
|
||||
.post('/api/votes/live')
|
||||
.set('Authorization', getAuthHeader())
|
||||
.set('Content-Type', 'application/json')
|
||||
.send({
|
||||
username: 'viewer1',
|
||||
vote: 'down',
|
||||
timestamp: '2026-03-15T20:05:00.500Z',
|
||||
});
|
||||
|
||||
expect(res.status).toBe(409);
|
||||
expect(res.body.error).toMatch(/Duplicate vote/);
|
||||
});
|
||||
|
||||
test('returns 401 without auth token', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/votes/live')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send({
|
||||
username: 'viewer1',
|
||||
vote: 'up',
|
||||
timestamp: '2026-03-15T20:05:00.000Z',
|
||||
});
|
||||
|
||||
expect(res.status).toBe(401);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user