deadhead-space-freight-tycoon/apps/server/test/guest.test.ts

90 lines
3.2 KiB
TypeScript

import { content } from "@deadhead/content";
import {
createGuestResponseSchema,
errorResponseSchema,
playerStateSchema,
} from "@deadhead/schema";
import { beforeEach, describe, expect, it } from "vitest";
import { buildApp } from "../src/app";
import { createRecordingQueue } from "../src/jobs/queue";
import { useTestDatabase } from "./db";
import { testConfig } from "./helpers";
const database = useTestDatabase();
/** Turns Set-Cookie headers into a Cookie request header. */
function cookieFrom(setCookie: string | string[] | undefined): string {
const values = Array.isArray(setCookie) ? setCookie : setCookie ? [setCookie] : [];
return values.map((c) => c.split(";")[0]).join("; ");
}
describe("guest accounts", () => {
let app: ReturnType<typeof buildApp>;
beforeEach(() => {
app = buildApp(testConfig(), { database: database(), jobs: createRecordingQueue() });
return () => app.close();
});
it("creates a guest with a session cookie and a new tutorial player", async () => {
const response = await app.inject({ method: "POST", url: "/api/guest" });
expect(response.statusCode).toBe(200);
expect(cookieFrom(response.headers["set-cookie"])).toMatch(/session_token=/);
const body = createGuestResponseSchema.parse(response.json());
expect(body.state.company.credits).toBe(content.config.startingCredits);
expect(body.state.tutorial).toMatchObject({ loanerLocationId: "earth", runInFlight: false });
});
it("returns the new player's state for that session", async () => {
const guest = await app.inject({ method: "POST", url: "/api/guest" });
const cookie = cookieFrom(guest.headers["set-cookie"]);
const response = await app.inject({ method: "GET", url: "/api/state", headers: { cookie } });
expect(response.statusCode).toBe(200);
const state = playerStateSchema.parse(response.json());
expect(state.company.credits).toBe(content.config.startingCredits);
expect(
state.locations
.filter((l) => l.discovered)
.map((l) => l.id)
.sort(),
).toEqual(["earth", "moon"]);
expect(state.pilots).toHaveLength(1);
expect(state.pilots[0]).toMatchObject({
isAvatar: true,
level: 0,
xp: 0,
locationId: "earth",
status: "idle",
});
expect(state.ships).toHaveLength(0);
expect(state.tutorial).not.toBeNull();
});
it("gives each guest their own player", async () => {
const a = createGuestResponseSchema.parse(
(await app.inject({ method: "POST", url: "/api/guest" })).json(),
);
const b = createGuestResponseSchema.parse(
(await app.inject({ method: "POST", url: "/api/guest" })).json(),
);
expect(a.playerId).not.toBe(b.playerId);
});
it("rejects a state request with no session", async () => {
const response = await app.inject({ method: "GET", url: "/api/state" });
expect(response.statusCode).toBe(401);
expect(errorResponseSchema.parse(response.json()).error.code).toBe("unauthorized");
});
it("rejects a state request with a forged session cookie", async () => {
const response = await app.inject({
method: "GET",
url: "/api/state",
headers: { cookie: "better-auth.session_token=forged.value" },
});
expect(response.statusCode).toBe(401);
});
});