Skip to content

Commit

Permalink
chore: tests migrated
Browse files Browse the repository at this point in the history
  • Loading branch information
LeeCheneler committed Feb 14, 2025
1 parent d73399e commit 28b090b
Show file tree
Hide file tree
Showing 51 changed files with 552 additions and 593 deletions.
20 changes: 15 additions & 5 deletions app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,21 @@ export class MageApp {
* @param req The request to handle
* @returns Response
*/
public async handler(req: Request) {
public handler: (req: Request) => Promise<Response> = this._handler.bind(
this,
);

/**
* Handle a request and return a response.
*
* @param req The request to handle
* @returns Response
*/
private async _handler(req: Request) {
const url = new URL(req.url);
const matchResult = this._router.match(url, req.method);

const context: MageContext = new MageContext({
const c: MageContext = new MageContext({
buildId: this.buildId,
req: new MageRequest(req, {
params: matchResult.params,
Expand Down Expand Up @@ -185,7 +195,7 @@ export class MageApp {
}

try {
await compose(middleware)(context);
await compose(middleware)(c);
} catch (error) {
console.error(error);

Expand All @@ -194,12 +204,12 @@ export class MageApp {
status = error.status;
}

context.text(
c.text(
statusText(status),
status,
);
}

return context.res;
return c.res;
}
}
4 changes: 2 additions & 2 deletions app/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { MageMiddleware } from "./router.ts";
* @returns MageMiddleware
*/
export const compose = (middleware: MageMiddleware[]) => {
return async function (context: MageContext): Promise<void> {
return async function (c: MageContext): Promise<void> {
let lastIndex = -1;

async function dispatch(i: number): Promise<void> {
Expand All @@ -24,7 +24,7 @@ export const compose = (middleware: MageMiddleware[]) => {
return;
}

await currentMiddleware(context, () => dispatch(i + 1));
await currentMiddleware(c, () => dispatch(i + 1));
}

await dispatch(0);
Expand Down
48 changes: 29 additions & 19 deletions app/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class MageContext {
private _buildId: string;
private _res: Response;
private _req: MageRequest;
private _data: Map<string, unknown>;

/**
* The unique identifier for the build
Expand All @@ -47,7 +48,7 @@ export class MageContext {
/**
* The current response
*/
public set res(res) {
public set res(res: Response) {
this._res = res;
}

Expand All @@ -62,6 +63,7 @@ export class MageContext {
this._buildId = args.buildId;
this._req = args.req;
this._res = new Response();
this._data = new Map<string, unknown>();
}

/**
Expand Down Expand Up @@ -100,14 +102,23 @@ export class MageContext {
);
}

/**
* Sends HTML response with the provided status code and body
*/
public html(body: string, status?: ContentfulStatus) {
this.text(body, status);

this._res.headers.set("Content-Type", "text/html; charset=UTF-8");
}

/**
* Sends an empty response with the provided status code
*
* @param status The status code of the response
*/
public empty(status?: ContentlessStatus) {
this._res = new Response(null, {
status,
status: status ?? 204,
headers: this._res.headers,
});
}
Expand All @@ -120,14 +131,15 @@ export class MageContext {
}

/**
* Redirects the request to the provided location with the specified redirect
* Redirects the request to the provided location with the specified redirect.
* Default to 307 temporary redirect.
*
* @param location The location to redirect to
* @param status The status code of the response
*/
public redirect(location: URL | string, status?: RedirectStatus) {
this._res = new Response(null, {
status,
status: status ?? 307,
headers: this._res.headers,
});

Expand Down Expand Up @@ -204,23 +216,21 @@ export class MageContext {
}

/**
* Get asset URL for the provided path with the build id embedded in the url for cache busting
* Get data stored via `set()` method
*
* @param path
* @param key The key of the data to get
*/
public asset(path: string): string {
const pathParts = path.split("/");

const filename = pathParts.pop();
if (filename?.includes(".")) {
const filenameParts = filename.split(".");
const extension = filenameParts.pop();
const basename = filenameParts.join(".");
pathParts.push(`${basename}-${this._buildId}.${extension}`);
} else {
pathParts.push(`${filename}-${this._buildId}`);
}
public get<TData = unknown>(key: string): TData {
return this._data.get(key) as TData;
}

return pathParts.join("/");
/**
* Set data to be stored in the context for the duraction of the request
*
* @param key The key of the data to set
* @param value The value of the data to set
*/
public set(key: string, value: unknown) {
this._data.set(key, value);
}
}
2 changes: 1 addition & 1 deletion app/mage-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class MageError extends Error {
) {
super(message, options);

this.name = "MageHTTPError";
this.name = "MageError";
this._status = status ?? 500;
}
}
16 changes: 11 additions & 5 deletions app/mage-request.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { memoize } from "@std/cache";
import type { StandardSchemaV1 } from "@standard-schema/spec";
import { MageError } from "./mage-error.ts";

type ValidationSource = "json" | "form" | "params" | "search-params";

Expand Down Expand Up @@ -135,12 +135,15 @@ export class MageRequest {
* Get a validation result, if it exists.
*
* @param source The source of the validation result
* @param schema The schema to validate the result against
*/
public valid(source: ValidationSource, schema: StandardSchemaV1) {
public valid<TResult>(source: ValidationSource): TResult {
const result = this._validationResults.get(source);

return schema["~standard"].validate(result);
if (!result) {
throw new MageError(`No validation result found for ${source}`);
}

return result as TResult;
}

/**
Expand All @@ -150,7 +153,10 @@ export class MageRequest {
* @param schema The schema to validate the result against
* @param result The result of the validation
*/
public setValidationResult(source: ValidationSource, result: unknown) {
public setValidationResult<TResult>(
source: ValidationSource,
result: TResult,
) {
this._validationResults.set(source, result);
}
}
8 changes: 4 additions & 4 deletions app/method-not-allowed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ interface UseMethodNotAllowedOptions {
export const useMethodNotAllowed = (
options: UseMethodNotAllowedOptions,
): MageMiddleware => {
return async (context, next) => {
if (context.req.method === "OPTIONS") {
return async (c, next) => {
if (c.req.method === "OPTIONS") {
// If the request is an OPTIONS request then don't respond with a 405
await next();
return;
}

context.text("Method Not Allowed", 405);
c.text("Method Not Allowed", 405);

context.res.headers.set(
c.res.headers.set(
"Allow",
options.getAllowedMethods().join(", "),
);
Expand Down
6 changes: 3 additions & 3 deletions app/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ interface UseOptionsOptions {
* @returns MageMiddleware
*/
export const useOptions = (options: UseOptionsOptions): MageMiddleware => {
return async (context, next) => {
context.empty();
return async (c, next) => {
c.empty();

context.res.headers.set(
c.res.headers.set(
"Allow",
options.getAllowedMethods().join(", "),
);
Expand Down
31 changes: 14 additions & 17 deletions tests/body.test.ts → app/tests/body.test.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,37 @@
import { afterAll, beforeAll, describe, it } from "@std/testing/bdd";
import { expect } from "@std/expect";
import { StatusCode } from "../app/mod.ts";
import { MageTestServer } from "../test-utils/server.ts";
import { MageTestServer } from "../../test-utils/server.ts";

let server: MageTestServer;

beforeAll(() => {
server = new MageTestServer();

server.app.post("/array-buffer", async (context) => {
context.text(
StatusCode.OK,
new TextDecoder().decode(await context.request.arrayBuffer()),
server.app.post("/array-buffer", async (c) => {
c.text(
new TextDecoder().decode(await c.req.arrayBuffer()),
);
});

server.app.post("/blob", async (context) => {
context.text(StatusCode.OK, await (await context.request.blob()).text());
server.app.post("/blob", async (c) => {
c.text(await (await c.req.blob()).text());
});

server.app.post("/form-data", async (context) => {
const formData = await context.request.formData();
context.json(StatusCode.OK, {
server.app.post("/form-data", async (c) => {
const formData = await c.req.formData();
c.json({
abc: formData.get("abc"),
});
});

server.app.post("/json", async (context) => {
context.json(
StatusCode.OK,
JSON.parse(JSON.stringify(await context.request.json())),
server.app.post("/json", async (c) => {
c.json(
JSON.parse(JSON.stringify(await c.req.json())),
);
});

server.app.post("/text", async (context) => {
context.text(StatusCode.OK, await context.request.text());
server.app.post("/text", async (c) => {
c.text(await c.req.text());
});

server.start();
Expand Down
43 changes: 43 additions & 0 deletions app/tests/empty.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { afterAll, beforeAll, describe, it } from "@std/testing/bdd";
import { expect } from "@std/expect";
import { MageTestServer } from "../../test-utils/server.ts";

let server: MageTestServer;

beforeAll(() => {
server = new MageTestServer();

server.app.get("/default", (c) => {
c.empty();
});

server.app.get("/set-status", (c) => {
c.empty(205);
});

server.start();
});

afterAll(async () => {
await server.stop();
});

describe("responses - empty", () => {
it("should return empty response with default status", async () => {
const response = await fetch(server.url("/default"), {
method: "GET",
});

expect(response.status).toBe(204);
expect(await response.text()).toBe("");
});

it("should return empty response with set status", async () => {
const response = await fetch(server.url("/set-status"), {
method: "GET",
});

expect(response.status).toBe(205);
expect(await response.text()).toBe("");
});
});
23 changes: 11 additions & 12 deletions tests/headers.test.ts → app/tests/headers.test.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
import { afterAll, beforeAll, describe, it } from "@std/testing/bdd";
import { expect } from "@std/expect";
import { StatusCode } from "../app/mod.ts";
import { MageTestServer } from "../test-utils/server.ts";
import { MageTestServer } from "../../test-utils/server.ts";

let server: MageTestServer;

beforeAll(() => {
server = new MageTestServer();

server.app.get("/get", (context) => {
context.text(StatusCode.OK, context.req.header("X-Test")!);
server.app.get("/get", (c) => {
c.text(c.req.header("X-Test")!);
});

server.app.get("/set", (context) => {
context.res.headers.set("X-Test", "test");
context.text(StatusCode.OK, "set");
server.app.get("/set", (c) => {
c.res.headers.set("X-Test", "test");
c.text("set");
});

server.app.get(
"/delete",
async (context, next) => {
context.res.headers.set("X-Test", "test");
async (c, next) => {
c.res.headers.set("X-Test", "test");
await next();
},
(context) => {
context.res.headers.delete("X-Test");
context.text(StatusCode.OK, "unset");
(c) => {
c.res.headers.delete("X-Test");
c.text("unset");
},
);

Expand Down
Loading

0 comments on commit 28b090b

Please sign in to comment.