feat: v0.12.0 — contracts → packages: load/fly/abandon gameplay (ATR 72-600 capacity 6, manual flight to any airport, time-scaled abandon penalty 15–60%), one-time DB migration preserving wallet/clock; new endpoints /api/packages/:id/load|abandon + /api/planes/:planeId/fly

This commit is contained in:
2026-09-18 11:02:56 +00:00
parent 0208673da7
commit ab2e9fa54a
49 changed files with 2424 additions and 1409 deletions
+5 -5
View File
@@ -10,7 +10,7 @@ interface StateBody {
account: { cash: number; reputation: number };
simState: { clockMs: number; timeScale: number; paused: boolean };
planes: Array<Record<string, unknown>>;
contracts: Array<Record<string, unknown>>;
packages: Array<Record<string, unknown>>;
}
test('GET /api/state returns a seeded snapshot', async ({ page }) => {
@@ -32,10 +32,10 @@ test('GET /api/state returns a seeded snapshot', async ({ page }) => {
expect(body.planes[0], `plane.${key}`).toHaveProperty(key);
}
expect(Array.isArray(body.contracts)).toBe(true);
expect(body.contracts.length).toBeGreaterThan(0);
expect(Array.isArray(body.packages)).toBe(true);
expect(body.packages.length).toBeGreaterThan(0);
for (const key of ['id', 'originIcao', 'destIcao', 'reward', 'cargoType', 'deadlineSimTime', 'status']) {
expect(body.contracts[0], `contract.${key}`).toHaveProperty(key);
expect(body.packages[0], `package.${key}`).toHaveProperty(key);
}
});
@@ -78,5 +78,5 @@ test('state survives a container restart (sim clock + account persisted)', async
expect(a.account.cash).toBe(before.account.cash);
expect(a.account.reputation).toBe(before.account.reputation);
expect(a.planes.map((p) => p.id).sort()).toEqual(before.planes.map((p) => p.id).sort());
expect(a.contracts.map((c) => c.id).sort()).toEqual(before.contracts.map((c) => c.id).sort());
expect(a.packages.map((p) => p.id).sort()).toEqual(before.packages.map((p) => p.id).sort());
});