eletrotupi / tcc/ api/tests/insights/index.test.ts5f128778e17bb00138230b7adc25bc7e69505c1f
5.1 KB | 166 lines Raw
1
import { describe, it, expect, afterAll, beforeEach } from 'vitest'
2
import request from 'supertest'
3
import { createApp } from '@app/createApp'
4
import { InsightPeriod, InsightType } from '@prisma/client'
5
import {
6
  buildPrisma,
7
  cleanupTestDb,
8
  createTestUser,
9
  generateTestToken,
10
} from '../test-helper'
11

12
const app = createApp()
13
const db = buildPrisma()
14

15
afterAll(() => db.$disconnect())
16
beforeEach(() => cleanupTestDb(db))
17

18
let seedCounter = 0
19
// The schema is unique on (userId, type, periodStart), so give every seeded
20
// row a distinct periodStart unless the test asks for a specific one
21
function uniquePeriodStart() {
22
  seedCounter += 1
23
  return new Date(Date.UTC(2026, 0, 1) + seedCounter * 60 * 1000)
24
}
25

26
async function seedInsight(
27
  userId: number,
28
  overrides: {
29
    type?: InsightType
30
    period?: InsightPeriod
31
    periodStart?: Date
32
    generatedAt?: Date
33
    title?: string
34
  } = {}
35
) {
36
  seedCounter += 1
37

38
  return db.insight.create({
39
    data: {
40
      userId,
41
      type: overrides.type ?? InsightType.MOOD_TREND,
42
      period: overrides.period ?? InsightPeriod.WEEKLY,
43
      title: overrides.title ?? `Insight ${seedCounter}`,
44
      body: 'Generated by the insight worker',
45
      periodStart: overrides.periodStart ?? uniquePeriodStart(),
46
      periodEnd: new Date(),
47
      generatedAt: overrides.generatedAt,
48
    },
49
  })
50
}
51

52
describe('GET /insights', () => {
53
  it('returns only the authenticated user\'s insights, newest first', async () => {
54
    const user = await createTestUser(db)
55
    const other = await createTestUser(db, { email: 'other@example.com' })
56
    const token = generateTestToken(user.id, user.email)
57

58
    await seedInsight(user.id, { title: 'oldest', generatedAt: new Date('2026-01-01') })
59
    await seedInsight(user.id, { title: 'newest', generatedAt: new Date('2026-03-01') })
60
    await seedInsight(user.id, { title: 'middle', generatedAt: new Date('2026-02-01') })
61
    await seedInsight(other.id, { title: 'not-mine' })
62

63
    const res = await request(app)
64
      .get('/insights')
65
      .set('Authorization', `Bearer ${token}`)
66

67
    expect(res.status).toBe(200)
68
    expect(res.body.insights).toHaveLength(3)
69
    expect(res.body.insights.every((i: any) => i.userId === user.id)).toBe(true)
70
    expect(res.body.insights.map((i: any) => i.title)).toEqual([
71
      'newest',
72
      'middle',
73
      'oldest',
74
    ])
75
  })
76

77
  it('returns an empty list when the user has no insights', async () => {
78
    const user = await createTestUser(db)
79
    const token = generateTestToken(user.id, user.email)
80

81
    const res = await request(app)
82
      .get('/insights')
83
      .set('Authorization', `Bearer ${token}`)
84

85
    expect(res.status).toBe(200)
86
    expect(res.body.insights).toEqual([])
87
  })
88

89
  it('filters by type', async () => {
90
    const user = await createTestUser(db)
91
    const token = generateTestToken(user.id, user.email)
92

93
    await seedInsight(user.id, { type: InsightType.MOOD_TREND })
94
    await seedInsight(user.id, { type: InsightType.STREAK })
95
    await seedInsight(user.id, { type: InsightType.STREAK })
96

97
    const res = await request(app)
98
      .get('/insights?type=STREAK')
99
      .set('Authorization', `Bearer ${token}`)
100

101
    expect(res.status).toBe(200)
102
    expect(res.body.insights).toHaveLength(2)
103
    expect(res.body.insights.every((i: any) => i.type === 'STREAK')).toBe(true)
104
  })
105

106
  it('filters by period', async () => {
107
    const user = await createTestUser(db)
108
    const token = generateTestToken(user.id, user.email)
109

110
    await seedInsight(user.id, { period: InsightPeriod.DAILY })
111
    await seedInsight(user.id, { period: InsightPeriod.MONTHLY })
112
    await seedInsight(user.id, { period: InsightPeriod.MONTHLY })
113

114
    const res = await request(app)
115
      .get('/insights?period=MONTHLY')
116
      .set('Authorization', `Bearer ${token}`)
117

118
    expect(res.status).toBe(200)
119
    expect(res.body.insights).toHaveLength(2)
120
    expect(res.body.insights.every((i: any) => i.period === 'MONTHLY')).toBe(true)
121
  })
122

123
  it('caps the result set at the requested limit', async () => {
124
    const user = await createTestUser(db)
125
    const token = generateTestToken(user.id, user.email)
126

127
    await seedInsight(user.id)
128
    await seedInsight(user.id)
129
    await seedInsight(user.id)
130

131
    const res = await request(app)
132
      .get('/insights?limit=2')
133
      .set('Authorization', `Bearer ${token}`)
134

135
    expect(res.status).toBe(200)
136
    expect(res.body.insights).toHaveLength(2)
137
  })
138

139
  it('returns 400 for an unknown type', async () => {
140
    const user = await createTestUser(db)
141
    const token = generateTestToken(user.id, user.email)
142

143
    const res = await request(app)
144
      .get('/insights?type=NOPE')
145
      .set('Authorization', `Bearer ${token}`)
146

147
    expect(res.status).toBe(400)
148
    expect(res.body.fields).toBeTruthy()
149
  })
150

151
  it('returns 400 for a non-positive limit', async () => {
152
    const user = await createTestUser(db)
153
    const token = generateTestToken(user.id, user.email)
154

155
    const res = await request(app)
156
      .get('/insights?limit=0')
157
      .set('Authorization', `Bearer ${token}`)
158

159
    expect(res.status).toBe(400)
160
  })
161

162
  it('returns 401 without a token', async () => {
163
    const res = await request(app).get('/insights')
164
    expect(res.status).toBe(401)
165
  })
166
})