import { describe, it, expect, beforeEach, vi } from 'vitest' 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' // 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') }) it('stores ProvisioningCode as a writable parameter (not property) after SPV success', async () => { const device = makeDevice() device.setDataModel('tr-181') device.tmpPC = 'code-xyz' // Device has no prior ProvisioningCode entry await executor.insertProvisioningCode(device, async () => '') const task = queue.getTaskForDevice(device.deviceId) await queue.executeTaskCallback(task!.id, ResultStatus.Success, {}) // Must be in parameters (writable), not properties (read-only) const asParam = device.getParameter('Device.DeviceInfo.ProvisioningCode') const asProp = device.getProperty('Device.DeviceInfo.ProvisioningCode') expect(asParam).not.toBeNull() expect(asProp).toBeNull() expect(asParam!.value).toBe('code-xyz') }) it('keeps ProvisioningCode as parameter when it already exists as property (reclassifies)', async () => { const device = makeDevice() device.setDataModel('tr-181') device.tmpPC = 'new-code' // Simulate device where ProvisioningCode was incorrectly stored as property device.setProperty(new DeviceProperty('Device.DeviceInfo.ProvisioningCode', 'old-code', ParameterType.STRING)) await executor.insertProvisioningCode(device, async () => '') const task = queue.getTaskForDevice(device.deviceId) await queue.executeTaskCallback(task!.id, ResultStatus.Success, {}) // After SPV success, must be a parameter, not a property const asParam = device.getParameter('Device.DeviceInfo.ProvisioningCode') const asProp = device.getProperty('Device.DeviceInfo.ProvisioningCode') expect(asParam).not.toBeNull() expect(asProp).toBeNull() expect(asParam!.value).toBe('new-code') }) }) 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') }) }) }) 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 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: '', 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: '', 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: '', 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: '', 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: '', 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') }) }) describe('DeviceActionExecutor — callback execution for remaining uncovered lines', () => { let taskQueue: CWMPTaskQueue let executor: DeviceActionExecutor beforeEach(() => { vi.clearAllMocks() taskQueue = new CWMPTaskQueue() executor = new DeviceActionExecutor(taskQueue) }) // --- getParameterAttributes callback (lines 118-145) --- it('getParameterAttributes wrappedCallback processes attributes on success', async () => { const device = makeDevice() device.setParameter(new DeviceParameter('Device.X.Param', 'val', ParameterType.STRING)) await executor.getParameterAttributes(device, ['Device.X.Param'], async () => '') const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, { ParameterList: { ParameterAttributeStruct: [ { Name: 'Device.X.Param', Notification: 2, AccessList: { string: ['Subscriber'] } }, ], }, }) // Should have updated the parameter's attributes const param = device.getParameter('Device.X.Param') expect(param).not.toBeNull() }) it('getParameterAttributes wrappedCallback handles property attributes', async () => { const device = makeDevice() device.setProperty(new DeviceProperty('Device.Y.Prop', 'pval', ParameterType.STRING)) await executor.getParameterAttributes(device, ['Device.Y.Prop'], async () => '') const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, { ParameterList: { ParameterAttributeStruct: [ { Name: 'Device.Y.Prop', Notification: 1 }, ], }, }) const prop = device.getProperty('Device.Y.Prop') expect(prop).not.toBeNull() }) it('getParameterAttributes callback skips entries without Name', async () => { const device = makeDevice() await executor.getParameterAttributes(device, ['Device.X'], async () => '') const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] // Entry without Name should be skipped without error await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, { ParameterList: { ParameterAttributeStruct: [{ Notification: 2 }] }, }) }) // --- persistDevice error path (line 81) --- it('persistDevice logs error when createOrUpdate fails', async () => { vi.mocked(DeviceModel.createOrUpdate).mockRejectedValueOnce(new Error('DB down')) const device = makeDevice() await executor.getParameterValues(device, ['Device.X'], async () => '') const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, { ParameterList: { ParameterValueStruct: [{ Name: 'Device.X', Value: { _: 'v' } }] }, }) expect(vi.mocked(logger.error)).toHaveBeenCalled() }) // --- getParameterValues wrappedCallback error path (line 103) --- it('getParameterValues wrappedCallback catches errors in updateFromValues', async () => { const device = makeDevice() // Force updateFromValues to throw vi.spyOn(device, 'updateFromValues').mockImplementationOnce(() => { throw new Error('parse error') }) await executor.getParameterValues(device, ['Device.X'], async () => '') const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, { ParameterList: { ParameterValueStruct: [{ Name: 'Device.X', Value: { _: 'v' } }] }, }) expect(vi.mocked(logger.error)).toHaveBeenCalled() }) // --- applySetParameterValues callback (line 247) --- it('applySetParameterValues callback updates device on success', async () => { const device = makeDevice() const callbackResult = vi.fn().mockResolvedValue('') await executor.applySetParameterValues(device, [{ parameterName: 'Device.X', value: '42', type: 'int' }], callbackResult) const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, {}) expect(callbackResult).toHaveBeenCalledWith(true) }) it('applySetParameterValues callback returns false on failure', async () => { const device = makeDevice() const callbackResult = vi.fn().mockResolvedValue('') await executor.applySetParameterValues(device, [{ parameterName: 'Device.X', value: '42' }], callbackResult) const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Error, { FaultCode: 9001 }) expect(callbackResult).toHaveBeenCalledWith(false) }) // --- applySetParameterAttributes callback (lines 270-273) --- it('applySetParameterAttributes callback invoked on success', async () => { const device = makeDevice() const cb = vi.fn().mockResolvedValue('') await executor.applySetParameterAttributes(device, [ { name: 'Device.X', notificationChange: true, notification: 2, accessListChange: false, accessList: [] }, ], cb) const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, {}) expect(cb).toHaveBeenCalledWith(true) }) // --- applyScheduleInform callback (line 289) --- it('applyScheduleInform callback invoked', async () => { const device = makeDevice() const cb = vi.fn().mockResolvedValue('') await executor.applyScheduleInform(device, { delayInSeconds: 600 }, cb) const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, {}) expect(cb).toHaveBeenCalledWith(true) }) // --- applyReboot callback (line 319) --- it('applyReboot callback invoked on success', async () => { const device = makeDevice() const cb = vi.fn().mockResolvedValue('') await executor.applyReboot(device, { commandKey: 'r1' }, cb) const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, {}) expect(cb).toHaveBeenCalledWith(true) }) // --- applyUpload callback (line 367) --- it('applyUpload returns Upload envelope and callback invoked', async () => { const device = makeDevice() const cb = vi.fn().mockResolvedValue('') const xml = await executor.applyUpload(device, { commandKey: 'upload-1', fileType: '2 Web Content', url: 'http://upload.example.com/cfg', }, cb) expect(xml).toContain('Upload') expect(xml).toContain('http://upload.example.com/cfg') const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, {}) expect(cb).toHaveBeenCalledWith(true) }) // --- insertProvisioningCode success callback (line 409) --- it('insertProvisioningCode callback clears tmpPC on success', async () => { const device = makeDevice() device.tmpPC = 'my-prov-code' const cb = vi.fn().mockResolvedValue('') await executor.insertProvisioningCode(device, cb) const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Success, {}) expect(cb).toHaveBeenCalledWith(true) expect(device.tmpPC).toBe('') }) it('insertProvisioningCode callback returns false on failure', async () => { const device = makeDevice() device.tmpPC = 'prov-code' const cb = vi.fn().mockResolvedValue('') await executor.insertProvisioningCode(device, cb) const taskCallbacks = (taskQueue as any).taskCallbacks as Map const taskId = Array.from(taskCallbacks.keys())[0] await taskQueue.executeTaskCallback(taskId!, ResultStatus.Error, {}) expect(cb).toHaveBeenCalledWith(false) // tmpPC should NOT be cleared on failure expect(device.tmpPC).toBe('prov-code') }) // --- GPN with nested fault code path (lines 204-205) --- it('GPN fault with nested Detail.Fault.FaultCode marks 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: '', taskId: 'tid-nested' } }) await executor.getParameterNames(device, 'Device.', async () => '') await capturedCallback({ status: ResultStatus.Error, data: { Detail: { Fault: { FaultCode: '9003' } } }, }) expect(device.gpnStatus).toBe('unsupported') }) // --- insertProvisioningCode legacy fallback path --- it('insertProvisioningCode uses legacy fallback when dataModel is unknown', async () => { const device = makeDevice('unknown') // No Device. prefixed params, so should use InternetGatewayDevice path device.tmpPC = 'test-pc' const xml = await executor.insertProvisioningCode(device, async () => '') expect(xml).toContain('InternetGatewayDevice.DeviceInfo.ProvisioningCode') }) it('insertProvisioningCode uses Device path when device has Device. params', async () => { const device = makeDevice('unknown') device.setProperty(new DeviceProperty('Device.DeviceInfo.X', 'val', ParameterType.STRING)) device.tmpPC = 'test-pc' const xml = await executor.insertProvisioningCode(device, async () => '') expect(xml).toContain('Device.DeviceInfo.ProvisioningCode') }) }) import logger from '@adonisjs/core/services/logger' import DeviceModel from '../../../../app/models/device.js'