97 lines
3.2 KiB
TypeScript
97 lines
3.2 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { contractBoardResponseSchema } from "@deadhead/schema";
|
|
import { eq } from "drizzle-orm";
|
|
import pg from "pg";
|
|
import { describe, expect, it } from "vitest";
|
|
import { contractBoards, dispatches } from "../src/db/schema";
|
|
import { useTestDatabase } from "./db";
|
|
import {
|
|
call,
|
|
getState,
|
|
insertDispatch,
|
|
putBoard,
|
|
signUpOwner,
|
|
testContract,
|
|
useTestApp,
|
|
} from "./game";
|
|
import { testConfig } from "./helpers";
|
|
|
|
const database = useTestDatabase();
|
|
const testApp = useTestApp(database);
|
|
|
|
const migration = readFileSync(
|
|
join(dirname(fileURLToPath(import.meta.url)), "../drizzle/0005_cr007_goods.sql"),
|
|
"utf8",
|
|
);
|
|
|
|
/** Runs the CR-007 backfill in one session (it uses a pg_temp helper). */
|
|
async function backfill() {
|
|
const client = new pg.Client({ connectionString: testConfig().databaseUrl });
|
|
await client.connect();
|
|
try {
|
|
for (const statement of migration.split("--> statement-breakpoint")) {
|
|
await client.query(statement);
|
|
}
|
|
} finally {
|
|
await client.end();
|
|
}
|
|
}
|
|
|
|
/** A contract as stored before CR-007: no good, shipper or Priority fields. */
|
|
function legacy(overrides: Parameters<typeof testContract>[0] = {}) {
|
|
const { goodId, shipper, priority, deliveryWindowGameHours, ...old } = testContract(overrides);
|
|
void [goodId, shipper, priority, deliveryWindowGameHours];
|
|
return old as ReturnType<typeof testContract>;
|
|
}
|
|
|
|
describe("CR-007 backfill migration", () => {
|
|
it("gives stored boards and in-flight contracts a good, shipper and no Priority, once", async () => {
|
|
const { app, clock } = testApp();
|
|
const owner = await signUpOwner(app, database().db);
|
|
const db = database().db;
|
|
await putBoard(
|
|
db,
|
|
owner.playerId,
|
|
[
|
|
legacy({ id: "earth:1:0", cargoTypeId: "standard" }),
|
|
legacy({ id: "earth:1:1", cargoTypeId: "high-value", originId: "moon" }),
|
|
],
|
|
clock.now,
|
|
);
|
|
const record = await insertDispatch(db, {
|
|
playerId: owner.playerId,
|
|
shipId: owner.shipId,
|
|
pilotId: owner.avatarId,
|
|
departAt: clock.now,
|
|
contract: legacy({ cargoTypeId: "dangerous" }),
|
|
});
|
|
|
|
await backfill();
|
|
const [board] = await db.select().from(contractBoards);
|
|
expect(board!.contracts.map((c) => [c.goodId, c.shipper, c.priority])).toEqual([
|
|
["consumer-goods", "Pacific Orbital Logistics", false],
|
|
["electronics", "Shackleton Mining Co.", false],
|
|
]);
|
|
const [row] = await db.select().from(dispatches).where(eq(dispatches.id, record.id));
|
|
expect(row!.record.contract).toMatchObject({
|
|
goodId: "industrial-chemicals",
|
|
priority: false,
|
|
deliveryWindowGameHours: null,
|
|
});
|
|
|
|
// A second run changes nothing.
|
|
await backfill();
|
|
const [again] = await db.select().from(contractBoards);
|
|
expect(again!.contracts).toEqual(board!.contracts);
|
|
|
|
// The API accepts the backfilled rows.
|
|
const state = await getState(app, owner);
|
|
expect(state.activeDispatches[0]!.contract!.goodId).toBe("industrial-chemicals");
|
|
const response = await call(app, owner, "GET", "/api/locations/earth/board");
|
|
expect(response.statusCode).toBe(200);
|
|
contractBoardResponseSchema.parse(response.json());
|
|
});
|
|
});
|