- Device domain: updateFromValues, organizeParameters, identifier - BaseModel: full CRUD mock tests - DeviceModel: all methods mocked - Other models: collectionName verification - MainStateMachine: start, handleResponse, state transitions - ConfigurationEvaluationState: init, GPN flow, GPN-unsupported skip - MongodbProvider: getMongoDb guard - device_utils: hash determinism - vitest.config.ts: coverage scope (excludes HTTP layer) - 492 tests passing, 78% line coverage
142 lines
5.2 KiB
TypeScript
142 lines
5.2 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
|
|
// Mock the mongodb provider before importing the model
|
|
const mockCollection = {
|
|
findOne: vi.fn(),
|
|
find: vi.fn().mockReturnThis(),
|
|
countDocuments: vi.fn(),
|
|
findOneAndUpdate: vi.fn(),
|
|
updateOne: vi.fn(),
|
|
updateMany: vi.fn(),
|
|
distinct: vi.fn(),
|
|
aggregate: vi.fn().mockReturnThis(),
|
|
skip: vi.fn().mockReturnThis(),
|
|
limit: vi.fn().mockReturnThis(),
|
|
sort: vi.fn().mockReturnThis(),
|
|
toArray: vi.fn(),
|
|
}
|
|
|
|
const mockDb = {
|
|
collection: vi.fn().mockReturnValue(mockCollection),
|
|
}
|
|
|
|
vi.mock('#providers/mongodb_provider', () => ({
|
|
getMongoDb: () => mockDb,
|
|
}))
|
|
|
|
import DeviceModel from '../../../app/models/device.js'
|
|
|
|
describe('DeviceModel', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
mockCollection.toArray.mockResolvedValue([])
|
|
})
|
|
|
|
it('findByIdentifier calls findOne with identifier', async () => {
|
|
mockCollection.findOne.mockResolvedValue({ identifier: 'abc', deviceId: {} })
|
|
const result = await DeviceModel.findByIdentifier('abc')
|
|
expect(mockCollection.findOne).toHaveBeenCalledWith({ identifier: 'abc' })
|
|
expect(result).toEqual({ identifier: 'abc', deviceId: {} })
|
|
})
|
|
|
|
it('findByIdentifier returns null when not found', async () => {
|
|
mockCollection.findOne.mockResolvedValue(null)
|
|
const result = await DeviceModel.findByIdentifier('xyz')
|
|
expect(result).toBeNull()
|
|
})
|
|
|
|
it('findByDeviceId queries by all 4 fields', async () => {
|
|
const { DeviceId } = await import('../../../app/domain/device/device_id.js')
|
|
const deviceId = new DeviceId('Mfr', 'OUI', 'PC', 'SN')
|
|
mockCollection.findOne.mockResolvedValue(null)
|
|
await DeviceModel.findByDeviceId(deviceId)
|
|
expect(mockCollection.findOne).toHaveBeenCalledWith({
|
|
'deviceId.manufacturer': 'Mfr',
|
|
'deviceId.oui': 'OUI',
|
|
'deviceId.productClass': 'PC',
|
|
'deviceId.serialNumber': 'SN',
|
|
})
|
|
})
|
|
|
|
it('paginateDevices returns data and total', async () => {
|
|
mockCollection.countDocuments.mockResolvedValue(25)
|
|
mockCollection.toArray.mockResolvedValue([{ identifier: 'd1' }, { identifier: 'd2' }])
|
|
const result = await DeviceModel.paginateDevices(0, 10)
|
|
expect(result.total).toBe(25)
|
|
expect(result.data).toHaveLength(2)
|
|
expect(mockCollection.skip).toHaveBeenCalledWith(0)
|
|
expect(mockCollection.limit).toHaveBeenCalledWith(10)
|
|
})
|
|
|
|
it('createOrUpdate upserts by deviceId', async () => {
|
|
const device = { deviceId: { manufacturer: 'M', oui: 'O', productClass: 'P', serialNumber: 'S' }, properties: [] }
|
|
mockCollection.findOneAndUpdate.mockResolvedValue(device)
|
|
const result = await DeviceModel.createOrUpdate(device as any)
|
|
expect(mockCollection.findOneAndUpdate).toHaveBeenCalled()
|
|
expect(result).toEqual(device)
|
|
})
|
|
|
|
it('createOrUpdate upserts by identifier', async () => {
|
|
const device = { identifier: 'hash123', properties: [] }
|
|
mockCollection.findOneAndUpdate.mockResolvedValue(device)
|
|
await DeviceModel.createOrUpdate(device as any)
|
|
expect(mockCollection.findOneAndUpdate).toHaveBeenCalledWith(
|
|
{ identifier: 'hash123' },
|
|
expect.any(Object),
|
|
expect.any(Object)
|
|
)
|
|
})
|
|
|
|
it('createOrUpdate throws if no identifier or deviceId', async () => {
|
|
await expect(DeviceModel.createOrUpdate({} as any)).rejects.toThrow('Either identifier or deviceId must be provided')
|
|
})
|
|
|
|
it('updateCompliance calls updateOne', async () => {
|
|
mockCollection.updateOne.mockResolvedValue({ modifiedCount: 1 })
|
|
await DeviceModel.updateCompliance('id1', { status: 'compliant' } as any)
|
|
expect(mockCollection.updateOne).toHaveBeenCalled()
|
|
})
|
|
|
|
it('updateProvisioningCode calls updateOne', async () => {
|
|
mockCollection.updateOne.mockResolvedValue({ modifiedCount: 1 })
|
|
await DeviceModel.updateProvisioningCode('id1', 'code-123')
|
|
expect(mockCollection.updateOne).toHaveBeenCalled()
|
|
})
|
|
|
|
it('findByComplianceStatus queries by status', async () => {
|
|
mockCollection.toArray.mockResolvedValue([{ identifier: 'x' }])
|
|
const result = await DeviceModel.findByComplianceStatus('compliant')
|
|
expect(result).toHaveLength(1)
|
|
})
|
|
|
|
it('findByConfigurationIdentifier queries by configId', async () => {
|
|
mockCollection.toArray.mockResolvedValue([])
|
|
const result = await DeviceModel.findByConfigurationIdentifier('cfg-1')
|
|
expect(result).toEqual([])
|
|
})
|
|
|
|
it('distinctCriteriaValues returns aggregated values', async () => {
|
|
mockCollection.distinct.mockResolvedValue(['Sagemcom'])
|
|
mockCollection.toArray.mockResolvedValue([{ _id: 'v1' }])
|
|
const result = await DeviceModel.distinctCriteriaValues()
|
|
expect(result.manufacturer).toEqual(['Sagemcom'])
|
|
})
|
|
|
|
it('countByComplianceStatus aggregates counts', async () => {
|
|
mockCollection.toArray.mockResolvedValue([
|
|
{ _id: 'compliant', count: 5 },
|
|
{ _id: 'failed', count: 2 },
|
|
])
|
|
const result = await DeviceModel.countByComplianceStatus('cfg-1')
|
|
expect(result.compliant).toBe(5)
|
|
expect(result.failed).toBe(2)
|
|
expect(result.in_progress).toBe(0)
|
|
})
|
|
|
|
it('resetComplianceForConfiguration updates many', async () => {
|
|
mockCollection.updateMany.mockResolvedValue({ modifiedCount: 3 })
|
|
await DeviceModel.resetComplianceForConfiguration('cfg-1')
|
|
expect(mockCollection.updateMany).toHaveBeenCalled()
|
|
})
|
|
})
|