Skip to content

Split the gateway endpoints and their tests under the file-length limit - #930

Merged
anth-volk merged 1 commit into
mainfrom
file-length-split-gateway-endpoints
Oct 27, 2026
Merged

anth-volk merged 1 commit into
mainfrom
file-length-split-gateway-endpoints

Conversation

@anth-volk

Copy link
Copy Markdown
Contributor

Fixes #929

Same-Repository Draft PR Check

  • This PR is a draft.
  • This PR is from a branch in PolicyEngine/policyengine-sim-api, not a personal fork.

Summary

Third of nine stacked PRs; it builds on #927. It splits the gateway's endpoints.py and test_endpoints.py without changing behaviour, and removes both from the file-length baseline (count 12 to 10).

Source: src/policyengine_simulation_gateway/endpoints.py (1,454 lines)

The code moves verbatim. A script compared every top-level statement of the old module with the new modules: all 64 appear exactly once, unchanged apart from the renames below and the logger line, in their original order.

Module Lines Holds
routes_common.py 53 the router, the logger, and the request and observability-header helpers every route module uses
route_resolution.py 312 RouteResolution, SUPPORTED_ROUTE_KINDS, and resolving a request's app from the active routing state
modal_routing.py 147 the Modal Dict names; Modal reads of job metadata, the routing state and the legacy version Dicts; Modal error classification; resolve_route and get_app_name
bundle_payloads.py 200 the release bundle a route serves, as responses and job payloads report it
job_creation_routes.py 409 POST /simulate/economy/comparison and the budget-window submission, with the helpers only they use
job_status_routes.py 336 GET /jobs/{job_id} and the budget-window status route, with the helpers only they use
version_routes.py 124 /versions, /versions/{kind}, /health and /ping
endpoints.py 55 re-exports of every public name, in __all__
  • Routes and OpenAPI: importing the three route modules (in the order ruff sorts them, which is the original order) registers the same routes on the same router in the same order. The OpenAPI document, serialized in its own key order, and the route list are byte-for-byte identical before and after, and the golden file is unchanged.
  • Logging: the moved routes keep logging under policyengine_simulation_gateway.endpoints. Since __name__ in the new module would differ, routes_common.py names that logger explicitly; this is the one statement that isn't a verbatim move.

Renames

Private names another module now uses are public, each with a one-line docstring where it had none (ruff requires one): _active_routing_state, _bind_observability_id, _build_policyengine_bundle, _bundle_identities, _bundle_payload, _is_modal_job_not_found, _job_metadata_store, _observability_headers, _optional_modal_dict, _policyengine_version_from_app_name, _record_not_found, _resolve_from_active_state, _routing_state_bundles, _routing_state_latest and _routing_state_routes drop their underscore; _runtime is observability_runtime, since the routes already have local variables named runtime.

Tests: tests/test_endpoints.py (1,801 lines)

File Lines Holds
tests/test_endpoints.py 284 TestGetAppName, TestVersionEndpoints
tests/test_simulation_submission.py 253 TestSubmitSimulationEndpoint: routing, spawning and observability ids
tests/test_simulation_datasets.py 432 TestSubmitSimulationEndpoint: dataset and bundle checks
tests/test_simulation_job_status.py 272 TestSubmitSimulationEndpoint: polling
tests/test_budget_window_endpoints.py 548 TestBudgetWindowBatchEndpoints
fixtures/gateway_responses.py 56 OBSERVABILITY_ID, LOCAL_AREA_BREAKDOWN_RESULT, expected_bundle (with a docstring added, as fixtures/ takes the full ruff rules)
  • 78 tests before and after, with the same class and test names; TestSubmitSimulationEndpoint keeps its name in each of the three files that hold its tests. A script compared every method and top-level statement of the old file with the new files: all appear exactly once and unchanged.
  • Patch targets. A patch replaces a name in one module only. After the split, modal is read in four modules (job_creation_routes, job_status_routes, modal_routing, version_routes), checked by a script that lists, for every patch, the modules that read the name:
    • the gateway's mock_modal fixture and the executor's budget_window_semi_integration_client fixture patch modal in all four, in place of endpoints;
    • tests/test_job_progress.py::TestStageReportingFailuresAreHarmless::test__unreadable_store_drops_stage_fields patches get_job_progress in job_status_routes, where the status route reads it;
    • in the executor's tests/test_canonical_spm.py, test_gateway_poll_returns_structured_400 patches job_metadata_store and modal in job_status_routes; test_gateway_submission_uses_registry_capability_before_spawn patches resolve_route, build_policyengine_bundle, modal and job_metadata_store in job_creation_routes; and test_versions_http_retains_capability_contract_version patches active_routing_state in version_routes. Each is the module the request in that test runs through.
    • One difference to note: in test_gateway_poll_returns_structured_400, the fake modal has only FunctionCall. Before, the status route's check for a Modal not-found error read modal.exception from the same fake, found none, and treated the error as not a Modal one. That check now lives in modal_routing, so it reads the real modal.exception, whose not-found classes the test's SPMInputError is not an instance of. The result, and the test, are the same.
  • tests/test_active_state_routes.py imports resolve_from_active_state from route_resolution, and the executor's test_as_of_presence_survives_budget_parent imports _build_budget_window_parent_payload and _resolve_request_spm from job_creation_routes, where they now live.
  • fixtures/package_imports.py: the import isolation fixture also clears the new modules from sys.modules, so test_gateway_endpoints_import_does_not_import_policyengine_bundle still re-imports all of the endpoint code.

Environment variables

No change: the moved code reads the same variables, in the same functions, and no workflow, image or deployment file changes.

Testing

  • Gateway: uv run --frozen pytest tests: 268 passed, including test_openapi_golden.py.
  • Executor: test_canonical_spm.py and test_budget_window_scheduler.py, which drive the gateway: 67 passed; the full executor suite is run again on this branch.
  • Pyright for the gateway: no errors.
  • Ruff 0.15.22 format and lint: clean.
  • python scripts/run_quality_guards.py and python -m scripts.rules guard --base-ref origin/file-length-split-version-registry: passed; python -m scripts.rules render leaves no diff.

Only the lowest PR in the stack runs full CI, so this PR's CI complete check fails by design until the PRs below it merge.

🤖 Generated with Claude Code

@anth-volk
anth-volk added this pull request to stack #928 October 27, 2026 12:52
@anth-volk
anth-volk force-pushed the file-length-split-gateway-endpoints branch from 1781f17 to 7aa565f Compare October 27, 2026 13:45
@anth-volk
anth-volk force-pushed the file-length-split-gateway-endpoints branch from 7aa565f to 1781f17 Compare October 27, 2026 13:55
Base automatically changed from file-length-split-version-registry to main October 27, 2026 14:48
The gateway's endpoints.py (1,454 lines) and test_endpoints.py (1,801
lines) were over the file-length limits. The code moves, unchanged, into:

- routes_common.py: the router, the logger, and the request and
  observability-header helpers every route module uses
- route_resolution.py: resolving a request's app from the active routing
  state
- modal_routing.py: Modal reads of job metadata, the routing state and
  the legacy version Dicts, and resolve_route
- bundle_payloads.py: the release bundle a route serves
- job_creation_routes.py, job_status_routes.py and version_routes.py: the
  routes, each with the helpers only it uses

endpoints.py re-exports every public name. Importing the three route
modules registers their routes on the router in the original order, so
the OpenAPI document is byte-for-byte the same, golden file included.
The routes keep logging under the policyengine_simulation_gateway.endpoints
logger, now named explicitly in routes_common.py.

Private helpers another module now uses are public, with a docstring
where they had none: active_routing_state, bind_observability_id,
build_policyengine_bundle, bundle_identities, bundle_payload,
is_modal_job_not_found, job_metadata_store, observability_headers,
optional_modal_dict, policyengine_version_from_app_name, record_not_found,
resolve_from_active_state, routing_state_bundles, routing_state_latest,
routing_state_routes, and _runtime as observability_runtime.

The tests move to test_endpoints.py, test_simulation_submission.py,
test_simulation_datasets.py, test_simulation_job_status.py and
test_budget_window_endpoints.py, with the shared expected responses in
fixtures/gateway_responses.py; all 78 keep their names and bodies.
Patches now target the module that looks each name up: the Modal fakes
in the gateway fixture and the executor's budget-window fixture patch
modal in each of the four modules that read it, and the executor's SPM
and job-progress tests patch the route module the request runs through.
The import isolation fixture also clears the new modules, so importing
endpoints re-runs all of the endpoint code as before.

The file-length baseline drops both files (count 12 to 10).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@anth-volk
anth-volk force-pushed the file-length-split-gateway-endpoints branch from 1781f17 to 17f9fb0 Compare October 27, 2026 14:48
@anth-volk
anth-volk marked this pull request as ready for review October 27, 2026 16:16
@anth-volk
anth-volk requested a review from a team as a code owner October 27, 2026 16:16
@anth-volk
anth-volk merged commit 294fef2 into main Oct 27, 2026
23 of 24 checks passed
@anth-volk
anth-volk deleted the file-length-split-gateway-endpoints branch October 27, 2026 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Split the gateway endpoints and their tests under the file-length limit

1 participant