WIP: test(boot): real-subprocess regression gate for build_routes 4-branch contract + boot ordering + credential_helper (#87) #88

Draft
molecule-code-reviewer wants to merge 2 commits from test/issue-87-boot-routes-real-subprocess into main
Member

Closes #87

What

Authors the missing tests/test_boot_routes.py referenced by boot_routes.py's own docstring ("Test coverage at tests/test_boot_routes.py pins the contract") but absent until now, plus a main() boot-ordering test and a credential_helper real-git subprocess test. Wires an explicit unit-tests-job step so the gate runs and skips are loud.

Test layer: real-subprocess (SOP rule internal#765 — mock-only does NOT satisfy a regression gate).

Why real-subprocess (the un-stubbing)

tests/conftest.py installs stub a2a.* modules into sys.modules at collect time (real a2a-sdk is a heavy workspace dep). Under those stubs create_agent_card_routes / create_jsonrpc_routes are no-op lambdas returning None — so an in-process test of build_routes would assert against the STUB, and a regression that broke the real wiring would still pass.

These tests therefore drive the behaviour in child Python processes that do not import this package's conftest, so the REAL a2a-sdk (pulled by pip install -e . in CI) + real Starlette load, and the routes are exercised over real ASGI via Starlette's TestClient. The credential_helper test shells out to real git against a throwaway $HOME.

Coverage (watch-fail intent per branch)

build_routes four-branch card/JSON-RPC contract:

  1. Happy path (executor non-None) — card route 200; / routed into real DefaultRequestHandler, NOT the -32603 not-configured error. Watch-fail: dropping create_agent_card_routes → card 404 (the "stuck booting" UX PR #2756 fixed).
  2. Not-configured (executor None) — card still 200; POST / → JSON-RPC -32603, reason echoed into error.data, request id echoed. Watch-fail: re-coupling the JSON-RPC route to adapter.setup() → POST / 404/405 instead of structured -32603.
  3. POST-only — GET / 405 on the not-configured route; None reason → non-empty fallback. Watch-fail: widening methods=["POST"].
  4. Mutual exclusion/ JSON-RPC surface mounted by exactly one source (handler XOR not-configured), card route present in both. Watch-fail: appending the not-configured route unconditionally → duplicate dead / mount.

main() boot ordering — install_credential_helper runs before load_config. Watch-fail: reorder after load_config or drop it → the #1933 gh-token cascade (39 lost workspaces) re-opens.

credential_helper — after install_credential_helper(), real git config --global --get credential.https://github.com.helper returns !<extracted-script>, useHttpPath=true, and the script is extracted + executable. Watch-fail: _configure_git_credential_helper stops shelling out / wrong key / no extraction.

CI wiring

Auto-collected by the existing pytest -q in the unit-tests job. Added an explicit pytest -v -rs tests/test_boot_routes.py step so the gate is visible and a skip-if-real-a2a-absent is loud (never a silent green).

DRAFT — what CI + CR2 must confirm (I cannot compile/run locally)

  • unit-tests job passes; the new gate step does NOT skip — confirm A2A_REAL_OK path (real a2a-sdk[http-server]>=1.0.0 + starlette.testclient import in the child). A loud SKIP means the real SDK isn't installed and the gate is inert.
  • Real a2a-sdk 1.x AgentCard(... supported_interfaces=[AgentInterface(url=..., protocol="jsonrpc")] ...) constructor matches the installed SDK version (mirrors main.py); if the SDK changed required fields, adjust the child's card construction.
  • create_jsonrpc_routes(... rpc_url="/", enable_v0_3_compat=True) real signature still accepted (it's the production call in boot_routes.py).
  • Boot-ordering test reaches the load_config sentinel without a real network/config dependency before it (steps 0–0.2 run for real: setup_telemetry no-op without exporter, ensure_workspace_writable best-effort). If an earlier step hard-fails in the runner, narrow the patch surface.
  • credential_helper test: runner has a real git binary (skipif guards otherwise); the detached refresh daemon spawned under the throwaway $HOME only logs (no platform reachable) and dies with the child session.

🤖 Generated with Claude Code

Closes #87 ## What Authors the **missing** `tests/test_boot_routes.py` referenced by `boot_routes.py`'s own docstring ("Test coverage at `tests/test_boot_routes.py` pins the contract") but absent until now, plus a `main()` boot-ordering test and a `credential_helper` real-`git` subprocess test. Wires an explicit `unit-tests`-job step so the gate runs and skips are loud. **Test layer: real-subprocess** (SOP rule internal#765 — mock-only does NOT satisfy a regression gate). ## Why real-subprocess (the un-stubbing) `tests/conftest.py` installs **stub** `a2a.*` modules into `sys.modules` at collect time (real `a2a-sdk` is a heavy workspace dep). Under those stubs `create_agent_card_routes` / `create_jsonrpc_routes` are no-op lambdas returning `None` — so an in-process test of `build_routes` would assert against the STUB, and a regression that broke the real wiring would still pass. These tests therefore drive the behaviour in **child Python processes that do not import this package's conftest**, so the REAL `a2a-sdk` (pulled by `pip install -e .` in CI) + real Starlette load, and the routes are exercised over real ASGI via Starlette's `TestClient`. The credential_helper test shells out to **real `git`** against a throwaway `$HOME`. ## Coverage (watch-fail intent per branch) `build_routes` four-branch card/JSON-RPC contract: 1. **Happy path (executor non-None)** — card route 200; `/` routed into real `DefaultRequestHandler`, NOT the -32603 not-configured error. _Watch-fail: dropping `create_agent_card_routes` → card 404 (the "stuck booting" UX PR #2756 fixed)._ 2. **Not-configured (executor None)** — card still 200; POST `/` → JSON-RPC `-32603`, reason echoed into `error.data`, request `id` echoed. _Watch-fail: re-coupling the JSON-RPC route to `adapter.setup()` → POST `/` 404/405 instead of structured -32603._ 3. **POST-only** — GET `/` 405 on the not-configured route; `None` reason → non-empty fallback. _Watch-fail: widening `methods=["POST"]`._ 4. **Mutual exclusion** — `/` JSON-RPC surface mounted by exactly one source (handler XOR not-configured), card route present in both. _Watch-fail: appending the not-configured route unconditionally → duplicate dead `/` mount._ `main()` boot ordering — `install_credential_helper` runs **before** `load_config`. _Watch-fail: reorder after `load_config` or drop it → the #1933 gh-token cascade (39 lost workspaces) re-opens._ `credential_helper` — after `install_credential_helper()`, real `git config --global --get credential.https://github.com.helper` returns `!<extracted-script>`, `useHttpPath=true`, and the script is extracted + executable. _Watch-fail: `_configure_git_credential_helper` stops shelling out / wrong key / no extraction._ ## CI wiring Auto-collected by the existing `pytest -q` in the `unit-tests` job. Added an explicit `pytest -v -rs tests/test_boot_routes.py` step so the gate is visible and a skip-if-real-a2a-absent is **loud** (never a silent green). ## DRAFT — what CI + CR2 must confirm (I cannot compile/run locally) - [ ] `unit-tests` job passes; the new gate step does NOT skip — confirm `A2A_REAL_OK` path (real `a2a-sdk[http-server]>=1.0.0` + `starlette.testclient` import in the child). A loud SKIP means the real SDK isn't installed and the gate is inert. - [ ] Real a2a-sdk 1.x `AgentCard(... supported_interfaces=[AgentInterface(url=..., protocol="jsonrpc")] ...)` constructor matches the installed SDK version (mirrors `main.py`); if the SDK changed required fields, adjust the child's card construction. - [ ] `create_jsonrpc_routes(... rpc_url="/", enable_v0_3_compat=True)` real signature still accepted (it's the production call in `boot_routes.py`). - [ ] Boot-ordering test reaches the `load_config` sentinel without a real network/config dependency before it (steps 0–0.2 run for real: `setup_telemetry` no-op without exporter, `ensure_workspace_writable` best-effort). If an earlier step hard-fails in the runner, narrow the patch surface. - [ ] `credential_helper` test: runner has a real `git` binary (skipif guards otherwise); the detached refresh daemon spawned under the throwaway `$HOME` only logs (no platform reachable) and dies with the child session. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
molecule-code-reviewer added 2 commits 2026-06-03 00:39:22 +00:00
Closes #87
ci(boot): run boot-routes real-subprocess regression gate explicitly with -rs (#87)
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 3s
ci / lint (pull_request) Successful in 51s
ci / build (pull_request) Successful in 53s
ci / smoke-install (pull_request) Successful in 1m45s
ci / unit-tests (pull_request) Failing after 1m46s
7d215b199b
Closes #87
molecule-code-reviewer marked the pull request as work in progress 2026-06-03 00:39:29 +00:00
Some required checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 3s
Required
Details
ci / lint (pull_request) Successful in 51s
Required
Details
ci / build (pull_request) Successful in 53s
Required
Details
ci / smoke-install (pull_request) Successful in 1m45s
Required
Details
ci / unit-tests (pull_request) Failing after 1m46s
Required
Details
This pull request is marked as a work in progress.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin test/issue-87-boot-routes-real-subprocess:test/issue-87-boot-routes-real-subprocess
git checkout test/issue-87-boot-routes-real-subprocess
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-ai-workspace-runtime#88