test(phase8 #44 H3): provider-aware Hurl sync to /api/v2 + v1 ghost guard#1675
Merged
Conversation
…uard Per architect canonical D7-1 (msg=8b6b4bc3) the OpenAI-compatible ``/api/v1/embeddings`` and ``/api/v1/rerank`` endpoints are pinned as permanent ``/api/v1`` mounts. The other v1 LLM provider routes (``/api/v1/llm_configuration``, ``/api/v1/llm_providers/*``, ``/api/v1/available_models``, ``/api/v1/default_models``) were already removed from main when ``providers_v2_routes.py`` shipped, so the provider-aware Hurl test was hitting 404s and bringing ``e2e-http-smoke`` red on every PR. Changes: - ``tests/e2e_http/hurl/full/10_provider_llm.hurl`` — rewrite all internal provider/model/default CRUD calls to ``/api/v2/providers/*`` and ``/api/v2/default-models``. Keep ``/api/v1/embeddings`` and ``/api/v1/rerank`` calls unchanged (OpenAI-compat allowlist). Adjust the create-model body to drop ``provider_name`` (now a path param in v2) and the delete-model assertion to expect 204 (v2 pure-command contract). - ``tests/unit_test/test_provider_v2_openapi_contract.py`` — empty out ``PROVIDER_V1_GHOST_PATHS`` baseline; the v1 LLM provider routes are no longer mounted, so the inventory baseline shrinks to zero and the existing stability test now strictly forbids regrowth. - ``tests/unit_test/test_v1_ghost_guard.py`` — new negative-allowlist guard that scans ``tests/e2e_http/hurl/**`` and asserts every ``/api/v1/...`` literal matches either the OpenAI-compat permanent allowlist or the transitional pre-migration set (apikeys, audit, marketplace, prompts, settings, export, collections, chats, quotas, system, bots, test). Each follow-up G* PR shrinks ``TRANSITIONAL_V1_PREFIXES`` as it migrates the corresponding domain to ``/api/v2``.
5 tasks
earayu
added a commit
that referenced
this pull request
Apr 25, 2026
…s hard-cut Phase 8 second batch G4a — per architect canonical D7-2 (msg=94f663f2 §3.2.2) + Option A locked (msg=25a30445), the legacy combined ``governance_router`` (which mounted both API keys and audit logs at ``/api/v1``) is split into two single-purpose routers so #50 G4a (audit-logs) and #51 G4b (apikeys) can migrate to ``/api/v2`` independently. This PR lands the audit-logs side: ``/api/v1/audit-logs*`` is hard-cut to ``/api/v2/audit-logs*`` (no v1 alias). The ``aperag/openapi_spec.py:23`` hidden filter is preserved so audit-logs stay outside the public OpenAPI spec. The apikeys router stays at ``/api/v1/apikeys*`` for now — #51 will flip its mount to ``/api/v2`` in a 1-line follow-up. File structure follows architect-recommended (β) pattern: per-router file, matching the convention of #36 / G1 / G2 / G3 / G4d carved routers. Changes: * Rename ``aperag/domains/governance/api/routes.py`` → ``aperag/domains/governance/api/audit_routes.py``; trim it to only audit routes + needed imports. * New ``aperag/domains/governance/api/apikeys_routes.py`` — apikey routes + needed imports; identical handler signatures (no behavior change). * ``aperag/app.py`` — replace single ``governance_router`` import + mount with two imports and split mounts: ``audit_router → /api/v2`` (this PR's hard-cut), ``apikeys_router → /api/v1`` (preserved; #51 will flip). * New boundary test ``test_no_module_imports_legacy_governance_routes`` — analogous to ``test_no_module_imports_legacy_views_settings`` from #48, forbids re-import of the deleted combined ``aperag.domains.governance.api.routes`` module. Total boundary tests now 23. * ``tests/unit_test/test_v1_ghost_guard.py`` — TRANSITIONAL_V1_PREFIXES shrinks ``/api/v1/audit-logs`` (G4a removed). 11 → 10 prefixes remaining, matching the "clean as you go" pattern established in #1675 H3. FE follow-up: ``web/src/features/audit/{client,server}-api.ts`` v1→v2 caller sync will be a sibling PR by @dongdong (per architect spec) once the backend OpenAPI diff is available. Gates: 25/25 boundary + ghost guard, 698 pass / 29 skip / 1 deselect / 0 fail unit suite, ruff check + format clean.
earayu
added a commit
that referenced
this pull request
Apr 25, 2026
…s hard-cut (#1684) * refactor(phase8 #50 G4a): split governance routes + /api/v2/audit-logs hard-cut Phase 8 second batch G4a — per architect canonical D7-2 (msg=94f663f2 §3.2.2) + Option A locked (msg=25a30445), the legacy combined ``governance_router`` (which mounted both API keys and audit logs at ``/api/v1``) is split into two single-purpose routers so #50 G4a (audit-logs) and #51 G4b (apikeys) can migrate to ``/api/v2`` independently. This PR lands the audit-logs side: ``/api/v1/audit-logs*`` is hard-cut to ``/api/v2/audit-logs*`` (no v1 alias). The ``aperag/openapi_spec.py:23`` hidden filter is preserved so audit-logs stay outside the public OpenAPI spec. The apikeys router stays at ``/api/v1/apikeys*`` for now — #51 will flip its mount to ``/api/v2`` in a 1-line follow-up. File structure follows architect-recommended (β) pattern: per-router file, matching the convention of #36 / G1 / G2 / G3 / G4d carved routers. Changes: * Rename ``aperag/domains/governance/api/routes.py`` → ``aperag/domains/governance/api/audit_routes.py``; trim it to only audit routes + needed imports. * New ``aperag/domains/governance/api/apikeys_routes.py`` — apikey routes + needed imports; identical handler signatures (no behavior change). * ``aperag/app.py`` — replace single ``governance_router`` import + mount with two imports and split mounts: ``audit_router → /api/v2`` (this PR's hard-cut), ``apikeys_router → /api/v1`` (preserved; #51 will flip). * New boundary test ``test_no_module_imports_legacy_governance_routes`` — analogous to ``test_no_module_imports_legacy_views_settings`` from #48, forbids re-import of the deleted combined ``aperag.domains.governance.api.routes`` module. Total boundary tests now 23. * ``tests/unit_test/test_v1_ghost_guard.py`` — TRANSITIONAL_V1_PREFIXES shrinks ``/api/v1/audit-logs`` (G4a removed). 11 → 10 prefixes remaining, matching the "clean as you go" pattern established in #1675 H3. FE follow-up: ``web/src/features/audit/{client,server}-api.ts`` v1→v2 caller sync will be a sibling PR by @dongdong (per architect spec) once the backend OpenAPI diff is available. Gates: 25/25 boundary + ghost guard, 698 pass / 29 skip / 1 deselect / 0 fail unit suite, ruff check + format clean. * fix(phase8 #50 G4a): sync HIDDEN_FROM_PUBLIC_PATH_PREFIXES audit-logs to /api/v2 + add regression guard Per @chenyexuan replacement CR (msg=1b0fe11c) — the original G4a backend correctly mounted the audit router at ``/api/v2`` but missed updating the ``HIDDEN_FROM_PUBLIC_PATH_PREFIXES`` filter in ``aperag/openapi_spec.py``, which was still keyed on ``/api/v1/audit-logs``. After the mount switch the filter no longer matched any path, leaking ``/api/v2/audit-logs*`` into the public OpenAPI spec — a violation of D7-2 ``hidden 保持`` and architect canonical msg=25a30445. Fix: - ``aperag/openapi_spec.py:23`` — flip prefix from ``/api/v1/audit-logs`` → ``/api/v2/audit-logs``. One-line. - ``tests/unit_test/test_openapi_spec.py``: - Update the existing synthetic-spec filter test to assert v2 paths (was v1; the assertion would otherwise pass vacuously after the real prefix changed). - Add new ``test_audit_logs_hidden_from_public_openapi_for_live_app`` — runs the real ``aperag.app`` through ``build_full_openapi_spec`` + ``filter_public_openapi`` and asserts no audit-logs path leaks into the public spec. This is the regression guard the architect + reviewer recommended so a future refactor cannot silently re-leak. No other write set in scope. Pure 1-line canonical fix + 1 new test + 1 small test rewrite. The Option A router split, mount changes, boundary gate, ghost guard shrink, and #51 hand-off shape from the original PR head are unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 8 第二批 task #44 (H3 — provider-aware Hurl sync after
/api/v1ghost hard-cut). Sibling to #43 (H1 ruff format sweep, weihong) and #46 (H4 graceful chat collection, chenyexuan).Per architect canonical D7-1 (msg=8b6b4bc3)
/api/v1/embeddingsand/api/v1/rerankare pinned as permanent OpenAI-compat mounts. The other v1 LLM provider routes (/api/v1/llm_configuration,/api/v1/llm_providers/*,/api/v1/available_models,/api/v1/default_models) were already removed from main whenproviders_v2_routes.pyshipped, so the provider-aware Hurl test was hitting 404s and bringinge2e-http-smokered on every Phase 8 first-batch PR (root cause flagged in #41 H3 evidence by weihong).Changes
tests/e2e_http/hurl/full/10_provider_llm.hurl— rewrite all internal provider/model/default CRUD calls to/api/v2/providers/*and/api/v2/default-models. Keep/api/v1/embeddingsand/api/v1/rerankcalls unchanged (allowlist). Adjust the create-model body to dropprovider_name(now path param in v2) and the delete-model assertion to expect 204 (v2 pure-command contract).tests/unit_test/test_provider_v2_openapi_contract.py— empty outPROVIDER_V1_GHOST_PATHSbaseline; the v1 LLM provider routes are no longer mounted, so the inventory baseline shrinks to zero and the existing stability test now strictly forbids regrowth.tests/unit_test/test_v1_ghost_guard.py— new negative-allowlist guard that scanstests/e2e_http/hurl/**and asserts every/api/v1/...literal matches either the OpenAI-compat permanent allowlist (/api/v1/embeddings,/api/v1/rerank) or the transitional pre-migration set (apikeys, audit, marketplace, prompts, settings, export, collections, chats, quotas, system, bots, test). Each follow-up G* PR shrinksTRANSITIONAL_V1_PREFIXESas it migrates the corresponding domain to/api/v2.Net: +136 / -29 LOC (1 new test file 115 LOC, 2 edits).
Scope discipline
This PR strictly does not include:
Test plan
uv run python -m pytest tests/unit_test/test_v1_ghost_guard.py tests/unit_test/test_provider_v2_openapi_contract.py tests/unit_test/test_modularization_boundaries.py -x -q→ 28 passeduv run python -m pytest tests/unit_test -q --deselect 'tests/unit_test/test_web_typed_api_contract.py::test_phase1_fe_complete_identity_auth_admin_audit_adapter_boundary'→ 688 passed / 28 skipped / 1 deselected / 0 faileduv run ruff check aperag/ tests/ config/→ cleanuv run ruff format --check tests/unit_test/test_v1_ghost_guard.py→ clean (other tests/ format drift is mainline existing, scoped to [BUG] get_collections_cron_job failed #43 H1)full/10_provider_llm.hurl— runs in CI; expected to no longer 404 on/api/v1/llm_configurationetc.CR ask
@weston blocker-level minimal CR — write-set is provider-aware Hurl + 2 unit tests; no backend touched.
@符炫炜 canonical drift quick check — preserve allowlist (embeddings/rerank), no scope creep into G5.
🤖 Generated with Claude Code