// Ported from apps/server/test (CR-014 Step 14.5): now runs on the engine. import { describe, expect, it } from "vitest"; import { resolveArrivals } from "../../src/index"; import { useTestApp, signUp, getState, readSave, setCredits, testContract, insertDispatch, makeOwner, } from "../support"; const testApp = useTestApp(); /** An owner (one Courier at Earth, crewed by the avatar) with 1,000 credits. */ async function owner() { const { app } = testApp(); const guest = await signUp(app); const { shipId } = await makeOwner(testApp().app, guest.avatarId); await setCredits(testApp().app, 1000); return { guest, shipId }; } describe("lazy resolution on read", () => { it("applies a past arrival's credits, XP, ship location and discovery", async () => { const { app, clock } = testApp(); const { guest, shipId } = await owner(); // A delivery to Mars, which the player has not discovered yet. const contract = testContract({ destinationId: "mars", payout: 5000 }); const record = await insertDispatch(testApp().app, { shipId, pilotId: guest.avatarId, departAt: clock.now, contract, }); clock.now = record.arrivalAt + 60_000; const state = await getState(app, guest); // The avatar flew it, so no pilot pay is deducted. expect(state.company.credits).toBe(1000 + 5000); expect(state.pilots[0]!.xp).toBeGreaterThan(0); expect(state.pilots[0]!.status).toBe("idle"); expect(state.ships[0]).toMatchObject({ status: "idle", locationId: "mars", dispatchId: null }); expect(state.locations.find((l) => l.id === "mars")!.discovered).toBe(true); expect(state.activeDispatches).toHaveLength(0); expect(state.summaries).toEqual([ expect.objectContaining({ dispatchId: record.id, credits: 5000, discoveredLocationId: "mars", }), ]); }); it("leaves dispatches that have not arrived yet in flight", async () => { const { app, clock } = testApp(); const { guest, shipId } = await owner(); const record = await insertDispatch(testApp().app, { shipId, pilotId: guest.avatarId, departAt: clock.now, }); clock.now = record.arrivalAt - 1; const state = await getState(app, guest); expect(state.company.credits).toBe(1000); expect(state.ships[0]).toMatchObject({ status: "in-flight", locationId: null, dispatchId: record.id, }); expect(state.activeDispatches.map((d) => d.id)).toEqual([record.id]); }); it("does not apply an arrival twice on a second fetch", async () => { const { app, clock } = testApp(); const { guest, shipId } = await owner(); const record = await insertDispatch(testApp().app, { shipId, pilotId: guest.avatarId, departAt: clock.now, }); clock.now = record.arrivalAt; const first = await getState(app, guest); const second = await getState(app, guest); expect(second.company.credits).toBe(first.company.credits); expect(second.pilots[0]!.xp).toBe(first.pilots[0]!.xp); expect(first.company.credits).toBe(1000 + 2000); }); it("applies an arrival exactly once under two concurrent fetches", async () => { const { app, clock } = testApp(); const { guest, shipId } = await owner(); const record = await insertDispatch(testApp().app, { shipId, pilotId: guest.avatarId, departAt: clock.now, }); clock.now = record.arrivalAt; const [a, b] = await Promise.all([getState(app, guest), getState(app, guest)]); expect(a.company.credits).toBe(3000); expect(b.company.credits).toBe(3000); }); it("resolves exactly once when resolution runs again on the same save", async () => { const { clock } = testApp(); const { guest, shipId } = await owner(); const record = await insertDispatch(testApp().app, { shipId, pilotId: guest.avatarId, departAt: clock.now, }); const save = await readSave(testApp().app); const counts = [1, 2, 3].map(() => resolveArrivals(save, record.arrivalAt)); expect(counts).toEqual([1, 0, 0]); }); it("resolves several arrivals in arrival order", async () => { const { app, clock } = testApp(); const { guest, shipId } = await owner(); // Fly to the Moon, then (already resolved in the DB as of arrival) on to Earth. const first = await insertDispatch(testApp().app, { shipId, pilotId: guest.avatarId, departAt: clock.now, }); clock.now = first.arrivalAt; await getState(app, guest); const second = await insertDispatch(testApp().app, { shipId, pilotId: guest.avatarId, departAt: first.arrivalAt, contract: testContract({ originId: "moon", destinationId: "earth", payout: 700 }), }); clock.now = second.arrivalAt + 1; const state = await getState(app, guest); expect(state.company.credits).toBe(1000 + 2000 + 700); expect(state.ships[0]!.locationId).toBe("earth"); expect(state.summaries).toHaveLength(2); }); });