LearnSpace: full-stack educational whiteboard platform
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>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
const { describe, it, after } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { db, inject, getToken, cleanup } = require('./setup');
|
||||
|
||||
after(() => cleanup());
|
||||
|
||||
describe('Auth', () => {
|
||||
it('registers a new user', async () => {
|
||||
const res = await inject('POST', '/api/auth/register', {
|
||||
email: 'auth1@test.com', password: 'pass123', name: 'Auth User'
|
||||
});
|
||||
assert.equal(res.status, 201);
|
||||
assert.ok(res.body.token);
|
||||
assert.equal(res.body.user.email, 'auth1@test.com');
|
||||
});
|
||||
|
||||
it('rejects duplicate email', async () => {
|
||||
const res = await inject('POST', '/api/auth/register', {
|
||||
email: 'auth1@test.com', password: 'pass123', name: 'Dupe'
|
||||
});
|
||||
assert.equal(res.status, 409);
|
||||
});
|
||||
|
||||
it('validates required fields', async () => {
|
||||
const res = await inject('POST', '/api/auth/register', { email: 'x@y.z' });
|
||||
assert.equal(res.status, 400);
|
||||
});
|
||||
|
||||
it('validates email format', async () => {
|
||||
const res = await inject('POST', '/api/auth/register', {
|
||||
email: 'not-an-email', password: 'pass123', name: 'Bad'
|
||||
});
|
||||
assert.equal(res.status, 400);
|
||||
});
|
||||
|
||||
it('validates password min length', async () => {
|
||||
const res = await inject('POST', '/api/auth/register', {
|
||||
email: 'short@test.com', password: '12345', name: 'Short'
|
||||
});
|
||||
assert.equal(res.status, 400);
|
||||
});
|
||||
|
||||
it('logs in with correct credentials', async () => {
|
||||
const res = await inject('POST', '/api/auth/login', {
|
||||
email: 'auth1@test.com', password: 'pass123'
|
||||
});
|
||||
assert.equal(res.status, 200);
|
||||
assert.ok(res.body.token);
|
||||
});
|
||||
|
||||
it('rejects wrong password', async () => {
|
||||
const res = await inject('POST', '/api/auth/login', {
|
||||
email: 'auth1@test.com', password: 'wrong'
|
||||
});
|
||||
assert.equal(res.status, 401);
|
||||
});
|
||||
|
||||
it('GET /me returns user with valid token', async () => {
|
||||
const { token } = await getToken();
|
||||
const res = await inject('GET', '/api/auth/me', null, token);
|
||||
assert.equal(res.status, 200);
|
||||
assert.ok(res.body.id);
|
||||
});
|
||||
|
||||
it('rejects request without token', async () => {
|
||||
const res = await inject('GET', '/api/auth/me');
|
||||
assert.equal(res.status, 401);
|
||||
});
|
||||
|
||||
it('invalidates token after password change', async () => {
|
||||
// Register
|
||||
const reg = await inject('POST', '/api/auth/register', {
|
||||
email: 'pwchange@test.com', password: 'old_pass123', name: 'PW Test'
|
||||
});
|
||||
const oldToken = reg.body.token;
|
||||
|
||||
// Change password
|
||||
const upd = await inject('PATCH', '/api/auth/profile', {
|
||||
currentPassword: 'old_pass123', newPassword: 'new_pass123'
|
||||
}, oldToken);
|
||||
assert.equal(upd.status, 200);
|
||||
const newToken = upd.body.token;
|
||||
|
||||
// Old token should be rejected
|
||||
const res1 = await inject('GET', '/api/auth/me', null, oldToken);
|
||||
assert.equal(res1.status, 401);
|
||||
|
||||
// New token should work
|
||||
const res2 = await inject('GET', '/api/auth/me', null, newToken);
|
||||
assert.equal(res2.status, 200);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user