Skip to content
90 changes: 90 additions & 0 deletions app/api/routes-f/__tests__/delay.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* Routes-F delay endpoint tests.
*/

jest.mock("next/server", () => ({
NextResponse: {
json: (body: unknown, init?: ResponseInit) =>
new Response(JSON.stringify(body), {
...init,
headers: { "Content-Type": "application/json" },
}),
},
}));

import { GET } from "../test/delay/route";

const makeRequest = (search = "") =>
new Request(`http://localhost/api/routes-f/test/delay${search}`);

describe("GET /api/routes-f/test/delay", () => {
it("returns 400 when ms parameter is missing", async () => {
const res = await GET(makeRequest());
expect(res.status).toBe(400);
const body = await res.json();
expect(body.error).toBe("Missing required parameter: ms");
expect(body.minDelay).toBe(0);
expect(body.maxDelay).toBe(10000);
});

it("returns 400 for non-numeric ms value", async () => {
const res = await GET(makeRequest("?ms=abc"));
expect(res.status).toBe(400);
const body = await res.json();
expect(body.error).toBe("Invalid delay value: must be a number");
expect(body.provided).toBe("abc");
});

it("returns 400 for delay below minimum (negative)", async () => {
const res = await GET(makeRequest("?ms=-1"));
expect(res.status).toBe(400);
const body = await res.json();
expect(body.error).toContain("Delay out of range");
expect(body.provided).toBe(-1);
});

it("returns 400 for delay above maximum", async () => {
const res = await GET(makeRequest("?ms=10001"));
expect(res.status).toBe(400);
const body = await res.json();
expect(body.error).toContain("Delay out of range");
expect(body.provided).toBe(10001);
});

it("accepts minimum delay (0ms)", async () => {
const res = await GET(makeRequest("?ms=0"));
expect(res.status).toBe(200);
const body = await res.json();
expect(body.requestedDelay).toBe(0);
expect(typeof body.actualDelay).toBe("number");
expect(body.minDelay).toBe(0);
expect(body.maxDelay).toBe(10000);
});

it("accepts maximum delay (10000ms)", async () => {
const res = await GET(makeRequest("?ms=10000"));
expect(res.status).toBe(200);
const body = await res.json();
expect(body.requestedDelay).toBe(10000);
}, 15000);

it("returns requestedDelay and actualDelay fields", async () => {
const res = await GET(makeRequest("?ms=50"));
expect(res.status).toBe(200);
const body = await res.json();
expect(body).toHaveProperty("requestedDelay");
expect(body).toHaveProperty("actualDelay");
expect(body.requestedDelay).toBe(50);
expect(body.actualDelay).toBeGreaterThanOrEqual(50);
});

it("actual delay is approximately equal to requested delay", async () => {
const requestedMs = 100;
const res = await GET(makeRequest(`?ms=${requestedMs}`));
expect(res.status).toBe(200);
const body = await res.json();
// Allow some tolerance for timing variance (within 50ms)
expect(body.actualDelay).toBeGreaterThanOrEqual(requestedMs);
expect(body.actualDelay).toBeLessThan(requestedMs + 50);
});
});
70 changes: 70 additions & 0 deletions app/api/routes-f/test/delay/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { NextResponse } from "next/server";

const MIN_DELAY_MS = 0;
const MAX_DELAY_MS = 10000;

export async function GET(req: Request) {
const url = new URL(req.url);
const msParam = url.searchParams.get("ms");

if (msParam === null) {
return NextResponse.json(
{
error: "Missing required parameter: ms",
minDelay: MIN_DELAY_MS,
maxDelay: MAX_DELAY_MS,
},
{ status: 400 }
);
}

const requestedDelay = parseInt(msParam, 10);

if (isNaN(requestedDelay)) {
return NextResponse.json(
{
error: "Invalid delay value: must be a number",
provided: msParam,
minDelay: MIN_DELAY_MS,
maxDelay: MAX_DELAY_MS,
},
{ status: 400 }
);
}

if (requestedDelay < MIN_DELAY_MS || requestedDelay > MAX_DELAY_MS) {
return NextResponse.json(
{
error: `Delay out of range: must be between ${MIN_DELAY_MS} and ${MAX_DELAY_MS}`,
provided: requestedDelay,
minDelay: MIN_DELAY_MS,
maxDelay: MAX_DELAY_MS,
},
{ status: 400 }
);
}

const safeDelay = Math.min(
Math.max(requestedDelay, MIN_DELAY_MS),
MAX_DELAY_MS
);

const startTime = Date.now();
await new Promise(resolve => setTimeout(resolve, safeDelay));
const actualDelay = Date.now() - startTime;

return NextResponse.json(
{
requestedDelay,
actualDelay,
minDelay: MIN_DELAY_MS,
maxDelay: MAX_DELAY_MS,
},
{
status: 200,
headers: {
"Cache-Control": "no-store",
},
}
);
}
Loading
Loading