293 lines
11 KiB
TypeScript
293 lines
11 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(
|
|
{
|
|
ParameterList: {
|
|
ParameterInfoStruct: [
|
|
{ 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([{ x: 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({
|
|
ParameterList: { ParameterInfoStruct: [{ 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({
|
|
ParameterList: { ParameterInfoStruct: [{ 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({
|
|
ParameterList: { ParameterInfoStruct: [{ 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({
|
|
ParameterList: { ParameterInfoStruct: [{ 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({
|
|
ParameterList: { ParameterInfoStruct: [{ 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()
|
|
})
|
|
})
|