Skip to content

Commit

Permalink
fixed a bunch of TypeScript error and a bug where exmaples were not r…
Browse files Browse the repository at this point in the history
…ead properly
  • Loading branch information
pmcelhaney committed Sep 4, 2023
1 parent 2155064 commit e81d32e
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 20 deletions.
7 changes: 6 additions & 1 deletion src/server/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,9 @@ export class Registry {
}
}

export type { CounterfactResponseObject, HttpMethods, Module };
export type {
CounterfactResponseObject,
HttpMethods,
Module,
RequestDataWithBody,
};
14 changes: 12 additions & 2 deletions src/server/response-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ function unknownStatusCodeResponse(statusCode: number | undefined) {
};
}

interface Example {
description: string;
summary: string;
value: unknown;
}

export type MediaType = `${string}/${string}`;

export interface OpenApiResponse {
Expand All @@ -64,7 +70,7 @@ export interface OpenApiOperation {
[status: string]: {
content?: {
[type: number | string]: {
examples?: unknown[];
examples?: { [key: string]: Example };
schema: unknown;
};
};
Expand Down Expand Up @@ -142,7 +148,11 @@ export function createResponseBuilder(

content: Object.keys(content).map((type) => ({
body: content[type]?.examples
? oneOf(content[type]?.examples ?? [])
? oneOf(
Object.values(content[type]?.examples ?? []).map(
(example) => example.value,
),
)
: JSONSchemaFaker.generate(
// eslint-disable-next-line total-functions/no-unsafe-readonly-mutable-assignment
content[type]?.schema ?? { type: "object" },
Expand Down
48 changes: 37 additions & 11 deletions test/server/koa-middleware.test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
// eslint-disable-next-line import/no-extraneous-dependencies, n/no-extraneous-import
import { jest } from "@jest/globals";
import type { Context as KoaContext, ParameterizedContext } from "koa";
import type KoaProxy from "koa-proxy";

import { ContextRegistry } from "../../src/server/context-registry.js";
import { Dispatcher } from "../../src/server/dispatcher.js";
import { koaMiddleware } from "../../src/server/koa-middleware.js";
import { Registry } from "../../src/server/registry.js";

function mockKoaProxy(options: { host: string }) {
return function proxy(ctx: { mockProxyHost: string }) {
ctx.mockProxyHost = options.host;
const mockKoaProxy = (options: KoaProxy.Options | undefined) =>
function proxy(ctx: KoaContext) {
ctx.mockProxyHost = options?.host;
};
}

describe("koa middleware", () => {
it("passes the request to the dispatcher and returns the response", async () => {
const registry = new Registry();

registry.add("/hello", {
// @ts-expect-error - not obvious how to make TS happy here, and it's just a unit test
POST({ body }: { body: { name: string } }) {
return {
body: `Hello, ${body.name}!`,
Expand All @@ -26,6 +28,7 @@ describe("koa middleware", () => {

const dispatcher = new Dispatcher(registry, new ContextRegistry());
const middleware = koaMiddleware(dispatcher);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const ctx = {
req: {
path: "/hello",
Expand All @@ -38,9 +41,11 @@ describe("koa middleware", () => {
},

set: jest.fn(),
};
} as unknown as ParameterizedContext;

await middleware(ctx, () => undefined);
await middleware(ctx, async () => {
await Promise.resolve(undefined);
});

expect(ctx.status).toBe(200);
expect(ctx.body).toBe("Hello, Homer!");
Expand All @@ -63,9 +68,13 @@ describe("koa middleware", () => {
request: { method: "GET", path: "/not-modified" },

set: () => undefined,
status: undefined,
};

await middleware(ctx);
// @ts-expect-error - not obvious how to make TS happy here, and it's just a unit test
await middleware(ctx, async () => {
await Promise.resolve(undefined);
});

expect(ctx.status).toBe(304);
});
Expand All @@ -86,10 +95,14 @@ describe("koa middleware", () => {
mockKoaProxy,
);
const ctx = {
mockProxyHost: undefined,
request: { method: "GET", path: "/proxy" },
};

await middleware(ctx);
// @ts-expect-error - not obvious how to make TS happy here, and it's just a unit test
await middleware(ctx, async () => {
await Promise.resolve(undefined);
});

expect(ctx.mockProxyHost).toBe("https://example.com");
});
Expand All @@ -100,14 +113,17 @@ describe("koa middleware", () => {
registry.add("/hello", {
POST({ body }) {
return {
body: `Hello, ${body.name}!`,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
body: `Hello, ${(body as { name: string }).name}!`,
};
},
});

const dispatcher = new Dispatcher(registry, new ContextRegistry());
const middleware = koaMiddleware(dispatcher);
const ctx = {
body: undefined,

req: {
path: "/hello",
},
Expand All @@ -119,9 +135,14 @@ describe("koa middleware", () => {
},

set: jest.fn(),

status: undefined,
};

await middleware(ctx);
// @ts-expect-error - not obvious how to make TS happy here, and it's just a unit test
await middleware(ctx, async () => {
await Promise.resolve(undefined);
});

expect(ctx.status).toBe(200);
expect(ctx.body).toBe("Hello, Homer!");
Expand All @@ -141,14 +162,17 @@ describe("koa middleware", () => {
registry.add("/hello", {
POST({ body }) {
return {
body: `Hello, ${body.name}!`,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
body: `Hello, ${(body as { name: string }).name}!`,
};
},
});

const dispatcher = new Dispatcher(registry, new ContextRegistry());
const middleware = koaMiddleware(dispatcher);
const ctx = {
body: undefined,

req: {
path: "/hello",
},
Expand All @@ -167,8 +191,10 @@ describe("koa middleware", () => {
},

set: jest.fn(),
status: undefined,
};

// @ts-expect-error - not obvious how to make TS happy here, and it's just a unit test
await middleware(ctx);

expect(ctx.status).toBe(200);
Expand Down
2 changes: 2 additions & 0 deletions test/server/module-loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,10 @@ describe("a module loader", () => {
const response = registry.endpoint(
"GET",
"/change",
// @ts-expect-error - not going to create a whole context object for a test
)({ headers: {}, matchedPath: "", path: {}, query: {} });

// @ts-expect-error - TypeScript doesn't know that the response will have a body property
expect(response.body).toBe("after change");
expect(registry.exists("GET", "/late/addition")).toBe(true);

Expand Down
24 changes: 19 additions & 5 deletions test/server/registry.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { type Module, Registry } from "../../src/server/registry.js";
import {
type Module,
Registry,
type RequestDataWithBody,
} from "../../src/server/registry.js";

function makeModule(name: string): Module {
return {
Expand All @@ -11,6 +15,7 @@ function makeModule(name: string): Module {
async function identifyModule(
node: { module?: Module } | undefined,
): Promise<unknown> {
// @ts-expect-error - not creating an entire request object
// eslint-disable-next-line new-cap
return await node?.module?.GET?.({
headers: {},
Expand Down Expand Up @@ -78,9 +83,13 @@ describe("a registry", () => {

query: {},
};
// @ts-expect-error - chill out, TypeScript
const getA = await registry.endpoint("GET", "/a")(props);
// @ts-expect-error - chill out, TypeScript
const getB = await registry.endpoint("GET", "/b")(props);
// @ts-expect-error - chill out, TypeScript
const postA = await registry.endpoint("POST", "/a")(props);
// @ts-expect-error - chill out, TypeScript
const postB = await registry.endpoint("POST", "/b")(props);

expect(getA).toBe("GET a");
Expand Down Expand Up @@ -119,9 +128,9 @@ describe("a registry", () => {
registry.add("/{organization}/users/{username}/friends/{page}", {
GET({ path }) {
return {
body: `page ${String(path.page)} of ${String(
path.username,
)}'s friends in ${String(path.organization)}`,
body: `page ${String(path?.page)} of ${String(
path?.username,
)}'s friends in ${String(path?.organization)}`,

headers: { "content-type": "text/plain" },

Expand All @@ -134,6 +143,7 @@ describe("a registry", () => {
await registry.endpoint(
"GET",
"/acme/users/alice/friends/2",
// @ts-expect-error - not creating an entire request object
)({ headers: {}, matchedPath: "", path: {}, query: {} }),
).toStrictEqual({
body: "page 2 of alice's friends in acme",
Expand All @@ -156,7 +166,11 @@ describe("a registry", () => {
});

expect(
await registry.endpoint("GET", "/Acme/users/alice/Friends/2")({}),
await registry.endpoint(
"GET",
"/Acme/users/alice/Friends/2",
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
)({} as RequestDataWithBody),
).toStrictEqual({
body: "page 2 of alice's friends in Acme",
});
Expand Down
8 changes: 7 additions & 1 deletion test/server/response-builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,13 @@ describe("a response builder", () => {
},

"text/plain": {
examples: { text: "example text response" },
examples: {
text: {
description: "a text response",
summary: "summary",
value: "example text response",
},
},

schema: {
examples: ["hello"],
Expand Down

0 comments on commit e81d32e

Please sign in to comment.