const assert = require('assert'); const { MetricAggregator } = require('../metric-aggregator'); describe('MetricAggregator', () => { it('merges latest metrics by device and always includes n', async () => { const flushed = []; const aggregator = new MetricAggregator({ mode: 'batch', onFlush: async (device, payload) => { flushed.push({ device, payload }); return true; }, }); aggregator.ingest({ deviceId: 'JH-001', ip: '127.0.0.1' }, { F: 24.6 }); aggregator.ingest({ deviceId: 'JH-001', ip: '127.0.0.1' }, { A: 0, F: 25.1 }); await aggregator.flush({ reason: 'test' }); assert.strictEqual(flushed.length, 1); assert.deepStrictEqual(flushed[0].payload, { n: 'JH-001', F: 25.1, A: 0, }); }); it('flushes only dirty devices', async () => { const flushed = []; const aggregator = new MetricAggregator({ mode: 'batch', onFlush: async (device, payload) => { flushed.push({ deviceId: device.deviceId, payload }); return true; }, }); aggregator.ingest({ deviceId: 'JH-001' }, { F: 24.6 }); await aggregator.flush({ reason: 'test-1' }); await aggregator.flush({ reason: 'test-2' }); assert.strictEqual(flushed.length, 1); assert.strictEqual(flushed[0].deviceId, 'JH-001'); }); it('keeps device dirty when flush callback reports failure', async () => { const flushed = []; const aggregator = new MetricAggregator({ mode: 'batch', onFlush: async (_device, payload) => { flushed.push(payload); return false; }, }); aggregator.ingest({ deviceId: 'JH-001' }, { F: 24.6 }); await aggregator.flush({ reason: 'test' }); const snapshot = aggregator.getSnapshot('JH-001'); assert.strictEqual(flushed.length, 1); assert.strictEqual(snapshot.dirty, true); }); it('supports immediate mode', async () => { const flushed = []; const aggregator = new MetricAggregator({ mode: 'immediate', onFlush: async (_device, payload) => { flushed.push(payload); return true; }, }); await aggregator.ingest({ deviceId: 'JH-001' }, { F: 24.6 }); assert.strictEqual(flushed.length, 1); assert.deepStrictEqual(flushed[0], { n: 'JH-001', F: 24.6, }); }); });