const { describe, it, beforeEach, afterEach, before, after } = require('mocha'); const { expect } = require('chai'); const sinon = require('sinon'); const request = require('supertest'); const express = require('express'); const { setupTestEnvironment, teardownTestEnvironment, cleanDatabase, createTestUser, createTestTenant, generateTestToken } = require('../setup'); const authRoutes = require('../../routes/auth'); describe('Auth Routes', () => { let app, models, sequelize; before(async () => { ({ models, sequelize } = await setupTestEnvironment()); // Setup express app for testing app = express(); app.use(express.json()); app.use('/auth', authRoutes); }); after(async () => { await teardownTestEnvironment(); }); beforeEach(async () => { await cleanDatabase(); }); describe('POST /auth/login', () => { it('should login with valid credentials', async () => { const tenant = await createTestTenant({ slug: 'test-tenant' }); const user = await createTestUser({ username: 'testuser', password_hash: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password tenant_id: tenant.id }); const response = await request(app) .post('/auth/login') .set('Host', 'test-tenant.example.com') .send({ username: 'testuser', password: 'password' }); expect(response.status).to.equal(200); expect(response.body.success).to.be.true; expect(response.body.data.token).to.exist; expect(response.body.data.user.username).to.equal('testuser'); }); it('should reject invalid username', async () => { const response = await request(app) .post('/auth/login') .set('Host', 'test-tenant.example.com') .send({ username: 'nonexistent', password: 'password' }); expect(response.status).to.equal(401); expect(response.body.success).to.be.false; expect(response.body.message).to.equal('Invalid credentials'); }); it('should reject invalid password', async () => { const tenant = await createTestTenant({ slug: 'test-tenant' }); const user = await createTestUser({ username: 'testuser', password_hash: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', tenant_id: tenant.id }); const response = await request(app) .post('/auth/login') .set('Host', 'test-tenant.example.com') .send({ username: 'testuser', password: 'wrongpassword' }); expect(response.status).to.equal(401); expect(response.body.success).to.be.false; }); it('should reject inactive user', async () => { const tenant = await createTestTenant({ slug: 'test-tenant' }); const user = await createTestUser({ username: 'inactive', password_hash: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', is_active: false, tenant_id: tenant.id }); const response = await request(app) .post('/auth/login') .set('Host', 'test-tenant.example.com') .send({ username: 'inactive', password: 'password' }); expect(response.status).to.equal(401); expect(response.body.success).to.be.false; expect(response.body.message).to.equal('Account is inactive'); }); it('should include tenant information in JWT token', async () => { const tenant = await createTestTenant({ slug: 'test-tenant' }); const user = await createTestUser({ username: 'testuser', password_hash: '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', tenant_id: tenant.id }); const response = await request(app) .post('/auth/login') .set('Host', 'test-tenant.example.com') .send({ username: 'testuser', password: 'password' }); expect(response.status).to.equal(200); const jwt = require('jsonwebtoken'); const decoded = jwt.verify(response.body.data.token, process.env.JWT_SECRET || 'test-secret'); expect(decoded.tenantId).to.equal(tenant.slug); }); it('should validate required fields', async () => { const tenant = await createTestTenant({ slug: 'test-tenant' }); const response = await request(app) .post('/auth/login') .set('Host', 'test-tenant.example.com') .send({ username: 'testuser' // missing password }); expect(response.status).to.equal(400); expect(response.body.success).to.be.false; }); it('should handle database errors gracefully', async () => { const tenant = await createTestTenant({ slug: 'test-tenant' }); // Mock database error const originalFindOne = models.User.findOne; models.User.findOne = sinon.stub().rejects(new Error('Database error')); const response = await request(app) .post('/auth/login') .set('Host', 'test-tenant.example.com') .send({ username: 'testuser', password: 'password' }); expect(response.status).to.equal(500); expect(response.body.success).to.be.false; // Restore original method models.User.findOne = originalFindOne; }); }); describe('POST /auth/register', () => { it('should register new user when registration allowed', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', allow_registration: true }); const response = await request(app) .post('/auth/register') .set('Host', 'test-tenant.example.com') .send({ username: 'newuser', email: 'new@example.com', password: 'Password123', first_name: 'New', last_name: 'User' }); expect(response.status).to.equal(201); expect(response.body.success).to.be.true; expect(response.body.data.user.username).to.equal('newuser'); }); it('should reject registration when not allowed', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', allow_registration: false }); const response = await request(app) .post('/auth/register') .set('Host', 'test-tenant.example.com') .send({ username: 'newuser', email: 'new@example.com', password: 'Password123' }); expect(response.status).to.equal(403); expect(response.body.success).to.be.false; expect(response.body.message).to.include('Registration not allowed'); }); it('should reject duplicate username', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', allow_registration: true }); await createTestUser({ username: 'existing', tenant_id: tenant.id }); const response = await request(app) .post('/auth/register') .set('Host', 'test-tenant.example.com') .send({ username: 'existing', email: 'new@example.com', password: 'Password123' }); expect(response.status).to.equal(400); expect(response.body.success).to.be.false; expect(response.body.message).to.include('already exists'); }); it('should reject duplicate email', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', allow_registration: true }); await createTestUser({ username: 'existing', email: 'existing@example.com', tenant_id: tenant.id }); const response = await request(app) .post('/auth/register') .set('Host', 'test-tenant.example.com') .send({ username: 'newuser', email: 'existing@example.com', password: 'password123' }); expect(response.status).to.equal(400); expect(response.body.success).to.be.false; }); it('should validate password strength', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', allow_registration: true }); const response = await request(app) .post('/auth/register') .set('Host', 'test-tenant.example.com') .send({ username: 'newuser', email: 'new@example.com', password: '123' // weak password }); expect(response.status).to.equal(400); expect(response.body.success).to.be.false; expect(response.body.message).to.include('password'); }); it('should validate email format', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', allow_registration: true }); const response = await request(app) .post('/auth/register') .set('Host', 'test-tenant.example.com') .send({ username: 'newuser', email: 'invalid-email', password: 'password123' }); expect(response.status).to.equal(400); expect(response.body.success).to.be.false; }); it('should enforce IP restrictions during registration', async () => { const tenant = await createTestTenant({ slug: 'test-tenant', allow_registration: true, ip_restriction_enabled: true, ip_whitelist: ['192.168.1.1'] }); const response = await request(app) .post('/auth/register') .set('Host', 'test-tenant.example.com') .set('X-Forwarded-For', '192.168.2.1') // Not allowed IP .send({ username: 'newuser', email: 'new@example.com', password: 'Password123' }); expect(response.status).to.equal(403); expect(response.body.success).to.be.false; expect(response.body.message).to.include('Access denied. Your IP address is not'); }); }); describe('POST /auth/refresh', () => { it('should refresh valid token', async () => { const user = await createTestUser(); const token = generateTestToken(user); const response = await request(app) .post('/auth/refresh') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.success).to.be.true; expect(response.body.data.token).to.exist; expect(response.body.data.token).to.not.equal(token); // New token }); it('should reject refresh without token', async () => { const response = await request(app) .post('/auth/refresh'); expect(response.status).to.equal(401); expect(response.body.success).to.be.false; }); it('should reject refresh with invalid token', async () => { const response = await request(app) .post('/auth/refresh') .set('Authorization', 'Bearer invalid.token'); expect(response.status).to.equal(401); expect(response.body.success).to.be.false; }); }); describe('POST /auth/logout', () => { it('should logout successfully', async () => { const user = await createTestUser(); const token = generateTestToken(user); const response = await request(app) .post('/auth/logout') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.success).to.be.true; expect(response.body.message).to.equal('Logged out successfully'); }); it('should logout without token', async () => { const response = await request(app) .post('/auth/logout'); expect(response.status).to.equal(200); expect(response.body.success).to.be.true; }); }); describe('GET /auth/me', () => { it('should return current user info', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const token = generateTestToken(user, tenant); const response = await request(app) .get('/auth/me') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.success).to.be.true; expect(response.body.data.username).to.equal(user.username); expect(response.body.data.email).to.equal(user.email); }); it('should require authentication', async () => { const response = await request(app) .get('/auth/me'); expect(response.status).to.equal(401); expect(response.body.success).to.be.false; }); }); });