Add comprehensive geofence testing scripts
Added three test scripts for validating geofence functionality: 1. test-geofence.js: Database-level geofence testing - Creates test geofence and mock locations - Tests distance calculations and event generation - Validates state tracking (enter/exit detection) 2. test-geofence-notification.js: Email notification testing - Tests SMTP connection and email delivery - Sends formatted geofence notification email - Validates email template rendering 3. test-mqtt-geofence.js: Full MQTT integration testing - Publishes OwnTracks-formatted MQTT messages - Tests complete flow: MQTT → Geofence → Email - Simulates device entering/exiting zones **NPM Scripts:** - npm run test:geofence - Database and logic test - npm run test:geofence:email - Email notification test - npm run test:geofence:mqtt - Full MQTT integration test **Other Changes:** - Updated admin user email to joachim.hummel@gmail.com - All scripts include cleanup instructions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
257
scripts/test-mqtt-geofence.js
Normal file
257
scripts/test-mqtt-geofence.js
Normal file
@@ -0,0 +1,257 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Complete MQTT Geofence Integration Test
|
||||
*
|
||||
* This script:
|
||||
* 1. Connects to MQTT broker
|
||||
* 2. Creates a test geofence
|
||||
* 3. Publishes OwnTracks location messages
|
||||
* 4. Triggers geofence events
|
||||
* 5. Results in email notifications (if dev server is running)
|
||||
*/
|
||||
|
||||
const mqtt = require('mqtt');
|
||||
const Database = require('better-sqlite3');
|
||||
const path = require('path');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
const dbPath = path.join(__dirname, '..', 'data', 'database.sqlite');
|
||||
|
||||
console.log('🧪 Complete MQTT Geofence Integration Test\n');
|
||||
|
||||
// Configuration
|
||||
const CONFIG = {
|
||||
mqttBroker: process.env.MQTT_BROKER_URL || 'mqtt://localhost:1883',
|
||||
mqttUsername: process.env.MQTT_USERNAME,
|
||||
mqttPassword: process.env.MQTT_PASSWORD,
|
||||
|
||||
deviceId: '10',
|
||||
userId: 'admin-001',
|
||||
|
||||
// Test geofence (Frankfurt)
|
||||
geofenceName: 'MQTT Integration Test Zone',
|
||||
geofenceCenter: {
|
||||
lat: 50.1109,
|
||||
lon: 8.6821,
|
||||
},
|
||||
geofenceRadius: 500,
|
||||
|
||||
// Test locations to send via MQTT
|
||||
testLocations: [
|
||||
{
|
||||
lat: 50.1200,
|
||||
lon: 8.6900,
|
||||
label: 'Outside (Start)',
|
||||
delay: 1000,
|
||||
},
|
||||
{
|
||||
lat: 50.1109,
|
||||
lon: 8.6821,
|
||||
label: 'Inside (Enter - should trigger EMAIL)',
|
||||
delay: 2000,
|
||||
},
|
||||
{
|
||||
lat: 50.1115,
|
||||
lon: 8.6825,
|
||||
label: 'Inside (Stay)',
|
||||
delay: 2000,
|
||||
},
|
||||
{
|
||||
lat: 50.1200,
|
||||
lon: 8.6900,
|
||||
label: 'Outside (Exit - should trigger EMAIL)',
|
||||
delay: 2000,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Create test geofence
|
||||
function createTestGeofence() {
|
||||
const db = new Database(dbPath);
|
||||
try {
|
||||
const geofenceId = uuidv4();
|
||||
|
||||
db.prepare(`
|
||||
INSERT INTO Geofence (
|
||||
id, name, description, shape_type,
|
||||
center_latitude, center_longitude, radius_meters,
|
||||
owner_id, device_id, color
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
geofenceId,
|
||||
CONFIG.geofenceName,
|
||||
'Auto-generated test geofence for MQTT integration test',
|
||||
'circle',
|
||||
CONFIG.geofenceCenter.lat,
|
||||
CONFIG.geofenceCenter.lon,
|
||||
CONFIG.geofenceRadius,
|
||||
CONFIG.userId,
|
||||
CONFIG.deviceId,
|
||||
'#8b5cf6'
|
||||
);
|
||||
|
||||
console.log(`✓ Created test geofence: "${CONFIG.geofenceName}"`);
|
||||
console.log(` ID: ${geofenceId}`);
|
||||
console.log(` Center: ${CONFIG.geofenceCenter.lat}, ${CONFIG.geofenceCenter.lon}`);
|
||||
console.log(` Radius: ${CONFIG.geofenceRadius}m\n`);
|
||||
|
||||
return geofenceId;
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Create OwnTracks location message
|
||||
function createOwnTracksMessage(lat, lon) {
|
||||
return {
|
||||
_type: 'location',
|
||||
tid: CONFIG.deviceId,
|
||||
lat: lat,
|
||||
lon: lon,
|
||||
tst: Math.floor(Date.now() / 1000), // Unix timestamp
|
||||
batt: 85,
|
||||
vel: 0,
|
||||
acc: 10,
|
||||
};
|
||||
}
|
||||
|
||||
// Send MQTT message
|
||||
function publishLocation(client, lat, lon, label) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const topic = `owntracks/user/${CONFIG.deviceId}`;
|
||||
const message = createOwnTracksMessage(lat, lon);
|
||||
const payload = JSON.stringify(message);
|
||||
|
||||
console.log(`📡 Publishing location: ${label}`);
|
||||
console.log(` Topic: ${topic}`);
|
||||
console.log(` Coordinates: ${lat}, ${lon}`);
|
||||
|
||||
client.publish(topic, payload, { qos: 1 }, (err) => {
|
||||
if (err) {
|
||||
console.error(` ❌ Failed: ${err.message}`);
|
||||
reject(err);
|
||||
} else {
|
||||
console.log(` ✓ Published\n`);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Wait helper
|
||||
function wait(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// Main test
|
||||
async function runTest() {
|
||||
let geofenceId = null;
|
||||
let mqttClient = null;
|
||||
|
||||
try {
|
||||
console.log('🔧 Step 1: Creating test geofence...\n');
|
||||
geofenceId = createTestGeofence();
|
||||
|
||||
console.log('🔌 Step 2: Connecting to MQTT broker...');
|
||||
console.log(` Broker: ${CONFIG.mqttBroker}\n`);
|
||||
|
||||
const options = {
|
||||
clean: true,
|
||||
connectTimeout: 10000,
|
||||
};
|
||||
|
||||
if (CONFIG.mqttUsername && CONFIG.mqttPassword) {
|
||||
options.username = CONFIG.mqttUsername;
|
||||
options.password = CONFIG.mqttPassword;
|
||||
}
|
||||
|
||||
mqttClient = mqtt.connect(CONFIG.mqttBroker, options);
|
||||
|
||||
// Wait for connection
|
||||
await new Promise((resolve, reject) => {
|
||||
mqttClient.on('connect', () => {
|
||||
console.log('✓ Connected to MQTT broker\n');
|
||||
resolve();
|
||||
});
|
||||
|
||||
mqttClient.on('error', (error) => {
|
||||
console.error('❌ MQTT connection error:', error.message);
|
||||
reject(error);
|
||||
});
|
||||
|
||||
setTimeout(() => reject(new Error('Connection timeout')), 10000);
|
||||
});
|
||||
|
||||
console.log('📤 Step 3: Publishing test locations...\n');
|
||||
console.log('⚠️ IMPORTANT: Make sure the dev server is running!');
|
||||
console.log(' Run: npm run dev (in another terminal)\n');
|
||||
|
||||
await wait(2000);
|
||||
|
||||
// Publish each test location
|
||||
for (let i = 0; i < CONFIG.testLocations.length; i++) {
|
||||
const loc = CONFIG.testLocations[i];
|
||||
|
||||
console.log(`[${i + 1}/${CONFIG.testLocations.length}]`);
|
||||
await publishLocation(mqttClient, loc.lat, loc.lon, loc.label);
|
||||
await wait(loc.delay);
|
||||
}
|
||||
|
||||
console.log('📊 Step 4: Test Summary\n');
|
||||
console.log('✅ Published 4 MQTT location messages');
|
||||
console.log('✅ Expected: 2 email notifications (Enter + Exit)');
|
||||
console.log(`✅ Geofence ID: ${geofenceId}\n`);
|
||||
|
||||
console.log('💡 What should happen:');
|
||||
console.log(' 1. MQTT subscriber receives the messages');
|
||||
console.log(' 2. Geofence engine detects ENTER and EXIT events');
|
||||
console.log(' 3. Email notifications are sent to joachim.hummel@gmail.com');
|
||||
console.log(' 4. Check your inbox!\n');
|
||||
|
||||
console.log('📧 Check events in database:');
|
||||
console.log(` sqlite3 data/database.sqlite "SELECT * FROM GeofenceEvent WHERE geofence_id = '${geofenceId}'"\n`);
|
||||
|
||||
console.log('🗑️ Cleanup:');
|
||||
console.log(` sqlite3 data/database.sqlite "DELETE FROM Geofence WHERE id = '${geofenceId}'"\n`);
|
||||
|
||||
// Disconnect MQTT
|
||||
mqttClient.end();
|
||||
console.log('✓ Disconnected from MQTT broker');
|
||||
|
||||
console.log('\n✅ Integration test completed!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Test failed:', error.message);
|
||||
|
||||
if (error.message.includes('ECONNREFUSED')) {
|
||||
console.error('\n💡 MQTT broker is not running or not accessible.');
|
||||
console.error(' Check your MQTT_BROKER_URL in .env');
|
||||
}
|
||||
|
||||
if (mqttClient) {
|
||||
mqttClient.end();
|
||||
}
|
||||
|
||||
if (geofenceId) {
|
||||
const db = new Database(dbPath);
|
||||
try {
|
||||
db.prepare('DELETE FROM Geofence WHERE id = ?').run(geofenceId);
|
||||
console.log('\n✓ Cleaned up test geofence');
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run
|
||||
console.log('Starting MQTT integration test...\n');
|
||||
runTest().then(() => {
|
||||
console.log('\nTest script finished.');
|
||||
process.exit(0);
|
||||
}).catch((error) => {
|
||||
console.error('Unexpected error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user