#!/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); });