Files
adam-acs/tests/unit/services/tr069_trace_service.spec.ts
T
Kevin L'Hereec bc9ac4f125 fix: state machine timing + reclassification tests
- Fix state machine start timing: don't call start() in startStateMachine,
  let the CPE's first empty body trigger the state machine via continue().
  This ensures GPN is sent at the correct point in the TR-069 session.
- Add unit tests for reclassifyDeviceFromCatalog (10 tests)
- Add unit tests for MainStateMachine transitions and GPN callbacks (11 tests)
- Test both scenarios: Huawei (GPN supported) and Livebox (GPN unsupported + fallback)
- Fix tr069_trace_service test path assertion (config/ → data/)
- Update steering doc with Livebox bootstrap command
2026-06-01 17:31:33 +02:00

149 lines
4.4 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
const fsMocks = vi.hoisted(() => ({
appendFileSync: vi.fn(),
mkdirSync: vi.fn(),
readFileSync: vi.fn(),
renameSync: vi.fn(),
statSync: vi.fn(),
}))
const fsPromisesMocks = vi.hoisted(() => ({
writeFile: vi.fn(),
}))
vi.mock('node:fs', () => ({
appendFileSync: fsMocks.appendFileSync,
mkdirSync: fsMocks.mkdirSync,
readFileSync: fsMocks.readFileSync,
renameSync: fsMocks.renameSync,
statSync: fsMocks.statSync,
}))
vi.mock('node:fs/promises', () => ({
writeFile: fsPromisesMocks.writeFile,
}))
import { Tr069TraceService } from '../../../app/services/tr069_trace_service.js'
describe('Tr069TraceService', () => {
const baseEntry = {
deviceId: 'device-123',
manufacturer: 'Orange',
oui: 'A1B2C3',
productClass: 'Livebox',
serialNumber: 'SN123456',
}
beforeEach(() => {
vi.clearAllMocks()
fsMocks.readFileSync.mockImplementation(() => {
throw new Error('missing config')
})
fsMocks.statSync.mockImplementation(() => {
throw new Error('missing log')
})
;(Tr069TraceService as any).instance = undefined
})
it('returns false for untracked devices', () => {
const service = Tr069TraceService.getInstance()
expect(service.isTraced('unknown-device')).toBe(false)
})
it('returns true after adding a device', async () => {
const service = Tr069TraceService.getInstance()
await service.addDevice(baseEntry)
expect(service.isTraced(baseEntry.deviceId)).toBe(true)
})
it('lists all traced devices', async () => {
const service = Tr069TraceService.getInstance()
await service.addDevice(baseEntry)
await service.addDevice({
...baseEntry,
deviceId: 'device-456',
serialNumber: 'SN654321',
})
expect(service.list()).toHaveLength(2)
expect(service.list().map((entry) => entry.deviceId)).toEqual(['device-123', 'device-456'])
})
it('adds a device with an auto-generated startedAt value', async () => {
const service = Tr069TraceService.getInstance()
await service.addDevice(baseEntry)
const [entry] = service.list()
expect(entry.startedAt).toBeTypeOf('string')
expect(Number.isNaN(Date.parse(entry.startedAt))).toBe(false)
})
it('removes a traced device and returns true', async () => {
const service = Tr069TraceService.getInstance()
await service.addDevice(baseEntry)
await expect(service.removeDevice(baseEntry.deviceId)).resolves.toBe(true)
expect(service.isTraced(baseEntry.deviceId)).toBe(false)
})
it('returns false when removing an unknown device', async () => {
const service = Tr069TraceService.getInstance()
await expect(service.removeDevice('unknown-device')).resolves.toBe(false)
})
it('persists config after add and remove operations', async () => {
const service = Tr069TraceService.getInstance()
await service.addDevice(baseEntry)
await service.removeDevice(baseEntry.deviceId)
expect(fsPromisesMocks.writeFile).toHaveBeenCalledTimes(2)
expect(fsPromisesMocks.writeFile).toHaveBeenNthCalledWith(
1,
expect.stringContaining('tr069-trace.json.tmp'),
expect.stringContaining(baseEntry.deviceId),
'utf-8'
)
expect(fsPromisesMocks.writeFile).toHaveBeenNthCalledWith(
2,
expect.stringContaining('tr069-trace.json.tmp'),
JSON.stringify({ entries: [] }, null, 2),
'utf-8'
)
expect(fsMocks.renameSync).toHaveBeenCalledWith(
expect.stringContaining('tr069-trace.json.tmp'),
expect.stringContaining('tr069-trace.json')
)
})
it('rotates log files when size exceeds the limit', async () => {
const service = Tr069TraceService.getInstance()
fsMocks.statSync.mockReturnValue({ size: 10 * 1024 * 1024 })
await (service as any).doWrite(baseEntry.deviceId, 'CPE→ACS', '<Inform />')
expect(fsMocks.renameSync).toHaveBeenNthCalledWith(
1,
expect.stringContaining(`${baseEntry.deviceId}.log.4`),
expect.stringContaining(`${baseEntry.deviceId}.log.5`)
)
expect(fsMocks.renameSync).toHaveBeenLastCalledWith(
expect.stringContaining(`${baseEntry.deviceId}.log`),
expect.stringContaining(`${baseEntry.deviceId}.log.1`)
)
expect(fsMocks.appendFileSync).toHaveBeenCalledWith(
expect.stringContaining(`${baseEntry.deviceId}.log`),
expect.stringContaining('CPE→ACS'),
'utf-8'
)
})
})