'use strict'; /** * Integration tests: /api/wishes — трекер пожеланий по улучшению. * Covers: auth-only; создание (валидация); приватность (автор видит только свои, * админ — все + counts); триаж только админом (403 ученику); смена статуса; удаление * (автор «новое» / админ; чужое нельзя). */ const { describe, it, before, after } = require('node:test'); const assert = require('node:assert/strict'); const { app, inject, getToken, cleanup } = require('./setup'); app.use('/api/wishes', require('../src/routes/wishes')); after(() => cleanup()); describe('/api/wishes', () => { let s1, s2, admin; before(async () => { s1 = await getToken('student'); s2 = await getToken('student'); admin = await getToken('admin'); }); it('POST /wishes requires auth (401)', async () => { const res = await inject('POST', '/api/wishes', { title: 'x' }, null); assert.equal(res.status, 401); }); it('создание: пустой заголовок → 400', async () => { const res = await inject('POST', '/api/wishes', { title: ' ' }, s1.token); assert.equal(res.status, 400); }); let wishId; it('создание пожелания учеником → 201, статус new', async () => { const res = await inject('POST', '/api/wishes', { title: 'Тёмная тема', body: 'Хочу ночной режим', category: 'ui' }, s1.token); assert.equal(res.status, 201, JSON.stringify(res.body)); assert.equal(res.body.status, 'new'); assert.equal(res.body.category, 'ui'); assert.equal(res.body.user_id, s1.userId); wishId = res.body.id; }); it('неизвестная категория → other', async () => { const res = await inject('POST', '/api/wishes', { title: 'Что-то', category: 'hack' }, s1.token); assert.equal(res.body.category, 'other'); }); it('приватность: автор видит свои', async () => { const res = await inject('GET', '/api/wishes', null, s1.token); assert.equal(res.status, 200); assert.ok(res.body.wishes.some(w => w.id === wishId)); assert.equal(res.body.isAdmin, false); }); it('приватность: другой ученик НЕ видит чужое', async () => { const res = await inject('GET', '/api/wishes', null, s2.token); assert.ok(!res.body.wishes.some(w => w.id === wishId)); }); it('админ видит все + counts', async () => { const res = await inject('GET', '/api/wishes', null, admin.token); assert.equal(res.status, 200); assert.equal(res.body.isAdmin, true); assert.ok(res.body.wishes.some(w => w.id === wishId)); assert.ok(res.body.counts && typeof res.body.counts.new === 'number'); // у админа в списке есть имя автора const w = res.body.wishes.find(x => x.id === wishId); assert.ok(w.author_name); }); it('триаж учеником запрещён (403)', async () => { const res = await inject('PATCH', `/api/wishes/${wishId}`, { status: 'done' }, s1.token); assert.equal(res.status, 403); }); it('админ меняет статус + ответ → 200', async () => { const res = await inject('PATCH', `/api/wishes/${wishId}`, { status: 'planned', admin_note: 'Запланировано на лето' }, admin.token); assert.equal(res.status, 200, JSON.stringify(res.body)); assert.equal(res.body.status, 'planned'); assert.equal(res.body.admin_note, 'Запланировано на лето'); }); it('неверный статус → 400', async () => { const res = await inject('PATCH', `/api/wishes/${wishId}`, { status: 'bogus' }, admin.token); assert.equal(res.status, 400); }); it('фильтр по статусу у админа', async () => { const res = await inject('GET', '/api/wishes?status=planned', null, admin.token); assert.ok(res.body.wishes.every(w => w.status === 'planned')); assert.ok(res.body.wishes.some(w => w.id === wishId)); }); it('автор НЕ может удалить уже обработанное (не new) → 403', async () => { const res = await inject('DELETE', `/api/wishes/${wishId}`, null, s1.token); assert.equal(res.status, 403); }); it('чужой ученик не может удалить → 403', async () => { const res = await inject('DELETE', `/api/wishes/${wishId}`, null, s2.token); assert.equal(res.status, 403); }); it('автор удаляет своё «новое» → 200', async () => { const c = await inject('POST', '/api/wishes', { title: 'Черновик' }, s1.token); const res = await inject('DELETE', `/api/wishes/${c.body.id}`, null, s1.token); assert.equal(res.status, 200); }); it('админ удаляет любое → 200', async () => { const res = await inject('DELETE', `/api/wishes/${wishId}`, null, admin.token); assert.equal(res.status, 200); const gone = await inject('GET', '/api/wishes', null, admin.token); assert.ok(!gone.body.wishes.some(w => w.id === wishId)); }); });