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() }) })