fix(api): R14 — promote route translates V1-0 ValueError to 400
POST /entities/{id}/promote now wraps the promote_entity call in
try/except ValueError → HTTPException(400). Previously the new
V1-0 provenance re-check raised ValueError that the route didn't
catch, so legacy no-provenance candidates promoted via the API
surfaced as 500 instead of 400.
Matches the existing ValueError → 400 handling on POST /entities
(api_create_entity at routes.py:1490).
Test: test_api_promote_returns_400_on_legacy_no_provenance inserts
a pre-V1-0 legacy candidate directly, POSTs to the promote route,
asserts 400 with the expected detail, asserts the row stays
candidate.
Test count: 547 -> 548. Full suite green in 72.91s.
Closes R14.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -160,6 +160,42 @@ def test_promote_rejects_legacy_candidate_without_provenance(tmp_data_dir):
|
||||
assert got.status == "candidate"
|
||||
|
||||
|
||||
def test_api_promote_returns_400_on_legacy_no_provenance(tmp_data_dir):
|
||||
"""R14 (Codex, 2026-04-22): the HTTP promote route must translate
|
||||
the V1-0 ValueError for no-provenance candidates into 400, not 500.
|
||||
Previously the route didn't catch ValueError so legacy bad
|
||||
candidates surfaced as a server error."""
|
||||
init_db()
|
||||
init_engineering_schema()
|
||||
|
||||
import uuid as _uuid
|
||||
from fastapi.testclient import TestClient
|
||||
from atocore.main import app
|
||||
|
||||
entity_id = str(_uuid.uuid4())
|
||||
with get_connection() as conn:
|
||||
conn.execute(
|
||||
"INSERT INTO entities (id, entity_type, name, project, "
|
||||
"description, properties, status, confidence, source_refs, "
|
||||
"extractor_version, canonical_home, hand_authored, "
|
||||
"created_at, updated_at) "
|
||||
"VALUES (?, 'component', 'Legacy HTTP', 'p04-gigabit', "
|
||||
"'', '{}', 'candidate', 1.0, '[]', '', 'entity', 0, "
|
||||
"CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)",
|
||||
(entity_id,),
|
||||
)
|
||||
|
||||
client = TestClient(app)
|
||||
r = client.post(f"/entities/{entity_id}/promote")
|
||||
assert r.status_code == 400
|
||||
assert "source_refs required" in r.json().get("detail", "")
|
||||
|
||||
# Row still candidate — the 400 didn't half-transition.
|
||||
got = get_entity(entity_id)
|
||||
assert got is not None
|
||||
assert got.status == "candidate"
|
||||
|
||||
|
||||
def test_promote_accepts_candidate_flagged_hand_authored(tmp_data_dir):
|
||||
"""The other side of the promote re-check: hand_authored=1 with
|
||||
empty source_refs still lets promote succeed, matching
|
||||
|
||||
Reference in New Issue
Block a user