From 3b63285bd15bafc9c2e1f6380970e332ddca6733 Mon Sep 17 00:00:00 2001 From: Julian Tabel Date: Sat, 21 Feb 2026 17:50:54 +0100 Subject: [PATCH 1/2] Fix FK violations when pruning stale routes Bulk delete bypasses ORM-level cascades, so manually delete route_encounters, nullify boss_battle.after_route_id, and skip routes referenced by user encounters before deleting stale routes. Co-Authored-By: Claude Opus 4.6 --- backend/src/app/seeds/loader.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/backend/src/app/seeds/loader.py b/backend/src/app/seeds/loader.py index efa1730..4b47db9 100644 --- a/backend/src/app/seeds/loader.py +++ b/backend/src/app/seeds/loader.py @@ -1,11 +1,12 @@ """Database upsert helpers for seed data.""" -from sqlalchemy import delete, select +from sqlalchemy import delete, select, update from sqlalchemy.dialects.postgresql import insert from sqlalchemy.ext.asyncio import AsyncSession from app.models.boss_battle import BossBattle from app.models.boss_pokemon import BossPokemon +from app.models.encounter import Encounter from app.models.evolution import Evolution from app.models.game import Game from app.models.pokemon import Pokemon @@ -195,17 +196,33 @@ async def upsert_routes( for child in route.get("children", []): seed_names.add(child["name"]) - pruned = await session.execute( - delete(Route) - .where( + # Find stale route IDs, excluding routes with user encounters + in_use_subq = select(Encounter.route_id).distinct().subquery() + stale_route_ids_result = await session.execute( + select(Route.id).where( Route.version_group_id == version_group_id, Route.name.not_in(seed_names), + Route.id.not_in(select(in_use_subq)), ) - .returning(Route.id) ) - pruned_count = len(pruned.all()) - if pruned_count: - print(f" Pruned {pruned_count} stale route(s)") + stale_route_ids = [row.id for row in stale_route_ids_result] + + if stale_route_ids: + # Delete encounters referencing stale routes (no DB-level cascade) + await session.execute( + delete(RouteEncounter).where( + RouteEncounter.route_id.in_(stale_route_ids) + ) + ) + # Nullify boss battle references to stale routes + await session.execute( + update(BossBattle) + .where(BossBattle.after_route_id.in_(stale_route_ids)) + .values(after_route_id=None) + ) + # Now safe to delete the routes + await session.execute(delete(Route).where(Route.id.in_(stale_route_ids))) + print(f" Pruned {len(stale_route_ids)} stale route(s)") await session.flush() -- 2.49.1 From 1513bb3658aea661542d75a1b1702df0b71de2b2 Mon Sep 17 00:00:00 2001 From: Julian Tabel Date: Sat, 21 Feb 2026 17:54:25 +0100 Subject: [PATCH 2/2] Split e2e tests into manual workflow_dispatch workflow Co-Authored-By: Claude Opus 4.6 --- ...i--split-e2e-tests-into-manual-workflow.md | 11 ++++++ .github/workflows/ci.yml | 29 +-------------- .github/workflows/e2e.yml | 35 +++++++++++++++++++ 3 files changed, 47 insertions(+), 28 deletions(-) create mode 100644 .beans/nuzlocke-tracker-m8ki--split-e2e-tests-into-manual-workflow.md create mode 100644 .github/workflows/e2e.yml diff --git a/.beans/nuzlocke-tracker-m8ki--split-e2e-tests-into-manual-workflow.md b/.beans/nuzlocke-tracker-m8ki--split-e2e-tests-into-manual-workflow.md new file mode 100644 index 0000000..bc537e5 --- /dev/null +++ b/.beans/nuzlocke-tracker-m8ki--split-e2e-tests-into-manual-workflow.md @@ -0,0 +1,11 @@ +--- +# nuzlocke-tracker-m8ki +title: Split e2e tests into manual workflow +status: completed +type: task +priority: normal +created_at: 2026-02-21T16:53:37Z +updated_at: 2026-02-21T16:54:04Z +--- + +Remove e2e-tests job from ci.yml and create a new e2e.yml workflow with workflow_dispatch trigger only. \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6934e15..319d48b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,31 +68,4 @@ jobs: working-directory: frontend - name: Run tests run: npm test - working-directory: frontend - - e2e-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - persist-credentials: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "24" - - name: Install dependencies - run: npm ci - working-directory: frontend - - name: Install Playwright browsers - run: npx playwright install --with-deps chromium - working-directory: frontend - - name: Run e2e tests - run: npm run test:e2e - working-directory: frontend - env: - E2E_API_URL: http://192.168.1.10:8100 - - name: Upload Playwright report - if: failure() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: playwright-report - path: frontend/playwright-report/ + working-directory: frontend \ No newline at end of file diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000..f7fdce5 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,35 @@ +name: E2E Tests + +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + e2e-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: "24" + - name: Install dependencies + run: npm ci + working-directory: frontend + - name: Install Playwright browsers + run: npx playwright install --with-deps chromium + working-directory: frontend + - name: Run e2e tests + run: npm run test:e2e + working-directory: frontend + env: + E2E_API_URL: http://192.168.1.10:8100 + - name: Upload Playwright report + if: failure() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: playwright-report + path: frontend/playwright-report/ -- 2.49.1