Files
adam-acs/tests/unit/services/reclassify_device.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

147 lines
5.9 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { reclassifyDeviceFromCatalog } from '../../../app/services/parameter_writability.js'
import { Device } from '../../../app/domain/device/device.js'
import { DeviceId } from '../../../app/domain/device/device_id.js'
import { DeviceParameter, DeviceProperty } from '../../../app/domain/device/device_parameter.js'
import { ParameterType } from '../../../app/domain/shared/types.js'
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function makeDevice(gpnStatus: 'supported' | 'unsupported' | 'unknown' = 'unsupported'): Device {
const device = new Device(new DeviceId('Sagemcom', 'F8B7FF', 'Livebox 4', 'LB4-TEST-001'))
device.setGpnStatus(gpnStatus)
device.setDataModel('tr-181')
return device
}
function addProperty(device: Device, name: string, value: any = null): void {
device.setProperty(new DeviceProperty(name, value, ParameterType.STRING))
}
// ---------------------------------------------------------------------------
// reclassifyDeviceFromCatalog
// ---------------------------------------------------------------------------
describe('reclassifyDeviceFromCatalog', () => {
it('moves writable properties to parameters (PeriodicInformInterval)', () => {
const device = makeDevice()
addProperty(device, 'Device.ManagementServer.PeriodicInformInterval', '30')
const count = reclassifyDeviceFromCatalog(device)
expect(count).toBe(1)
expect(device.getParameter('Device.ManagementServer.PeriodicInformInterval')).not.toBeNull()
expect(device.getProperty('Device.ManagementServer.PeriodicInformInterval')).toBeNull()
})
it('moves writable properties to parameters (ProvisioningCode)', () => {
const device = makeDevice()
addProperty(device, 'Device.DeviceInfo.ProvisioningCode', 'test-code')
const count = reclassifyDeviceFromCatalog(device)
expect(count).toBe(1)
expect(device.getParameter('Device.DeviceInfo.ProvisioningCode')).not.toBeNull()
expect(device.getParameter('Device.DeviceInfo.ProvisioningCode')!.value).toBe('test-code')
expect(device.getProperty('Device.DeviceInfo.ProvisioningCode')).toBeNull()
})
it('does NOT move read-only properties (Manufacturer)', () => {
const device = makeDevice()
addProperty(device, 'Device.DeviceInfo.Manufacturer', 'Sagemcom')
const count = reclassifyDeviceFromCatalog(device)
expect(count).toBe(0)
expect(device.getProperty('Device.DeviceInfo.Manufacturer')).not.toBeNull()
expect(device.getParameter('Device.DeviceInfo.Manufacturer')).toBeNull()
})
it('does NOT move properties not in catalog', () => {
const device = makeDevice()
addProperty(device, 'Device.Vendor.CustomParam.Unknown', 'value')
const count = reclassifyDeviceFromCatalog(device)
// Not in catalog → stays as property (default behavior for unknown paths)
expect(count).toBe(0)
expect(device.getProperty('Device.Vendor.CustomParam.Unknown')).not.toBeNull()
})
it('handles instance paths with numeric segments', () => {
const device = makeDevice()
// Device.IP.Interface.{i}.Enable is writable in catalog
addProperty(device, 'Device.IP.Interface.1.Enable', 'true')
const count = reclassifyDeviceFromCatalog(device)
expect(count).toBe(1)
expect(device.getParameter('Device.IP.Interface.1.Enable')).not.toBeNull()
expect(device.getProperty('Device.IP.Interface.1.Enable')).toBeNull()
})
it('reclassifies multiple properties in one call', () => {
const device = makeDevice()
addProperty(device, 'Device.ManagementServer.PeriodicInformInterval', '60')
addProperty(device, 'Device.DeviceInfo.ProvisioningCode', 'code')
addProperty(device, 'Device.DeviceInfo.Manufacturer', 'Sagemcom') // read-only, stays
addProperty(device, 'Device.DeviceInfo.SerialNumber', 'SN123') // read-only, stays
const count = reclassifyDeviceFromCatalog(device)
expect(count).toBe(2)
expect(device.getParameters()).toHaveLength(2)
expect(device.getProperties()).toHaveLength(2)
})
it('preserves value and type when moving property to parameter', () => {
const device = makeDevice()
device.setProperty(
new DeviceProperty('Device.ManagementServer.PeriodicInformInterval', '120', ParameterType.UNSIGNED_INT)
)
reclassifyDeviceFromCatalog(device)
const param = device.getParameter('Device.ManagementServer.PeriodicInformInterval')
expect(param).not.toBeNull()
expect(param!.value).toBe('120')
expect(param!.type).toBe(ParameterType.UNSIGNED_INT)
})
it('returns 0 when device has no properties', () => {
const device = makeDevice()
const count = reclassifyDeviceFromCatalog(device)
expect(count).toBe(0)
})
it('does not touch existing parameters', () => {
const device = makeDevice()
device.setParameter(new DeviceParameter('Device.ManagementServer.URL', 'http://acs', ParameterType.STRING))
addProperty(device, 'Device.DeviceInfo.Manufacturer', 'Sagemcom')
reclassifyDeviceFromCatalog(device)
expect(device.getParameter('Device.ManagementServer.URL')!.value).toBe('http://acs')
expect(device.getParameters()).toHaveLength(1)
})
it('works with InternetGatewayDevice paths (TR-098) — not in TR-181 catalog', () => {
const device = new Device(new DeviceId('Huawei', '202BC1', 'BM632w', '000000'))
device.setGpnStatus('unsupported')
device.setDataModel('tr-098')
addProperty(device, 'InternetGatewayDevice.DeviceInfo.ProvisioningCode', 'code')
addProperty(device, 'InternetGatewayDevice.ManagementServer.PeriodicInformInterval', '60')
const count = reclassifyDeviceFromCatalog(device)
// TR-098 paths are NOT in the TR-181 catalog → nothing reclassified
expect(count).toBe(0)
expect(device.getProperties()).toHaveLength(2)
expect(device.getParameters()).toHaveLength(0)
})
})