62 lines
2.3 KiB
TypeScript
62 lines
2.3 KiB
TypeScript
// Ported from apps/server/test (CR-014 Step 14.5): request and response checks
|
|
// from the route catalog, now on engine commands. (Malformed JSON bodies and
|
|
// HTTP 404s for unknown URLs no longer exist.)
|
|
import { errorResponseSchema, type RouteResponse } from "@deadhead/schema";
|
|
import { describe, expect, it } from "vitest";
|
|
import { GameEngine, MemoryBackend, SaveStorage } from "../../src/index";
|
|
import { useTestApp, signUp, call } from "../support";
|
|
|
|
const testApp = useTestApp();
|
|
|
|
const expectValidationError = (response: { statusCode: number; json(): unknown }) => {
|
|
expect(response.statusCode).toBe(400);
|
|
expect(errorResponseSchema.parse(response.json()).error.code).toBe("validation-error");
|
|
};
|
|
|
|
describe("request validation from the command catalog", () => {
|
|
it("rejects unexpected fields on a command that takes none", async () => {
|
|
expectValidationError(
|
|
await call(testApp().app, null, "POST", "/api/guest", { surprise: true }),
|
|
);
|
|
});
|
|
|
|
it("rejects unexpected parameters before anything else", async () => {
|
|
expectValidationError(await call(testApp().app, null, "GET", "/api/state?evil=1"));
|
|
});
|
|
|
|
it("still answers valid requests", async () => {
|
|
expect((await call(testApp().app, null, "GET", "/api/time")).statusCode).toBe(200);
|
|
});
|
|
|
|
it("refuses an unknown command with not-found", async () => {
|
|
await expect(testApp().app.call("nope" as never, {} as never)).rejects.toMatchObject({
|
|
code: "not-found",
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("response validation", () => {
|
|
const engine = (validateResponses: boolean) =>
|
|
new GameEngine({
|
|
storage: new SaveStorage(new MemoryBackend()),
|
|
clock: { now: () => 0 },
|
|
validateResponses,
|
|
// A handler that breaks the state response schema.
|
|
overrides: {
|
|
getPlayerState: () => ({ playerId: 42 }) as unknown as RouteResponse<"getPlayerState">,
|
|
},
|
|
});
|
|
|
|
it("fails a response that violates its schema when checking is on", async () => {
|
|
const app = engine(true);
|
|
await signUp(app);
|
|
await expect(app.call("getPlayerState", {})).rejects.toThrow(/violates its schema/);
|
|
});
|
|
|
|
it("does not check responses when checking is off (production)", async () => {
|
|
const app = engine(false);
|
|
await signUp(app);
|
|
await expect(app.call("getPlayerState", {})).resolves.toEqual({ playerId: 42 });
|
|
});
|
|
});
|