import { beforeEach, describe, expect, it } from 'vitest';
import { MockLifecycleService } from '../mockLifecycleService';
describe('MockLifecycleService', () => {
let service: MockLifecycleService;
beforeEach(async () => {
service = new MockLifecycleService();
await service.resetToDefaultSeed();
});
describe('Authentication & User Management', () => {
it('initializes with seed user Alex Taylor', async () => {
const user = await service.getCurrentUser();
expect(user).not.toBeNull();
expect(user?.email).toBe('alex@example.com');
});
it('signs up and logs in a new user with email and password', async () => {
const newUser = await service.signup('clara@example.com', 'secret123', 'Clara Barton');
expect(newUser.email).toBe('clara@example.com');
expect(newUser.name).toBe('Clara Barton');
const current = await service.getCurrentUser();
expect(current?.id).toBe(newUser.id);
});
it('switches between multiple users', async () => {
const users = await service.listUsers();
const sarah = users.find((u) => u.email === 'sarah@example.com');
expect(sarah).toBeDefined();
if (sarah) {
await service.switchUser(sarah.id);
const current = await service.getCurrentUser();
expect(current?.email).toBe('sarah@example.com');
}
});
});
describe('Lifecycle Items CRUD & Common Fields', () => {
it('creates an item with guided lifecycle type and common fields only', async () => {
const item = await service.createItem({
name: 'Annual Dental Checkup',
lifecycleType: 'renewal',
dueDate: '2026-11-01',
status: 'active',
});
expect(item.id).toBeDefined();
expect(item.name).toBe('Annual Dental Checkup');
expect(item.lifecycleType).toBe('renewal');
expect(item.dueDate).toBe('2026-11-01');
expect(item.status).toBe('active');
});
it('supports missing due date per Decision 29', async () => {
const item = await service.createItem({
name: 'Smoke Detector Battery Test',
lifecycleType: 'maintenance',
dueDate: undefined,
status: 'active',
});
expect(item.dueDate).toBeUndefined();
});
it('retains completed items in main list per Decision 8 & 28', async () => {
const item = await service.createItem({
name: 'Coffee Maker Descaling',
lifecycleType: 'maintenance',
dueDate: '2026-10-15',
status: 'active',
});
const toggled = await service.toggleItemCompletion(item.id);
expect(toggled.status).toBe('completed');
const allItems = await service.getItems();
const found = allItems.find((i) => i.id === item.id);
expect(found).toBeDefined();
expect(found?.status).toBe('completed');
});
it('permanently deletes an item per Decision 19', async () => {
const item = await service.createItem({
name: 'Temporary item',
lifecycleType: 'warranty',
});
await service.deleteItem(item.id);
const allItems = await service.getItems();
expect(allItems.find((i) => i.id === item.id)).toBeUndefined();
});
});
describe('Groups & Shared Access', () => {
it('allows a user to create a group and invite members by email', async () => {
const group = await service.createGroup('Vacation Cabin Care');
expect(group.name).toBe('Vacation Cabin Care');
expect(group.memberEmails).toContain('alex@example.com');
const updatedGroup = await service.inviteMember(group.id, 'sarah@example.com');
expect(updatedGroup.memberEmails).toContain('sarah@example.com');
});
it('allows all group members to manage shared items per Decision 14', async () => {
// Alex creates group item
const group = await service.createGroup('Co-op Garden');
await service.inviteMember(group.id, 'sarah@example.com');
const sharedItem = await service.createItem({
name: 'Irrigation Winterization',
lifecycleType: 'maintenance',
dueDate: '2026-11-15',
groupId: group.id,
});
// Switch to Sarah
const users = await service.listUsers();
const sarah = users.find((u) => u.email === 'sarah@example.com')!;
await service.switchUser(sarah.id);
// Sarah can edit or complete the shared item
const updatedBySarah = await service.updateItem(sharedItem.id, {
name: 'Irrigation Winterization & Valve Drain',
});
expect(updatedBySarah.name).toBe('Irrigation Winterization & Valve Drain');
const completed = await service.toggleItemCompletion(sharedItem.id);
expect(completed.status).toBe('completed');
});
it('deleting a group deletes the group and all its shared items per Decision 30', async () => {
const group = await service.createGroup('Cabin Maintenance');
const item = await service.createItem({
name: 'Chimney Sweep',
lifecycleType: 'maintenance',
groupId: group.id,
});
await service.deleteGroup(group.id);
const groups = await service.getGroups();
expect(groups.find((g) => g.id === group.id)).toBeUndefined();
const items = await service.getItems();
expect(items.find((i) => i.id === item.id)).toBeUndefined();
});
});
describe('Account Deletion cascading per Decision 23', () => {
it('removes everything associated with the account', async () => {
// Create user Clara
const clara = await service.signup('clara2@example.com', 'pass', 'Clara Barton');
const group = await service.createGroup('Clara Group');
await service.createItem({
name: 'Clara Personal Item',
lifecycleType: 'renewal',
});
await service.createItem({
name: 'Clara Group Item',
lifecycleType: 'warranty',
groupId: group.id,
});
// Delete Clara's account
await service.deleteAccount(clara.id);
const users = await service.listUsers();
expect(users.find((u) => u.id === clara.id)).toBeUndefined();
// Log in as Alex and verify Clara's owned group is gone
const alex = users.find((u) => u.email === 'alex@example.com')!;
await service.switchUser(alex.id);
const alexGroups = await service.getGroups();
expect(alexGroups.find((g) => g.id === group.id)).toBeUndefined();
});
});
describe('Reminders and Daily Digest', () => {
it('computes reminders summary containing overdue and upcoming items', async () => {
const reminders = await service.getRemindersSummary();
expect(reminders.overdue.length).toBeGreaterThan(0);
expect(reminders.upcoming.length).toBeGreaterThan(0);
});
it('generates daily digest email with upcoming and overdue items per Decision 32 & 33', async () => {
const digest = await service.generateAndSendDailyDigest('alex@example.com');
expect(digest.recipientEmail).toBe('alex@example.com');
expect(digest.overdueItems.length).toBeGreaterThan(0);
expect(digest.upcomingItems.length).toBeGreaterThan(0);
expect(digest.htmlContent).toContain('Personal Lifecycle Tracker');
expect(digest.textContent).toContain('DAILY DIGEST');
});
it('allows updating daily digest delivery time per Question 34', async () => {
const updated = await service.updateDigestConfig({ deliveryTime: '06:00' });
expect(updated.deliveryTime).toBe('06:00');
});
});
});