import { describe, it, expect, beforeEach, vi } from 'vitest' import { DeviceActionExecutor } from '../../../../app/services/cwmp/device_action_executor.js' import { CWMPTaskQueue, ResultStatus, TaskResult } from '../../../../app/services/cwmp/task_queue.js' import { Device } from '../../../../app/domain/device/device.js' import { DeviceId } from '../../../../app/domain/device/device_id.js' // Mock AdonisJS logger vi.mock('@adonisjs/core/services/logger', () => ({ default: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }, })) // Mock DeviceModel to avoid MongoDB dependency vi.mock('../../../../app/models/device.js', () => ({ default: { createOrUpdate: vi.fn().mockResolvedValue(undefined) }, })) function makeDevice(): Device { return new Device(new DeviceId('TestMfr', 'AABBCC', 'TestBox', 'SN001')) } describe('DeviceActionExecutor', () => { let queue: CWMPTaskQueue let executor: DeviceActionExecutor beforeEach(() => { queue = new CWMPTaskQueue() executor = new DeviceActionExecutor(queue) }) describe('getParameterValues', () => { it('returns SOAP XML for GPV', async () => { const device = makeDevice() const xml = await executor.getParameterValues(device, ['Device.Info.SW'], async () => '') expect(xml).toContain('GetParameterValues') expect(xml).toContain('Device.Info.SW') }) }) describe('getParameterNames', () => { it('returns SOAP XML for GPN', async () => { const device = makeDevice() const xml = await executor.getParameterNames(device, 'Device.ManagementServer.', async () => '') expect(xml).toContain('GetParameterNames') expect(xml).toContain('Device.ManagementServer.') }) }) describe('getParameterAttributes', () => { it('returns SOAP XML for GPA', async () => { const device = makeDevice() const xml = await executor.getParameterAttributes(device, ['Device.Info.'], async () => '') expect(xml).toContain('GetParameterAttributes') }) }) describe('applySetParameterValues', () => { it('returns SOAP XML for SPV', async () => { const device = makeDevice() const xml = await executor.applySetParameterValues( device, [{ parameterName: 'Device.X', value: '42', type: 'unsignedInt' }], async () => '' ) expect(xml).toContain('SetParameterValues') expect(xml).toContain('Device.X') }) }) describe('applySetParameterAttributes', () => { it('returns SOAP XML for SPA', async () => { const device = makeDevice() const xml = await executor.applySetParameterAttributes( device, [{ name: 'Device.X', notificationChange: true, notification: 2, accessListChange: false, accessList: [] }], async () => '' ) expect(xml).toContain('SetParameterAttributes') }) }) describe('applyScheduleInform', () => { it('returns SOAP XML for ScheduleInform', async () => { const device = makeDevice() const xml = await executor.applyScheduleInform(device, { delayInSeconds: 300 }, async () => '') expect(xml).toContain('ScheduleInform') expect(xml).toContain('300') }) }) describe('applyReboot', () => { it('returns SOAP XML for Reboot', async () => { const device = makeDevice() const xml = await executor.applyReboot(device, { commandKey: 'reboot-1' }, async () => '') expect(xml).toContain('Reboot') expect(xml).toContain('reboot-1') }) }) describe('applyFactoryReset', () => { it('returns SOAP XML for FactoryReset', async () => { const device = makeDevice() const xml = await executor.applyFactoryReset(device, {}, async () => '') expect(xml).toContain('FactoryReset') }) }) describe('applyDownload', () => { it('returns SOAP XML for Download', async () => { const device = makeDevice() const xml = await executor.applyDownload( device, { commandKey: 'fw', fileType: '1 Firmware Upgrade Image', url: 'http://fw.bin' }, async () => '' ) expect(xml).toContain('Download') expect(xml).toContain('http://fw.bin') }) }) describe('applyUpload', () => { it('returns SOAP XML for Upload', async () => { const device = makeDevice() const xml = await executor.applyUpload( device, { commandKey: 'cfg', fileType: '3 Vendor Configuration File', url: 'http://up.bin' }, async () => '' ) expect(xml).toContain('Upload') expect(xml).toContain('http://up.bin') }) }) describe('insertProvisioningCode', () => { it('returns SOAP XML for SPV with provisioning code', async () => { const device = makeDevice() device.setDataModel('tr-181') device.tmpPC = 'my-prov-code' const xml = await executor.insertProvisioningCode(device, async () => '') expect(xml).toContain('SetParameterValues') expect(xml).toContain('ProvisioningCode') }) }) describe('callbacks', () => { it('GPV callback updates device on success', async () => { const device = makeDevice() let callbackCalled = false executor.getParameterValues(device, ['Device.Info.SW'], async () => { callbackCalled = true; return '' }) // Simulate task completion const task = queue.getTaskForDevice(device.deviceId) expect(task).toBeDefined() await queue.executeTaskCallback(task!.id, ResultStatus.Success, { ParameterList: { ParameterValueStruct: [{ Name: 'Device.Info.SW', Value: { _: '2.0', $: { Type: 'xsd:string' } } }] }, }) expect(callbackCalled).toBe(true) }) it('GPN callback sets gpnStatus to supported on success', async () => { const device = makeDevice() executor.getParameterNames(device, 'Device.', async () => '') const task = queue.getTaskForDevice(device.deviceId) await queue.executeTaskCallback(task!.id, ResultStatus.Success, { ParameterList: { ParameterInfoStruct: [{ Name: 'Device.Info.SW', Writable: '1' }] }, }) expect(device.gpnStatus).toBe('supported') }) it('GPN callback sets gpnStatus to unsupported on fault 9001', async () => { const device = makeDevice() executor.getParameterNames(device, 'Device.', async () => '') const task = queue.getTaskForDevice(device.deviceId) await queue.executeTaskCallback(task!.id, ResultStatus.Error, { FaultCode: 9001 }) expect(device.gpnStatus).toBe('unsupported') }) }) })