api/insights: cover the daily window rules with tests
The daily energy and sleep processors had no coverage, which is how the seven-day energy window and the timezone drift went unnoticed. The tests pin the today-only window, the day boundary, and the empty case
Pedro Lucas Porcellis porcellis@eletrotupi.com · 12 days ago
269c1050a0fedbfadc33cb251e67d54cdc399f8f Browse filesParents
6b26342Changed files
1
Additions
+128
Deletions
0
/dev/null → api/tests/jobs/daily-insights.test.ts
1 +
import { describe, it, expect, afterAll, beforeEach } from 'vitest'
2 +
import { processDailyEnergy } from '@app/lib/queue/processors/insights/dailyEnergy'
3 +
import { processDailySleep } from '@app/lib/queue/processors/insights/dailySleep'
4 +
import { appDayWindow } from '@app/lib/queue/processors/insights/utils'
5 +
import { buildPrisma, cleanupTestDb, createTestUser } from '../test-helper'
6 +
7 +
const db = buildPrisma()
8 +
9 +
afterAll(() => db.$disconnect())
10 +
beforeEach(() => cleanupTestDb(db))
11 +
12 +
const HOUR = 60 * 60 * 1000
13 +
14 +
function createMood(userId: number, energyLevel: number, moment: Date) {
15 +
return db.mood.create({
16 +
data: {
17 +
userId,
18 +
selectedMood: 'GOOD',
19 +
anxietyLevel: 2,
20 +
stressLevel: 2,
21 +
energyLevel,
22 +
moment,
23 +
},
24 +
})
25 +
}
26 +
27 +
describe('processDailyEnergy', () => {
28 +
it('averages only the mood entries logged inside today', async () => {
29 +
const user = await createTestUser(db)
30 +
const ref = new Date()
31 +
const { start } = appDayWindow(ref)
32 +
33 +
await createMood(user.id, 8, new Date(start.getTime() + HOUR))
34 +
await createMood(user.id, 4, new Date(start.getTime() + 2 * HOUR))
35 +
// one hour before the app day begins, so it belongs to yesterday
36 +
await createMood(user.id, 10, new Date(start.getTime() - HOUR))
37 +
38 +
await processDailyEnergy({
39 +
userId: user.id,
40 +
periodStart: ref.toISOString(),
41 +
periodEnd: ref.toISOString(),
42 +
})
43 +
44 +
const insight = await db.insight.findFirst({
45 +
where: { userId: user.id, type: 'DAILY_ENERGY' },
46 +
})
47 +
48 +
expect(insight).not.toBeNull()
49 +
expect(insight!.periodStart).toEqual(start)
50 +
expect(insight!.metadata).toMatchObject({ avgEnergy: 6, entryCount: 2 })
51 +
})
52 +
53 +
it('writes nothing when no mood entry falls inside today', async () => {
54 +
const user = await createTestUser(db)
55 +
const ref = new Date()
56 +
const { start } = appDayWindow(ref)
57 +
58 +
await createMood(user.id, 9, new Date(start.getTime() - HOUR))
59 +
60 +
await processDailyEnergy({
61 +
userId: user.id,
62 +
periodStart: ref.toISOString(),
63 +
periodEnd: ref.toISOString(),
64 +
})
65 +
66 +
const insight = await db.insight.findFirst({
67 +
where: { userId: user.id, type: 'DAILY_ENERGY' },
68 +
})
69 +
70 +
expect(insight).toBeNull()
71 +
})
72 +
})
73 +
74 +
describe('processDailySleep', () => {
75 +
it('sums today and compares against yesterday', async () => {
76 +
const user = await createTestUser(db)
77 +
const ref = new Date()
78 +
const { start: today } = appDayWindow(ref)
79 +
const { start: yesterday } = appDayWindow(new Date(today.getTime() - 1))
80 +
81 +
await db.sleepRecord.create({
82 +
data: { userId: user.id, date: today, average: 8 },
83 +
})
84 +
await db.sleepRecord.create({
85 +
data: { userId: user.id, date: yesterday, average: 6 },
86 +
})
87 +
88 +
await processDailySleep({
89 +
userId: user.id,
90 +
periodStart: ref.toISOString(),
91 +
periodEnd: ref.toISOString(),
92 +
})
93 +
94 +
const insight = await db.insight.findFirst({
95 +
where: { userId: user.id, type: 'DAILY_SLEEP' },
96 +
})
97 +
98 +
expect(insight).not.toBeNull()
99 +
expect(insight!.periodStart).toEqual(today)
100 +
expect(insight!.metadata).toMatchObject({
101 +
totalHours: 8,
102 +
diffMinutes: 120,
103 +
})
104 +
})
105 +
106 +
it('writes nothing when no sleep record falls inside today', async () => {
107 +
const user = await createTestUser(db)
108 +
const ref = new Date()
109 +
const { start: today } = appDayWindow(ref)
110 +
const { start: yesterday } = appDayWindow(new Date(today.getTime() - 1))
111 +
112 +
await db.sleepRecord.create({
113 +
data: { userId: user.id, date: yesterday, average: 7 },
114 +
})
115 +
116 +
await processDailySleep({
117 +
userId: user.id,
118 +
periodStart: ref.toISOString(),
119 +
periodEnd: ref.toISOString(),
120 +
})
121 +
122 +
const insight = await db.insight.findFirst({
123 +
where: { userId: user.id, type: 'DAILY_SLEEP' },
124 +
})
125 +
126 +
expect(insight).toBeNull()
127 +
})
128 +
})