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/organizer-fill.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 { OrganizerFillModel: FakeModel }; }); import { OrganizerRepository } from './organizer.repository.js'; describe(OrganizerRepository.name, () => { let repo: OrganizerRepository; beforeEach(() => { vi.clearAllMocks(); repo = new OrganizerRepository(); }); describe('findByHousehold', () => { it('returns paginated items', async () => { const items = [{ _id: 'fill-1', regimenName: '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: 'fill-2', regimenName: 'Evening' }]; mockFind.mockResolvedValue(items); const cursor = Buffer.from('fill-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: `fill-${i}`, regimenName: `R${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 regimenId', async () => { mockFind.mockResolvedValue([]); await repo.findByHousehold('hh1', 'user-1', { regimenId: 'reg-1', limit: 20 }); expect(mockFind).toHaveBeenCalled(); }); it('filters by status', async () => { mockFind.mockResolvedValue([]); await repo.findByHousehold('hh1', 'user-1', { status: 'completed' as never, limit: 20 }); expect(mockFind).toHaveBeenCalled(); }); it('returns cursor as null when hasMore is false even with data', async () => { const items = [{ _id: 'fill-1', regimenName: '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 fill by id and householdId', async () => { const fill = { _id: 'fill-1', householdId: 'hh1', regimenName: 'Morning' }; mockFindOne.mockResolvedValue(fill); const result = await repo.findById('fill-1', 'hh1'); expect(result).toEqual(fill); }); it('returns null when not found', async () => { mockFindOne.mockResolvedValue(null); const result = await repo.findById('fill-missing', 'hh1'); expect(result).toBeNull(); }); }); describe('create', () => { it('creates and returns organizer fill', async () => { const data = { householdId: 'hh1', userId: 'user-1', regimenId: 'reg-1', regimenName: 'Morning', numberOfDays: 7, fillDate: new Date(), items: [], status: 'completed' as const, }; mockSave.mockImplementation(function (this: { toObject: () => unknown }) { return Promise.resolve(this); }); const result = await repo.create(data as never); expect(result).toBeTruthy(); expect(mockSave).toHaveBeenCalled(); }); }); describe('updateStatus', () => { it('updates and returns fill with new status', async () => { const updated = { _id: 'fill-1', status: 'reversed' }; mockFindOneAndUpdate.mockResolvedValue(updated); const result = await repo.updateStatus('fill-1', 'hh1', 'reversed' as never); expect(result).toEqual(updated); }); it('returns null when fill not found', async () => { mockFindOneAndUpdate.mockResolvedValue(null); const result = await repo.updateStatus('fill-missing', 'hh1', 'reversed' as never); expect(result).toBeNull(); }); }); });