207 lines
6.1 KiB
TypeScript
207 lines
6.1 KiB
TypeScript
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('../../../src/schemas/refill-list.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 { RefillListModel: FakeModel };
|
|
});
|
|
|
|
import { RefillsRepository } from '../../../src/modules/refills/refills.repository.js';
|
|
|
|
describe(RefillsRepository.name, () => {
|
|
let repo: RefillsRepository;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
repo = new RefillsRepository();
|
|
});
|
|
|
|
describe('create', () => {
|
|
it('saves and returns refill list', async () => {
|
|
const data = {
|
|
householdId: 'hh1',
|
|
name: 'Monthly Refills',
|
|
status: 'active',
|
|
createdBy: 'user-1',
|
|
items: [],
|
|
};
|
|
mockSave.mockImplementation(function (this: { toObject: () => unknown }) {
|
|
return Promise.resolve(this);
|
|
});
|
|
|
|
const result = await repo.create(data);
|
|
|
|
expect(mockSave).toHaveBeenCalled();
|
|
expect(result).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe('findByHousehold', () => {
|
|
it('returns paginated lists', async () => {
|
|
const lists = [{ _id: 'rl-1', name: 'Monthly Refills' }];
|
|
mockFind.mockResolvedValue(lists);
|
|
|
|
const result = await repo.findByHousehold('hh1', { limit: 20 });
|
|
|
|
expect(result.data).toEqual(lists);
|
|
expect(result.pagination.hasMore).toBe(false);
|
|
expect(result.pagination.cursor).toBeNull();
|
|
});
|
|
|
|
it('sets hasMore when more lists exist', async () => {
|
|
const lists = [{ _id: 'rl-1' }, { _id: 'rl-2' }, { _id: 'rl-3' }];
|
|
mockFind.mockResolvedValue(lists);
|
|
|
|
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('rl-1').toString('base64');
|
|
const result = await repo.findByHousehold('hh1', { cursor, limit: 20 });
|
|
|
|
expect(result.pagination.hasMore).toBe(false);
|
|
});
|
|
|
|
it('filters by status', async () => {
|
|
mockFind.mockResolvedValue([]);
|
|
|
|
await repo.findByHousehold('hh1', { status: 'active' as never, 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 list when found', async () => {
|
|
const list = { _id: 'rl-1', name: 'Monthly Refills' };
|
|
mockFindOne.mockResolvedValue(list);
|
|
|
|
const result = await repo.findById('rl-1', 'hh1');
|
|
|
|
expect(result).toEqual(list);
|
|
});
|
|
|
|
it('returns null when not found', async () => {
|
|
mockFindOne.mockResolvedValue(null);
|
|
|
|
expect(await repo.findById('missing', 'hh1')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('update', () => {
|
|
it('updates and returns list', async () => {
|
|
const updated = { _id: 'rl-1', name: 'Updated' };
|
|
mockFindOneAndUpdate.mockResolvedValue(updated);
|
|
|
|
const result = await repo.update('rl-1', 'hh1', { name: 'Updated' });
|
|
|
|
expect(result).toEqual(updated);
|
|
});
|
|
|
|
it('updates status field', async () => {
|
|
mockFindOneAndUpdate.mockResolvedValue({ _id: 'rl-1', status: 'shopping' });
|
|
|
|
const result = await repo.update('rl-1', 'hh1', { status: 'shopping' as never });
|
|
|
|
expect(result).toBeTruthy();
|
|
});
|
|
|
|
it('updates preferredStoreId field', async () => {
|
|
mockFindOneAndUpdate.mockResolvedValue({ _id: 'rl-1', preferredStoreId: 'st-1' });
|
|
|
|
const result = await repo.update('rl-1', 'hh1', { preferredStoreId: 'st-1' });
|
|
|
|
expect(result).toBeTruthy();
|
|
});
|
|
|
|
it('returns null when not found', async () => {
|
|
mockFindOneAndUpdate.mockResolvedValue(null);
|
|
|
|
expect(await repo.update('missing', 'hh1', {})).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('updateItem', () => {
|
|
it('updates item and returns list', async () => {
|
|
const updated = { _id: 'rl-1', items: [{ _id: 'item-1', checked: true }] };
|
|
mockFindOneAndUpdate.mockResolvedValue(updated);
|
|
|
|
const result = await repo.updateItem('rl-1', 'hh1', 'item-1', { checked: true });
|
|
|
|
expect(result).toEqual(updated);
|
|
});
|
|
|
|
it('updates actualPrice, storeId, and notes', async () => {
|
|
mockFindOneAndUpdate.mockResolvedValue({ _id: 'rl-1', items: [] });
|
|
|
|
await repo.updateItem('rl-1', 'hh1', 'item-1', {
|
|
actualPrice: 9.99,
|
|
storeId: 'st-1',
|
|
notes: 'picked up at CVS',
|
|
});
|
|
|
|
expect(mockFindOneAndUpdate).toHaveBeenCalled();
|
|
});
|
|
|
|
it('includes checkedAt when provided', async () => {
|
|
mockFindOneAndUpdate.mockResolvedValue({ _id: 'rl-1', items: [] });
|
|
|
|
const checkedAt = new Date();
|
|
await repo.updateItem('rl-1', 'hh1', 'item-1', { checked: true, checkedAt });
|
|
|
|
expect(mockFindOneAndUpdate).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('markItemsAddedToCabinet', () => {
|
|
it('marks items and returns list', async () => {
|
|
const updated = { _id: 'rl-1', items: [] };
|
|
mockFindOneAndUpdate.mockResolvedValue(updated);
|
|
|
|
const result = await repo.markItemsAddedToCabinet('rl-1', 'hh1', ['item-1', 'item-2']);
|
|
|
|
expect(result).toEqual(updated);
|
|
});
|
|
});
|
|
});
|