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/store.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 { StoreModel: FakeModel }; }); import { StoresRepository } from './stores.repository.js'; describe(StoresRepository.name, () => { let repo: StoresRepository; beforeEach(() => { vi.clearAllMocks(); repo = new StoresRepository(); }); describe('findByHousehold', () => { it('returns paginated items', async () => { const items = [{ _id: 'st-1', name: 'Walgreens' }]; mockFind.mockResolvedValue(items); const result = await repo.findByHousehold('hh1', { limit: 20 }); expect(result.data).toEqual(items); expect(result.pagination.hasMore).toBe(false); expect(result.pagination.cursor).toBeNull(); }); it('sets hasMore when more items exist', async () => { const items = [{ _id: 'st-1' }, { _id: 'st-2' }, { _id: 'st-3' }]; mockFind.mockResolvedValue(items); const result = await repo.findByHousehold('hh1', { limit: 2 }); expect(result.data).toHaveLength(2); expect(result.pagination.hasMore).toBe(true); expect(result.pagination.cursor).toBeTruthy(); }); it('handles cursor pagination', async () => { mockFind.mockResolvedValue([]); const cursor = Buffer.from('st-1').toString('base64'); const result = await repo.findByHousehold('hh1', { cursor, limit: 20 }); expect(result.pagination.hasMore).toBe(false); }); it('filters by tags', async () => { mockFind.mockResolvedValue([]); await repo.findByHousehold('hh1', { tags: 'pharmacy,online', limit: 20 }); expect(mockFind).toHaveBeenCalled(); }); it('filters by search', async () => { mockFind.mockResolvedValue([]); await repo.findByHousehold('hh1', { search: 'cvs', limit: 20 }); expect(mockFind).toHaveBeenCalled(); }); it('skips tag filter when tags string is empty after trim', async () => { mockFind.mockResolvedValue([]); await repo.findByHousehold('hh1', { tags: ' , ', limit: 20 }); expect(mockFind).toHaveBeenCalled(); }); it('returns null cursor when no data', async () => { mockFind.mockResolvedValue([]); const result = await repo.findByHousehold('hh1', { limit: 20 }); expect(result.pagination.cursor).toBeNull(); }); }); describe('findById', () => { it('returns store when found', async () => { const store = { _id: 'st-1', name: 'CVS' }; mockFindOne.mockResolvedValue(store); const result = await repo.findById('st-1', 'hh1'); expect(result).toEqual(store); }); it('returns null when not found', async () => { mockFindOne.mockResolvedValue(null); expect(await repo.findById('missing', 'hh1')).toBeNull(); }); }); describe('create', () => { it('creates and returns store', async () => { const data = { name: 'Walgreens', tags: [], isActive: true }; mockSave.mockImplementation(function (this: { toObject: () => unknown }) { return Promise.resolve(this); }); const result = await repo.create(data as never, 'hh1', 'user-1'); expect(result).toBeTruthy(); expect(mockSave).toHaveBeenCalled(); }); }); describe('update', () => { it('updates and returns store', async () => { const updated = { _id: 'st-1', name: 'Updated' }; mockFindOneAndUpdate.mockResolvedValue(updated); const result = await repo.update('st-1', 'hh1', { name: 'Updated' }); expect(result).toEqual(updated); }); it('returns null when not found', async () => { mockFindOneAndUpdate.mockResolvedValue(null); expect(await repo.update('missing', 'hh1', {})).toBeNull(); }); }); describe('deactivate', () => { it('sets isActive=false and returns store', async () => { const deactivated = { _id: 'st-1', isActive: false }; mockFindOneAndUpdate.mockResolvedValue(deactivated); const result = await repo.deactivate('st-1', 'hh1'); expect(result).toEqual(deactivated); }); it('returns null when not found', async () => { mockFindOneAndUpdate.mockResolvedValue(null); expect(await repo.deactivate('missing', 'hh1')).toBeNull(); }); }); });