// Unit tests for summarizeMulticast — the pure verdict function // under integrations/meraki/switches.js. No Meraki API needed; the // summarizer takes a fixture-shaped response and computes flags. // // The target end-state (per the plan) is: // defaultSettings.igmpSnoopingEnabled = false // defaultSettings.floodUnknownMulticastTrafficEnabled = true // overrides = [] (no per-switch drift) // // `needsFix` is true if any layer deviates from that. import test from 'node:test'; import assert from 'node:assert/strict'; import { summarizeMulticast } from '../integrations/meraki/switches.js'; test('target state → needsFix: false', () => { const s = summarizeMulticast({ defaultSettings: { igmpSnoopingEnabled: false, floodUnknownMulticastTrafficEnabled: true }, overrides: [], }); assert.equal(s.needsFix, false); assert.equal(s.defaultOk, true); assert.equal(s.defaultSnoopOn, false); assert.equal(s.defaultFloodOff, false); assert.equal(s.deviatingOverrides.length, 0); }); test('snoop on at default → needsFix: true', () => { const s = summarizeMulticast({ defaultSettings: { igmpSnoopingEnabled: true, floodUnknownMulticastTrafficEnabled: true }, overrides: [], }); assert.equal(s.needsFix, true); assert.equal(s.defaultSnoopOn, true); assert.equal(s.defaultFloodOff, false); assert.equal(s.deviatingOverrides.length, 0); }); test('flood off at default → needsFix: true', () => { const s = summarizeMulticast({ defaultSettings: { igmpSnoopingEnabled: false, floodUnknownMulticastTrafficEnabled: false }, overrides: [], }); assert.equal(s.needsFix, true); assert.equal(s.defaultSnoopOn, false); assert.equal(s.defaultFloodOff, true); }); test('both default settings wrong → both flags true', () => { const s = summarizeMulticast({ defaultSettings: { igmpSnoopingEnabled: true, floodUnknownMulticastTrafficEnabled: false }, overrides: [], }); assert.equal(s.needsFix, true); assert.equal(s.defaultSnoopOn, true); assert.equal(s.defaultFloodOff, true); }); test('default correct + one override with snoop=true → needsFix: true', () => { const s = summarizeMulticast({ defaultSettings: { igmpSnoopingEnabled: false, floodUnknownMulticastTrafficEnabled: true }, overrides: [ { switches: ['Q234-ABCD-0001'], igmpSnoopingEnabled: true, floodUnknownMulticastTrafficEnabled: true }, ], }); assert.equal(s.needsFix, true); assert.equal(s.defaultOk, true); assert.equal(s.deviatingOverrides.length, 1); }); test('default correct + override that matches target exactly → needsFix: false', () => { // An override that duplicates the default is redundant but harmless. // We don't nag the operator about it because there's no user-visible // impact — the switch behaves identically to no override. const s = summarizeMulticast({ defaultSettings: { igmpSnoopingEnabled: false, floodUnknownMulticastTrafficEnabled: true }, overrides: [ { switches: ['Q234-ABCD-0001'], igmpSnoopingEnabled: false, floodUnknownMulticastTrafficEnabled: true }, ], }); assert.equal(s.needsFix, false); assert.equal(s.deviatingOverrides.length, 0); }); test('default correct + override with flood=false only → needsFix: true', () => { const s = summarizeMulticast({ defaultSettings: { igmpSnoopingEnabled: false, floodUnknownMulticastTrafficEnabled: true }, overrides: [ { switches: ['Q234-ABCD-0001'], igmpSnoopingEnabled: false, floodUnknownMulticastTrafficEnabled: false }, ], }); assert.equal(s.needsFix, true); assert.equal(s.deviatingOverrides.length, 1); }); test('default correct + multiple overrides mixed → deviatingOverrides only counts the bad ones', () => { const s = summarizeMulticast({ defaultSettings: { igmpSnoopingEnabled: false, floodUnknownMulticastTrafficEnabled: true }, overrides: [ { switches: ['A'], igmpSnoopingEnabled: false, floodUnknownMulticastTrafficEnabled: true }, // OK { switches: ['B'], igmpSnoopingEnabled: true, floodUnknownMulticastTrafficEnabled: true }, // bad { stacks: ['1'], igmpSnoopingEnabled: false, floodUnknownMulticastTrafficEnabled: false }, // bad ], }); assert.equal(s.needsFix, true); assert.equal(s.deviatingOverrides.length, 2); }); test('malformed / missing defaultSettings → needsFix: false, raw preserved', () => { // Renderer stays silent on this state — the caller sets // data.multicast.error separately, so a broken read doesn't produce // a bogus "everything is fine" warning line. const s = summarizeMulticast({}); assert.equal(s.needsFix, false); assert.equal(s.defaultSnoopOn, false); assert.equal(s.defaultFloodOff, false); assert.equal(s.deviatingOverrides.length, 0); assert.deepEqual(s.raw, {}); }); test('null / undefined input → does not throw, needsFix: false', () => { assert.doesNotThrow(() => summarizeMulticast(null)); assert.doesNotThrow(() => summarizeMulticast(undefined)); assert.equal(summarizeMulticast(null).needsFix, false); assert.equal(summarizeMulticast(undefined).needsFix, false); }); test('overrides field missing entirely → treated as empty', () => { const s = summarizeMulticast({ defaultSettings: { igmpSnoopingEnabled: false, floodUnknownMulticastTrafficEnabled: true }, }); assert.equal(s.needsFix, false); assert.equal(s.deviatingOverrides.length, 0); });