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, createTestDevice, createTestDetection, generateTestToken } = require('../setup'); const { authenticateToken, setModels } = require('../../middleware/auth'); describe('Detections Routes', () => { let app, models, sequelize, detectionsRoutes; before(async () => { ({ models, sequelize } = await setupTestEnvironment()); // Inject models globally for routes and into auth middleware for testing global.__TEST_MODELS__ = models; setModels(models); // Require detections routes AFTER setting up global models detectionsRoutes = require('../../routes/detections'); // Setup express app for testing app = express(); app.use(express.json()); app.use(authenticateToken); app.use('/detections', detectionsRoutes); }); after(async () => { // Clean up global test models delete global.__TEST_MODELS__; await teardownTestEnvironment(); }); beforeEach(async () => { await cleanDatabase(); }); describe('GET /detections', () => { it('should return detections for user tenant', async () => { const tenant = await createTestTenant({ slug: 'test-tenant' }); const user = await createTestUser({ tenant_id: tenant.id }); const device = await createTestDevice({ tenant_id: tenant.id }); const detection = await createTestDetection({ device_id: device.id }); const token = generateTestToken(user, tenant); const response = await request(app) .get('/detections') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.success).to.be.true; expect(response.body.data.detections).to.be.an('array'); expect(response.body.data.detections).to.have.length(1); expect(response.body.data.detections[0].id).to.equal(detection.id); }); it('should not return detections from other tenants', async () => { const tenant1 = await createTestTenant({ slug: 'tenant1' }); const tenant2 = await createTestTenant({ slug: 'tenant2' }); const user1 = await createTestUser({ tenant_id: tenant1.id }); const device1 = await createTestDevice({ tenant_id: tenant1.id }); const device2 = await createTestDevice({ tenant_id: tenant2.id }); await createTestDetection({ device_id: device1.id }); await createTestDetection({ device_id: device2.id }); const token = generateTestToken(user1, tenant1); const response = await request(app) .get('/detections') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.data.detections).to.have.length(1); }); it('should support pagination', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const device = await createTestDevice({ tenant_id: tenant.id }); // Create multiple detections for (let i = 0; i < 15; i++) { await createTestDetection({ device_id: device.id }); } const token = generateTestToken(user, tenant); const response = await request(app) .get('/detections?page=1&limit=10') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.data.detections).to.have.length(10); expect(response.body.data.pagination.totalPages).to.equal(2); expect(response.body.data.pagination.hasNextPage).to.be.true; }); it('should support sorting', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const device = await createTestDevice({ tenant_id: tenant.id }); const detection1 = await createTestDetection({ device_id: device.id, server_timestamp: new Date('2023-01-01') }); const detection2 = await createTestDetection({ device_id: device.id, server_timestamp: new Date('2023-01-02') }); const token = generateTestToken(user, tenant); const response = await request(app) .get('/detections?sort=server_timestamp&order=desc') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.data.detections[0].id).to.equal(detection2.id); }); it('should support filtering by device', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const device1 = await createTestDevice({ tenant_id: tenant.id }); const device2 = await createTestDevice({ tenant_id: tenant.id }); await createTestDetection({ device_id: device1.id }); await createTestDetection({ device_id: device2.id }); const token = generateTestToken(user, tenant); const response = await request(app) .get(`/detections?device_id=${device1.id}`) .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.data.detections).to.have.length(1); expect(response.body.data.detections[0].device_id).to.equal(device1.id); }); it('should support filtering by drone type', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const device = await createTestDevice({ tenant_id: tenant.id }); await createTestDetection({ device_id: device.id, drone_type: 2 }); await createTestDetection({ device_id: device.id, drone_type: 3 }); const token = generateTestToken(user, tenant); const response = await request(app) .get('/detections?drone_type=2') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.data.detections).to.have.length(1); expect(response.body.data.detections[0].drone_type).to.equal(2); }); it('should support filtering by date range', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const device = await createTestDevice({ tenant_id: tenant.id }); await createTestDetection({ device_id: device.id, server_timestamp: new Date('2023-01-01') }); await createTestDetection({ device_id: device.id, server_timestamp: new Date('2023-02-01') }); const token = generateTestToken(user, tenant); const response = await request(app) .get('/detections?start_date=2023-01-15&end_date=2023-02-15') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.data.detections).to.have.length(1); }); it('should exclude drone type 0 by default', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const device = await createTestDevice({ tenant_id: tenant.id }); await createTestDetection({ device_id: device.id, drone_type: 0 }); await createTestDetection({ device_id: device.id, drone_type: 2 }); const token = generateTestToken(user, tenant); const response = await request(app) .get('/detections') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.data.detections).to.have.length(1); expect(response.body.data.detections[0].drone_type).to.equal(2); }); it('should enhance detections with drone type info', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const device = await createTestDevice({ tenant_id: tenant.id }); const detection = await createTestDetection({ device_id: device.id, drone_type: 2 }); const token = generateTestToken(user, tenant); const response = await request(app) .get('/detections') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); const returnedDetection = response.body.data.detections[0]; expect(returnedDetection.drone_type_info).to.exist; expect(returnedDetection.drone_type_info.name).to.exist; expect(returnedDetection.drone_type_info.threat_level).to.exist; }); it('should include device information', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const device = await createTestDevice({ tenant_id: tenant.id, name: 'Test Device' }); await createTestDetection({ device_id: device.id }); const token = generateTestToken(user, tenant); const response = await request(app) .get('/detections') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); const detection = response.body.data.detections[0]; expect(detection.device).to.exist; expect(detection.device.name).to.equal('Test Device'); }); it('should reject request without tenant', async () => { const user = await createTestUser({ tenant_id: null }); // Explicitly no tenant const token = generateTestToken(user); // No tenant const response = await request(app) .get('/detections') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(400); expect(response.body.success).to.be.false; expect(response.body.message).to.equal('No tenant context available'); }); it('should reject request for inactive tenant', async () => { const tenant = await createTestTenant({ slug: 'inactive-tenant', is_active: false }); const user = await createTestUser({ tenant_id: tenant.id }); const token = generateTestToken(user, tenant); const response = await request(app) .get('/detections') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(404); expect(response.body.success).to.be.false; }); it('should handle invalid pagination parameters', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const token = generateTestToken(user, tenant); const response = await request(app) .get('/detections?page=-1&limit=1000') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); // Should use default values expect(response.body.data.pagination.page).to.equal(1); expect(response.body.data.pagination.limit).to.equal(20); }); }); describe('GET /detections/:id', () => { it('should return specific detection', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const device = await createTestDevice({ tenant_id: tenant.id }); const detection = await createTestDetection({ device_id: device.id }); const token = generateTestToken(user, tenant); const response = await request(app) .get(`/detections/${detection.id}`) .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.success).to.be.true; expect(response.body.data.id).to.equal(detection.id); }); it('should not return detection from other tenant', async () => { const tenant1 = await createTestTenant({ slug: 'tenant1' }); const tenant2 = await createTestTenant({ slug: 'tenant2' }); const user1 = await createTestUser({ tenant_id: tenant1.id }); const device2 = await createTestDevice({ tenant_id: tenant2.id }); const detection2 = await createTestDetection({ device_id: device2.id }); const token = generateTestToken(user1, tenant1); const response = await request(app) .get(`/detections/${detection2.id}`) .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(404); expect(response.body.success).to.be.false; }); it('should return 404 for non-existent detection', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const token = generateTestToken(user, tenant); const response = await request(app) .get('/detections/99999') .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(404); expect(response.body.success).to.be.false; }); it('should include enhanced detection data', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id }); const device = await createTestDevice({ tenant_id: tenant.id }); const detection = await createTestDetection({ device_id: device.id, drone_type: 2 }); const token = generateTestToken(user, tenant); const response = await request(app) .get(`/detections/${detection.id}`) .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.data.drone_type_info).to.exist; expect(response.body.data.device).to.exist; }); }); describe('DELETE /detections/:id', () => { it('should delete detection with admin role', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id, role: 'admin' }); const device = await createTestDevice({ tenant_id: tenant.id }); const detection = await createTestDetection({ device_id: device.id }); const token = generateTestToken(user, tenant); const response = await request(app) .delete(`/detections/${detection.id}`) .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(200); expect(response.body.success).to.be.true; expect(response.body.message).to.equal('Detection deleted successfully'); }); it('should reject deletion without proper permissions', async () => { const tenant = await createTestTenant(); const user = await createTestUser({ tenant_id: tenant.id, role: 'viewer' }); const device = await createTestDevice({ tenant_id: tenant.id }); const detection = await createTestDetection({ device_id: device.id }); const token = generateTestToken(user, tenant); const response = await request(app) .delete(`/detections/${detection.id}`) .set('Authorization', `Bearer ${token}`); expect(response.status).to.equal(403); expect(response.body.success).to.be.false; }); }); });