import { describe, it, expect, beforeEach, vi } from 'vitest' import { registerSubscriber, getSubscriberBboxUnion, clearSubscribers, notifySubscribersForEntity, notifySubscribersRemove, broadcastSubscriberSnapshots, } from '../../../server/utils/cotSubscribers.js' import { updateFromCot, clearCotStore } from '../../../server/utils/cotStore.js' describe('cotSubscribers', () => { beforeEach(() => { clearSubscribers() }) it('unions subscriber bboxes', () => { registerSubscriber({ bbox: { west: -123, south: 37, east: -122, north: 38 }, layers: new Set(['air']), push: vi.fn(), }) registerSubscriber({ bbox: { west: -124, south: 36, east: -121, north: 39 }, layers: new Set(['surface']), push: vi.fn(), }) expect(getSubscriberBboxUnion()).toEqual({ west: -124, south: 36, east: -121, north: 39 }) }) it('notifies subscribers inside bbox and matching layer', async () => { const push = vi.fn() registerSubscriber({ bbox: { west: -123, south: 37, east: -122, north: 38 }, layers: new Set(['air']), push, }) await notifySubscribersForEntity('update', { entity: { id: 'ICAO.x' } }, { id: 'ICAO.x', lat: 37.5, lng: -122.5, type: 'a-f-A-C-F', }) expect(push).toHaveBeenCalledWith('update', expect.any(String)) }) it('skips subscribers when entity outside bbox', async () => { const push = vi.fn() registerSubscriber({ bbox: { west: -123, south: 37, east: -122, north: 38 }, layers: new Set(['air']), push, }) await notifySubscribersForEntity('update', { entity: { id: 'ICAO.x' } }, { id: 'ICAO.x', lat: 40, lng: -122.5, type: 'a-f-A-C-F', }) expect(push).not.toHaveBeenCalled() }) it('notifySubscribersRemove pushes to all subscribers', async () => { const pushA = vi.fn() const pushB = vi.fn() registerSubscriber({ bbox: { west: -123, south: 37, east: -122, north: 38 }, layers: new Set(['air']), push: pushA, }) registerSubscriber({ bbox: { west: -125, south: 35, east: -120, north: 40 }, layers: new Set(['surface']), push: pushB, }) await notifySubscribersRemove('ICAO.removed') expect(pushA).toHaveBeenCalledWith('remove', JSON.stringify({ id: 'ICAO.removed' })) expect(pushB).toHaveBeenCalledWith('remove', JSON.stringify({ id: 'ICAO.removed' })) }) it('broadcastSubscriberSnapshots sends per-subscriber filtered snapshot', async () => { clearCotStore() await updateFromCot({ id: 'ICAO.in', lat: 37.5, lng: -122.5, type: 'a-f-A-C-F' }) await updateFromCot({ id: 'ICAO.out', lat: 40, lng: -100, type: 'a-f-A-C-F' }) const push = vi.fn() registerSubscriber({ bbox: { west: -123, south: 37, east: -122, north: 38 }, layers: new Set(['air']), push, }) await broadcastSubscriberSnapshots({ ttlMs: 90_000, osintTtlMs: 30_000, takFilterBbox: false }) expect(push).toHaveBeenCalledWith('snapshot', expect.any(String)) const payload = JSON.parse(push.mock.calls[0][1]) expect(payload.entities.map(e => e.id)).toEqual(['ICAO.in']) }) })