Skip to content

Commit dca05c0

Browse files
authored
Merge pull request #12 from GTBitsOfGood/davidgu/ListUnclaimedItemsQuery
Davidgu/list unclaimed items query
2 parents f322047 + ee3e32c commit dca05c0

File tree

7 files changed

+184
-3
lines changed

7 files changed

+184
-3
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

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

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"next-test-api-route-handler": "^4.0.14",
5151
"open-html": "^1.0.1",
5252
"postcss": "^8",
53+
"prettier": "^3.4.2",
5354
"prisma": "^6.2.1",
5455
"tailwindcss": "^3.4.1",
5556
"ts-jest": "^29.2.5",
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { testApiHandler } from "next-test-api-route-handler";
2+
import * as appHandler from "./route";
3+
import { expect, test } from "@jest/globals";
4+
// import { authMock } from "@/test/authMock";
5+
import { validateSession, invalidateSession } from "@/test/util/authMockUtils";
6+
import { fillDbMockWithManyUnclaimedItems } from "@/test/util/dbMockUtils";
7+
import { dbMock } from "@/test/dbMock";
8+
9+
test("Should return 401 for invalid session", async () => {
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+
});
21+
});
22+
23+
test("Should return 200 for valid session", async () => {
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+
});
33+
});
34+
35+
test("Should give correct database queries", async () => {
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+
unclaimedItems: await dbMock.unclaimedItem.findMany(),
48+
};
49+
const json = await res.json();
50+
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
51+
},
52+
});
53+
});

src/app/api/unclaimedItems/route.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// import { NextRequest } from 'next/server';
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+
unclaimedItems: {
10+
id: number;
11+
name: string;
12+
quantity: number;
13+
expirationDate: Date | null;
14+
}[];
15+
}
16+
17+
/**
18+
* Handles GET requests to retrieve unclaimed items from the unclaimedItem database.
19+
* @returns 401 if the session is invalid
20+
* @returns 500 if an unknown error occurs
21+
* @returns 200 and a json response with the unclaimed items
22+
*/
23+
export async function GET() {
24+
const session = await auth();
25+
if (!session) return authenticationError("Session required");
26+
27+
if (!session?.user) {
28+
return authenticationError("User not found");
29+
}
30+
31+
// Get all unclaimed items
32+
const unclaimedItems = await db.unclaimedItem.findMany();
33+
34+
return NextResponse.json({
35+
unclaimedItems: unclaimedItems,
36+
} as UnclaimedItemsResponse);
37+
}

src/test/util/authMockUtils.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { authMock } from "@/test/authMock";
2+
import { UserType } from "@prisma/client";
3+
import { v4 as uuidv4 } from "uuid";
4+
5+
// Helper util methods for testing
6+
7+
/**
8+
* Helper method for invalidating a session
9+
*/
10+
export async function invalidateSession() {
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

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { dbMock } from "@/test/dbMock";
2+
import { UnclaimedItem } from "@prisma/client";
3+
4+
// Helper util methods for testing
5+
6+
/**
7+
* Helper method for creating unclaimed items
8+
* Defines the db.unclaimedItem.findMany mock
9+
* @param num Number of items to create
10+
* @returns Array of UnclaimedItems returned by db.unclaimedItem.findMany mock
11+
*/
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+
}

0 commit comments

Comments
 (0)