Files
drone-detector/server/tests/setup.js
2025-09-15 12:12:06 +02:00

329 lines
8.2 KiB
JavaScript

const { Sequelize } = require('sequelize');
const path = require('path');
// Set test environment variables
process.env.NODE_ENV = 'test';
process.env.JWT_SECRET = 'test-jwt-secret-key-for-testing-only';
process.env.DATABASE_URL = ':memory:';
process.env.DB_DIALECT = 'sqlite';
process.env.DB_STORAGE = ':memory:';
process.env.DB_LOGGING = 'false';
// Test database configuration
const testDatabase = {
dialect: 'sqlite',
storage: ':memory:', // In-memory database for fast tests
logging: false, // Disable SQL logging in tests
sync: { force: true } // Always recreate tables for tests
};
let sequelize;
let models;
/**
* Setup test environment before all tests
*/
async function setupTestEnvironment() {
// Create test database connection
sequelize = new Sequelize(testDatabase);
// Import and initialize models with test sequelize instance
const Device = require('../models/Device')(sequelize);
const DroneDetection = require('../models/DroneDetection')(sequelize);
const Heartbeat = require('../models/Heartbeat')(sequelize);
const User = require('../models/User')(sequelize);
const AlertRule = require('../models/AlertRule')(sequelize);
const AlertLog = require('../models/AlertLog')(sequelize);
const Tenant = require('../models/Tenant')(sequelize);
const ManagementUser = require('../models/ManagementUser')(sequelize);
// Define associations
Device.hasMany(DroneDetection, { foreignKey: 'device_id', as: 'detections' });
DroneDetection.belongsTo(Device, { foreignKey: 'device_id', as: 'device' });
Device.hasMany(Heartbeat, { foreignKey: 'device_id', as: 'heartbeats' });
Heartbeat.belongsTo(Device, { foreignKey: 'device_id', as: 'device' });
User.hasMany(AlertRule, { foreignKey: 'user_id', as: 'alertRules' });
AlertRule.belongsTo(User, { foreignKey: 'user_id', as: 'user' });
AlertRule.hasMany(AlertLog, { foreignKey: 'alert_rule_id', as: 'logs' });
AlertLog.belongsTo(AlertRule, { foreignKey: 'alert_rule_id', as: 'rule' });
DroneDetection.hasMany(AlertLog, { foreignKey: 'detection_id', as: 'alerts' });
AlertLog.belongsTo(DroneDetection, { foreignKey: 'detection_id', as: 'detection' });
// Tenant associations
Tenant.hasMany(User, { foreignKey: 'tenant_id', as: 'users' });
User.belongsTo(Tenant, { foreignKey: 'tenant_id', as: 'tenant' });
Tenant.hasMany(Device, { foreignKey: 'tenant_id', as: 'devices' });
Device.belongsTo(Tenant, { foreignKey: 'tenant_id', as: 'tenant' });
// Create models object
models = {
sequelize,
Sequelize,
Device,
DroneDetection,
Heartbeat,
User,
AlertRule,
AlertLog,
Tenant,
ManagementUser
};
// Override the main models module with our test models
// This ensures that when other modules import '../models', they get our test models
const mainModelsPath = path.resolve(__dirname, '../models/index.js');
require.cache[mainModelsPath] = {
exports: models,
loaded: true,
id: mainModelsPath
};
// Inject test models into middleware modules
const authMiddleware = require('../middleware/auth');
authMiddleware.setModels(models);
// Sync database
await sequelize.sync({ force: true });
// Return test context
return { sequelize, models };
}
/**
* Cleanup test environment after all tests
*/
async function teardownTestEnvironment() {
if (sequelize) {
await sequelize.close();
}
}
/**
* Clean database between tests
*/
async function cleanDatabase() {
if (sequelize) {
await sequelize.sync({ force: true });
}
}
/**
* Create test user with specified role and tenant
*/
async function createTestUser(userData = {}) {
const { User, Tenant } = models;
// Create default tenant if not exists
let tenant = await Tenant.findOne({ where: { slug: 'test-tenant' } });
if (!tenant) {
try {
tenant = await Tenant.create({
name: 'Test Tenant',
slug: 'test-tenant',
domain: 'test.example.com',
is_active: true
});
} catch (error) {
console.error('❌ Default tenant creation failed:', error.message);
console.error('❌ Validation errors:', error.errors);
throw error;
}
}
// Generate a unique username to avoid conflicts
const uniqueSuffix = Date.now() + '-' + Math.random().toString(36).substr(2, 5);
const defaultUserData = {
username: userData.username || 'testuser-' + uniqueSuffix,
email: userData.email || 'test-' + uniqueSuffix + '@example.com',
password_hash: '$2b$10$example.hash.for.testing.purposes.only',
role: 'admin',
tenant_id: tenant.id,
is_active: true,
...userData
};
return await User.create(defaultUserData);
}
/**
* Create test device with specified tenant
*/
async function createTestDevice(deviceData = {}) {
const { Device, Tenant } = models;
// Create default tenant if not exists
let tenant = await Tenant.findOne({ where: { slug: 'test-tenant' } });
if (!tenant) {
tenant = await Tenant.create({
name: 'Test Tenant',
slug: 'test-tenant',
domain: 'test.example.com',
is_active: true
});
}
const defaultDeviceData = {
id: Math.floor(Math.random() * 1000000000),
name: 'Test Device',
geo_lat: 59.3293,
geo_lon: 18.0686,
location_description: 'Test Location',
tenant_id: tenant.id,
is_active: true,
is_approved: true,
...deviceData
};
return await Device.create(defaultDeviceData);
}
/**
* Create test detection data
*/
async function createTestDetection(detectionData = {}) {
const { DroneDetection, Device } = models;
// Create device if not provided
let device;
if (detectionData.device_id) {
device = await Device.findByPk(detectionData.device_id);
}
if (!device) {
device = await createTestDevice();
}
const defaultDetectionData = {
device_id: device.id,
geo_lat: device.geo_lat,
geo_lon: device.geo_lon,
device_timestamp: Date.now(),
server_timestamp: new Date(),
drone_type: 2,
rssi: -65,
freq: 2400,
drone_id: Math.floor(Math.random() * 10000),
...detectionData
};
return await DroneDetection.create(defaultDetectionData);
}
/**
* Create test tenant
*/
async function createTestTenant(tenantData = {}) {
const { Tenant } = models;
const defaultTenantData = {
name: 'Test Tenant',
slug: 'test-tenant-' + Date.now() + '-' + Math.random().toString(36).substr(2, 5),
domain: 'test.example.com',
is_active: true,
...tenantData
};
try {
return await Tenant.create(defaultTenantData);
} catch (error) {
console.error('❌ Tenant creation failed:', error.message);
console.error('❌ Validation errors:', error.errors);
console.error('❌ Tenant data:', defaultTenantData);
throw error;
}
}
/**
* Generate JWT token for test user
*/
function generateTestToken(user, tenant = null) {
const jwt = require('jsonwebtoken');
const payload = {
userId: user.id,
username: user.username,
role: user.role,
email: user.email
};
if (tenant) {
payload.tenantId = tenant.slug;
}
return jwt.sign(payload, process.env.JWT_SECRET || 'test-secret', { expiresIn: '1h' });
}
/**
* Mock Express request object
*/
function mockRequest(overrides = {}) {
return {
body: {},
params: {},
query: {},
headers: {},
get: function(header) {
return this.headers[header.toLowerCase()];
},
...overrides
};
}
/**
* Mock Express response object
*/
function mockResponse() {
const res = {
statusCode: 200,
data: null,
status: function(code) {
this.statusCode = code;
return this;
},
json: function(data) {
this.data = data;
return this;
},
send: function(data) {
this.data = data;
return this;
},
setHeader: function(name, value) {
this.headers = this.headers || {};
this.headers[name] = value;
return this;
}
};
return res;
}
/**
* Mock Express next function
*/
function mockNext() {
const errors = [];
const next = function(error) {
if (error) errors.push(error);
};
next.errors = errors;
return next;
}
module.exports = {
setupTestEnvironment,
teardownTestEnvironment,
cleanDatabase,
createTestUser,
createTestDevice,
createTestDetection,
createTestTenant,
generateTestToken,
mockRequest,
mockResponse,
mockNext
};