import { describe, it, expect, vi, beforeEach } from 'vitest'; import { MealPlanRepository } from '../../../src/modules/meal-plans/meal-plans.repository.js'; import { MealPlanStatus } from '@meshitrack/shared'; const { mockSave, MockMealPlanModel } = vi.hoisted(() => { const mockSave = vi.fn(); function MockModel(this: { save: typeof mockSave }, data: unknown) { Object.assign(this, data); this.save = mockSave; } Object.assign(MockModel, { findOne: vi.fn(), find: vi.fn(), findOneAndUpdate: vi.fn(), findOneAndDelete: vi.fn(), }); return { mockSave, MockMealPlanModel: MockModel }; }); vi.mock('../../../src/schemas/meal-plan.schema.js', () => ({ MealPlanModel: MockMealPlanModel, })); const { MealPlanModel } = await import('../../../src/schemas/meal-plan.schema.js'); function makeChain(result: unknown = null) { return { sort: vi.fn().mockReturnThis(), limit: vi.fn().mockReturnThis(), lean: vi.fn().mockReturnThis(), exec: vi.fn().mockResolvedValue(result), }; } describe(MealPlanRepository.name, () => { let repo: MealPlanRepository; beforeEach(() => { vi.clearAllMocks(); repo = new MealPlanRepository(); }); describe('findByHousehold', () => { it('applies householdId filter', async () => { const chain = makeChain([]); vi.mocked(MealPlanModel.find).mockReturnValue(chain as never); await repo.findByHousehold('hh1', { limit: 20 }); expect(MealPlanModel.find).toHaveBeenCalledWith( expect.objectContaining({ householdId: 'hh1' }), ); }); }); describe('findByWeek', () => { it('queries by householdId and weekStartDate', async () => { const mockPlan = { _id: 'mp1', weekStartDate: '2026-05-18' }; vi.mocked(MealPlanModel.findOne).mockReturnValue(makeChain(mockPlan) as never); const result = await repo.findByWeek('hh1', '2026-05-18'); expect(MealPlanModel.findOne).toHaveBeenCalledWith({ householdId: 'hh1', weekStartDate: '2026-05-18', }); expect(result).toEqual(mockPlan); }); }); describe('create', () => { it('saves and returns new document', async () => { const plainDoc = { _id: 'new-id', weekStartDate: '2026-05-18' }; mockSave.mockResolvedValue({ toObject: () => plainDoc }); const result = await repo.create({ weekStartDate: '2026-05-18' }); expect(mockSave).toHaveBeenCalled(); expect(result).toEqual(plainDoc); }); }); describe('updateStatus', () => { it('updates status only', async () => { vi.mocked(MealPlanModel.findOneAndUpdate).mockReturnValue(makeChain({ _id: 'mp1' }) as never); await repo.updateStatus('mp1', 'hh1', MealPlanStatus.ACTIVE); expect(MealPlanModel.findOneAndUpdate).toHaveBeenCalledWith( { _id: 'mp1', householdId: 'hh1' }, { $set: { status: MealPlanStatus.ACTIVE } }, { new: true, lean: true }, ); }); }); describe('findById', () => { it('finds meal plan by id and householdId', async () => { const mockPlan = { _id: 'mp1', weekStartDate: '2026-05-18' }; vi.mocked(MealPlanModel.findOne).mockReturnValue(makeChain(mockPlan) as never); const result = await repo.findById('mp1', 'hh1'); expect(MealPlanModel.findOne).toHaveBeenCalledWith({ _id: 'mp1', householdId: 'hh1', }); expect(result).toEqual(mockPlan); }); }); describe('update', () => { it('updates meal plan using findOneAndUpdate', async () => { vi.mocked(MealPlanModel.findOneAndUpdate).mockReturnValue(makeChain({ _id: 'mp1' }) as never); await repo.update('mp1', 'hh1', { status: MealPlanStatus.ACTIVE }); expect(MealPlanModel.findOneAndUpdate).toHaveBeenCalledWith( { _id: 'mp1', householdId: 'hh1' }, { $set: { status: MealPlanStatus.ACTIVE } }, { new: true, lean: true }, ); }); }); describe('delete', () => { it('deletes meal plan using findOneAndDelete', async () => { vi.mocked(MealPlanModel.findOneAndDelete).mockReturnValue(makeChain({ _id: 'mp1' }) as never); const result = await repo.delete('mp1', 'hh1'); expect(MealPlanModel.findOneAndDelete).toHaveBeenCalledWith({ _id: 'mp1', householdId: 'hh1', }); expect(result).toEqual({ _id: 'mp1' }); }); }); describe('findByHousehold pagination cursor', () => { it('applies pagination filter when cursor is provided', async () => { const chain = makeChain([]); vi.mocked(MealPlanModel.find).mockReturnValue(chain as never); const cursor = Buffer.from('some-mongo-id').toString('base64'); await repo.findByHousehold('hh1', { limit: 20, cursor }); expect(MealPlanModel.find).toHaveBeenCalledWith({ householdId: 'hh1', _id: { $gt: 'some-mongo-id' } }); }); }); });