Repository navigation
Phase 2 — Server-side data mocking for SvelteKit / Astro / Remix #47
Description
Phase 2 — Server-side data mocking for SvelteKit / Astro / Remix
Part of the multi-framework effort (PR #45, single PR, gated per phase). Builds on Phase 0's mock-writer registry (#42) and Phase 1's allowlist writers (#46). This is the big one: make SvelteKit/Astro/Remix SSR routes render with data instead of crashing on a missing backend — replicating, per framework, what NextMockWriter already does.
No main.py change needed — it already dispatches write_server_mocks/write_unmocked_api_fallbacks/write_unmocked_banner + changed_api_paths through the registry by framework name. Implementing the three writers wires Phase 2 automatically.
Writer contract (each framework implements all four, mirroring NextMockWriter)
write_server_mocks(repo_dir, mocks, diff)— for each mocked/api/...not inchanged_api_paths(diff), write an on-disk endpoint returning the mock body+status.write_unmocked_fallbacks(repo_dir, mocks, diff)— discover existing server API endpoints; blind-stub unmocked/non-auth ones to[]+x-renderpr-unmockedheader; wrap PR-changed ones so the real handler runs with an error guard (5xx/throw → fallback +x-renderpr-route-error). No detectable handler → blind-stub.write_banner(repo_dir)— write_BANNER_JSto the framework's static dir + inject<script src="/__renderpr-unmocked.js">into its served HTML host.changed_api_paths(diff)— diff → that framework's server API paths.
All return list[str] of generated relative paths (incl. .renderpr.bak) so restore_runtime_files reverts them. Reuse _backup_file, _explicit_mock_paths, _AUTH_API_PREFIX, API_FALLBACK_HEADER, API_ERROR_HEADER, _BANNER_JS from mock_server.py.
Structure: prep-then-parallel (file-disjoint lanes)
New package src/agent/mock_writers/ with __init__.py (imports the 3 submodules so they self-register) + sveltekit.py / astro.py / remix.py.
| Lane | Owns |
|---|---|
| 0 (prep, first) | mock_server.py, test_mock_server.py, mock_writers/__init__.py, the 3 stub modules |
| 1 (parallel) | mock_writers/sveltekit.py + test_mock_writers_sveltekit.py |
| 2 (parallel) | mock_writers/astro.py + test_mock_writers_astro.py |
| 3 (parallel) | mock_writers/remix.py + test_mock_writers_remix.py |
Lane 0: create the package with the 3 writer classes as behavior-preserving stubs (allowlist method delegates to write_vite_allowed_hosts/write_astro_allowed_hosts; other methods no-op). In mock_server.py: delete the 3 thin Phase-1 writer classes + their registrations, add from src.agent import mock_writers # noqa at the very bottom (registers them; safe "register at the bottom" pattern — no circular import, src/agent/__init__.py is empty). In test_mock_server.py: change _STUB_FRAMEWORKS to ("cra","spa","vite") (the three frameworks now have full writers, tested in their own files). Suite stays green; behavior identical. Smoke-check: get_mock_writer('sveltekit').framework=='sveltekit'.
Per-framework mechanisms
SvelteKit — endpoint src/routes/api/<path>/+server.ts exporting GET → json(body,{status}) (from @sveltejs/kit); internal event.fetch resolves same-origin to these. Discover existing src/routes/api/**/+server.{ts,js}. Banner host src/app.html (before </body>); static static/__renderpr-unmocked.js. changed = src/routes/api/**/+server.* → /api/.... Wrap uses GET/POST/... method set + sibling _renderpr_orig_server.
Astro — endpoint src/pages/api/<path>.ts exporting GET → new Response(JSON.stringify(body),{status,headers}). Discover existing src/pages/api/**/*.{ts,js}. Banner via src/middleware.ts (onRequest appends the <script> to HTML responses) — chosen over layout-patching because Astro has no guaranteed root layout; create-when-absent, skip-if-present (never merge an existing middleware → no corruption); static public/__renderpr-unmocked.js. changed = src/pages/api/** → /api/.... Wrap uses method set + sibling <name>._renderpr_orig.
Remix — resource route app/routes/api.<seg>.<seg>.ts (dots=slashes; [id]/:id→$id) exporting loader → json(body,{status}) (from @remix-run/node). Discover existing app/routes/api.*.{ts,js}. Banner host app/root.tsx (before <Scripts/</body>); static public/__renderpr-unmocked.js. changed = app/routes/api.* → /api/... (exclude api.auth.*). Wrap uses loader/action exports + sibling api.<...>._renderpr_orig (leading-_ segment keeps it non-routable).
Test plan (per framework lane, own test file)
Assert: server-mock file path+content; skipped-for-changed; backup of existing; blind-stub content+header for unmocked; auth/explicit skipped; changed→wrapped (sibling orig, __renderprWrap, error header, >=500 + ASCII-strip guards); no-handler→blind-stub; banner injection + static file; changed_api_paths mapping incl. auth exclusion; restore_runtime_files round-trip; idempotency (second run stable, banner returns []). failing-test-first; full suite green after every commit.
Non-goals / risks
- Direct data access not via
/api/(SvelteKitload/+page.server.ts, Astro frontmatterawait db(), Remix non-api.*loaders, Next RSC/direct-DB) is not mockable on disk —infer_routesonly emits mocks for literal/api/fetch/axios calls. Same limitation Next has. - Runtime correctness is unverified here — unit tests assert generated artifacts (paths/content/restore/idempotency), not that a booted dev server renders with the mock. That's Phase 3 against real framework repos (esp. SvelteKit
event.fetch, Astro middleware HTML rewrite, Remix._renderpr_orignon-routability). - Wrap-changed-handler is best-effort, framework-specific export shapes; falls back to blind-stub when undetected (parity with Next).
Commit breakdown (per lane, small commits)
Lane 0: (1) package + stubs, (2) move writers out of mock_server + bottom import, (3) _STUB_FRAMEWORKS edit. Lanes 1-3: (1) path mapping + changed_api_paths, (2) server_mocks, (3) unmocked_fallbacks, (4) banner, (5) test suite.
Activity
feat(mock): add mock_writers package with behavior-preserving stub wr…
feat(astro): path mapping + changed_api_paths
feat(sveltekit): path mapping + changed_api_paths
feat(remix): flat-route path mapping + changed_api_paths
Merge Phase 2 lanes: SvelteKit/Astro/Remix server-side mocking
style(mock): drop unused imports in phase-2 writer tests
fix(mock): idempotency guard for SvelteKit/Astro unmocked fallbacks