feat(release): v1.14.0 discovery and conf pages

This commit is contained in:
2026-08-01 01:21:27 -04:00
parent ecc9b62842
commit 011d4b2975
42 changed files with 3091 additions and 96 deletions
+75
View File
@@ -0,0 +1,75 @@
require('./setup');
const { Resource } = require('../models/resource');
const { DiscoveryReconciler } = require('../services/discovery_reconciler');
describe('DiscoveryReconciler', () => {
beforeEach(async () => {
// Clear resources before each test
const all = await Resource.list();
for (const r of all) {
await r.delete();
}
});
it('should create a new device if no MAC or IP matches', async () => {
const payload = {
resources: [{
kind: 'host',
name: 'New Host',
slug: 'new-host',
metadata: {
interfaces: [{ mac: '00:11:22:33:44:55', ip: '192.168.1.100' }]
}
}]
};
await DiscoveryReconciler.reconcile('test-plugin', payload);
const all = await Resource.list();
expect(all).toHaveLength(1);
expect(all[0].name).toBe('New Host');
expect(all[0].metadata.discovery_sources).toContain('test-plugin');
});
it('should merge into an existing device if MAC matches', async () => {
// 1. Initial creation
await DiscoveryReconciler.reconcile('plugin-A', {
resources: [{
kind: 'unmanaged_device',
name: 'Old Host',
slug: 'old-host',
metadata: {
os: 'Linux',
interfaces: [{ mac: 'AA:BB:CC:DD:EE:FF', ip: '10.0.0.5' }]
}
}]
});
// 2. Secondary discovery from a different plugin, same MAC but new IP
await DiscoveryReconciler.reconcile('plugin-B', {
resources: [{
kind: 'host',
name: 'Updated Host', // Name updates aren't overwritten in simple merge, but let's see
metadata: {
cpu_cores: 4,
interfaces: [{ mac: 'AA:BB:CC:DD:EE:FF', ip: '10.0.0.6' }]
}
}]
});
const all = await Resource.list();
expect(all).toHaveLength(1); // Should have merged, not created a new one
const merged = all[0];
expect(merged.metadata.discovery_sources).toContain('plugin-A');
expect(merged.metadata.discovery_sources).toContain('plugin-B');
// Metadata should be merged
expect(merged.metadata.os).toBe('Linux');
expect(merged.metadata.cpu_cores).toBe(4);
// Interface array should be merged/updated
expect(merged.metadata.interfaces).toHaveLength(1);
expect(merged.metadata.interfaces[0].ip).toBe('10.0.0.6'); // Updated IP
});
});
+32
View File
@@ -0,0 +1,32 @@
require('./setup');
const { Webhook } = require('../models/webhook');
const { WebhookEmitter } = require('../services/webhook_emitter');
const crypto = require('crypto');
describe('WebhookEmitter', () => {
let webhook;
beforeEach(async () => {
// Clear webhooks before each test
const all = await Webhook.list();
for (const w of all) {
await w.delete();
}
webhook = await Webhook.create({
id: crypto.randomUUID(),
name: 'Test Webhook',
url: 'http://localhost:9999/dummy',
events: ['discovery.new_device'],
secret: 'mysecret',
created_on: Math.floor(Date.now() / 1000)
});
});
it('should not throw when emitting an event', async () => {
// We expect this to fail network connection but be caught gracefully by the emitter
await WebhookEmitter.emit('discovery.new_device', { name: 'Device1' });
// If it doesn't throw, test passes
expect(true).toBe(true);
});
});