Fix ignore route slug collision ([packageId] → [id]) — Next.js runtime 500s (ISS-047 part B)
CI / build + unit + e2e (pull_request) Successful in 8m21s

This commit is contained in:
2026-09-20 18:07:18 +00:00
parent 8a12f07703
commit b4b25551ff
5 changed files with 8 additions and 8 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
## v0.29.0 — 2026-09-20 14:27 ## v0.29.0 — 2026-09-20 14:27
- **Ignore a job (ISS-047, part B).** A player can now dismiss an open job so it leaves the market permanently; new random jobs spawn in its place via the existing top-up logic (`spawnOpenPackages` counts 'open', so the market refills to 10 on the next tick). New terminal package status `'ignored'` added to the status union/enum in `lib/db/schema.ts`, `lib/game/state.ts`, `lib/openapi.ts` (PackageState.status) and the e2e-local interface copy in `e2e/right-pane.spec.ts`. No DB migration — the column is plain TEXT with no CHECK constraint. New action `ignorePackage` (`lib/game/actions.ts`) next to abandon: 404 unknown id, 409 unless open ("only open packages can be ignored"), otherwise flips status to 'ignored' — no body, no penalty, nothing else changes - **Ignore a job (ISS-047, part B).** A player can now dismiss an open job so it leaves the market permanently; new random jobs spawn in its place via the existing top-up logic (`spawnOpenPackages` counts 'open', so the market refills to 10 on the next tick). New terminal package status `'ignored'` added to the status union/enum in `lib/db/schema.ts`, `lib/game/state.ts`, `lib/openapi.ts` (PackageState.status) and the e2e-local interface copy in `e2e/right-pane.spec.ts`. No DB migration — the column is plain TEXT with no CHECK constraint. New action `ignorePackage` (`lib/game/actions.ts`) next to abandon: 404 unknown id, 409 unless open ("only open packages can be ignored"), otherwise flips status to 'ignored' — no body, no penalty, nothing else changes
- **New route.** `POST /api/packages/{packageId}/ignore` (`app/api/packages/[packageId]/ignore/route.ts`) — no body, mirrors the abandon route exactly (same performAction wrapper, full state snapshot on success). The 13th public endpoint: OpenAPI spec gains the path + 'ignored' in PackageState.status; `lib/openapi.test.ts` and `e2e/api.spec.ts` path lists updated twelve → thirteen - **New route.** `POST /api/packages/{id}/ignore` (`app/api/packages/[id]/ignore/route.ts`) — no body, mirrors the abandon route exactly (same performAction wrapper, full state snapshot on success). The 13th public endpoint: OpenAPI spec gains the path + 'ignored' in PackageState.status; `lib/openapi.test.ts` and `e2e/api.spec.ts` path lists updated twelve → thirteen
- **UI.** Each open-package row in the Jobs tab gains a small "Ignore" button beside the Load controls (`components/RightPane.tsx`, testid `ignore-{pkg.id}`, disabled while the pane action is busy, calls runAction with no body). Ignored rows vanish from the list automatically — the section filters status==='open'; no toast (deriveEvents untouched) - **UI.** Each open-package row in the Jobs tab gains a small "Ignore" button beside the Load controls (`components/RightPane.tsx`, testid `ignore-{pkg.id}`, disabled while the pane action is busy, calls runAction with no body). Ignored rows vanish from the list automatically — the section filters status==='open'; no toast (deriveEvents untouched)
- Verified-safe consumers unchanged: expirePackages selects only open|loaded ('ignored' never expires), deliverPackages handles loaded/expired, RightPane filters 'open' - Verified-safe consumers unchanged: expirePackages selects only open|loaded ('ignored' never expires), deliverPackages handles loaded/expired, RightPane filters 'open'
- Tests: unit — new `ignorePackage` block in `lib/game/actions.test.ts` (open → ignored with cash/planeId untouched; 409 for each of loaded/delivered/expired/abandoned/already-ignored; 404 unknown id), `lib/sim/engine.test.ts` terminal-statuses test gains an 'ignored' row, `lib/openapi.test.ts` status enum list gains 'ignored'. E2E — new "Ignore removes an open job from the list permanently" test in `e2e/right-pane.spec.ts`: hook-created package renders with an enabled Ignore button, UI click → /api/state shows 'ignored', row gone from `[data-testid="open-packages"]`, second API ignore 409s - Tests: unit — new `ignorePackage` block in `lib/game/actions.test.ts` (open → ignored with cash/planeId untouched; 409 for each of loaded/delivered/expired/abandoned/already-ignored; 404 unknown id), `lib/sim/engine.test.ts` terminal-statuses test gains an 'ignored' row, `lib/openapi.test.ts` status enum list gains 'ignored'. E2E — new "Ignore removes an open job from the list permanently" test in `e2e/right-pane.spec.ts`: hook-created package renders with an enabled Ignore button, UI click → /api/state shows 'ignored', row gone from `[data-testid="open-packages"]`, second API ignore 409s
@@ -4,7 +4,7 @@ import { ignorePackage, performAction } from '@/lib/game/actions';
// leaves the market permanently; the top-up logic spawns a replacement on the next tick. No penalty, // leaves the market permanently; the top-up logic spawns a replacement on the next tick. No penalty,
// nothing else changes. 404 unknown id; 409 unless open. On success returns the full state snapshot. // nothing else changes. 404 unknown id; 409 unless open. On success returns the full state snapshot.
export async function POST(_req: Request, ctx: { params: Promise<{ packageId: string }> }) { export async function POST(_req: Request, ctx: { params: Promise<{ id: string }> }) {
const { packageId } = await ctx.params; const { id } = await ctx.params;
return performAction((inst) => ignorePackage(inst, packageId)); return performAction((inst) => ignorePackage(inst, id));
} }
+1 -1
View File
@@ -26,8 +26,8 @@ test('GET /api/openapi serves the spec with all thirteen endpoints and the packa
'/api/market/buy-plane', '/api/market/buy-plane',
'/api/market/sell-plane', '/api/market/sell-plane',
'/api/packages/{id}/abandon', '/api/packages/{id}/abandon',
'/api/packages/{id}/ignore', // v0.29.0 (ISS-047 part B)
'/api/packages/{id}/load', '/api/packages/{id}/load',
'/api/packages/{packageId}/ignore', // v0.29.0 (ISS-047 part B)
'/api/planes/{planeId}/fly', '/api/planes/{planeId}/fly',
'/api/planes/{planeId}/queue', '/api/planes/{planeId}/queue',
'/api/planes/{planeId}/queue/cancel', '/api/planes/{planeId}/queue/cancel',
+1 -1
View File
@@ -9,8 +9,8 @@ describe('OpenAPI spec', () => {
'/api/market/buy-plane', '/api/market/buy-plane',
'/api/market/sell-plane', '/api/market/sell-plane',
'/api/packages/{id}/abandon', '/api/packages/{id}/abandon',
'/api/packages/{id}/ignore', // v0.29.0 (ISS-047 part B)
'/api/packages/{id}/load', '/api/packages/{id}/load',
'/api/packages/{packageId}/ignore', // v0.29.0 (ISS-047 part B)
'/api/planes/{planeId}/fly', '/api/planes/{planeId}/fly',
'/api/planes/{planeId}/queue', // v0.25.0 (ISS-043) '/api/planes/{planeId}/queue', // v0.25.0 (ISS-043)
'/api/planes/{planeId}/queue/cancel', // v0.25.0 (ISS-043) '/api/planes/{planeId}/queue/cancel', // v0.25.0 (ISS-043)
+2 -2
View File
@@ -165,14 +165,14 @@ export const openApiSpec = {
}, },
}, },
}, },
'/api/packages/{packageId}/ignore': { '/api/packages/{id}/ignore': {
post: { post: {
summary: 'Ignore an open job', summary: 'Ignore an open job',
description: description:
'Dismiss an OPEN job so it leaves the market permanently (v0.29.0, ISS-047 part B). No penalty and nothing else changes — the top-up logic spawns a replacement on the next tick. No body.', 'Dismiss an OPEN job so it leaves the market permanently (v0.29.0, ISS-047 part B). No penalty and nothing else changes — the top-up logic spawns a replacement on the next tick. No body.',
operationId: 'ignorePackage', operationId: 'ignorePackage',
parameters: [ parameters: [
{ name: 'packageId', in: 'path', required: true, schema: { type: 'string' } }, { name: 'id', in: 'path', required: true, schema: { type: 'string' } },
], ],
responses: { responses: {
200: { 200: {