'use strict'; /** * Tests: конструктор генераторов тренажёра (P13) — валидация + CRUD + доступ. */ const { describe, it, before, after } = require('node:test'); const assert = require('node:assert/strict'); const { app, inject, getToken, cleanup } = require('./setup'); const cg = require('../src/controllers/customGeneratorController'); app.use('/api/practice', require('../src/routes/practice')); after(() => cleanup()); const SPEC = { title: 'Моё уравнение', topic: 'custom', kind: 'solve', pick: { a: [2, 9], b: [1, 20], root: [-9, 9] }, derive: { c: 'a*root + b', cmb: 'a*root' }, require: 'root != 0', lhs: '{a}*x + {b}', rhs: '{c}', answer: 'root', integerAnswer: true, solution: [{ note: 'делим на {a}', tex: 'x = {cmb} / {a}' }] }; describe('validateGenSpec', () => { it('принимает корректный спек', () => { const v = cg.validateGenSpec(SPEC); assert.equal(v.ok, true, v.error); assert.equal(v.clean.kind, 'solve'); assert.deepEqual(v.clean.pick.a, [2, 9]); assert.equal(v.clean.integerAnswer, true); }); it('отвергает без заголовка', () => { assert.equal(cg.validateGenSpec(Object.assign({}, SPEC, { title: '' })).ok, false); }); it('фильтрует нецелые диапазоны pick', () => { const v = cg.validateGenSpec(Object.assign({}, SPEC, { pick: { a: [1.5, 9], b: [1, 20] } })); assert.equal(v.ok, true); assert.equal(v.clean.pick.a, undefined, 'нецелый диапазон отброшен'); assert.deepEqual(v.clean.pick.b, [1, 20]); }); it('отвергает слишком большой спек', () => { assert.equal(cg.validateGenSpec(Object.assign({}, SPEC, { display: 'x'.repeat(30000) })).ok, false); }); }); describe('/api/practice/generators CRUD', () => { let teacher, other, student, gid; before(async () => { teacher = (await getToken('teacher')).token; other = (await getToken('teacher')).token; student = (await getToken('student')).token; }); it('учитель создаёт генератор', async () => { const res = await inject('POST', '/api/practice/generators', { spec: SPEC }, teacher); assert.equal(res.status, 200, `got ${res.status}`); assert.equal(res.body.ok, true); assert.ok(/^cg\d+$/.test(res.body.generator.id), 'id вида cg'); gid = res.body.generator.dbid; }); it('ученику создавать запрещено (403)', async () => { const res = await inject('POST', '/api/practice/generators', { spec: SPEC }, student); assert.equal(res.status, 403, `got ${res.status}`); }); it('невалидный спек → 400', async () => { const res = await inject('POST', '/api/practice/generators', { spec: { title: '' } }, teacher); assert.equal(res.status, 400, `got ${res.status}`); }); it('автор видит свой генератор в списке', async () => { const res = await inject('GET', '/api/practice/generators', null, teacher); assert.equal(res.status, 200); assert.ok(res.body.generators.some(g => g.dbid === gid), 'свой генератор в списке'); }); it('чужой draft не виден другому учителю', async () => { const res = await inject('GET', '/api/practice/generators', null, other); assert.ok(!res.body.generators.some(g => g.dbid === gid), 'чужой draft скрыт'); }); it('чужой не может изменить (403)', async () => { const res = await inject('PUT', '/api/practice/generators/' + gid, { spec: SPEC }, other); assert.equal(res.status, 403, `got ${res.status}`); }); it('публикация делает генератор видимым другим', async () => { const pub = await inject('PUT', '/api/practice/generators/' + gid, { status: 'published' }, teacher); assert.equal(pub.status, 200); assert.equal(pub.body.generator.status, 'published'); const res = await inject('GET', '/api/practice/generators', null, other); assert.ok(res.body.generators.some(g => g.dbid === gid), 'published виден другому'); }); it('автор удаляет свой генератор', async () => { const res = await inject('DELETE', '/api/practice/generators/' + gid, null, teacher); assert.equal(res.status, 200); const after = await inject('GET', '/api/practice/generators/' + gid, null, teacher); assert.equal(after.status, 404, 'после удаления 404'); }); });