148 lines
4.8 KiB
TypeScript
148 lines
4.8 KiB
TypeScript
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' }
|
|
});
|
|
});
|
|
});
|
|
});
|