Skip to content
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
node_modules/
.next/
dist/
.env.local
.DS_Store
.next
playwright-report/
.env
178 changes: 178 additions & 0 deletions app/api/lab/[id]/__tests__/routes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { GET, PUT, DELETE } from "@/app/api/lab/[id]/route";
import {
getLab,
updateLab,
deleteLab
} from "@/services/labs/labs";

jest.mock("@/services/labs/labs", () => ({
getLab: jest.fn(),
updateLab: jest.fn(),
deleteLab: jest.fn(),
}));

const mockGetLab = jest.mocked(getLab);
const mockUpdateLab = jest.mocked(updateLab);
const mockDeleteLab = jest.mocked(deleteLab);

describe( "Lab API by ID", () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe("GET /api/lab/[id]", () => {
it("should return a lab by ID", async () => {
// Mock the getLab function to return a sample lab
const mockLab = { id: "1", name: "Lab 1", department: "Cognitive Science", createdAt: new Date("2024-08-01T00:00:00Z") };
mockGetLab.mockResolvedValue(mockLab);
// Create a mock request object
const response = await GET(new Request("http://localhost/api/lab/1"), { params: { id: "1" } });
// Assert that the response is correct
expect(response.status).toBe(200);
const data = await response.json();
expect(data).toEqual({ ...mockLab, createdAt: mockLab.createdAt.toISOString() });
});
it("should return 404 if lab is not found", async () => {
// Mock the getLab function to return null
mockGetLab.mockResolvedValue(null);
// Create a mock request object
const response = await GET(new Request("http://localhost/api/lab/999"), { params: { id: "999" } });
// Assert that the response is correct
expect(response.status).toBe(404);
const data = await response.json();
expect(data).toEqual({ message: "Lab not found" });
});
it("should return 400 for invalid ID", async () => {
// Create a mock request object with an invalid ID
const response = await GET(new Request("http://localhost/api/lab/"), { params: { id: "" } });
// Assert that the response is correct
expect(response.status).toBe(400);
const data = await response.json();
expect(data).toEqual({ message: "Invalid ID" });
});
it("should return 500 on server error", async () => {
// Mock the getLab function to throw an error
mockGetLab.mockRejectedValue(new Error("Database error"));
// Create a mock request object
const response = await GET(new Request("http://localhost/api/lab/1"), { params: { id: "1" } });
// Assert that the response is correct
expect(response.status).toBe(500);
const data = await response.json();
expect(data).toEqual({ message: "Internal server error" });
});
});
describe ("PUT /api/lab/[id]", () => {
it("should update a lab by ID", async () => {
// Mock the updateLab function to return an updated lab
const mockUpdatedLab = { id: "1", name: "Updated Lab", department: "Cognitive Science", createdAt: new Date("2024-08-01T00:00:00Z") };
mockUpdateLab.mockResolvedValue(mockUpdatedLab);
// Create a mock request object with valid data
const request = new Request("http://localhost/api/lab/1", {
method: "PUT",
body: JSON.stringify({ name: "Updated Lab", department: "Cognitive Science" }),
headers: { "Content-Type": "application/json" },
});
const response = await PUT(request, { params: { id: "1" } });
// Assert that the response is correct
expect(response.status).toBe(200);
const data = await response.json();
expect(data).toEqual({ ...mockUpdatedLab, createdAt: mockUpdatedLab.createdAt.toISOString() });
});
it("should return 404 if lab to update is not found", async () => {
// Mock the updateLab function to return null
mockUpdateLab.mockResolvedValue(null);
// Create a mock request object with valid data
const request = new Request("http://localhost/api/lab/999", {
method: "PUT",
body: JSON.stringify({ name: "Updated Lab", department: "Cognitive Science" }),
headers: { "Content-Type": "application/json" },
});
const response = await PUT(request, { params: { id: "999" } });
// Assert that the response is correct
expect(response.status).toBe(404);
const data = await response.json();
expect(data).toEqual({ message: "Lab not found" });
});
it("should return 400 for invalid ID", async () => {
// Create a mock request object with an invalid ID
const request = new Request("http://localhost/api/lab/", {
method: "PUT",
body: JSON.stringify({ name: "Updated Lab", department: "Cognitive Science" }),
headers: { "Content-Type": "application/json" },
});
const response = await PUT(request, { params: { id: "" } });
// Assert that the response is correct
expect(response.status).toBe(400);
const data = await response.json();
expect(data).toEqual({ message: "Invalid ID" });
});
it("should return 400 for invalid data", async () => {
// Create a mock request object with invalid data
const request = new Request("http://localhost/api/lab/1", {
method: "PUT",
body: JSON.stringify({ name: "", department: "" }),
headers: { "Content-Type": "application/json" },
});
const response = await PUT(request, { params: { id: "1" } });
// Assert that the response is correct
expect(response.status).toBe(400);
const data = await response.json();
expect(data).toEqual({ message: "Invalid data" });
});
it("should return 500 on server error", async () => {
// Mock the updateLab function to throw an error
mockUpdateLab.mockRejectedValue(new Error("Database error"));
// Create a mock request object with valid data
const request = new Request("http://localhost/api/lab/1", {
method: "PUT",
body: JSON.stringify({ name: "Updated Lab", department: "Cognitive Science" }),
headers: { "Content-Type": "application/json" },
});
const response = await PUT(request, { params: { id: "1" } });
// Assert that the response is correct
expect(response.status).toBe(500);
const data = await response.json();
expect(data).toEqual({ message: "Internal server error" });
});
});
describe("DELETE /api/lab/[id]", () => {
it("should delete a lab by ID", async () => {
// Mock the deleteLab function to return true
mockDeleteLab.mockResolvedValue(true);
// Create a mock request object
const response = await DELETE(new Request("http://localhost/api/lab/1"), { params: { id: "1" } });
// Assert that the response is correct
expect(response.status).toBe(200);
const data = await response.json();
expect(data).toEqual({ message: "Lab deleted successfully" });
});
it("should return 404 if lab to delete is not found", async () => {
// Mock the deleteLab function to return false
mockDeleteLab.mockResolvedValue(false);
// Create a mock request object
const response = await DELETE(new Request("http://localhost/api/lab/999"), { params: { id: "999" } });
// Assert that the response is correct
expect(response.status).toBe(404);
const data = await response.json();
expect(data).toEqual({ message: "Lab not found" });
});
it("should return 400 for invalid ID", async () => {
// Create a mock request object with an invalid ID
const response = await DELETE(new Request("http://localhost/api/lab/"), { params: { id: "" } });
// Assert that the response is correct
expect(response.status).toBe(400);
const data = await response.json();
expect(data).toEqual({ message: "Invalid ID" });
});
it("should return 500 on server error", async () => {
// Mock the deleteLab function to throw an error
mockDeleteLab.mockRejectedValue(new Error("Database error"));
// Create a mock request object
const response = await DELETE(new Request("http://localhost/api/lab/1"), { params: { id: "1" } });
// Assert that the response is correct
expect(response.status).toBe(500);
const data = await response.json();
expect(data).toEqual({ message: "Internal server error" });
});
});

});
124 changes: 124 additions & 0 deletions app/api/lab/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// /app/api/lab/[id]/route.ts

/**
* API Route for Lab Management by ID
* This file defines the API routes for managing lab entries in the inventory
* system based on their unique ID. It includes handlers for fetching,
* updating, and deleting lab entries by ID.
*/

'use server'

import { NextResponse } from "next/server";
import { z } from "zod";
import { getLab, updateLab, deleteLab } from "@/services/labs/labs";

type Params = { id: string };

// Define a Zod schema for validating lab updates, allowing for partial updates
const labUpdateSchema = z
.object({
name: z.string().min(1),
department: z.string().min(1),
createdAt: z.coerce.date(),
});

/**
* Get one lab entry by ID
* @param request request object
* @param context context object containing route parameters
* @return response with lab data or error message
*/
export async function GET(request: Request, context : { params: Params }) {
try {
const parsedParams = z.object({ id: z.string().min(1) })
.safeParse(context.params);
if (!parsedParams.success) {
return NextResponse.json({ message: "Invalid ID" },
{ status: 400 });
}
const item = await getLab(parsedParams.data.id);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good but let's update this to return a list of labs when we pass in user id. Because what if a user is a part of multiple labs?

if (!item) {
return NextResponse.json({ message: "Lab not found" },
{ status: 404 });
}
return NextResponse.json(item, { status: 200 });
} catch (err) {
console.error(err);
return NextResponse.json({ message: "Internal server error" },
{ status: 500 });
}
}

Comment thread
arnavjk007 marked this conversation as resolved.
/**
* Update a lab entry by ID
* @param request request object
* @param context context object containing route parameters
* @return response after updating lab entry
*/
export async function PUT(request: Request, context : { params: Params }) {
try {
// Validate the ID parameter and request body, then attempt to update
// the lab entry
const parsedParams = z.object({ id: z.string().min(1) })
.safeParse(context.params);
if (!parsedParams.success) {
return NextResponse.json({ message: "Invalid ID" },
{ status: 400 });
}
// Validate the request body against the lab update schema, allowing
// for partial updates
const parsedBody = labUpdateSchema.partial().safeParse(
await request.json());
if (!parsedBody.success) {
return NextResponse.json({ message: "Invalid data" },
{ status: 400 });
}
// Attempt to update the lab entry and return appropriate response
// based on the result
const updatedLab = await updateLab(parsedParams.data.id,
parsedBody.data);
if (!updatedLab) {
return NextResponse.json({ message: "Lab not found" },
{ status: 404 });
}
// Return the updated lab entry if the update was successful
return NextResponse.json(updatedLab, { status: 200 });
} catch (err) {
console.error(err);
return NextResponse.json({ message: "Internal server error" },
{ status: 500 });
}
}

/**
* Delete a lab entry by ID
* @param request request object
* @param context context object containing route parameters
* @return response after deleting the lab entry
*/
export async function DELETE(request: Request, context : { params: Params }) {
try {
// Validate the ID parameter and attempt to delete the lab entry
const parsedParams = z.object({ id: z.string().min(1) }).safeParse(
context.params);
if (!parsedParams.success) {
return NextResponse.json({ message: "Invalid ID" },
{ status: 400 });
}
// Attempt to delete the lab entry and return appropriate response
// based on the result
const deleted = await deleteLab(parsedParams.data.id);
if (!deleted) {
return NextResponse.json({ message: "Lab not found" },
{ status: 404 });
}
// Return a success message if the lab entry was deleted successfully
return NextResponse.json({ message: "Lab deleted successfully" },
{ status: 200 });
} catch (err) {
console.error(err);
return NextResponse.json({ message: "Internal server error" },
{ status: 500 });
}
}
Loading
Loading