aboutsummaryrefslogtreecommitdiff
path: root/src/lib/server/services/routines.test.ts
blob: 05abcaf0e01a8483332ccc757f47e7e3cbc21f1d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

import { describe, it, expect, beforeEach } from 'vitest';
import { createTestDb } from '$lib/server/db/test-helper';
import { add as addProject } from './projects';
import {
	add,
	edit,
	archive,
	complete,
	loadAllVersions,
	loadProjectDashboard,
	loadOverview,
} from './routines';

const DAY_MS = 24 * 60 * 60 * 1000;

describe('routines service', () => {
	let db: Awaited<ReturnType<typeof createTestDb>>['db'];
	let projectId: number;

	beforeEach(async () => {
		const testDb = await createTestDb();
		db = testDb.db;
		const project = await addProject('Household', db);
		projectId = project.id;
	});

	it('creates a routine with version 1', async () => {
		const r = await add(projectId, 'clean kitchen', 7, db);
		expect(r.id).toBe(1);
		expect(r.version).toBe(1);
		expect(r.frequencyDays).toBe(7);
	});

	it('edit inserts a new snapshot version and keeps history', async () => {
		const r = await add(projectId, 'clean kitchen', 7, db);
		const updated = await edit(
			r.id,
			{ name: 'clean kitchen deeply', frequencyDays: 3 },
			db,
		);
		expect(updated.version).toBe(2);
		expect(updated.frequencyDays).toBe(3);

		const versions = await loadAllVersions(r.id, db);
		expect(versions).toHaveLength(2);
		expect(versions[0].frequencyDays).toBe(7);
		expect(versions[1].frequencyDays).toBe(3);
	});

	it('new routines start with an empty description', async () => {
		const r = await add(projectId, 'clean kitchen', 7, db);
		expect(r.description).toBe('');
	});

	it('edit is partial: name/frequency edits keep the description and vice-versa', async () => {
		const r = await add(projectId, 'clean kitchen', 7, db);
		const described = await edit(r.id, { description: 'wax on, wax off' }, db);
		expect(described.name).toBe('clean kitchen');
		expect(described.frequencyDays).toBe(7);
		expect(described.description).toBe('wax on, wax off');

		const renamed = await edit(r.id, { name: 'clean the whole kitchen' }, db);
		expect(renamed.version).toBe(3);
		expect(renamed.description).toBe('wax on, wax off');
		expect(renamed.frequencyDays).toBe(7);

		const refreq = await edit(r.id, { frequencyDays: 14 }, db);
		expect(refreq.version).toBe(4);
		expect(refreq.name).toBe('clean the whole kitchen');
		expect(refreq.description).toBe('wax on, wax off');

		const cleared = await edit(r.id, { description: '' }, db);
		expect(cleared.version).toBe(5);
		expect(cleared.description).toBe('');
		expect(cleared.name).toBe('clean the whole kitchen');
	});

	it('archive inserts an archived snapshot without removing history', async () => {
		const r = await add(projectId, 'clean kitchen', 7, db);
		await archive(r.id, db);
		const versions = await loadAllVersions(r.id, db);
		expect(versions).toHaveLength(2);
		expect(versions[0].archived).toBe(false);
		expect(versions[1].archived).toBe(true);

		const dashboard = await loadProjectDashboard(projectId, Date.now(), db);
		expect(dashboard.routines).toHaveLength(0);
		expect(dashboard.healthScore).toBe(100);
	});

	it('complete records the snapshot version it was made against', async () => {
		const r = await add(projectId, 'clean kitchen', 7, db);
		await edit(r.id, { frequencyDays: 5 }, db);
		const doneAt = new Date().toISOString();
		const completion = await complete(r.id, 'with vinegar', doneAt, db);
		expect(completion.routineVersion).toBe(2);
		expect(completion.note).toBe('with vinegar');
	});

	it('dashboard computes urgency and health from completions', async () => {
		const r = await add(projectId, 'clean kitchen', 7, db);
		const createdAt = Date.parse(r.createdAt);
		await complete(r.id, null, new Date(createdAt).toISOString(), db);

		// 15 days after completion with a 7-day routine => very late
		const now = createdAt + 15 * DAY_MS;
		const dashboard = await loadProjectDashboard(projectId, now, db);
		expect(dashboard.routines).toHaveLength(1);
		expect(dashboard.routines[0].status).toBe('veryLate');
		expect(dashboard.healthScore).toBe(0);
	});

	it('overview aggregates per-project health and a global score', async () => {
		await add(projectId, 'clean kitchen', 7, db);
		const growth = await addProject('Personal growth', db);
		await add(growth.id, 'read 30 minutes', 1, db);

		const now = Date.now();
		const overview = await loadOverview(now, db);
		expect(overview.projects).toHaveLength(2);
		expect(overview.globalScore).toBe(
			Math.round(overview.projects.reduce((s, p) => s + p.healthScore, 0) / 2),
		);
	});
});