import { describe, expect, test } from 'bun:test'; import { moduleAlertKey, moduleCheckAlertKey } from './keys'; import { type SuppressionTopology, ancestorKeysFor, findSuppressor, machineAlertKey, } from './suppression'; /** * Six modules on machine `iot`, plus a resolver providing dns_internal for the * `internal` zone, plus caddy off in the dmz on a container. */ const TOPOLOGY: SuppressionTopology = { moduleSystems: [ ...['homebridge', 'module-b', 'module-c', 'module-d', 'module-e', 'module-f'].map( (moduleId) => ({ moduleId, hostname: 'iot', zone: 'internal', infraType: 'machine' as const, }), ), { moduleId: 'technitium', hostname: 'dns', zone: 'internal', infraType: 'machine' as const }, { moduleId: 'caddy', hostname: 'caddy-1', zone: 'dmz', infraType: 'container_service' as const, }, ], zoneProviders: [{ moduleId: 'technitium', capabilityName: 'dns_internal', zones: ['internal'] }], }; const noSuppression = { suppressible: true, modulesInDeployWindow: new Set(), pausedModules: new Set(), topology: TOPOLOGY, }; describe('ancestorKeysFor', () => { test('a check item is owned by its module, nearest first', () => { const ancestors = ancestorKeysFor(moduleCheckAlertKey('homebridge', 'port'), TOPOLOGY); expect(ancestors[0]).toBe(moduleAlertKey('homebridge')); }); test('a module is owned by the machine it runs on', () => { expect(ancestorKeysFor(moduleAlertKey('homebridge'), TOPOLOGY)).toContain( machineAlertKey('iot'), ); }); test('a module is owned by a zone-capability provider covering its zone', () => { expect(ancestorKeysFor(moduleAlertKey('homebridge'), TOPOLOGY)).toContain( moduleAlertKey('technitium'), ); }); // Without this the resolver, whose own check fails BECAUSE dns is down, // silences its own alert — and the one thing naming the cause vanishes. test('a zone provider is not its own ancestor', () => { expect(ancestorKeysFor(moduleAlertKey('technitium'), TOPOLOGY)).not.toContain( moduleAlertKey('technitium'), ); }); test('a module in another zone is unaffected by that zone provider', () => { expect(ancestorKeysFor(moduleAlertKey('caddy'), TOPOLOGY)).not.toContain( moduleAlertKey('technitium'), ); }); // machines_reachable probes the machine POOL, so a container has no // corresponding alert. A real gap, documented rather than papered over. test('a container-hosted module gets no machine ancestor', () => { const ancestors = ancestorKeysFor(moduleAlertKey('caddy'), TOPOLOGY); expect(ancestors.filter((k) => k.startsWith('builtin:machines_reachable'))).toEqual([]); }); test('a built-in alert is a root cause with no ancestors', () => { expect(ancestorKeysFor(machineAlertKey('iot'), TOPOLOGY)).toEqual([]); }); test('an unparseable key yields no ancestors rather than throwing', () => { expect(ancestorKeysFor('not-a-key', TOPOLOGY)).toEqual([]); }); }); describe('findSuppressor — six pages become one', () => { test('every module on a downed machine is suppressed', () => { const firingKeys = new Set([machineAlertKey('iot')]); const moduleIds = ['homebridge', 'module-b', 'module-c', 'module-d', 'module-e', 'module-f']; for (const moduleId of moduleIds) { expect( findSuppressor({ key: moduleAlertKey(moduleId), firingKeys, ...noSuppression }), ).toEqual({ kind: 'alert', key: machineAlertKey('iot'), }); } }); test('their check items are suppressed too', () => { const firingKeys = new Set([machineAlertKey('iot')]); const suppressor = findSuppressor({ key: moduleCheckAlertKey('homebridge', 'port'), firingKeys, ...noSuppression, }); expect(suppressor).toEqual({ kind: 'alert', key: machineAlertKey('iot') }); }); test('the machine alert itself is never suppressed — it is the page you get', () => { const firingKeys = new Set([machineAlertKey('iot')]); expect( findSuppressor({ key: machineAlertKey('iot'), firingKeys, ...noSuppression }), ).toBeNull(); }); test('a module on a DIFFERENT machine is not suppressed', () => { const firingKeys = new Set([machineAlertKey('iot')]); expect( findSuppressor({ key: moduleAlertKey('caddy'), firingKeys, ...noSuppression }), ).toBeNull(); }); test('the module-level alert suppresses its own check items', () => { const firingKeys = new Set([moduleAlertKey('homebridge')]); expect( findSuppressor({ key: moduleCheckAlertKey('homebridge', 'port'), firingKeys, ...noSuppression, }), ).toEqual({ kind: 'alert', key: moduleAlertKey('homebridge') }); }); test('nothing firing means nothing suppressed', () => { expect( findSuppressor({ key: moduleAlertKey('homebridge'), firingKeys: new Set(), ...noSuppression, }), ).toBeNull(); }); test('the nearest ancestor is reported as the cause', () => { // Both the module-level alert and the machine alert are firing; the module // one is the more specific explanation. const firingKeys = new Set([moduleAlertKey('homebridge'), machineAlertKey('iot')]); expect( findSuppressor({ key: moduleCheckAlertKey('homebridge', 'port'), firingKeys, ...noSuppression, }), ).toEqual({ kind: 'alert', key: moduleAlertKey('homebridge') }); }); }); describe('findSuppressor — dead resolver', () => { test('a firing zone provider suppresses modules in its zone', () => { const firingKeys = new Set([moduleAlertKey('technitium')]); expect( findSuppressor({ key: moduleAlertKey('homebridge'), firingKeys, ...noSuppression }), ).toEqual({ kind: 'alert', key: moduleAlertKey('technitium') }); }); test('but not itself', () => { const firingKeys = new Set([moduleAlertKey('technitium')]); expect( findSuppressor({ key: moduleAlertKey('technitium'), firingKeys, ...noSuppression }), ).toBeNull(); }); }); describe('findSuppressor — guards', () => { // A cascading failure must never silence the component reporting it. test('an unsuppressible monitor is never suppressed, whatever is firing', () => { const firingKeys = new Set([machineAlertKey('iot'), moduleAlertKey('technitium')]); expect( findSuppressor({ key: moduleAlertKey('homebridge'), firingKeys, suppressible: false, modulesInDeployWindow: new Set(), pausedModules: new Set(), topology: TOPOLOGY, }), ).toBeNull(); }); test('a deploy window suppresses the module being deployed', () => { expect( findSuppressor({ key: moduleCheckAlertKey('forgejo', 'http'), firingKeys: new Set(), suppressible: true, modulesInDeployWindow: new Set(['forgejo']), pausedModules: new Set(), topology: TOPOLOGY, }), ).toEqual({ kind: 'deploy_window', moduleId: 'forgejo' }); }); test('a deploy window does not suppress other modules', () => { expect( findSuppressor({ key: moduleAlertKey('homebridge'), firingKeys: new Set(), suppressible: true, modulesInDeployWindow: new Set(['forgejo']), pausedModules: new Set(), topology: TOPOLOGY, }), ).toBeNull(); }); test('a deploy window outranks an ancestor alert as the explanation', () => { expect( findSuppressor({ key: moduleAlertKey('homebridge'), firingKeys: new Set([machineAlertKey('iot')]), suppressible: true, modulesInDeployWindow: new Set(['homebridge']), pausedModules: new Set(), topology: TOPOLOGY, }), ).toEqual({ kind: 'deploy_window', moduleId: 'homebridge' }); }); });