test(today): Add/update test cases for backend "today" functionality

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-25 23:20:00 -07:00
parent 6c04395727
commit 1606057e12

View file

@ -0,0 +1,249 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { TodayService } from '../today.service';
import { TasksService } from '@projects/productivity/tasks/backend/tasks.service';
import { HabitsService } from '@projects/wellness/habits/backend/habits.service';
import { TimeBlock } from '@projects/productivity/scheduling/backend/entities/time-block.entity';
import { DailyPlan } from '@projects/productivity/scheduling/backend/entities/daily-plan.entity';
import { mockRepository, MockRepository } from '@test-helpers/mock-repository';
const UUID_1 = 'a1b2c3d4-1111-2222-3333-444455556666';
const UUID_2 = 'b2c3d4e5-2222-3333-4444-555566667777';
const UUID_TASK1 = 'c3d4e5f6-3333-4444-5555-666677778888';
const UUID_TASK2 = 'd4e5f6a7-4444-5555-6666-777788889999';
function makeTask(overrides: Record<string, unknown> = {}): Record<string, unknown> {
return {
id: UUID_TASK1,
title: 'Test Task',
status: 'todo',
priority: 'medium',
isQuickWin: false,
dueDate: null,
...overrides,
};
}
function makeDailyPlan(overrides: Record<string, unknown> = {}): Record<string, unknown> {
return {
id: UUID_1,
date: new Date().toISOString().split('T')[0],
focusDomains: [],
energyLevel: null,
wakeIntention: null,
sleepReflection: null,
moodWake: null,
moodSleep: null,
createdAt: new Date(),
updatedAt: new Date(),
...overrides,
};
}
function makeTimeBlock(overrides: Record<string, unknown> = {}): Record<string, unknown> {
return {
id: UUID_2,
date: new Date().toISOString().split('T')[0],
startTime: '09:00',
endTime: '10:00',
title: 'Deep Work',
...overrides,
};
}
describe('TodayService', () => {
let service: TodayService;
let tasksService: {
findAll: jest.Mock;
findOverdue: jest.Mock;
};
let habitsService: {
findDueTodayWithStatus: jest.Mock;
};
let timeBlockRepo: MockRepository;
let dailyPlanRepo: MockRepository;
beforeEach(async () => {
tasksService = {
findAll: jest.fn().mockResolvedValue({ data: [], meta: { total: 0 } }),
findOverdue: jest.fn().mockResolvedValue([]),
};
habitsService = {
findDueTodayWithStatus: jest.fn().mockResolvedValue([]),
};
timeBlockRepo = mockRepository();
dailyPlanRepo = mockRepository();
const module: TestingModule = await Test.createTestingModule({
providers: [
TodayService,
{ provide: TasksService, useValue: tasksService },
{ provide: HabitsService, useValue: habitsService },
{ provide: getRepositoryToken(TimeBlock), useValue: timeBlockRepo },
{ provide: getRepositoryToken(DailyPlan), useValue: dailyPlanRepo },
],
}).compile();
service = module.get(TodayService);
});
describe('getToday', () => {
it('should return a complete TodayView', async () => {
const plan = makeDailyPlan();
dailyPlanRepo.findOne.mockResolvedValue(plan);
timeBlockRepo.find.mockResolvedValue([makeTimeBlock()]);
habitsService.findDueTodayWithStatus.mockResolvedValue([
{ habit: { id: UUID_1, name: 'Drink water' }, checkedIn: false },
]);
tasksService.findOverdue.mockResolvedValue([]);
tasksService.findAll
.mockResolvedValueOnce({ data: [makeTask({ priority: 'high' })], meta: { total: 1 } }) // priority tasks
.mockResolvedValueOnce({ data: [makeTask({ isQuickWin: true })], meta: { total: 1 } }) // quick wins
.mockResolvedValueOnce({ data: [], meta: { total: 3 } }); // completed today
const result = await service.getToday();
expect(result.date).toBe(new Date().toISOString().split('T')[0]);
expect(result.dailyPlan).toEqual(plan);
expect(result.timeBlocks).toHaveLength(1);
expect(result.habitsDueToday).toHaveLength(1);
expect(result.priorityTasks).toHaveLength(1);
expect(result.quickWins).toHaveLength(1);
expect(result.completedTodayCount).toBe(3);
});
it('should create a new daily plan when none exists', async () => {
dailyPlanRepo.findOne.mockResolvedValue(null);
const newPlan = makeDailyPlan();
dailyPlanRepo.create.mockReturnValue(newPlan);
dailyPlanRepo.save.mockResolvedValue(newPlan);
timeBlockRepo.find.mockResolvedValue([]);
const result = await service.getToday();
expect(dailyPlanRepo.create).toHaveBeenCalledWith(expect.objectContaining({
date: new Date().toISOString().split('T')[0],
focusDomains: [],
energyLevel: null,
}));
expect(dailyPlanRepo.save).toHaveBeenCalled();
expect(result.dailyPlan).toEqual(newPlan);
});
it('should use existing daily plan when available', async () => {
const existingPlan = makeDailyPlan({ energyLevel: 'high' });
dailyPlanRepo.findOne.mockResolvedValue(existingPlan);
timeBlockRepo.find.mockResolvedValue([]);
const result = await service.getToday();
expect(dailyPlanRepo.create).not.toHaveBeenCalled();
expect(result.dailyPlan).toEqual(existingPlan);
});
it('should call tasksService.findAll with correct params for priority tasks', async () => {
dailyPlanRepo.findOne.mockResolvedValue(makeDailyPlan());
timeBlockRepo.find.mockResolvedValue([]);
await service.getToday();
expect(tasksService.findAll).toHaveBeenCalledWith(expect.objectContaining({
status: 'todo,in_progress',
sort: 'priority',
order: 'ASC',
limit: 10,
page: 1,
}));
});
it('should call tasksService.findAll with correct params for quick wins', async () => {
dailyPlanRepo.findOne.mockResolvedValue(makeDailyPlan());
timeBlockRepo.find.mockResolvedValue([]);
await service.getToday();
expect(tasksService.findAll).toHaveBeenCalledWith(expect.objectContaining({
isQuickWin: true,
status: 'todo',
limit: 5,
page: 1,
}));
});
});
describe('getNextAction', () => {
it('should return overdue task with highest priority', async () => {
const overdueTask = makeTask({ title: 'Overdue Task', priority: 'high', dueDate: '2026-03-15' });
tasksService.findOverdue.mockResolvedValue([overdueTask]);
const result = await service.getNextAction();
expect(result).not.toBeNull();
expect(result!.task).toEqual(overdueTask);
expect(result!.score).toBe(100);
expect(result!.reason).toBe('Overdue task needs immediate attention');
});
it('should return null when no tasks exist', async () => {
tasksService.findOverdue.mockResolvedValue([]);
tasksService.findAll.mockResolvedValue({ data: [], meta: { total: 0 } });
const result = await service.getNextAction();
expect(result).toBeNull();
});
it('should score critical tasks highest', async () => {
tasksService.findOverdue.mockResolvedValue([]);
const criticalTask = makeTask({ priority: 'critical' });
const lowTask = makeTask({ id: UUID_TASK2, priority: 'low' });
tasksService.findAll.mockResolvedValue({ data: [lowTask, criticalTask], meta: { total: 2 } });
const result = await service.getNextAction();
expect(result).not.toBeNull();
expect(result!.task).toEqual(criticalTask);
expect(result!.reason).toContain('Critical priority');
});
it('should boost score for quick wins', async () => {
tasksService.findOverdue.mockResolvedValue([]);
const quickWin = makeTask({ priority: 'medium', isQuickWin: true });
const normalTask = makeTask({ id: UUID_TASK2, priority: 'medium', isQuickWin: false });
tasksService.findAll.mockResolvedValue({ data: [normalTask, quickWin], meta: { total: 2 } });
const result = await service.getNextAction();
expect(result).not.toBeNull();
expect(result!.task).toEqual(quickWin);
expect(result!.reason).toContain('Quick win');
});
it('should boost score for tasks due today', async () => {
const today = new Date().toISOString().split('T')[0];
tasksService.findOverdue.mockResolvedValue([]);
const dueToday = makeTask({ priority: 'low', dueDate: today });
const notDue = makeTask({ id: UUID_TASK2, priority: 'low', dueDate: null });
tasksService.findAll.mockResolvedValue({ data: [notDue, dueToday], meta: { total: 2 } });
const result = await service.getNextAction();
expect(result).not.toBeNull();
expect(result!.task).toEqual(dueToday);
expect(result!.reason).toContain('Due today');
});
it('should boost score for in-progress tasks', async () => {
tasksService.findOverdue.mockResolvedValue([]);
const inProgress = makeTask({ priority: 'low', status: 'in_progress' });
const todo = makeTask({ id: UUID_TASK2, priority: 'low', status: 'todo' });
tasksService.findAll.mockResolvedValue({ data: [todo, inProgress], meta: { total: 2 } });
const result = await service.getNextAction();
expect(result).not.toBeNull();
expect(result!.task).toEqual(inProgress);
expect(result!.reason).toContain('In progress');
});
});
});