Skip to content

Commit 25bf07e

Browse files
Merge pull request #18 from Nandgopal-R/test/forms
add unit tests for forms
2 parents 9e844eb + dc25869 commit 25bf07e

5 files changed

Lines changed: 270 additions & 1 deletion

File tree

‎.husky/pre-commit‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
#!/usr/bin/env sh
22
npx --no -- lint-staged
3+
bun run test

‎bunfig.toml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[test]
2+
# Enable coverage by default when running 'bun test'
3+
coverage = true
4+
5+
# Exclude test files from coverage reports to get more accurate metrics
6+
coverageSkipTestFiles = true
7+
8+
# Set a coverage threshold (0.0 to 1.0)
9+
# This will cause 'bun test' to exit with a non-zero code if coverage is below this value
10+
coverageThreshold = 0.5
11+
12+
# Specify reporters. "text" is for CLI, "lcov" generates an lcov.info file
13+
coverageReporter = ["text", "lcov"]

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"dev": "bun --watch src/index.ts",
88
"check": "biome check .",
99
"typecheck": "tsc --noEmit",
10+
"test": "bun test",
1011
"build": "bun build src/index.ts --outdir dist --target bun --minify",
1112
"prepare": "husky"
1213
},

‎src/api/auth/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { prisma } from "../../db/prisma";
55

66
const transporter = nodemailer.createTransport({
77
host: process.env.SMTP_HOST,
8-
port: parseInt(process.env.SMTP_PORT || "587"),
8+
port: Number(process.env.SMTP_PORT || "587"),
99
secure: false, // true for 465, false for other ports
1010
auth: {
1111
user: process.env.SMTP_USER,

‎src/test/forms.test.ts‎

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
import { beforeEach, describe, expect, it, mock } from "bun:test";
2+
3+
// ---------- MOCK PRISMA ----------
4+
const findManyMock = mock();
5+
const createMock = mock();
6+
const findFirstMock = mock();
7+
const updateMock = mock();
8+
const deleteManyMock = mock();
9+
10+
mock.module("../db/prisma", () => ({
11+
prisma: {
12+
form: {
13+
findMany: findManyMock,
14+
create: createMock,
15+
findFirst: findFirstMock,
16+
update: updateMock,
17+
deleteMany: deleteManyMock,
18+
},
19+
},
20+
}));
21+
22+
// ---------- MOCK LOGGER ----------
23+
const mockInfo = mock();
24+
const mockWarn = mock();
25+
26+
mock.module("../logger", () => ({
27+
logger: {
28+
info: mockInfo,
29+
warn: mockWarn,
30+
error: mock(),
31+
},
32+
}));
33+
34+
// IMPORT AFTER MOCKS
35+
const { getAllForms, createForm, getFormById, updateForm, deleteForm } =
36+
await import("../api/forms/controller");
37+
38+
describe("Forms Controller Tests", () => {
39+
beforeEach(() => {
40+
findManyMock.mockReset();
41+
createMock.mockReset();
42+
findFirstMock.mockReset();
43+
updateMock.mockReset();
44+
deleteManyMock.mockReset();
45+
mockInfo.mockReset();
46+
mockWarn.mockReset();
47+
});
48+
49+
const user = { id: "user1" };
50+
51+
// ===== getAllForms =====
52+
53+
it("getAllForms → success", async () => {
54+
findManyMock.mockResolvedValue([
55+
{ id: "1", title: "A", isPublished: true, createdAt: new Date() },
56+
]);
57+
58+
const res = await getAllForms({ user } as any);
59+
60+
expect(res.success).toBe(true);
61+
expect(res.data.length).toBe(1);
62+
});
63+
64+
it("getAllForms → empty", async () => {
65+
findManyMock.mockResolvedValue([]);
66+
67+
const res = await getAllForms({ user } as any);
68+
69+
expect(res.message).toBe("No forms found");
70+
expect(res.data).toEqual([]);
71+
});
72+
73+
it("getAllForms → DB error", async () => {
74+
findManyMock.mockRejectedValue(new Error("DB fail"));
75+
76+
expect(getAllForms({ user } as any)).rejects.toThrow();
77+
});
78+
79+
// ===== createForm =====
80+
81+
it("createForm → success", async () => {
82+
createMock.mockResolvedValue({ id: "1", title: "New" });
83+
84+
const res = await createForm({
85+
user,
86+
body: { title: "New", description: "desc" },
87+
} as any);
88+
89+
expect(res.success).toBe(true);
90+
});
91+
92+
it("createForm → called", async () => {
93+
createMock.mockResolvedValue({ id: "1" });
94+
95+
await createForm({
96+
user,
97+
body: { title: "T", description: "D" },
98+
} as any);
99+
100+
expect(createMock).toHaveBeenCalled();
101+
});
102+
103+
it("createForm → DB error", async () => {
104+
createMock.mockRejectedValue(new Error("DB crash"));
105+
106+
expect(
107+
createForm({
108+
user,
109+
body: { title: "X", description: "Y" },
110+
} as any),
111+
).rejects.toThrow();
112+
});
113+
114+
// ===== getFormById =====
115+
116+
it("getFormById → found", async () => {
117+
findFirstMock.mockResolvedValue({ id: "1" });
118+
119+
const set: any = {};
120+
const res = await getFormById({
121+
user,
122+
params: { id: "1" },
123+
set,
124+
} as any);
125+
126+
expect(res.success).toBe(true);
127+
});
128+
129+
it("getFormById → not found", async () => {
130+
findFirstMock.mockResolvedValue(null);
131+
132+
const set: any = {};
133+
const res = await getFormById({
134+
user,
135+
params: { id: "2" },
136+
set,
137+
} as any);
138+
139+
expect(res.success).toBe(false);
140+
expect(set.status).toBe(404);
141+
});
142+
143+
it("getFormById → DB error", async () => {
144+
findFirstMock.mockRejectedValue(new Error("DB error"));
145+
146+
const set: any = {};
147+
148+
expect(
149+
getFormById({
150+
user,
151+
params: { id: "1" },
152+
set,
153+
} as any),
154+
).rejects.toThrow();
155+
});
156+
157+
// ===== updateForm =====
158+
159+
it("updateForm → success", async () => {
160+
findFirstMock.mockResolvedValue({ id: "1" });
161+
162+
updateMock.mockResolvedValue({
163+
id: "1",
164+
title: "Updated",
165+
});
166+
167+
const set: any = {};
168+
169+
const res = await updateForm({
170+
user,
171+
params: { id: "1" },
172+
body: { title: "Updated", description: "D" },
173+
set,
174+
} as any);
175+
176+
expect(res.success).toBe(true);
177+
});
178+
179+
it("updateForm → not found", async () => {
180+
findFirstMock.mockResolvedValue(null);
181+
182+
const set: any = {};
183+
184+
const res = await updateForm({
185+
user,
186+
params: { id: "1" },
187+
body: { title: "T", description: "D" },
188+
set,
189+
} as any);
190+
191+
expect(res.success).toBe(false);
192+
expect(set.status).toBe(404);
193+
});
194+
195+
it("updateForm → DB error", async () => {
196+
findFirstMock.mockRejectedValue(new Error("DB fail"));
197+
198+
const set: any = {};
199+
200+
expect(
201+
updateForm({
202+
user,
203+
params: { id: "1" },
204+
body: { title: "T", description: "D" },
205+
set,
206+
} as any),
207+
).rejects.toThrow();
208+
});
209+
210+
// ===== deleteForm =====
211+
212+
it("deleteForm → success", async () => {
213+
deleteManyMock.mockResolvedValue({ count: 1 });
214+
215+
const set: any = {};
216+
217+
const res = await deleteForm({
218+
user,
219+
params: { id: "1" },
220+
set,
221+
} as any);
222+
223+
expect(res.success).toBe(true);
224+
});
225+
226+
it("deleteForm → not found", async () => {
227+
deleteManyMock.mockResolvedValue({ count: 0 });
228+
229+
const set: any = {};
230+
231+
const res = await deleteForm({
232+
user,
233+
params: { id: "1" },
234+
set,
235+
} as any);
236+
237+
expect(res.success).toBe(false);
238+
expect(set.status).toBe(404);
239+
});
240+
241+
it("deleteForm → DB error", async () => {
242+
deleteManyMock.mockRejectedValue(new Error("DB crash"));
243+
244+
const set: any = {};
245+
246+
expect(
247+
deleteForm({
248+
user,
249+
params: { id: "1" },
250+
set,
251+
} as any),
252+
).rejects.toThrow();
253+
});
254+
});

0 commit comments

Comments
 (0)