import { describe, it, expect, vi, beforeEach } from 'vitest'; const { mockFind, mockFindOne, mockFindOneAndUpdate, mockSave } = vi.hoisted(() => ({ mockFind: vi.fn(), mockFindOne: vi.fn(), mockFindOneAndUpdate: vi.fn(), mockSave: vi.fn(), })); vi.mock('../../schemas/regimen.schema.js', () => { const chain = () => ({ sort: vi.fn().mockReturnThis(), limit: vi.fn().mockReturnThis(), lean: vi.fn().mockReturnThis(), exec: mockFind, }); const findOneChain = () => ({ lean: vi.fn().mockReturnThis(), exec: mockFindOne, }); const updateChain = () => ({ exec: mockFindOneAndUpdate, }); class FakeModel { data: unknown; constructor(data: unknown) { this.data = data; } save = mockSave; toObject() { return this.data; } static find = vi.fn(() => chain()); static findOne = vi.fn(() => findOneChain()); static findOneAndUpdate = vi.fn(() => updateChain()); } return { RegimenModel: FakeModel }; }); import { RegimensRepository } from './regimens.repository.js'; describe(RegimensRepository.name, () => { let repo: RegimensRepository; beforeEach(() => { vi.clearAllMocks(); repo = new RegimensRepository(); }); describe('findByHousehold', () => { it('returns paginated items', async () => { const items = [{ _id: 'reg-1', name: 'Morning' }]; mockFind.mockResolvedValue(items); const result = await repo.findByHousehold('hh1', 'user-1', { limit: 20 }); expect(result.data).toEqual(items); expect(result.pagination.hasMore).toBe(false); }); it('handles cursor-based pagination', async () => { const items = [{ _id: 'reg-2', name: 'Evening' }]; mockFind.mockResolvedValue(items); const cursor = Buffer.from('reg-1').toString('base64'); const result = await repo.findByHousehold('hh1', 'user-1', { cursor, limit: 20 }); expect(result.data).toEqual(items); expect(result.pagination.hasMore).toBe(false); }); it('sets hasMore when more items exist', async () => { const items = Array.from({ length: 3 }, (_, i) => ({ _id: `reg-${i}`, name: `Reg ${i}` })); mockFind.mockResolvedValue(items); const result = await repo.findByHousehold('hh1', 'user-1', { limit: 2 }); expect(result.data).toHaveLength(2); expect(result.pagination.hasMore).toBe(true); expect(result.pagination.cursor).toBeTruthy(); }); it('returns null cursor when no data', async () => { mockFind.mockResolvedValue([]); const result = await repo.findByHousehold('hh1', 'user-1', { limit: 20 }); expect(result.pagination.cursor).toBeNull(); expect(result.pagination.hasMore).toBe(false); }); it('filters by isActive', async () => { mockFind.mockResolvedValue([]); await repo.findByHousehold('hh1', 'user-1', { isActive: true, limit: 20 }); expect(mockFind).toHaveBeenCalled(); }); it('does not add isActive filter when undefined', async () => { mockFind.mockResolvedValue([]); await repo.findByHousehold('hh1', 'user-1', { limit: 20 }); expect(mockFind).toHaveBeenCalled(); }); it('returns cursor only when hasMore is true', async () => { const items = [{ _id: 'reg-1', name: 'Morning' }]; mockFind.mockResolvedValue(items); const result = await repo.findByHousehold('hh1', 'user-1', { limit: 20 }); expect(result.pagination.cursor).toBeNull(); expect(result.pagination.hasMore).toBe(false); }); }); describe('findById', () => { it('returns regimen by id, householdId, and userId', async () => { const regimen = { _id: 'reg-1', householdId: 'hh1', userId: 'user-1', name: 'Morning' }; mockFindOne.mockResolvedValue(regimen); const result = await repo.findById('reg-1', 'hh1', 'user-1'); expect(result).toEqual(regimen); }); it('returns null when not found', async () => { mockFindOne.mockResolvedValue(null); const result = await repo.findById('reg-missing', 'hh1', 'user-1'); expect(result).toBeNull(); }); }); describe('findActiveByUser', () => { it('returns active regimens for user', async () => { const regimens = [ { _id: 'reg-1', isActive: true }, { _id: 'reg-2', isActive: true }, ]; mockFind.mockResolvedValue(regimens); const result = await repo.findActiveByUser('hh1', 'user-1'); expect(result).toEqual(regimens); }); it('returns empty array when no active regimens', async () => { mockFind.mockResolvedValue([]); const result = await repo.findActiveByUser('hh1', 'user-1'); expect(result).toEqual([]); }); }); describe('create', () => { it('creates and returns regimen', async () => { const data = { name: 'Morning Routine', isActive: true, medications: [ { medicineId: 'med-1', medicineName: 'Metformin', medicineStrength: 500, medicineStrengthUnit: 'mg', medicineForm: 'tablet', dosage: 1, dosageUnit: 'tablet', frequency: 'daily', }, ], }; mockSave.mockImplementation(function (this: { toObject: () => unknown }) { return Promise.resolve(this); }); const result = await repo.create(data, 'hh1', 'user-1', 'user-1'); expect(result).toBeTruthy(); expect(mockSave).toHaveBeenCalled(); }); }); describe('update', () => { it('updates and returns regimen', async () => { const updated = { _id: 'reg-1', name: 'Updated' }; mockFindOneAndUpdate.mockResolvedValue(updated); const result = await repo.update('reg-1', 'hh1', 'user-1', { name: 'Updated' }); expect(result).toEqual(updated); }); it('returns null when regimen not found', async () => { mockFindOneAndUpdate.mockResolvedValue(null); const result = await repo.update('reg-missing', 'hh1', 'user-1', { name: 'Updated' }); expect(result).toBeNull(); }); }); describe('softDelete', () => { it('soft deletes and returns regimen', async () => { const deleted = { _id: 'reg-1', isDeleted: true }; mockFindOneAndUpdate.mockResolvedValue(deleted); const result = await repo.softDelete('reg-1', 'hh1', 'user-1'); expect(result).toEqual(deleted); }); it('returns null when regimen not found', async () => { mockFindOneAndUpdate.mockResolvedValue(null); const result = await repo.softDelete('reg-missing', 'hh1', 'user-1'); expect(result).toBeNull(); }); }); });