MeshiTrack/packages/api/tests/modules/organizer/organizer.repository.test.ts

187 lines
5.3 KiB
TypeScript
Raw Permalink Normal View History

2026-03-28 18:25:49 +09:00
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(),
}));
2026-05-19 11:06:03 +09:00
vi.mock('../../../src/schemas/organizer-fill.schema.js', () => {
2026-03-28 18:25:49 +09:00
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 { OrganizerFillModel: FakeModel };
});
2026-05-19 11:06:03 +09:00
import { OrganizerRepository } from '../../../src/modules/organizer/organizer.repository.js';
2026-03-28 18:25:49 +09:00
describe(OrganizerRepository.name, () => {
let repo: OrganizerRepository;
beforeEach(() => {
vi.clearAllMocks();
repo = new OrganizerRepository();
});
describe('findByHousehold', () => {
it('returns paginated items', async () => {
const items = [{ _id: 'fill-1', regimenName: 'Morning' }];
mockFind.mockResolvedValue(items);
const result = await repo.findByHousehold('hh1', 'user-1', { limit: 20 });
expect(result.data).toEqual(items);
expect(result.pagination.hasMore).toBe(false);
});
it('handles cursor-based pagination', async () => {
const items = [{ _id: 'fill-2', regimenName: 'Evening' }];
mockFind.mockResolvedValue(items);
const cursor = Buffer.from('fill-1').toString('base64');
const result = await repo.findByHousehold('hh1', 'user-1', { cursor, limit: 20 });
expect(result.data).toEqual(items);
expect(result.pagination.hasMore).toBe(false);
});
it('sets hasMore when more items exist', async () => {
2026-04-26 18:44:59 +09:00
const items = Array.from({ length: 3 }, (_, i) => ({
_id: `fill-${i}`,
regimenName: `R${i}`,
}));
2026-03-28 18:25:49 +09:00
mockFind.mockResolvedValue(items);
const result = await repo.findByHousehold('hh1', 'user-1', { limit: 2 });
expect(result.data).toHaveLength(2);
expect(result.pagination.hasMore).toBe(true);
expect(result.pagination.cursor).toBeTruthy();
});
it('returns null cursor when no data', async () => {
mockFind.mockResolvedValue([]);
const result = await repo.findByHousehold('hh1', 'user-1', { limit: 20 });
expect(result.pagination.cursor).toBeNull();
expect(result.pagination.hasMore).toBe(false);
});
it('filters by regimenId', async () => {
mockFind.mockResolvedValue([]);
await repo.findByHousehold('hh1', 'user-1', { regimenId: 'reg-1', limit: 20 });
expect(mockFind).toHaveBeenCalled();
});
it('filters by status', async () => {
mockFind.mockResolvedValue([]);
await repo.findByHousehold('hh1', 'user-1', { status: 'completed' as never, limit: 20 });
expect(mockFind).toHaveBeenCalled();
});
it('returns cursor as null when hasMore is false even with data', async () => {
const items = [{ _id: 'fill-1', regimenName: 'Morning' }];
mockFind.mockResolvedValue(items);
const result = await repo.findByHousehold('hh1', 'user-1', { limit: 20 });
expect(result.pagination.cursor).toBeNull();
expect(result.pagination.hasMore).toBe(false);
});
});
describe('findById', () => {
it('returns fill by id and householdId', async () => {
const fill = { _id: 'fill-1', householdId: 'hh1', regimenName: 'Morning' };
mockFindOne.mockResolvedValue(fill);
const result = await repo.findById('fill-1', 'hh1');
expect(result).toEqual(fill);
});
it('returns null when not found', async () => {
mockFindOne.mockResolvedValue(null);
const result = await repo.findById('fill-missing', 'hh1');
expect(result).toBeNull();
});
});
describe('create', () => {
it('creates and returns organizer fill', async () => {
const data = {
householdId: 'hh1',
userId: 'user-1',
regimenId: 'reg-1',
regimenName: 'Morning',
numberOfDays: 7,
fillDate: new Date(),
items: [],
status: 'completed' as const,
};
mockSave.mockImplementation(function (this: { toObject: () => unknown }) {
return Promise.resolve(this);
});
const result = await repo.create(data as never);
expect(result).toBeTruthy();
expect(mockSave).toHaveBeenCalled();
});
});
describe('updateStatus', () => {
it('updates and returns fill with new status', async () => {
const updated = { _id: 'fill-1', status: 'reversed' };
mockFindOneAndUpdate.mockResolvedValue(updated);
const result = await repo.updateStatus('fill-1', 'hh1', 'reversed' as never);
expect(result).toEqual(updated);
});
it('returns null when fill not found', async () => {
mockFindOneAndUpdate.mockResolvedValue(null);
const result = await repo.updateStatus('fill-missing', 'hh1', 'reversed' as never);
expect(result).toBeNull();
});
});
});