forked from jordanlambrecht/tracker-tracker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreset-stats.test.ts
More file actions
115 lines (96 loc) · 3.58 KB
/
Copy pathreset-stats.test.ts
File metadata and controls
115 lines (96 loc) · 3.58 KB
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
// src/app/api/settings/reset-stats/reset-stats.test.ts
import { NextResponse } from "next/server"
import { beforeEach, describe, expect, it, vi } from "vitest"
import { authenticate, parseJsonBody } from "@/lib/api-helpers"
import { verifyPassword } from "@/lib/auth"
import { db } from "@/lib/db"
import { POST } from "./route"
vi.mock("@/lib/api-helpers", () => ({
authenticate: vi.fn(),
parseJsonBody: vi.fn(),
}))
vi.mock("@/lib/auth", () => ({
verifyPassword: vi.fn(),
}))
vi.mock("@/lib/db", () => ({
db: {
select: vi.fn(),
delete: vi.fn(),
update: vi.fn(),
transaction: vi.fn(),
},
}))
vi.mock("@/lib/db/schema", () => ({
appSettings: {},
trackerSnapshots: {},
clientSnapshots: {},
trackers: {},
}))
const VALID_KEY = "abcd1234".repeat(8)
function makeRequest() {
return new Request("http://localhost/api/settings/reset-stats", { method: "POST" })
}
function mockSelectSettings(settings: Record<string, unknown>) {
;(db.select as ReturnType<typeof vi.fn>).mockReturnValue({
from: vi.fn().mockReturnValue({
limit: vi.fn().mockResolvedValue([settings]),
}),
})
}
describe("POST /api/settings/reset-stats", () => {
beforeEach(() => {
vi.restoreAllMocks()
})
it("returns 401 when not authenticated", async () => {
;(authenticate as ReturnType<typeof vi.fn>).mockResolvedValue(
NextResponse.json({ error: "Unauthorized" }, { status: 401 })
)
const res = await POST(makeRequest())
expect(res.status).toBe(401)
})
it("returns 400 when password is missing", async () => {
;(authenticate as ReturnType<typeof vi.fn>).mockResolvedValue({ encryptionKey: VALID_KEY })
;(parseJsonBody as ReturnType<typeof vi.fn>).mockResolvedValue({})
const res = await POST(makeRequest())
expect(res.status).toBe(400)
})
it("returns 401 when password is wrong", async () => {
;(authenticate as ReturnType<typeof vi.fn>).mockResolvedValue({ encryptionKey: VALID_KEY })
;(parseJsonBody as ReturnType<typeof vi.fn>).mockResolvedValue({ password: "wrong" })
mockSelectSettings({ id: 1, passwordHash: "hash" })
;(verifyPassword as ReturnType<typeof vi.fn>).mockResolvedValue(false)
const res = await POST(makeRequest())
expect(res.status).toBe(401)
})
it("deletes all snapshots and resets tracker poll state with valid password", async () => {
;(authenticate as ReturnType<typeof vi.fn>).mockResolvedValue({ encryptionKey: VALID_KEY })
;(parseJsonBody as ReturnType<typeof vi.fn>).mockResolvedValue({ password: "correct" })
mockSelectSettings({ id: 1, passwordHash: "hash" })
;(verifyPassword as ReturnType<typeof vi.fn>).mockResolvedValue(true)
// Mock the transaction to invoke the callback with a tx mock
const txDeleteCalls: unknown[] = []
const txSetCalls: unknown[] = []
const txMock = {
delete: vi.fn().mockImplementation((table) => {
txDeleteCalls.push(table)
return Promise.resolve(undefined)
}),
update: vi.fn().mockReturnValue({
set: vi.fn().mockImplementation((data) => {
txSetCalls.push(data)
return Promise.resolve(undefined)
}),
}),
}
;(db.transaction as ReturnType<typeof vi.fn>).mockImplementation(
async (fn: (tx: unknown) => Promise<unknown>) => fn(txMock)
)
const res = await POST(makeRequest())
const data = await res.json()
expect(res.status).toBe(200)
expect(data.success).toBe(true)
expect(txMock.delete).toHaveBeenCalledTimes(2)
expect(txMock.update).toHaveBeenCalledTimes(1)
expect(txSetCalls[0]).toEqual({ lastPolledAt: null, lastError: null })
})
})