Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 });
}
}

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.

There should be a POST function

/**
* 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