Files
adam-acs/tests/unit/services/exchange_index_checker.spec.ts
T
Kevin L'Hereec 0c83a4ccb2 feat: TR-069 Exchange Viewer - structured MongoDB storage with SSE streaming and sequence diagram UI
- MongoDB storage: cwmp_exchanges collection with indexes (deviceId+timestamp, deviceId+sequenceNumber unique, TTL 24h)
- ExchangeStoreService: CWMP method extraction, per-device queue for ordering, retry logic, message cap (5000/device)
- Exchange API: GET/DELETE/SSE stream endpoints with Last-Event-ID reconnection
- CwmpLoggerMiddleware: dual-write (MongoDB + legacy files) with fire-and-forget
- Frontend: SequenceViewer (CPE↔ACS lifelines, arrows, auto-scroll, virtual scroll) + InspectorPanel (formatted XML, copy, syntax highlighting)
- Tr069Trace page: integrated new viewer with col-6/col-6 layout
- DeviceDetail page: collapsible exchange section when device is traced
- 14 property-based tests, 40+ unit tests, 5 integration tests
- Health check includes index verification
- Config: feature flags, SSE timeouts, retry policy
2026-06-03 15:52:44 +02:00

146 lines
5.1 KiB
TypeScript

import { describe, expect, it, vi, beforeEach } from 'vitest'
import { verifyCwmpExchangesIndexes, verifyIndexesAtStartup } from '../../../app/services/exchange_index_checker.js'
describe('exchange_index_checker', () => {
describe('verifyCwmpExchangesIndexes', () => {
it('should report all indexes present when they all exist', async () => {
const mockDb = {
collection: vi.fn().mockReturnValue({
indexes: vi.fn().mockResolvedValue([
{ name: '_id_', key: { _id: 1 } },
{ name: 'idx_deviceId_timestamp', key: { deviceId: 1, timestamp: 1 } },
{ name: 'idx_deviceId_sequenceNumber', key: { deviceId: 1, sequenceNumber: 1 } },
{ name: 'idx_createdAt_ttl', key: { createdAt: 1 } },
]),
}),
} as any
const result = await verifyCwmpExchangesIndexes(mockDb)
expect(result.collection).toBe('cwmp_exchanges')
expect(result.allPresent).toBe(true)
expect(result.indexes).toHaveLength(3)
expect(result.indexes.every((idx) => idx.exists)).toBe(true)
})
it('should report missing indexes when some are absent', async () => {
const mockDb = {
collection: vi.fn().mockReturnValue({
indexes: vi.fn().mockResolvedValue([
{ name: '_id_', key: { _id: 1 } },
{ name: 'idx_deviceId_timestamp', key: { deviceId: 1, timestamp: 1 } },
]),
}),
} as any
const result = await verifyCwmpExchangesIndexes(mockDb)
expect(result.allPresent).toBe(false)
expect(result.indexes[0]).toEqual({
name: 'idx_deviceId_timestamp',
description: 'Compound index (deviceId, timestamp)',
exists: true,
})
expect(result.indexes[1]).toEqual({
name: 'idx_deviceId_sequenceNumber',
description: 'Unique index (deviceId, sequenceNumber)',
exists: false,
})
expect(result.indexes[2]).toEqual({
name: 'idx_createdAt_ttl',
description: 'TTL index (createdAt, 86400s)',
exists: false,
})
})
it('should report all indexes missing when collection does not exist', async () => {
const mockDb = {
collection: vi.fn().mockReturnValue({
indexes: vi.fn().mockRejectedValue(new Error('ns not found')),
}),
} as any
const result = await verifyCwmpExchangesIndexes(mockDb)
expect(result.allPresent).toBe(false)
expect(result.indexes.every((idx) => !idx.exists)).toBe(true)
})
it('should report all indexes missing when no custom indexes exist', async () => {
const mockDb = {
collection: vi.fn().mockReturnValue({
indexes: vi.fn().mockResolvedValue([{ name: '_id_', key: { _id: 1 } }]),
}),
} as any
const result = await verifyCwmpExchangesIndexes(mockDb)
expect(result.allPresent).toBe(false)
expect(result.indexes.every((idx) => !idx.exists)).toBe(true)
})
})
describe('verifyIndexesAtStartup', () => {
let mockLog: { warn: ReturnType<typeof vi.fn>; info: ReturnType<typeof vi.fn> }
beforeEach(() => {
mockLog = {
warn: vi.fn(),
info: vi.fn(),
}
})
it('should log info when all indexes are present', async () => {
const mockDb = {
collection: vi.fn().mockReturnValue({
indexes: vi.fn().mockResolvedValue([
{ name: '_id_', key: { _id: 1 } },
{ name: 'idx_deviceId_timestamp', key: { deviceId: 1, timestamp: 1 } },
{ name: 'idx_deviceId_sequenceNumber', key: { deviceId: 1, sequenceNumber: 1 } },
{ name: 'idx_createdAt_ttl', key: { createdAt: 1 } },
]),
}),
} as any
await verifyIndexesAtStartup(mockDb, mockLog)
expect(mockLog.info).toHaveBeenCalledWith('[ExchangeIndexCheck] All cwmp_exchanges indexes verified OK')
expect(mockLog.warn).not.toHaveBeenCalled()
})
it('should log warnings for each missing index', async () => {
const mockDb = {
collection: vi.fn().mockReturnValue({
indexes: vi.fn().mockResolvedValue([
{ name: '_id_', key: { _id: 1 } },
{ name: 'idx_deviceId_timestamp', key: { deviceId: 1, timestamp: 1 } },
]),
}),
} as any
await verifyIndexesAtStartup(mockDb, mockLog)
expect(mockLog.info).not.toHaveBeenCalled()
expect(mockLog.warn).toHaveBeenCalledTimes(2)
expect(mockLog.warn).toHaveBeenCalledWith(
'[ExchangeIndexCheck] Missing index on cwmp_exchanges: idx_deviceId_sequenceNumber (Unique index (deviceId, sequenceNumber))'
)
expect(mockLog.warn).toHaveBeenCalledWith(
'[ExchangeIndexCheck] Missing index on cwmp_exchanges: idx_createdAt_ttl (TTL index (createdAt, 86400s))'
)
})
it('should log warnings for all indexes when collection does not exist', async () => {
const mockDb = {
collection: vi.fn().mockReturnValue({
indexes: vi.fn().mockRejectedValue(new Error('ns not found')),
}),
} as any
await verifyIndexesAtStartup(mockDb, mockLog)
expect(mockLog.warn).toHaveBeenCalledTimes(3)
})
})
})