- 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
147 lines
5.9 KiB
TypeScript
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)
|
|
})
|
|
})
|