Files
adam-acs/tests/unit/services/cwmp/device_action_executor_coverage.spec.ts
T
2026-06-08 15:15:24 +02:00

257 lines
9.4 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
// Mock logger
vi.mock('@adonisjs/core/services/logger', () => ({
default: { info: vi.fn(), debug: vi.fn(), warn: vi.fn(), error: vi.fn() },
}))
// Mock DeviceModel
vi.mock('../../../../app/models/device.js', () => ({
default: {
createOrUpdate: vi.fn().mockResolvedValue({}),
findByDeviceId: vi.fn().mockResolvedValue(null),
updateOne: vi.fn().mockResolvedValue({}),
},
}))
import { DeviceActionExecutor } from '../../../../app/services/cwmp/device_action_executor.js'
import { CWMPTaskQueue, ResultStatus } from '../../../../app/services/cwmp/task_queue.js'
import { Device } from '../../../../app/domain/device/device.js'
import { DeviceId } from '../../../../app/domain/device/device_id.js'
import { DeviceProperty, DeviceParameter } from '../../../../app/domain/device/device_parameter.js'
import { ParameterType } from '../../../../app/domain/shared/types.js'
import DeviceModel from '../../../../app/models/device.js'
function makeDevice(dataModel: string = 'tr-181'): Device {
const device = new Device(new DeviceId('Sagemcom', 'F8B7FF', 'Livebox 4', 'LB4-TEST-001'))
device.setDataModel(dataModel as any)
return device
}
describe('DeviceActionExecutor — coverage', () => {
let taskQueue: CWMPTaskQueue
let executor: DeviceActionExecutor
beforeEach(() => {
vi.clearAllMocks()
taskQueue = new CWMPTaskQueue()
executor = new DeviceActionExecutor(taskQueue)
})
// --- getParameterValues ---
it('getParameterValues returns XML containing GetParameterValues', async () => {
const device = makeDevice()
const xml = await executor.getParameterValues(device, ['Device.Info.SW'], async () => '')
expect(xml).toContain('GetParameterValues')
expect(xml).toContain('Device.Info.SW')
})
it('getParameterValues wrappedCallback updates device on success', async () => {
const device = makeDevice()
await executor.getParameterValues(device, ['Device.Info.SW'], async () => '')
// Execute the callback directly via executeTaskCallback
const taskCallbacks = (taskQueue as any).taskCallbacks as Map<string, any>
const taskId = Array.from(taskCallbacks.keys())[0]
expect(taskId).toBeDefined()
await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, {
ParameterList: {
ParameterValueStruct: [
{ Name: 'Device.Info.SW', Value: { _: '5.0.1', $: { Type: 'xsd:string' } } },
],
},
})
// Device should have been updated
const prop = device.getProperty('Device.Info.SW')
expect(prop).not.toBeNull()
expect(prop!.value).toBe('5.0.1')
})
// --- getParameterNames ---
it('getParameterNames returns GPN envelope', async () => {
const device = makeDevice()
const xml = await executor.getParameterNames(device, 'Device.ManagementServer.', async () => '')
expect(xml).toContain('GetParameterNames')
expect(xml).toContain('Device.ManagementServer.')
})
it('GPN success marks device as supported and records success', async () => {
const device = makeDevice()
device.setGpnStatus('unknown')
let capturedCallback: any
vi.spyOn(taskQueue, 'addTask').mockImplementation((deviceId, type, props, cb) => {
capturedCallback = cb
return { xml: '<gpn/>', taskId: 'tid-1' }
})
await executor.getParameterNames(device, 'Device.', async () => '')
await capturedCallback({ status: ResultStatus.Success, data: { ParameterList: { ParameterInfoStruct: [] } } })
expect(device.gpnStatus).toBe('supported')
expect(device.gpnCircuitBreaker.failureCount).toBe(0)
})
it('GPN fault 9001 marks device as unsupported and records failure', async () => {
const device = makeDevice()
device.setGpnStatus('unknown')
let capturedCallback: any
vi.spyOn(taskQueue, 'addTask').mockImplementation((deviceId, type, props, cb) => {
capturedCallback = cb
return { xml: '<gpn/>', taskId: 'tid-2' }
})
await executor.getParameterNames(device, 'Device.', async () => '')
await capturedCallback({ status: ResultStatus.Error, data: { FaultCode: '9001', FaultString: 'Not supported' } })
expect(device.gpnStatus).toBe('unsupported')
expect(device.gpnCircuitBreaker.failureCount).toBe(1)
})
it('GPN fault 9005 marks device as unsupported', async () => {
const device = makeDevice()
device.setGpnStatus('unknown')
let capturedCallback: any
vi.spyOn(taskQueue, 'addTask').mockImplementation((deviceId, type, props, cb) => {
capturedCallback = cb
return { xml: '<gpn/>', taskId: 'tid-3' }
})
await executor.getParameterNames(device, 'Device.', async () => '')
await capturedCallback({ status: ResultStatus.Error, data: { FaultCode: 9005 } })
expect(device.gpnStatus).toBe('unsupported')
expect(device.gpnCircuitBreaker.failureCount).toBe(1)
})
it('GPN fault 9002 is treated as transient (no unsupported mark)', async () => {
const device = makeDevice()
device.setGpnStatus('unknown')
let capturedCallback: any
vi.spyOn(taskQueue, 'addTask').mockImplementation((deviceId, type, props, cb) => {
capturedCallback = cb
return { xml: '<gpn/>', taskId: 'tid-4' }
})
await executor.getParameterNames(device, 'Device.', async () => '')
await capturedCallback({ status: ResultStatus.Error, data: { FaultCode: '9002' } })
// Should NOT mark as unsupported
expect(device.gpnStatus).toBe('unknown')
expect(device.gpnCircuitBreaker.failureCount).toBe(0)
})
// --- applySetParameterValues ---
it('applySetParameterValues returns SPV envelope', async () => {
const device = makeDevice()
const xml = await executor.applySetParameterValues(
device,
[{ parameterName: 'Device.X.Interval', value: '300', type: 'unsignedInt' }],
async () => ''
)
expect(xml).toContain('SetParameterValues')
expect(xml).toContain('Device.X.Interval')
expect(xml).toContain('300')
expect(xml).toContain('xsd:unsignedInt')
})
it('applySetParameterValues infers type from device param when not specified', async () => {
const device = makeDevice()
device.setParameter(new DeviceParameter('Device.X.Flag', 'true', ParameterType.BOOLEAN))
const xml = await executor.applySetParameterValues(
device,
[{ parameterName: 'Device.X.Flag', value: 'false' }],
async () => ''
)
expect(xml).toContain('xsd:boolean')
})
it('applySetParameterValues defaults to xsd:string for unknown type', async () => {
const device = makeDevice()
const xml = await executor.applySetParameterValues(
device,
[{ parameterName: 'Device.X.New', value: 'hello' }],
async () => ''
)
expect(xml).toContain('xsd:string')
})
// --- insertProvisioningCode ---
it('insertProvisioningCode uses tr-181 path for tr-181 device', async () => {
const device = makeDevice('tr-181')
device.tmpPC = 'my-prov-code'
const xml = await executor.insertProvisioningCode(device, async () => '')
expect(xml).toContain('Device.DeviceInfo.ProvisioningCode')
expect(xml).toContain('my-prov-code')
})
it('insertProvisioningCode uses tr-098 path for tr-098 device', async () => {
const device = makeDevice('tr-098')
device.tmpPC = 'code-098'
const xml = await executor.insertProvisioningCode(device, async () => '')
expect(xml).toContain('InternetGatewayDevice.DeviceInfo.ProvisioningCode')
})
it('insertProvisioningCode uses known param path when available', async () => {
const device = makeDevice('tr-181')
device.setProperty(new DeviceProperty('Device.DeviceInfo.ProvisioningCode', 'old-code', ParameterType.STRING))
device.tmpPC = 'new-code'
const xml = await executor.insertProvisioningCode(device, async () => '')
expect(xml).toContain('Device.DeviceInfo.ProvisioningCode')
expect(xml).toContain('new-code')
})
// --- persistDevice ---
it('persistDevice calls DeviceModel.createOrUpdate', async () => {
const device = makeDevice()
let capturedCallback: any
vi.spyOn(taskQueue, 'addTask').mockImplementation((deviceId, type, props, cb) => {
capturedCallback = cb
return { xml: '<xml/>', taskId: 'tid-p' }
})
await executor.getParameterValues(device, ['Device.X'], async () => '')
await capturedCallback({
status: ResultStatus.Success,
data: { ParameterList: { ParameterValueStruct: [{ Name: 'Device.X', Value: { _: 'v' } }] } },
})
expect(vi.mocked(DeviceModel.createOrUpdate)).toHaveBeenCalledWith(
expect.objectContaining({ identifier: expect.any(String) })
)
})
// --- Download/Reboot ---
it('applyDownload returns Download envelope', async () => {
const device = makeDevice()
const xml = await executor.applyDownload(device, {
commandKey: 'fw-1',
fileType: '1 Firmware Upgrade Image',
url: 'http://example.com/fw.bin',
}, async () => '')
expect(xml).toContain('Download')
expect(xml).toContain('http://example.com/fw.bin')
})
it('applyReboot returns Reboot envelope', async () => {
const device = makeDevice()
const xml = await executor.applyReboot(device, { commandKey: 'reboot-1' }, async () => '')
expect(xml).toContain('Reboot')
expect(xml).toContain('reboot-1')
})
it('applyFactoryReset returns FactoryReset envelope', async () => {
const device = makeDevice()
const xml = await executor.applyFactoryReset(device, {}, async () => '')
expect(xml).toContain('FactoryReset')
})
})