Files
adam-acs/tests/unit/domain/device.spec.ts
T
Kevin L'Hereec b643077e8d fix: correct orphan references after TR-069 typing refactor
- tests/device.spec.ts: fix 6× organizeParameters() calls passing wrapped
  response object instead of flat ParameterInfoStruct[] (runtime break)
- tests/device.spec.ts: fix setTmpParamValues([{x:1}]) with valid shape
- immediate_command_state_machine.ts: type handleResponse message param as
  CWMPTaskResultData (was any, gap in typing contract)
- configuration_evaluation_state.ts: replace fragile ../../../app/ dynamic
  import paths with proper static imports from ../../protocol/tr069_types.js
- main_state_machine.ts: replace all inline dynamic import() type casts with
  static imports (GetParameterNamesResponse, GetParameterValuesResponse,
  ParameterValueStruct, SetParameterAttribute, DownloadParameter, etc.)
2026-07-20 16:27:38 +02:00

279 lines
10 KiB
TypeScript

import { describe, it, expect } from 'vitest'
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'
function makeDevice() {
return new Device(new DeviceId('Sagemcom', 'F8B7FF', 'Livebox 6', 'SN001'))
}
describe('Device', () => {
it('creates with deviceId', () => {
const d = makeDevice()
expect(d.deviceId.manufacturer).toBe('Sagemcom')
expect(d.gpnStatus).toBe('unknown')
expect(d.dataModel).toBe('unknown')
})
it('setProperty and getProperty', () => {
const d = makeDevice()
d.setProperty(new DeviceProperty('Device.Info.SW', '1.0'))
expect(d.getProperty('Device.Info.SW')).toBeDefined()
expect(d.getProperty('Device.Info.SW')!.value).toBe('1.0')
})
it('setParameter and getParameter', () => {
const d = makeDevice()
d.setParameter(new DeviceParameter('Device.X', '42'))
expect(d.getParameter('Device.X')).toBeDefined()
expect(d.getParameter('Device.X')!.value).toBe('42')
})
it('getProperties returns all properties', () => {
const d = makeDevice()
d.setProperty(new DeviceProperty('A', '1'))
d.setProperty(new DeviceProperty('B', '2'))
expect(d.getProperties()).toHaveLength(2)
})
it('getParameters returns all parameters', () => {
const d = makeDevice()
d.setParameter(new DeviceParameter('X', '1'))
expect(d.getParameters()).toHaveLength(1)
})
it('setGpnStatus updates gpnStatus', () => {
const d = makeDevice()
d.setGpnStatus('supported')
expect(d.gpnStatus).toBe('supported')
})
it('setDataModel updates dataModel', () => {
const d = makeDevice()
d.setDataModel('tr-181')
expect(d.dataModel).toBe('tr-181')
})
it('updateLastSeen does not crash', () => {
const d = makeDevice()
expect(() => d.updateLastSeen()).not.toThrow()
})
it('updateFromValues adds new properties from Inform ParameterValueStruct', () => {
const d = makeDevice()
d.updateFromValues([
{ Name: 'Device.Info.SW', Value: { _: '3.0', $: { Type: 'xsd:string' } } },
{ Name: 'Device.Info.HW', Value: { _: '1.0', $: { Type: 'xsd:string' } } },
])
expect(d.getProperties().length).toBeGreaterThanOrEqual(2)
})
it('toSnapshot returns serializable object', () => {
const d = makeDevice()
d.setProperty(new DeviceProperty('A', '1'))
d.setParameter(new DeviceParameter('X', '2'))
d.setGpnStatus('supported')
d.setDataModel('tr-181')
const snap = d.toSnapshot()
expect(snap.identifier).toBeDefined()
expect(snap.deviceId).toBeDefined()
expect(snap.properties).toHaveLength(1)
expect(snap.parameters).toHaveLength(1)
expect(snap.gpnStatus).toBe('supported')
expect(snap.dataModel).toBe('tr-181')
})
it('organizeParameters classifies properties vs parameters from GPN response', () => {
const d = makeDevice()
// Simulate a GPN response
d.setProperty(new DeviceProperty('Device.ManagementServer.PeriodicInformInterval', '60'))
d.setProperty(new DeviceProperty('Device.DeviceInfo.Manufacturer', 'Sagemcom'))
d.organizeParameters(
[
{ Name: 'Device.ManagementServer.PeriodicInformInterval', Writable: '1' },
{ Name: 'Device.DeviceInfo.Manufacturer', Writable: '0' },
],
new Date()
)
// Writable=1 → parameter, Writable=0 → property
expect(d.getParameter('Device.ManagementServer.PeriodicInformInterval')).toBeDefined()
// Manufacturer stays as property (Writable=0)
expect(d.getProperty('Device.DeviceInfo.Manufacturer')).toBeDefined()
})
it('setTmpParamValues stores values', () => {
const d = makeDevice()
d.setTmpParamValues([{ Name: 'X', Value: { _: 'v' } }])
// No crash — internal state
})
it('identifier is computed from deviceId', () => {
const d = makeDevice()
expect(d.identifier).toBeDefined()
expect(d.identifier!.length).toBe(24)
})
it('updateFromValues handles empty array', () => {
const d = makeDevice()
d.updateFromValues([])
expect(d.getProperties()).toHaveLength(0)
})
it('updateFromValues updates existing property value', () => {
const d = makeDevice()
d.setProperty(new DeviceProperty('Device.Info.SW', 'old'))
d.updateFromValues([{ Name: 'Device.Info.SW', Value: { _: 'new', $: { Type: 'xsd:string' } } }])
const prop = d.getProperty('Device.Info.SW')
expect(prop!.value).toBe('new')
})
it('updateProperty updates value of existing property', () => {
const d = makeDevice()
d.setProperty(new DeviceProperty('Device.Info.SW', 'v1'))
d.updateProperty('Device.Info.SW', 'v2')
expect(d.getProperty('Device.Info.SW')!.value).toBe('v2')
})
it('getConnectionRequestURL returns URL from properties', () => {
const d = makeDevice()
d.setProperty(new DeviceProperty('Device.ManagementServer.ConnectionRequestURL', 'http://example.com:7547/'))
const url = d.getConnectionRequestURL()
expect(url).not.toBeNull()
expect(url!.hostname).toBe('example.com')
})
it('getConnectionRequestURL returns null when not present', () => {
const d = makeDevice()
expect(d.getConnectionRequestURL()).toBeNull()
})
it('clearTmpValues resets temporary state', () => {
const d = makeDevice()
d.setTmpParamValues([{ Name: 'Device.X', Value: { _: '1' } }])
d.tmpPC = 'code'
d.clearTmpValues()
expect(d.getTmpParamValues()).toEqual([])
expect(d.getTmpPC()).toBeNull()
})
it('organizeParameters moves writable property to parameter', () => {
const d = makeDevice()
d.setProperty(new DeviceProperty('Device.X', 'val'))
d.organizeParameters([{ Name: 'Device.X', Writable: '1' }], new Date())
expect(d.getParameter('Device.X')).toBeDefined()
expect(d.getProperty('Device.X')).toBeNull()
})
it('organizeParameters moves non-writable parameter to property', () => {
const d = makeDevice()
d.setParameter(new DeviceParameter('Device.Y', 'val'))
d.organizeParameters([{ Name: 'Device.Y', Writable: 0 }], new Date())
expect(d.getProperty('Device.Y')).toBeDefined()
expect(d.getParameter('Device.Y')).toBeNull()
})
it('organizeParameters adds new writable as parameter', () => {
const d = makeDevice()
d.organizeParameters([{ Name: 'Device.New.Param', Writable: '1' }], new Date())
expect(d.getParameter('Device.New.Param')).toBeDefined()
})
it('organizeParameters adds new non-writable as property', () => {
const d = makeDevice()
d.organizeParameters([{ Name: 'Device.New.Prop', Writable: '0' }], new Date())
expect(d.getProperty('Device.New.Prop')).toBeDefined()
})
it('organizeParameters skips object paths (ending with dot)', () => {
const d = makeDevice()
d.organizeParameters([{ Name: 'Device.Info.', Writable: '1' }], new Date())
expect(d.getParameter('Device.Info.')).toBeNull()
expect(d.getProperty('Device.Info.')).toBeNull()
})
it('updateFromValues maps xsd:int type', () => {
const d = makeDevice()
d.updateFromValues([{ Name: 'Device.Port', Value: { _: '8080', $: { Type: 'xsd:int' } } }])
const prop = d.getProperty('Device.Port')
expect(prop).toBeDefined()
})
it('updateFromValues maps xsd:boolean type', () => {
const d = makeDevice()
d.updateFromValues([{ Name: 'Device.Enabled', Value: { _: 'true', $: { Type: 'xsd:boolean' } } }])
const prop = d.getProperty('Device.Enabled')
expect(prop).toBeDefined()
})
it('updateFromValues updates existing parameter value', () => {
const d = makeDevice()
d.setParameter(new DeviceParameter('Device.X', 'old'))
d.updateFromValues([{ Name: 'Device.X', Value: { _: 'new', $: { Type: 'xsd:string' } } }])
expect(d.getParameter('Device.X')!.value).toBe('new')
})
})
describe('Device.reclassifyInformWritables', () => {
it('promotes ProvisioningCode from property to parameter', () => {
const d = makeDevice()
// Simulate what updateFromValues does on Inform: stores as property (unknown writability)
d.setProperty(new DeviceProperty('InternetGatewayDevice.DeviceInfo.ProvisioningCode', 'abc'))
d.reclassifyInformWritables([
{ Name: 'InternetGatewayDevice.DeviceInfo.ProvisioningCode', Value: { _: 'abc' } },
])
expect(d.getParameter('InternetGatewayDevice.DeviceInfo.ProvisioningCode')).not.toBeNull()
expect(d.getProperty('InternetGatewayDevice.DeviceInfo.ProvisioningCode')).toBeNull()
})
it('promotes ParameterKey from property to parameter', () => {
const d = makeDevice()
d.setProperty(new DeviceProperty('InternetGatewayDevice.ManagementServer.ParameterKey', ''))
d.reclassifyInformWritables([
{ Name: 'InternetGatewayDevice.ManagementServer.ParameterKey', Value: { _: '' } },
])
expect(d.getParameter('InternetGatewayDevice.ManagementServer.ParameterKey')).not.toBeNull()
expect(d.getProperty('InternetGatewayDevice.ManagementServer.ParameterKey')).toBeNull()
})
it('does not touch params that are already parameters', () => {
const d = makeDevice()
d.setParameter(new DeviceParameter('Device.DeviceInfo.ProvisioningCode', 'existing'))
d.reclassifyInformWritables([
{ Name: 'Device.DeviceInfo.ProvisioningCode', Value: { _: 'existing' } },
])
expect(d.getParameter('Device.DeviceInfo.ProvisioningCode')!.value).toBe('existing')
expect(d.getProperty('Device.DeviceInfo.ProvisioningCode')).toBeNull()
})
it('does not reclassify unknown names (e.g. SoftwareVersion stays property)', () => {
const d = makeDevice()
d.setProperty(new DeviceProperty('InternetGatewayDevice.DeviceInfo.SoftwareVersion', 'v1'))
d.reclassifyInformWritables([
{ Name: 'InternetGatewayDevice.DeviceInfo.SoftwareVersion', Value: { _: 'v1' } },
])
// SoftwareVersion is NOT in the writable list — should stay as property
expect(d.getProperty('InternetGatewayDevice.DeviceInfo.SoftwareVersion')).not.toBeNull()
expect(d.getParameter('InternetGatewayDevice.DeviceInfo.SoftwareVersion')).toBeNull()
})
it('handles Name as object node (XMLParser artefact)', () => {
const d = makeDevice()
d.setProperty(new DeviceProperty('Device.DeviceInfo.ProvisioningCode', 'code'))
d.reclassifyInformWritables([
{ Name: { _: 'Device.DeviceInfo.ProvisioningCode' }, Value: { _: 'code' } },
])
expect(d.getParameter('Device.DeviceInfo.ProvisioningCode')).not.toBeNull()
expect(d.getProperty('Device.DeviceInfo.ProvisioningCode')).toBeNull()
})
})