test: add jest/supertest infrastructure and make server.js testable
Made-with: Cursor
This commit is contained in:
6540
backend/package-lock.json
generated
Normal file
6540
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,24 +5,27 @@
|
|||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server.js",
|
"start": "node server.js",
|
||||||
"dev": "nodemon server.js"
|
"dev": "nodemon server.js",
|
||||||
|
"test": "jest --config ../jest.config.js --runInBand --verbose",
|
||||||
|
"test:watch": "jest --config ../jest.config.js --runInBand --watch"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2",
|
|
||||||
"cors": "^2.8.5",
|
|
||||||
"better-sqlite3": "^9.2.2",
|
"better-sqlite3": "^9.2.2",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.3.1",
|
|
||||||
"csv-parse": "^5.5.3",
|
"csv-parse": "^5.5.3",
|
||||||
"csv-stringify": "^6.4.5",
|
"csv-stringify": "^6.4.5",
|
||||||
"ws": "^8.14.0",
|
"dotenv": "^16.3.1",
|
||||||
"puppeteer": "^24.0.0"
|
"express": "^4.18.2",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"puppeteer": "^24.0.0",
|
||||||
|
"ws": "^8.14.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^3.0.2"
|
"jest": "^29.7.0",
|
||||||
|
"nodemon": "^3.0.2",
|
||||||
|
"supertest": "^6.3.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ const PORT = process.env.PORT || 5000;
|
|||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
// Bootstrap database with games
|
|
||||||
bootstrapGames();
|
|
||||||
|
|
||||||
// Health check
|
// Health check
|
||||||
app.get('/health', (req, res) => {
|
app.get('/health', (req, res) => {
|
||||||
res.json({ status: 'ok', message: 'Jackbox Game Picker API is running' });
|
res.json({ status: 'ok', message: 'Jackbox Game Picker API is running' });
|
||||||
@@ -50,8 +47,12 @@ const server = http.createServer(app);
|
|||||||
const wsManager = new WebSocketManager(server);
|
const wsManager = new WebSocketManager(server);
|
||||||
setWebSocketManager(wsManager);
|
setWebSocketManager(wsManager);
|
||||||
|
|
||||||
server.listen(PORT, '0.0.0.0', () => {
|
if (require.main === module) {
|
||||||
|
bootstrapGames();
|
||||||
|
server.listen(PORT, '0.0.0.0', () => {
|
||||||
console.log(`Server is running on port ${PORT}`);
|
console.log(`Server is running on port ${PORT}`);
|
||||||
console.log(`WebSocket server available at ws://localhost:${PORT}/api/sessions/live`);
|
console.log(`WebSocket server available at ws://localhost:${PORT}/api/sessions/live`);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { app, server };
|
||||||
|
|||||||
8
jest.config.js
Normal file
8
jest.config.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
module.exports = {
|
||||||
|
testEnvironment: 'node',
|
||||||
|
roots: ['<rootDir>/tests'],
|
||||||
|
setupFiles: ['<rootDir>/tests/jest.setup.js'],
|
||||||
|
testMatch: ['**/*.test.js'],
|
||||||
|
testTimeout: 10000,
|
||||||
|
moduleDirectories: ['node_modules', '<rootDir>/backend/node_modules'],
|
||||||
|
};
|
||||||
81
tests/helpers/test-utils.js
Normal file
81
tests/helpers/test-utils.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const db = require('../../backend/database');
|
||||||
|
|
||||||
|
function getAuthToken() {
|
||||||
|
return jwt.sign({ role: 'admin' }, process.env.JWT_SECRET, { expiresIn: '1h' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAuthHeader() {
|
||||||
|
return `Bearer ${getAuthToken()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanDb() {
|
||||||
|
db.exec('DELETE FROM live_votes');
|
||||||
|
db.exec('DELETE FROM chat_logs');
|
||||||
|
db.exec('DELETE FROM session_games');
|
||||||
|
db.exec('DELETE FROM sessions');
|
||||||
|
db.exec('DELETE FROM webhook_logs');
|
||||||
|
db.exec('DELETE FROM webhooks');
|
||||||
|
db.exec('DELETE FROM games');
|
||||||
|
db.exec('DELETE FROM packs');
|
||||||
|
}
|
||||||
|
|
||||||
|
function seedGame(overrides = {}) {
|
||||||
|
const defaults = {
|
||||||
|
pack_name: 'Party Pack 7',
|
||||||
|
title: 'Quiplash 3',
|
||||||
|
min_players: 3,
|
||||||
|
max_players: 8,
|
||||||
|
length_minutes: 15,
|
||||||
|
has_audience: 1,
|
||||||
|
family_friendly: 1,
|
||||||
|
game_type: 'Writing',
|
||||||
|
enabled: 1,
|
||||||
|
upvotes: 0,
|
||||||
|
downvotes: 0,
|
||||||
|
popularity_score: 0,
|
||||||
|
};
|
||||||
|
const g = { ...defaults, ...overrides };
|
||||||
|
const result = db.prepare(`
|
||||||
|
INSERT INTO games (pack_name, title, min_players, max_players, length_minutes, has_audience, family_friendly, game_type, enabled, upvotes, downvotes, popularity_score)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
`).run(g.pack_name, g.title, g.min_players, g.max_players, g.length_minutes, g.has_audience, g.family_friendly, g.game_type, g.enabled, g.upvotes, g.downvotes, g.popularity_score);
|
||||||
|
return db.prepare('SELECT * FROM games WHERE id = ?').get(result.lastInsertRowid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function seedSession(overrides = {}) {
|
||||||
|
const defaults = { is_active: 1, notes: null };
|
||||||
|
const s = { ...defaults, ...overrides };
|
||||||
|
const result = db.prepare('INSERT INTO sessions (is_active, notes) VALUES (?, ?)').run(s.is_active, s.notes);
|
||||||
|
return db.prepare('SELECT * FROM sessions WHERE id = ?').get(result.lastInsertRowid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function seedSessionGame(sessionId, gameId, overrides = {}) {
|
||||||
|
const defaults = { status: 'playing', played_at: new Date().toISOString() };
|
||||||
|
const sg = { ...defaults, ...overrides };
|
||||||
|
const result = db.prepare(`
|
||||||
|
INSERT INTO session_games (session_id, game_id, status, played_at)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
`).run(sessionId, gameId, sg.status, sg.played_at);
|
||||||
|
return db.prepare('SELECT * FROM session_games WHERE id = ?').get(result.lastInsertRowid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function seedVote(sessionId, gameId, username, voteType, timestamp) {
|
||||||
|
const vt = voteType === 'up' ? 1 : -1;
|
||||||
|
const ts = timestamp || new Date().toISOString();
|
||||||
|
db.prepare(`
|
||||||
|
INSERT INTO live_votes (session_id, game_id, username, vote_type, timestamp)
|
||||||
|
VALUES (?, ?, ?, ?, ?)
|
||||||
|
`).run(sessionId, gameId, username, vt, ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getAuthToken,
|
||||||
|
getAuthHeader,
|
||||||
|
cleanDb,
|
||||||
|
seedGame,
|
||||||
|
seedSession,
|
||||||
|
seedSessionGame,
|
||||||
|
seedVote,
|
||||||
|
db,
|
||||||
|
};
|
||||||
4
tests/jest.setup.js
Normal file
4
tests/jest.setup.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
process.env.DB_PATH = ':memory:';
|
||||||
|
process.env.JWT_SECRET = 'test-jwt-secret-do-not-use-in-prod';
|
||||||
|
process.env.ADMIN_KEY = 'test-admin-key';
|
||||||
|
process.env.PORT = '0';
|
||||||
Reference in New Issue
Block a user