import { describe, it, expect, vi, beforeEach } from 'vitest'; import { HouseholdRole } from '@meshitrack/shared'; const { _mockLean, mockExec, mockFindById, mockFindOne, mockFindByIdAndUpdate, mockSave } = vi.hoisted(() => { const mockExec = vi.fn(); const mockLean = vi.fn(() => ({ exec: mockExec })); return { mockExec, mockLean, mockFindById: vi.fn(() => ({ lean: mockLean })), mockFindOne: vi.fn(() => ({ lean: mockLean })), mockFindByIdAndUpdate: vi.fn(() => ({ exec: mockExec })), mockSave: vi.fn(), }; }); vi.mock('../../../src/schemas/household.schema.js', () => { class MockHouseholdModel { _data: Record; constructor(data: Record) { this._data = data; Object.assign(this, data); } save() { mockSave(); return Promise.resolve(this); } toObject() { return { _id: 'hh-new', ...this._data }; } static findById = mockFindById; static findOne = mockFindOne; static findByIdAndUpdate = mockFindByIdAndUpdate; } return { HouseholdModel: MockHouseholdModel }; }); import { HouseholdsRepository } from '../../../src/modules/households/households.repository.js'; describe('HouseholdsRepository', () => { let repo: HouseholdsRepository; beforeEach(() => { vi.clearAllMocks(); repo = new HouseholdsRepository(); }); describe('findById', () => { it('calls findById with lean', async () => { const household = { _id: 'hh1', name: 'Test' }; mockExec.mockResolvedValue(household); const result = await repo.findById('hh1'); expect(mockFindById).toHaveBeenCalledWith('hh1'); expect(result).toEqual(household); }); }); describe('findByInviteCode', () => { it('calls findOne with inviteCode', async () => { const household = { _id: 'hh1', inviteCode: 'ABCD' }; mockExec.mockResolvedValue(household); const result = await repo.findByInviteCode('ABCD'); expect(mockFindOne).toHaveBeenCalledWith({ inviteCode: 'ABCD' }); expect(result).toEqual(household); }); }); describe('create', () => { it('creates a household with owner as first member', async () => { mockSave.mockResolvedValue({}); const result = await repo.create({ name: 'Test' }, 'owner-1', 'INVITE1'); expect(mockSave).toHaveBeenCalled(); expect(result).toMatchObject({ name: 'Test', ownerUserId: 'owner-1', inviteCode: 'INVITE1' }); }); }); describe('update', () => { it('calls findByIdAndUpdate with $set', async () => { mockExec.mockResolvedValue({ _id: 'hh1', name: 'Updated' }); const result = await repo.update('hh1', { name: 'Updated' }); expect(mockFindByIdAndUpdate).toHaveBeenCalledWith( 'hh1', { $set: { name: 'Updated' } }, { new: true, lean: true }, ); expect(result).toEqual({ _id: 'hh1', name: 'Updated' }); }); }); describe('addMember', () => { it('pushes a new member to the array', async () => { mockExec.mockResolvedValue({ _id: 'hh1', members: [] }); await repo.addMember('hh1', 'user-2', HouseholdRole.MEMBER); expect(mockFindByIdAndUpdate).toHaveBeenCalledWith( 'hh1', { $push: { members: expect.objectContaining({ userId: 'user-2', role: HouseholdRole.MEMBER, }), }, }, { new: true, lean: true }, ); }); }); describe('updateInviteCode', () => { it('sets the new invite code', async () => { mockExec.mockResolvedValue({ _id: 'hh1', inviteCode: 'NEWCODE' }); const result = await repo.updateInviteCode('hh1', 'NEWCODE'); expect(mockFindByIdAndUpdate).toHaveBeenCalledWith( 'hh1', { $set: { inviteCode: 'NEWCODE' } }, { new: true, lean: true }, ); expect(result).toEqual({ _id: 'hh1', inviteCode: 'NEWCODE' }); }); }); });