be4d43105e
Node.js/Express backend + vanilla JS frontend. Features: real-time collaborative whiteboard (SSE), multi-page support, LaTeX formulas, shapes/connectors, coordinate systems, number lines, compass, zoom/pan, Catmull-Rom pencil smoothing, ruler/protractor with rotation & resize controls, minimap navigation overlay, auto-measurements, multi-page thumbnails sidebar, PNG export, page templates. Student/teacher workflows: classes, assignments, library, dashboard. Mobile responsive. SQLite (better-sqlite3). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
107 lines
3.6 KiB
JavaScript
107 lines
3.6 KiB
JavaScript
const { describe, it, before, after } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { db, inject, getToken, cleanup } = require('./setup');
|
|
|
|
after(() => cleanup());
|
|
|
|
describe('Sessions', () => {
|
|
let token, userId;
|
|
|
|
before(async () => {
|
|
const u = await getToken('student');
|
|
token = u.token; userId = u.userId;
|
|
|
|
// Ensure subject + questions exist
|
|
const subj = db.prepare('SELECT id FROM subjects WHERE slug = ?').get('chem');
|
|
if (!subj) {
|
|
db.prepare("INSERT INTO subjects (slug, name, icon) VALUES ('chem', 'Химия', 'atom')").run();
|
|
}
|
|
const subjId = db.prepare('SELECT id FROM subjects WHERE slug = ?').get('chem').id;
|
|
|
|
// Create some questions
|
|
const ins = db.prepare('INSERT INTO questions (subject_id, text, difficulty) VALUES (?, ?, 1)');
|
|
for (let i = 0; i < 5; i++) {
|
|
const qId = ins.run(subjId, `Test question ${i + 1}`).lastInsertRowid;
|
|
// Add options
|
|
db.prepare('INSERT INTO options (question_id, text, is_correct) VALUES (?, ?, ?)').run(qId, 'Wrong', 0);
|
|
db.prepare('INSERT INTO options (question_id, text, is_correct) VALUES (?, ?, ?)').run(qId, 'Correct', 1);
|
|
}
|
|
});
|
|
|
|
it('validates subject_slug required', async () => {
|
|
const res = await inject('POST', '/api/sessions', { mode: 'exam' }, token);
|
|
assert.equal(res.status, 400);
|
|
});
|
|
|
|
it('validates mode enum', async () => {
|
|
const res = await inject('POST', '/api/sessions', {
|
|
subject_slug: 'chem', mode: 'invalid'
|
|
}, token);
|
|
assert.equal(res.status, 400);
|
|
});
|
|
|
|
it('validates count range', async () => {
|
|
const res = await inject('POST', '/api/sessions', {
|
|
subject_slug: 'chem', count: 999
|
|
}, token);
|
|
assert.equal(res.status, 400);
|
|
});
|
|
|
|
it('starts a session successfully', async () => {
|
|
const res = await inject('POST', '/api/sessions', {
|
|
subject_slug: 'chem', count: 3, mode: 'exam'
|
|
}, token);
|
|
assert.equal(res.status, 201);
|
|
assert.ok(res.body.session_id);
|
|
assert.equal(res.body.mode, 'exam');
|
|
assert.ok(res.body.questions.length > 0);
|
|
});
|
|
|
|
it('submits an answer', async () => {
|
|
// Start fresh session
|
|
const s = await inject('POST', '/api/sessions', {
|
|
subject_slug: 'chem', count: 2, mode: 'practice'
|
|
}, token);
|
|
const sessionId = s.body.session_id;
|
|
const q = s.body.questions[0];
|
|
|
|
const res = await inject('POST', `/api/sessions/${sessionId}/answer`, {
|
|
question_id: q.id, option_id: q.options[0].id
|
|
}, token);
|
|
assert.equal(res.status, 200);
|
|
assert.ok('is_correct' in res.body);
|
|
});
|
|
|
|
it('validates answer question_id required', async () => {
|
|
const s = await inject('POST', '/api/sessions', {
|
|
subject_slug: 'chem', count: 2
|
|
}, token);
|
|
const sessionId = s.body.session_id;
|
|
|
|
const res = await inject('POST', `/api/sessions/${sessionId}/answer`, {}, token);
|
|
assert.equal(res.status, 400);
|
|
});
|
|
|
|
it('finishes a session', async () => {
|
|
const s = await inject('POST', '/api/sessions', {
|
|
subject_slug: 'chem', count: 1
|
|
}, token);
|
|
const sessionId = s.body.session_id;
|
|
|
|
const res = await inject('POST', `/api/sessions/${sessionId}/finish`, {}, token);
|
|
assert.equal(res.status, 200);
|
|
assert.ok('score' in res.body);
|
|
});
|
|
|
|
it('returns session history', async () => {
|
|
const res = await inject('GET', '/api/sessions/history', null, token);
|
|
assert.equal(res.status, 200);
|
|
assert.ok(Array.isArray(res.body.rows));
|
|
});
|
|
|
|
it('rejects session start without auth', async () => {
|
|
const res = await inject('POST', '/api/sessions', { subject_slug: 'chem' });
|
|
assert.equal(res.status, 401);
|
|
});
|
|
});
|