const { describe, it, beforeEach, afterEach, before, after } = require('mocha'); const { expect } = require('chai'); const sinon = require('sinon'); const MultiTenantAuth = require('../../middleware/multi-tenant-auth'); const { setupTestEnvironment, teardownTestEnvironment, cleanDatabase, mockRequest, mockResponse, mockNext, createTestUser, createTestTenant, generateTestToken } = require('../setup'); describe('Multi-Tenant Authentication Middleware', () => { let models, sequelize, multiAuth; before(async () => { ({ models, sequelize } = await setupTestEnvironment()); multiAuth = new MultiTenantAuth(); }); after(async () => { await teardownTestEnvironment(); }); beforeEach(async () => { await cleanDatabase(); }); describe('determineTenant', () => { it('should extract tenant from tenantId in JWT token', async () => { const tenant = await createTestTenant({ slug: 'test-tenant' }); const user = await createTestUser({ tenant_id: tenant.id }); const req = mockRequest({ user: { tenantId: tenant.slug } }); const result = await multiAuth.determineTenant(req); expect(result).to.equal(tenant.slug); }); it('should extract tenant from subdomain', async () => { const req = mockRequest({ headers: { host: 'tenant1.example.com' } }); const result = await multiAuth.determineTenant(req); expect(result).to.equal('tenant1'); }); it('should extract tenant from complex subdomain', async () => { const req = mockRequest({ headers: { host: 'uamils-ab.dev.uggla.uamils.com' } }); const result = await multiAuth.determineTenant(req); expect(result).to.equal('uamils-ab'); }); it('should extract tenant from domain path', async () => { const req = mockRequest({ headers: { host: 'example.com' }, url: '/tenant2/api/devices' }); const result = await multiAuth.determineTenant(req); expect(result).to.equal('tenant2'); }); it('should prioritize JWT tenantId over subdomain', async () => { const req = mockRequest({ user: { tenantId: 'jwt-tenant' }, headers: { host: 'subdomain-tenant.example.com' } }); const result = await multiAuth.determineTenant(req); expect(result).to.equal('jwt-tenant'); }); it('should return null for localhost without tenant info', async () => { const req = mockRequest({ headers: { host: 'localhost:3000' } }); const result = await multiAuth.determineTenant(req); expect(result).to.be.null; }); it('should handle x-forwarded-host header', async () => { const req = mockRequest({ headers: { host: 'localhost:3000', 'x-forwarded-host': 'tenant3.example.com' } }); const result = await multiAuth.determineTenant(req); expect(result).to.equal('tenant3'); }); }); describe('middleware function', () => { it('should pass through when tenant is determined', async () => { const tenant = await createTestTenant({ slug: 'valid-tenant' }); const req = mockRequest({ user: { tenantId: tenant.slug } }); const res = mockResponse(); const next = mockNext(); await multiAuth.middleware(req, res, next); expect(req.tenant).to.equal(tenant.slug); expect(next.errors).to.have.length(0); }); it('should reject when tenant cannot be determined', async () => { const req = mockRequest({ headers: { host: 'localhost:3000' } }); const res = mockResponse(); const next = mockNext(); await multiAuth.middleware(req, res, next); expect(res.statusCode).to.equal(400); expect(res.data).to.deep.equal({ success: false, message: 'Unable to determine tenant' }); }); it('should reject when tenant does not exist in database', async () => { const req = mockRequest({ user: { tenantId: 'nonexistent-tenant' } }); const res = mockResponse(); const next = mockNext(); await multiAuth.middleware(req, res, next); expect(res.statusCode).to.equal(404); expect(res.data).to.deep.equal({ success: false, message: 'Tenant not found' }); }); it('should reject when tenant is inactive', async () => { const tenant = await createTestTenant({ slug: 'inactive-tenant', is_active: false }); const req = mockRequest({ user: { tenantId: tenant.slug } }); const res = mockResponse(); const next = mockNext(); await multiAuth.middleware(req, res, next); expect(res.statusCode).to.equal(403); expect(res.data).to.deep.equal({ success: false, message: 'Tenant is not active' }); }); }); describe('validateTenantAccess', () => { it('should validate user belongs to tenant', async () => { const tenant = await createTestTenant({ slug: 'user-tenant' }); const user = await createTestUser({ tenant_id: tenant.id }); const isValid = await multiAuth.validateTenantAccess(user.id, tenant.slug); expect(isValid).to.be.true; }); it('should reject user from different tenant', async () => { const tenant1 = await createTestTenant({ slug: 'tenant1' }); const tenant2 = await createTestTenant({ slug: 'tenant2' }); const user = await createTestUser({ tenant_id: tenant1.id }); const isValid = await multiAuth.validateTenantAccess(user.id, tenant2.slug); expect(isValid).to.be.false; }); it('should reject nonexistent user', async () => { const tenant = await createTestTenant({ slug: 'valid-tenant' }); const isValid = await multiAuth.validateTenantAccess(99999, tenant.slug); expect(isValid).to.be.false; }); }); });