aboutsummaryrefslogtreecommitdiff
path: root/src/lib/server/services/routines.test.ts
blob: 9e1f4295222b71bc01b538325e57b039996c8b22 (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

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, 'clean kitchen deeply', 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('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, 'clean kitchen', 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),
		);
	});
});