// server/test/authProtect.test.js // Tests for protect and adminOnly middleware (authMiddleware.js) // Uses node:test + node:assert/strict — no real DB or server required. // Set JWT_SECRET BEFORE any module require so configs/env.js captures it. // (If authLogin.test.js already set it in this process, this is a no-op // because the value is the same.) process.env.JWT_SECRET = 'test-secret-for-auth-login-tests-min32ch'; const test = require('node:test'); const assert = require('node:assert/strict'); const jwt = require('jsonwebtoken'); const { protect, adminOnly } = require('../middleware/authMiddleware'); const AdminUser = require('../models/AdminUser'); // Secret used by authMiddleware — must match what was set in process.env above // at the time the module was first loaded (or in authLogin.test.js). const TEST_SECRET = 'test-secret-for-auth-login-tests-min32ch'; // ─── Helpers ────────────────────────────────────────────────────────────────── function makeProtectRes() { const res = { _status: null, _body: null, status(code) { this._status = code; return this; }, json(body) { this._body = body; return this; }, }; return res; } // ─── protect tests ──────────────────────────────────────────────────────────── test('protect rejects request with no cookie', async () => { const req = { cookies: {} }; // no token const res = makeProtectRes(); let nextCalled = false; await protect(req, res, () => { nextCalled = true; }); assert.equal(nextCalled, false, 'next() must not be called when no token is present'); assert.equal(res._status, 401, 'should respond 401 when no cookie token'); }); test('protect rejects invalid/expired JWT', async () => { const req = { cookies: { token: 'this.is.not.a.valid.jwt' } }; const res = makeProtectRes(); let nextCalled = false; await protect(req, res, () => { nextCalled = true; }); assert.equal(nextCalled, false, 'next() must not be called for invalid token'); assert.equal(res._status, 401, 'should respond 401 for invalid/expired JWT'); }); test('protect rejects deactivated user', async () => { // Create a valid token signed with the test secret const token = jwt.sign( { id: '507f1f77bcf86cd799439011' }, TEST_SECRET, { expiresIn: '1h' } ); const originalFindById = AdminUser.findById.bind(AdminUser); // Simulate the chained .select('-passwordHash').lean() call AdminUser.findById = () => ({ select: () => ({ lean: async () => ({ _id: '507f1f77bcf86cd799439011', username: 'deactivated', name: 'Deactivated User', role: 'admin', isActive: false, }), }), }); try { const req = { cookies: { token } }; const res = makeProtectRes(); let nextCalled = false; await protect(req, res, () => { nextCalled = true; }); assert.equal(nextCalled, false, 'next() must not be called for deactivated user'); assert.equal(res._status, 403, 'should respond 403 for deactivated user'); } finally { AdminUser.findById = originalFindById; } }); test('protect passes and sets req.user for valid active user', async () => { const token = jwt.sign( { id: '507f1f77bcf86cd799439011' }, TEST_SECRET, { expiresIn: '1h' } ); const originalFindById = AdminUser.findById.bind(AdminUser); AdminUser.findById = () => ({ select: () => ({ lean: async () => ({ _id: '507f1f77bcf86cd799439011', username: 'admin', name: 'Administrator', role: 'admin', isActive: true, }), }), }); try { const req = { cookies: { token } }; const res = makeProtectRes(); let nextCalled = false; await protect(req, res, () => { nextCalled = true; }); assert.equal(nextCalled, true, 'next() must be called for valid active user'); assert.ok(req.user, 'req.user must be set'); assert.equal(req.user.username, 'admin'); } finally { AdminUser.findById = originalFindById; } }); // ─── adminOnly tests ────────────────────────────────────────────────────────── test('adminOnly rejects non-admin role', () => { const req = { user: { role: 'user' } }; const res = makeProtectRes(); let nextCalled = false; adminOnly(req, res, () => { nextCalled = true; }); assert.equal(nextCalled, false, 'next() must not be called for non-admin role'); assert.equal(res._status, 403, 'should respond 403 for non-admin role'); }); test('adminOnly passes for admin role', () => { const req = { user: { role: 'admin' } }; const res = makeProtectRes(); let nextCalled = false; adminOnly(req, res, () => { nextCalled = true; }); assert.equal(nextCalled, true, 'next() must be called for admin role'); assert.equal(res._status, null, 'no error response should be set for admin role'); });