import { describe, it, expect } from 'vitest'; import { InMemoryTasksClient } from '../src/dev-client.js'; const LAUREN = 'lauren@seahavenind.com'; const ADAM = 'adam@seahavenind.com'; describe('InMemoryTasksClient', () => { describe('createTask', () => { it('returns a task with a generated id and completed:false', async () => { const client = new InMemoryTasksClient(); const task = await client.createTask({ sub: LAUREN, title: 'Buy gloves' }); expect(task.taskId).toMatch(/^task-\d{4}$/); expect(task.sub).toBe(LAUREN); expect(task.title).toBe('Buy gloves'); expect(task.completed).toBe(false); expect(task.createdAt).toBeDefined(); expect(task.description).toBeUndefined(); }); it('carries an optional description through', async () => { const client = new InMemoryTasksClient(); const task = await client.createTask({ sub: LAUREN, title: 'T', description: 'detail' }); expect(task.description).toBe('detail'); }); it('a task created for sub A is not visible to sub B (partitioned)', async () => { const client = new InMemoryTasksClient(); const task = await client.createTask({ sub: LAUREN, title: 'Lauren only' }); const adamTasks = await client.listTasks({ sub: ADAM, includeCompleted: true }); expect(adamTasks.map((t) => t.taskId)).not.toContain(task.taskId); const laurenTasks = await client.listTasks({ sub: LAUREN, includeCompleted: true }); expect(laurenTasks.map((t) => t.taskId)).toContain(task.taskId); }); it('creates a task for a previously-unknown sub', async () => { const client = new InMemoryTasksClient(); const task = await client.createTask({ sub: 'fresh@seahavenind.com', title: 'First' }); const tasks = await client.listTasks({ sub: 'fresh@seahavenind.com', includeCompleted: true, }); expect(tasks.map((t) => t.taskId)).toEqual([task.taskId]); }); }); describe('listTasks', () => { it('omits completed tasks by default', async () => { const client = new InMemoryTasksClient(); const tasks = await client.listTasks({ sub: LAUREN }); const ids = tasks.map((t) => t.taskId); expect(ids).toContain('task-0001'); // open expect(ids).not.toContain('task-0002'); // completed }); it('includes completed tasks when includeCompleted is true', async () => { const client = new InMemoryTasksClient(); const tasks = await client.listTasks({ sub: LAUREN, includeCompleted: true }); const ids = tasks.map((t) => t.taskId); expect(ids).toContain('task-0001'); expect(ids).toContain('task-0002'); }); it('returns an empty list for an unknown sub', async () => { const client = new InMemoryTasksClient(); expect(await client.listTasks({ sub: 'nobody@example.com' })).toEqual([]); }); }); describe('completeTask', () => { it('sets completed and completedAt', async () => { const client = new InMemoryTasksClient(); const completed = await client.completeTask({ sub: LAUREN, taskId: 'task-0001' }); expect(completed.completed).toBe(true); expect(completed.completedAt).toBeDefined(); // now omitted from the default (open-only) listing const open = await client.listTasks({ sub: LAUREN }); expect(open.map((t) => t.taskId)).not.toContain('task-0001'); }); it('throws for an unknown taskId', async () => { const client = new InMemoryTasksClient(); await expect(client.completeTask({ sub: LAUREN, taskId: 'task-missing' })).rejects.toThrow( /not found/, ); }); it('throws when the task belongs to another sub', async () => { const client = new InMemoryTasksClient(); await expect(client.completeTask({ sub: ADAM, taskId: 'task-0001' })).rejects.toThrow( /not found/, ); }); }); describe('deleteTask', () => { it('removes the task so subsequent listings omit it', async () => { const client = new InMemoryTasksClient(); await client.deleteTask({ sub: LAUREN, taskId: 'task-0001' }); const tasks = await client.listTasks({ sub: LAUREN, includeCompleted: true }); expect(tasks.map((t) => t.taskId)).not.toContain('task-0001'); }); it('is a no-op for an unknown taskId', async () => { const client = new InMemoryTasksClient(); await expect( client.deleteTask({ sub: LAUREN, taskId: 'task-missing' }), ).resolves.toBeUndefined(); const tasks = await client.listTasks({ sub: LAUREN, includeCompleted: true }); expect(tasks).toHaveLength(2); }); it('is a no-op for an unknown sub', async () => { const client = new InMemoryTasksClient(); await expect( client.deleteTask({ sub: 'nobody@example.com', taskId: 'task-0001' }), ).resolves.toBeUndefined(); }); }); });