'use strict'; /** * Integration tests: /api/game — прогресс игрока «Квантик» (Фаза 1). * Covers: submit создаёт строку; лучший результат перезаписывает, худший — нет; * attempts++; auth-only (401 без токена); валидация входа (400). */ const { describe, it, before, after } = require('node:test'); const assert = require('node:assert/strict'); const { app, inject, getToken, cleanup } = require('./setup'); // Mount /api/game on the shared test app (setup.js не монтирует новые роуты). app.use('/api/game', require('../src/routes/game')); after(() => cleanup()); const LVL = 'phys-artillery-1'; describe('/api/game progress', () => { let token; before(async () => { token = (await getToken('student')).token; }); it('GET /progress requires auth (401)', async () => { const res = await inject('GET', '/api/game/progress', null, null); assert.equal(res.status, 401, `got ${res.status}`); }); it('POST /progress requires auth (401)', async () => { const res = await inject('POST', '/api/game/progress', { level_id: LVL, time_ms: 1000, stars: 1 }, null); assert.equal(res.status, 401, `got ${res.status}`); }); it('submit creates a progress row', async () => { const res = await inject('POST', '/api/game/progress', { level_id: LVL, time_ms: 5000, stars: 1 }, token); assert.equal(res.status, 200, `got ${res.status}`); assert.equal(res.body.ok, true); assert.equal(res.body.progress.level_id, LVL); assert.equal(res.body.progress.best_time_ms, 5000); assert.equal(res.body.progress.best_stars, 1); assert.equal(res.body.progress.attempts, 1); }); it('GET /progress lists the row', async () => { const res = await inject('GET', '/api/game/progress', null, token); assert.equal(res.status, 200, `got ${res.status}`); assert.ok(Array.isArray(res.body.progress), 'progress is array'); const row = res.body.progress.find(r => r.level_id === LVL); assert.ok(row, 'level row present'); assert.equal(row.best_time_ms, 5000); }); it('better result (less time, more stars) overwrites best; attempts++', async () => { const res = await inject('POST', '/api/game/progress', { level_id: LVL, time_ms: 3200, stars: 2 }, token); assert.equal(res.status, 200, `got ${res.status}`); assert.equal(res.body.progress.best_time_ms, 3200, 'time improved'); assert.equal(res.body.progress.best_stars, 2, 'stars improved'); assert.equal(res.body.progress.attempts, 2, 'attempts incremented'); }); it('worse result does NOT overwrite best, but still counts an attempt', async () => { const res = await inject('POST', '/api/game/progress', { level_id: LVL, time_ms: 9999, stars: 0 }, token); assert.equal(res.status, 200, `got ${res.status}`); assert.equal(res.body.progress.best_time_ms, 3200, 'best time kept'); assert.equal(res.body.progress.best_stars, 2, 'best stars kept'); assert.equal(res.body.progress.attempts, 3, 'attempts still incremented'); }); it('progress is per-user (другой игрок начинает с нуля)', async () => { const other = (await getToken('student')).token; const res = await inject('POST', '/api/game/progress', { level_id: LVL, time_ms: 7000, stars: 1 }, other); assert.equal(res.status, 200, `got ${res.status}`); assert.equal(res.body.progress.attempts, 1, 'fresh user has attempts=1'); assert.equal(res.body.progress.best_time_ms, 7000); }); it('validation: missing level_id → 400', async () => { const res = await inject('POST', '/api/game/progress', { time_ms: 1000, stars: 1 }, token); assert.equal(res.status, 400, `got ${res.status}`); }); it('validation: negative time_ms → 400', async () => { const res = await inject('POST', '/api/game/progress', { level_id: LVL, time_ms: -5, stars: 1 }, token); assert.equal(res.status, 400, `got ${res.status}`); }); it('validation: non-integer time_ms → 400', async () => { const res = await inject('POST', '/api/game/progress', { level_id: LVL, time_ms: 12.5, stars: 1 }, token); assert.equal(res.status, 400, `got ${res.status}`); }); it('validation: stars out of range (>3) → 400', async () => { const res = await inject('POST', '/api/game/progress', { level_id: LVL, time_ms: 1000, stars: 4 }, token); assert.equal(res.status, 400, `got ${res.status}`); }); it('validation: negative stars → 400', async () => { const res = await inject('POST', '/api/game/progress', { level_id: LVL, time_ms: 1000, stars: -1 }, token); assert.equal(res.status, 400, `got ${res.status}`); }); it('validation: level_id too long → 400', async () => { const res = await inject('POST', '/api/game/progress', { level_id: 'x'.repeat(200), time_ms: 1000, stars: 1 }, token); assert.equal(res.status, 400, `got ${res.status}`); }); });