Skip to content

Commit 7da5c28

Browse files
committed
Fixed/changed code according to review comments
1 parent b6a3bff commit 7da5c28

File tree

9 files changed

+163
-126
lines changed

9 files changed

+163
-126
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,6 @@ yarn-error.log*
3939
# typescript
4040
*.tsbuildinfo
4141
next-env.d.ts
42+
43+
# vscode
44+
.vscode/

package-lock.json

+34-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"react-dom": "^19.0.0",
2525
"react-hot-toast": "^2.4.1",
2626
"react-icons": "^5.4.0",
27+
"uuid": "^11.0.5",
2728
"zod": "^3.24.1",
2829
"zod-form-data": "^2.0.5"
2930
},
@@ -47,6 +48,7 @@
4748
"next-test-api-route-handler": "^4.0.14",
4849
"open-html": "^1.0.1",
4950
"postcss": "^8",
51+
"prettier": "^3.4.2",
5052
"prisma": "^6.2.1",
5153
"tailwindcss": "^3.4.1",
5254
"ts-jest": "^29.2.5",

src/app/api/unclaimedItems/route.test.ts

+40-47
Original file line numberDiff line numberDiff line change
@@ -3,59 +3,52 @@ import * as appHandler from "./route";
33
import { expect, test } from "@jest/globals";
44
// import { authMock } from "@/test/authMock";
55
import { validateSession, invalidateSession } from "@/test/util/authMockUtils";
6-
import { manyUnclaimedItems } from "@/test/util/dbMockUtils";
6+
import { fillDbMockWithManyUnclaimedItems } from "@/test/util/dbMockUtils";
77
import { dbMock } from "@/test/dbMock";
8-
import { UnclaimedItem } from "@prisma/client";
98

109
test("Should return 401 for invalid session", async () => {
11-
await testApiHandler({
12-
appHandler,
13-
async test({ fetch }) {
14-
invalidateSession();
15-
16-
const res = await fetch({ method: "GET" });
17-
await expect(res.status).toBe(401);
18-
const json = await res.json();
19-
await expect(json).toEqual({ message: "Session required" });
20-
}
21-
})
10+
await testApiHandler({
11+
appHandler,
12+
async test({ fetch }) {
13+
invalidateSession();
14+
15+
const res = await fetch({ method: "GET" });
16+
await expect(res.status).toBe(401);
17+
const json = await res.json();
18+
await expect(json).toEqual({ message: "Session required" });
19+
},
20+
});
2221
});
2322

2423
test("Should return 200 for valid session", async () => {
25-
await testApiHandler({
26-
appHandler,
27-
async test({ fetch }) {
28-
validateSession();
29-
30-
const res = await fetch({ method: "GET" });
31-
await expect(res.status).toBe(200);
32-
}
33-
})
24+
await testApiHandler({
25+
appHandler,
26+
async test({ fetch }) {
27+
validateSession("ADMIN");
28+
29+
const res = await fetch({ method: "GET" });
30+
await expect(res.status).toBe(200);
31+
},
32+
});
3433
});
3534

3635
test("Should give correct database queries", async () => {
37-
await testApiHandler({
38-
appHandler,
39-
async test({ fetch }) {
40-
// Create mock data
41-
dbMock.unclaimedItem.create({ data: { id: 1, name: "Test Item 1", quantity: 1, expirationDate: new Date() } });
42-
const items: UnclaimedItem[] = await manyUnclaimedItems(3);
43-
// Convert items expirationDate to ISO-8601 string
44-
const expectedRet = items.map(item => {
45-
return { ...item, expirationDate: item.expirationDate?.toISOString() || "" }
46-
});
47-
48-
validateSession();
49-
50-
const res = await fetch({ method: "GET" });
51-
await expect(res.status).toBe(200);
52-
53-
// Check that the database was queried
54-
await expect(dbMock.unclaimedItem.findMany).toHaveBeenCalledTimes(1);
55-
56-
// Check that the response json was written correctly
57-
const json = await res.json();
58-
await expect(json["unclaimedItems"]).toEqual(expectedRet);
59-
}
60-
})
61-
});
36+
await testApiHandler({
37+
appHandler,
38+
async test({ fetch }) {
39+
await fillDbMockWithManyUnclaimedItems(3);
40+
validateSession("ADMIN");
41+
42+
const res = await fetch({ method: "GET" });
43+
await expect(res.status).toBe(200);
44+
45+
// Check that the response json was written correctly
46+
const expectedRet = {
47+
numberOfItems: 3,
48+
unclaimedItems: await dbMock.unclaimedItem.findMany(),
49+
};
50+
const json = await res.json();
51+
await expect(json).toEqual(JSON.parse(JSON.stringify(expectedRet))); // Needed to stringify and parse because the expiration field would cause an error because Date != ISOstring
52+
},
53+
});
54+
});

src/app/api/unclaimedItems/route.ts

+34-42
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,41 @@
11
// import { NextRequest } from 'next/server';
2-
import { auth } from '@/auth';
3-
import { internalError, authenticationError, successResponse } from '@/util/responses';
4-
import { db } from '@/db';
2+
import { auth } from "@/auth";
3+
import { authenticationError } from "@/util/responses";
4+
import { db } from "@/db";
5+
import { NextResponse } from "next/server";
6+
7+
// Response for GET /api/unclaimedItems
8+
interface UnclaimedItemsResponse {
9+
numberOfItems: number | 0;
10+
unclaimedItems:
11+
| {
12+
id: number;
13+
name: string;
14+
quantity: number;
15+
expirationDate: Date | null;
16+
}[]
17+
| [];
18+
}
519

620
/**
721
* Handles GET requests to retrieve unclaimed items from the unclaimedItem database.
8-
*
9-
* @returns {NextResponse} On success, returns a status of 200 and a JSON object containing an array of unclaimed items.
10-
* @returns {NextResponse} On authentication error, returns a status of 401 and a JSON object containing an error message.
11-
* @returns {NextResponse} On error, returns a status of 500 and a JSON object containing an error message.
12-
*
13-
* @example
14-
* // Example response:
15-
* {
16-
* "unclaimedItems": [
17-
* {
18-
* "id": 1,
19-
* "name": "Banana",
20-
* "quantity": 10,
21-
* "expirationDate": "2025-01-22T14:45:43.241Z"
22-
* },
23-
* {
24-
* "id": 2,
25-
* "name": "Apple",
26-
* "quantity": 100,
27-
* "expirationDate": "2025-01-22T14:45:43.243Z"
28-
* }
29-
* ]
30-
* }
22+
* @returns 401 if the session is invalid
23+
* @returns 500 if an unknown error occurs
24+
* @returns 200 and a json response with the unclaimed items
3125
*/
3226
export async function GET() {
33-
try {
34-
const session = await auth();
35-
if (!session) return authenticationError('Session required');
36-
37-
// Get all unclaimed items
38-
const unclaimedItems = await db.unclaimedItem.findMany();
27+
const session = await auth();
28+
if (!session) return authenticationError("Session required");
29+
30+
if (!session?.user) {
31+
return authenticationError("User not found");
32+
}
33+
34+
// Get all unclaimed items
35+
const unclaimedItems = await db.unclaimedItem.findMany();
3936

40-
return successResponse({ unclaimedItems });
41-
} catch (err: unknown) {
42-
if (err instanceof Error) {
43-
console.error(err.message);
44-
} else {
45-
console.error('An unknown error occurred');
46-
}
47-
return internalError();
48-
}
49-
}
37+
return NextResponse.json({
38+
numberOfItems: unclaimedItems?.length, // ?.length for when unclaimedItems is undefined
39+
unclaimedItems: unclaimedItems,
40+
} as UnclaimedItemsResponse);
41+
}

src/test/authMock.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ beforeEach(() => {
1313
mockReset(authMock);
1414
});
1515

16-
export const authMock = auth as unknown as jest.Mock<() => Session | null>;
16+
export const authMock = auth as unknown as jest.Mock<() => Session | null>;

src/test/util/authMockUtils.ts

+24-19
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,33 @@
11
import { authMock } from "@/test/authMock";
22
import { UserType } from "@prisma/client";
3+
import { v4 as uuidv4 } from "uuid";
34

45
// Helper util methods for testing
56

67
/**
78
* Helper method for invalidating a session
89
*/
910
export async function invalidateSession() {
10-
authMock.mockReturnValueOnce(null);
11-
}
12-
13-
/**
14-
* Helper method for validating a session
15-
* @param user Optional, default is { id: "1234", type: "ADMIN" }
16-
* @param expires Optional, default is ""
17-
*/
18-
export async function validateSession(
19-
user: { id: string, type: UserType } = { id: "1234", type: "ADMIN" },
20-
expires: string = "") {
21-
authMock.mockReturnValueOnce({
22-
user: {
23-
id: user.id,
24-
type: user.type,
25-
},
26-
expires: expires,
27-
});
28-
}
11+
authMock.mockReturnValueOnce(null);
12+
}
13+
14+
/**
15+
* Helper method for validating a session
16+
* @param user Optional, default is { id: "1234", type: "ADMIN" }
17+
* @param expires Optional, default is a day from now
18+
* @returns A session object with the user and expires fields
19+
*/
20+
export async function validateSession(
21+
userType: UserType,
22+
expires: Date = new Date(Date.now() + 86400000)
23+
) {
24+
const createdSession = {
25+
user: {
26+
id: uuidv4(),
27+
type: userType,
28+
},
29+
expires: expires.toISOString(),
30+
};
31+
authMock.mockReturnValueOnce(createdSession);
32+
return createdSession;
33+
}

src/test/util/dbMockUtils.ts

+25-10
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
11
import { dbMock } from "@/test/dbMock";
22
import { UnclaimedItem } from "@prisma/client";
33

4-
54
// Helper util methods for testing
65

7-
86
/**
97
* Helper method for creating unclaimed items
108
* Defines the db.unclaimedItem.findMany mock
119
* @param num Number of items to create
1210
* @returns Array of UnclaimedItems returned by db.unclaimedItem.findMany mock
1311
*/
14-
export async function manyUnclaimedItems(num: number): Promise<UnclaimedItem[]> {
15-
const items: UnclaimedItem[] = [];
16-
for (let i = 0; i < num; i++) {
17-
items.push({id: i, name: `Test Item ${i}`, quantity: i, expirationDate: new Date()});
18-
}
19-
dbMock.unclaimedItem.findMany.mockResolvedValue(items);
20-
return items;
21-
}
12+
export async function fillDbMockWithManyUnclaimedItems(
13+
num: number
14+
): Promise<UnclaimedItem[]> {
15+
const items: UnclaimedItem[] = [];
16+
const generatedIds = new Set<number>();
17+
18+
for (let i = 0; i < num; i++) {
19+
let id: number;
20+
do {
21+
id = Math.floor(Math.random() * 1000000); // Generate a random integer ID
22+
} while (generatedIds.has(id)); // Ensure the ID is unique
23+
24+
generatedIds.add(id);
25+
26+
items.push({
27+
id: id,
28+
name: `Test Item ${id}`,
29+
quantity: Math.floor(Math.random() * 1000),
30+
expirationDate: new Date(Date.now() + Math.floor(Math.random() * 10000)),
31+
});
32+
}
33+
34+
dbMock.unclaimedItem.findMany.mockResolvedValue(items);
35+
return items;
36+
}

src/util/responses.ts

-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,3 @@ export function internalError() {
2727
export function ok() {
2828
return NextResponse.json({ message: "OK" }, { status: 200 });
2929
}
30-
31-
export function successResponse<T>(data: T) {
32-
return NextResponse.json(data, { status: 200 });
33-
}

0 commit comments

Comments
 (0)