Fix jwt-token
This commit is contained in:
584
server/tests/routes/device.test.js
Normal file
584
server/tests/routes/device.test.js
Normal file
@@ -0,0 +1,584 @@
|
||||
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, generateTestToken } = require('../setup');
|
||||
|
||||
describe('Device Routes', () => {
|
||||
let app, models, sequelize;
|
||||
|
||||
before(async () => {
|
||||
({ models, sequelize } = await setupTestEnvironment());
|
||||
|
||||
// Setup express app for testing
|
||||
app = express();
|
||||
app.use(express.json());
|
||||
|
||||
// Mock authentication middleware
|
||||
app.use((req, res, next) => {
|
||||
if (req.headers.authorization) {
|
||||
const token = req.headers.authorization.replace('Bearer ', '');
|
||||
try {
|
||||
const jwt = require('jsonwebtoken');
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'test-secret');
|
||||
req.user = { id: decoded.userId, tenant_id: decoded.tenantId };
|
||||
req.tenant = { id: decoded.tenantId };
|
||||
} catch (error) {
|
||||
return res.status(401).json({ success: false, message: 'Invalid token' });
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// Setup device routes
|
||||
const deviceRoutes = require('../../routes/device');
|
||||
app.use('/devices', deviceRoutes);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await teardownTestEnvironment();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await cleanDatabase();
|
||||
});
|
||||
|
||||
describe('GET /devices', () => {
|
||||
it('should return all devices for authenticated user', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({ tenant_id: tenant.id, role: 'admin' });
|
||||
const token = generateTestToken(user, tenant);
|
||||
|
||||
const device1 = await createTestDevice({
|
||||
id: 123,
|
||||
tenant_id: tenant.id,
|
||||
name: 'Test Device 1',
|
||||
is_approved: true
|
||||
});
|
||||
|
||||
const device2 = await createTestDevice({
|
||||
id: 124,
|
||||
tenant_id: tenant.id,
|
||||
name: 'Test Device 2',
|
||||
is_approved: false
|
||||
});
|
||||
|
||||
const response = await request(app)
|
||||
.get('/devices')
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.body.success).to.be.true;
|
||||
expect(response.body.data).to.have.length(2);
|
||||
|
||||
const deviceIds = response.body.data.map(d => d.id);
|
||||
expect(deviceIds).to.include.members([123, 124]);
|
||||
});
|
||||
|
||||
it('should only return devices for user tenant', async () => {
|
||||
const tenant1 = await createTestTenant({ slug: 'tenant1' });
|
||||
const tenant2 = await createTestTenant({ slug: 'tenant2' });
|
||||
|
||||
const user1 = await createTestUser({ tenant_id: tenant1.id });
|
||||
const user2 = await createTestUser({ tenant_id: tenant2.id });
|
||||
|
||||
await createTestDevice({ id: 111, tenant_id: tenant1.id });
|
||||
await createTestDevice({ id: 222, tenant_id: tenant2.id });
|
||||
|
||||
const token1 = generateTestToken(user1, tenant1);
|
||||
const response = await request(app)
|
||||
.get('/devices')
|
||||
.set('Authorization', `Bearer ${token1}`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.body.data).to.have.length(1);
|
||||
expect(response.body.data[0].id).to.equal(111);
|
||||
});
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const response = await request(app)
|
||||
.get('/devices');
|
||||
|
||||
expect(response.status).to.equal(401);
|
||||
});
|
||||
|
||||
it('should include device statistics', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({ tenant_id: tenant.id });
|
||||
const device = await createTestDevice({
|
||||
tenant_id: tenant.id,
|
||||
is_approved: true
|
||||
});
|
||||
const token = generateTestToken(user, tenant);
|
||||
|
||||
// Create some detections
|
||||
await models.DroneDetection.bulkCreate([
|
||||
{
|
||||
device_id: device.id,
|
||||
tenant_id: tenant.id,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
device_timestamp: new Date(Date.now() - 3600000), // 1 hour ago
|
||||
drone_type: 2,
|
||||
threat_level: 'medium'
|
||||
},
|
||||
{
|
||||
device_id: device.id,
|
||||
tenant_id: tenant.id,
|
||||
geo_lat: 59.3294,
|
||||
geo_lon: 18.0687,
|
||||
device_timestamp: new Date(Date.now() - 1800000), // 30 min ago
|
||||
drone_type: 3,
|
||||
threat_level: 'high'
|
||||
}
|
||||
]);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/devices')
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
const deviceData = response.body.data[0];
|
||||
expect(deviceData.recent_detections_count).to.be.greaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /devices/:id', () => {
|
||||
it('should return specific device details', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({ tenant_id: tenant.id });
|
||||
const device = await createTestDevice({
|
||||
id: 12345,
|
||||
tenant_id: tenant.id,
|
||||
name: 'Specific Device',
|
||||
location_description: 'Test Location'
|
||||
});
|
||||
const token = generateTestToken(user, tenant);
|
||||
|
||||
const response = await request(app)
|
||||
.get(`/devices/${device.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(12345);
|
||||
expect(response.body.data.name).to.equal('Specific Device');
|
||||
expect(response.body.data.location_description).to.equal('Test Location');
|
||||
});
|
||||
|
||||
it('should not return device from different tenant', async () => {
|
||||
const tenant1 = await createTestTenant();
|
||||
const tenant2 = await createTestTenant();
|
||||
|
||||
const user1 = await createTestUser({ tenant_id: tenant1.id });
|
||||
const device2 = await createTestDevice({ tenant_id: tenant2.id });
|
||||
|
||||
const token1 = generateTestToken(user1, tenant1);
|
||||
const response = await request(app)
|
||||
.get(`/devices/${device2.id}`)
|
||||
.set('Authorization', `Bearer ${token1}`);
|
||||
|
||||
expect(response.status).to.equal(404);
|
||||
});
|
||||
|
||||
it('should return 404 for non-existent device', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({ tenant_id: tenant.id });
|
||||
const token = generateTestToken(user, tenant);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/devices/999999')
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
expect(response.status).to.equal(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /devices', () => {
|
||||
it('should create new device with admin role', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const admin = await createTestUser({
|
||||
tenant_id: tenant.id,
|
||||
role: 'admin'
|
||||
});
|
||||
const token = generateTestToken(admin, tenant);
|
||||
|
||||
const deviceData = {
|
||||
id: 987654,
|
||||
name: 'New Test Device',
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
location_description: 'New Device Location'
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
.post('/devices')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send(deviceData);
|
||||
|
||||
expect(response.status).to.equal(201);
|
||||
expect(response.body.success).to.be.true;
|
||||
expect(response.body.data.id).to.equal(987654);
|
||||
expect(response.body.data.name).to.equal('New Test Device');
|
||||
|
||||
// Verify device was saved to database
|
||||
const savedDevice = await models.Device.findByPk(987654);
|
||||
expect(savedDevice).to.exist;
|
||||
expect(savedDevice.tenant_id).to.equal(tenant.id);
|
||||
expect(savedDevice.is_approved).to.be.false; // Default value
|
||||
});
|
||||
|
||||
it('should require admin role for device creation', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({
|
||||
tenant_id: tenant.id,
|
||||
role: 'user' // Not admin
|
||||
});
|
||||
const token = generateTestToken(user, tenant);
|
||||
|
||||
const deviceData = {
|
||||
id: 111111,
|
||||
name: 'Unauthorized Device',
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
.post('/devices')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send(deviceData);
|
||||
|
||||
expect(response.status).to.equal(403);
|
||||
});
|
||||
|
||||
it('should validate required fields', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const admin = await createTestUser({
|
||||
tenant_id: tenant.id,
|
||||
role: 'admin'
|
||||
});
|
||||
const token = generateTestToken(admin, tenant);
|
||||
|
||||
const invalidPayloads = [
|
||||
{}, // Missing all fields
|
||||
{ name: 'No ID Device' }, // Missing device ID
|
||||
{ id: 123 }, // Missing name
|
||||
{ id: 123, name: 'No Location' } // Missing coordinates
|
||||
];
|
||||
|
||||
for (const payload of invalidPayloads) {
|
||||
const response = await request(app)
|
||||
.post('/devices')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send(payload);
|
||||
|
||||
expect(response.status).to.be.oneOf([400, 422]);
|
||||
}
|
||||
});
|
||||
|
||||
it('should prevent duplicate device IDs', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const admin = await createTestUser({ tenant_id: tenant.id, role: 'admin' });
|
||||
const token = generateTestToken(admin, tenant);
|
||||
|
||||
// Create first device
|
||||
await createTestDevice({ id: 555555, tenant_id: tenant.id });
|
||||
|
||||
// Attempt to create duplicate
|
||||
const response = await request(app)
|
||||
.post('/devices')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({
|
||||
id: 555555,
|
||||
name: 'Duplicate Device',
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686
|
||||
});
|
||||
|
||||
expect(response.status).to.be.oneOf([400, 409, 422]);
|
||||
});
|
||||
|
||||
it('should validate coordinate ranges', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const admin = await createTestUser({ tenant_id: tenant.id, role: 'admin' });
|
||||
const token = generateTestToken(admin, tenant);
|
||||
|
||||
const invalidCoordinates = [
|
||||
{ geo_lat: 91, geo_lon: 18.0686 }, // Latitude too high
|
||||
{ geo_lat: -91, geo_lon: 18.0686 }, // Latitude too low
|
||||
{ geo_lat: 59.3293, geo_lon: 181 }, // Longitude too high
|
||||
{ geo_lat: 59.3293, geo_lon: -181 }, // Longitude too low
|
||||
{ geo_lat: 'invalid', geo_lon: 18.0686 }, // Invalid type
|
||||
{ geo_lat: 59.3293, geo_lon: 'invalid' } // Invalid type
|
||||
];
|
||||
|
||||
for (const coords of invalidCoordinates) {
|
||||
const response = await request(app)
|
||||
.post('/devices')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({
|
||||
id: Math.floor(Math.random() * 1000000),
|
||||
name: 'Invalid Coord Device',
|
||||
...coords
|
||||
});
|
||||
|
||||
expect(response.status).to.be.oneOf([400, 422]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /devices/:id', () => {
|
||||
it('should update device with admin role', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const admin = await createTestUser({ tenant_id: tenant.id, role: 'admin' });
|
||||
const device = await createTestDevice({
|
||||
tenant_id: tenant.id,
|
||||
name: 'Original Name',
|
||||
is_approved: false
|
||||
});
|
||||
const token = generateTestToken(admin, tenant);
|
||||
|
||||
const updateData = {
|
||||
name: 'Updated Device Name',
|
||||
is_approved: true,
|
||||
location_description: 'Updated Location'
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
.put(`/devices/${device.id}`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send(updateData);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.body.success).to.be.true;
|
||||
|
||||
// Verify update in database
|
||||
const updatedDevice = await models.Device.findByPk(device.id);
|
||||
expect(updatedDevice.name).to.equal('Updated Device Name');
|
||||
expect(updatedDevice.is_approved).to.be.true;
|
||||
expect(updatedDevice.location_description).to.equal('Updated Location');
|
||||
});
|
||||
|
||||
it('should require admin role for updates', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({ tenant_id: tenant.id, role: 'user' });
|
||||
const device = await createTestDevice({ tenant_id: tenant.id });
|
||||
const token = generateTestToken(user, tenant);
|
||||
|
||||
const response = await request(app)
|
||||
.put(`/devices/${device.id}`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({ name: 'Unauthorized Update' });
|
||||
|
||||
expect(response.status).to.equal(403);
|
||||
});
|
||||
|
||||
it('should not update device from different tenant', async () => {
|
||||
const tenant1 = await createTestTenant();
|
||||
const tenant2 = await createTestTenant();
|
||||
|
||||
const admin1 = await createTestUser({ tenant_id: tenant1.id, role: 'admin' });
|
||||
const device2 = await createTestDevice({ tenant_id: tenant2.id });
|
||||
|
||||
const token1 = generateTestToken(admin1, tenant1);
|
||||
const response = await request(app)
|
||||
.put(`/devices/${device2.id}`)
|
||||
.set('Authorization', `Bearer ${token1}`)
|
||||
.send({ name: 'Cross-tenant hack' });
|
||||
|
||||
expect(response.status).to.equal(404);
|
||||
});
|
||||
|
||||
it('should validate update data', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const admin = await createTestUser({ tenant_id: tenant.id, role: 'admin' });
|
||||
const device = await createTestDevice({ tenant_id: tenant.id });
|
||||
const token = generateTestToken(admin, tenant);
|
||||
|
||||
// Test invalid coordinate update
|
||||
const response = await request(app)
|
||||
.put(`/devices/${device.id}`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({ geo_lat: 100 }); // Invalid latitude
|
||||
|
||||
expect(response.status).to.be.oneOf([400, 422]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /devices/:id', () => {
|
||||
it('should delete device with admin role', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const admin = await createTestUser({ tenant_id: tenant.id, role: 'admin' });
|
||||
const device = await createTestDevice({ tenant_id: tenant.id });
|
||||
const token = generateTestToken(admin, tenant);
|
||||
|
||||
const response = await request(app)
|
||||
.delete(`/devices/${device.id}`)
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.body.success).to.be.true;
|
||||
|
||||
// Verify deletion
|
||||
const deletedDevice = await models.Device.findByPk(device.id);
|
||||
expect(deletedDevice).to.be.null;
|
||||
});
|
||||
|
||||
it('should require admin role for deletion', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({ tenant_id: tenant.id, role: 'user' });
|
||||
const device = await createTestDevice({ tenant_id: tenant.id });
|
||||
const token = generateTestToken(user, tenant);
|
||||
|
||||
const response = await request(app)
|
||||
.delete(`/devices/${device.id}`)
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
expect(response.status).to.equal(403);
|
||||
});
|
||||
|
||||
it('should not delete device from different tenant', async () => {
|
||||
const tenant1 = await createTestTenant();
|
||||
const tenant2 = await createTestTenant();
|
||||
|
||||
const admin1 = await createTestUser({ tenant_id: tenant1.id, role: 'admin' });
|
||||
const device2 = await createTestDevice({ tenant_id: tenant2.id });
|
||||
|
||||
const token1 = generateTestToken(admin1, tenant1);
|
||||
const response = await request(app)
|
||||
.delete(`/devices/${device2.id}`)
|
||||
.set('Authorization', `Bearer ${token1}`);
|
||||
|
||||
expect(response.status).to.equal(404);
|
||||
});
|
||||
|
||||
it('should handle deletion of device with associated data', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const admin = await createTestUser({ tenant_id: tenant.id, role: 'admin' });
|
||||
const device = await createTestDevice({ tenant_id: tenant.id });
|
||||
const token = generateTestToken(admin, tenant);
|
||||
|
||||
// Create associated detection data
|
||||
await models.DroneDetection.create({
|
||||
device_id: device.id,
|
||||
tenant_id: tenant.id,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
device_timestamp: new Date(),
|
||||
drone_type: 2,
|
||||
threat_level: 'medium'
|
||||
});
|
||||
|
||||
// Create associated alert logs
|
||||
await models.AlertLog.create({
|
||||
device_id: device.id,
|
||||
tenant_id: tenant.id,
|
||||
alert_type: 'proximity',
|
||||
message: 'Test alert',
|
||||
threat_level: 'medium'
|
||||
});
|
||||
|
||||
const response = await request(app)
|
||||
.delete(`/devices/${device.id}`)
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
|
||||
// Verify device and associated data are handled properly
|
||||
const deletedDevice = await models.Device.findByPk(device.id);
|
||||
expect(deletedDevice).to.be.null;
|
||||
|
||||
// Check if associated data was also deleted (depending on cascade settings)
|
||||
const associatedDetections = await models.DroneDetection.findAll({
|
||||
where: { device_id: device.id }
|
||||
});
|
||||
const associatedAlerts = await models.AlertLog.findAll({
|
||||
where: { device_id: device.id }
|
||||
});
|
||||
|
||||
// Depending on your cascade settings, these might be empty or still exist
|
||||
// Adjust expectations based on your database schema
|
||||
});
|
||||
});
|
||||
|
||||
describe('Device Status and Health', () => {
|
||||
it('should track device last seen timestamp', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({ tenant_id: tenant.id });
|
||||
const device = await createTestDevice({
|
||||
tenant_id: tenant.id,
|
||||
is_approved: true
|
||||
});
|
||||
const token = generateTestToken(user, tenant);
|
||||
|
||||
// Simulate recent detection to update last seen
|
||||
await models.DroneDetection.create({
|
||||
device_id: device.id,
|
||||
tenant_id: tenant.id,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
device_timestamp: new Date(),
|
||||
drone_type: 2
|
||||
});
|
||||
|
||||
const response = await request(app)
|
||||
.get(`/devices/${device.id}`)
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
const deviceData = response.body.data;
|
||||
expect(deviceData.last_seen).to.exist;
|
||||
});
|
||||
|
||||
it('should indicate device online/offline status', async () => {
|
||||
const tenant = await createTestTenant();
|
||||
const user = await createTestUser({ tenant_id: tenant.id });
|
||||
const token = generateTestToken(user, tenant);
|
||||
|
||||
// Create device with recent activity (online)
|
||||
const onlineDevice = await createTestDevice({
|
||||
tenant_id: tenant.id,
|
||||
is_approved: true
|
||||
});
|
||||
|
||||
await models.DroneDetection.create({
|
||||
device_id: onlineDevice.id,
|
||||
tenant_id: tenant.id,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
device_timestamp: new Date(), // Recent
|
||||
drone_type: 2
|
||||
});
|
||||
|
||||
// Create device with old activity (offline)
|
||||
const offlineDevice = await createTestDevice({
|
||||
tenant_id: tenant.id,
|
||||
is_approved: true
|
||||
});
|
||||
|
||||
await models.DroneDetection.create({
|
||||
device_id: offlineDevice.id,
|
||||
tenant_id: tenant.id,
|
||||
geo_lat: 59.3293,
|
||||
geo_lon: 18.0686,
|
||||
device_timestamp: new Date(Date.now() - 3600000 * 24), // 24 hours ago
|
||||
drone_type: 2
|
||||
});
|
||||
|
||||
const response = await request(app)
|
||||
.get('/devices')
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
const devices = response.body.data;
|
||||
|
||||
const online = devices.find(d => d.id === onlineDevice.id);
|
||||
const offline = devices.find(d => d.id === offlineDevice.id);
|
||||
|
||||
// These assertions depend on your business logic for determining online status
|
||||
expect(online).to.exist;
|
||||
expect(offline).to.exist;
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user