- 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
129 lines
4.4 KiB
TypeScript
129 lines
4.4 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest'
|
|
|
|
const mockCollection = {
|
|
find: vi.fn().mockReturnThis(),
|
|
findOne: vi.fn(),
|
|
insertOne: vi.fn(),
|
|
insertMany: vi.fn(),
|
|
updateOne: vi.fn(),
|
|
updateMany: vi.fn(),
|
|
deleteOne: vi.fn(),
|
|
deleteMany: vi.fn(),
|
|
countDocuments: vi.fn(),
|
|
sort: vi.fn().mockReturnThis(),
|
|
skip: vi.fn().mockReturnThis(),
|
|
limit: vi.fn().mockReturnThis(),
|
|
toArray: vi.fn(),
|
|
}
|
|
|
|
const mockDb = { collection: vi.fn().mockReturnValue(mockCollection) }
|
|
|
|
vi.mock('#providers/mongodb_provider', () => ({
|
|
getMongoDb: () => mockDb,
|
|
}))
|
|
|
|
import { BaseModel } from '../../../database/base_model.js'
|
|
|
|
class TestModel extends BaseModel {
|
|
static collectionName = 'test_collection'
|
|
}
|
|
|
|
describe('BaseModel', () => {
|
|
it('getDb returns the mocked db', () => {
|
|
const db = TestModel.getDb()
|
|
expect(db).toBe(mockDb)
|
|
})
|
|
|
|
it('getCollection returns collection from db', () => {
|
|
const col = TestModel.getCollection()
|
|
expect(mockDb.collection).toHaveBeenCalledWith('test_collection')
|
|
expect(col).toBe(mockCollection)
|
|
})
|
|
|
|
it('find calls collection.find', async () => {
|
|
mockCollection.toArray.mockResolvedValue([{ name: 'a' }])
|
|
const results = await TestModel.find({ name: 'a' })
|
|
expect(mockCollection.find).toHaveBeenCalledWith({ name: 'a' })
|
|
expect(results).toEqual([{ name: 'a' }])
|
|
})
|
|
|
|
it('findOne calls collection.findOne', async () => {
|
|
mockCollection.findOne.mockResolvedValue({ x: 1 })
|
|
const result = await TestModel.findOne({ x: 1 })
|
|
expect(result).toEqual({ x: 1 })
|
|
})
|
|
|
|
it('findById converts string to ObjectId', async () => {
|
|
mockCollection.findOne.mockResolvedValue({ _id: 'abc' })
|
|
await TestModel.findById('507f1f77bcf86cd799439011')
|
|
expect(mockCollection.findOne).toHaveBeenCalled()
|
|
})
|
|
|
|
it('create inserts with timestamps', async () => {
|
|
mockCollection.insertOne.mockResolvedValue({ insertedId: 'new-id' })
|
|
const result = await TestModel.create({ name: 'test' })
|
|
expect(result.name).toBe('test')
|
|
expect(result.createdAt).toBeDefined()
|
|
expect(result.updatedAt).toBeDefined()
|
|
expect(result._id).toBe('new-id')
|
|
})
|
|
|
|
it('createMany inserts multiple docs', async () => {
|
|
mockCollection.insertMany.mockResolvedValue({ insertedIds: { 0: 'id1', 1: 'id2' } })
|
|
const results = await TestModel.createMany([{ a: 1 }, { a: 2 }])
|
|
expect(results).toHaveLength(2)
|
|
})
|
|
|
|
it('updateOne with $set passes through', async () => {
|
|
mockCollection.updateOne.mockResolvedValue({ modifiedCount: 1 })
|
|
await TestModel.updateOne({ name: 'x' }, { $set: { val: 2 } })
|
|
expect(mockCollection.updateOne).toHaveBeenCalledWith({ name: 'x' }, { $set: { val: 2 } })
|
|
})
|
|
|
|
it('updateOne without $set wraps in $set', async () => {
|
|
mockCollection.updateOne.mockResolvedValue({ modifiedCount: 1 })
|
|
await TestModel.updateOne({ name: 'x' }, { val: 3 })
|
|
expect(mockCollection.updateOne).toHaveBeenCalledWith(
|
|
{ name: 'x' },
|
|
expect.objectContaining({ $set: expect.objectContaining({ val: 3 }) })
|
|
)
|
|
})
|
|
|
|
it('updateMany works', async () => {
|
|
mockCollection.updateMany.mockResolvedValue({ modifiedCount: 5 })
|
|
await TestModel.updateMany({ type: 'a' }, { $set: { active: false } })
|
|
expect(mockCollection.updateMany).toHaveBeenCalled()
|
|
})
|
|
|
|
it('deleteOne returns boolean', async () => {
|
|
mockCollection.deleteOne.mockResolvedValue({ deletedCount: 1 })
|
|
const result = await TestModel.deleteOne({ name: 'x' })
|
|
expect(result).toBe(true)
|
|
})
|
|
|
|
it('deleteOne returns false when not found', async () => {
|
|
mockCollection.deleteOne.mockResolvedValue({ deletedCount: 0 })
|
|
const result = await TestModel.deleteOne({ name: 'nonexistent' })
|
|
expect(result).toBe(false)
|
|
})
|
|
|
|
it('deleteMany returns count', async () => {
|
|
mockCollection.deleteMany.mockResolvedValue({ deletedCount: 3 })
|
|
const count = await TestModel.deleteMany({ old: true })
|
|
expect(count).toBe(3)
|
|
})
|
|
|
|
it('paginate returns paginated data', async () => {
|
|
mockCollection.countDocuments.mockResolvedValue(50)
|
|
mockCollection.toArray.mockResolvedValue([{ id: 1 }, { id: 2 }])
|
|
const result = await TestModel.paginate(2, 10, {})
|
|
expect(result.total).toBe(50)
|
|
expect(result.page).toBe(2)
|
|
expect(result.perPage).toBe(10)
|
|
expect(result.lastPage).toBe(5)
|
|
expect(result.data).toHaveLength(2)
|
|
expect(mockCollection.skip).toHaveBeenCalledWith(10) // (2-1) * 10
|
|
expect(mockCollection.limit).toHaveBeenCalledWith(10)
|
|
})
|
|
})
|