state-hub/tests/test_suggestions.py

107 lines
4.5 KiB
Python
Raw Normal View History

CUST-WP-0061-T06: retire suggestions to read-only legacy Founder-reviewed decision (WorkOrchestrationArchitectureDraft.md v0.2 section 8 item 6): the fresh intake work-record entity replaces suggestions, not a rename-bridge. All 5 mutation endpoints (create, vet, decline, promote, bump-relevance) now 410 with a pointer to POST /intakes/ and the migration doc; GET/list stay live for the historical record (10 rows migrated to file-backed intake records in the-custodian, see that repo's intake-legacy-suggestions-migration.md and CUST-IN-0001 through CUST-IN-0010). Removed dead code the retirement makes unreachable: Task/TaskPriority/ TaskStatus/normalize_task_status imports (only used by the deleted promote body), the suggestion_relevance.bump_relevance import, and the _ALLOWED_*_FROM stage-guard sets + _reject_stage helper (only used by the deleted vet/decline/promote bodies). WSJF ranking (compute_wsjf, cost_of_delay, suggestion_sort_key) stays -- still exercised by the surviving GET /suggestions/?rank=wsjf read path. MCP tool docstrings (create_suggestion, vet_suggestion, decline_suggestion, promote_suggestion_to_task, bump_suggestion_relevance) updated to point at the replacement (create_intake/route_intake/close_intake) rather than silently 410ing with no guidance. tests/test_suggestions.py rewritten: verifies all 5 mutations 410, GET/list still work for historical rows (seeded directly via the DB session since creation is retired -- there's no other way to get historical data into the table anymore), 404 still behaves normally on unknown ids. Live-verified against the running dev API: POST 410s, GET with include_terminal=true still returns all 10 migrated-and-declined historical rows. No regressions: full repo suite green (563 tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:59 +02:00
"""Suggestion (legacy) tests — CUST-WP-0061-T06, 2026-07-21.
The suggestions table/router is retired to read-only legacy: a fresh
`intake` work-record entity replaces it (canon/standards/
work-record-types_v0.1.md), not a rename. The 10 records still open at
retirement were migrated to file-backed intake records (see
the-custodian/intake-legacy-suggestions-migration.md) and declined here.
These tests verify the retirement itself: every mutation endpoint 410s;
GET/list still works for the historical record. Since creation is
retired, historical rows are seeded directly via the ORM/DB session
(bypassing the retired POST) rather than through the API.
"""
from __future__ import annotations
CUST-WP-0061-T06: retire suggestions to read-only legacy Founder-reviewed decision (WorkOrchestrationArchitectureDraft.md v0.2 section 8 item 6): the fresh intake work-record entity replaces suggestions, not a rename-bridge. All 5 mutation endpoints (create, vet, decline, promote, bump-relevance) now 410 with a pointer to POST /intakes/ and the migration doc; GET/list stay live for the historical record (10 rows migrated to file-backed intake records in the-custodian, see that repo's intake-legacy-suggestions-migration.md and CUST-IN-0001 through CUST-IN-0010). Removed dead code the retirement makes unreachable: Task/TaskPriority/ TaskStatus/normalize_task_status imports (only used by the deleted promote body), the suggestion_relevance.bump_relevance import, and the _ALLOWED_*_FROM stage-guard sets + _reject_stage helper (only used by the deleted vet/decline/promote bodies). WSJF ranking (compute_wsjf, cost_of_delay, suggestion_sort_key) stays -- still exercised by the surviving GET /suggestions/?rank=wsjf read path. MCP tool docstrings (create_suggestion, vet_suggestion, decline_suggestion, promote_suggestion_to_task, bump_suggestion_relevance) updated to point at the replacement (create_intake/route_intake/close_intake) rather than silently 410ing with no guidance. tests/test_suggestions.py rewritten: verifies all 5 mutations 410, GET/list still work for historical rows (seeded directly via the DB session since creation is retired -- there's no other way to get historical data into the table anymore), 404 still behaves normally on unknown ids. Live-verified against the running dev API: POST 410s, GET with include_terminal=true still returns all 10 migrated-and-declined historical rows. No regressions: full repo suite green (563 tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:59 +02:00
import uuid
CUST-WP-0061-T06: retire suggestions to read-only legacy Founder-reviewed decision (WorkOrchestrationArchitectureDraft.md v0.2 section 8 item 6): the fresh intake work-record entity replaces suggestions, not a rename-bridge. All 5 mutation endpoints (create, vet, decline, promote, bump-relevance) now 410 with a pointer to POST /intakes/ and the migration doc; GET/list stay live for the historical record (10 rows migrated to file-backed intake records in the-custodian, see that repo's intake-legacy-suggestions-migration.md and CUST-IN-0001 through CUST-IN-0010). Removed dead code the retirement makes unreachable: Task/TaskPriority/ TaskStatus/normalize_task_status imports (only used by the deleted promote body), the suggestion_relevance.bump_relevance import, and the _ALLOWED_*_FROM stage-guard sets + _reject_stage helper (only used by the deleted vet/decline/promote bodies). WSJF ranking (compute_wsjf, cost_of_delay, suggestion_sort_key) stays -- still exercised by the surviving GET /suggestions/?rank=wsjf read path. MCP tool docstrings (create_suggestion, vet_suggestion, decline_suggestion, promote_suggestion_to_task, bump_suggestion_relevance) updated to point at the replacement (create_intake/route_intake/close_intake) rather than silently 410ing with no guidance. tests/test_suggestions.py rewritten: verifies all 5 mutations 410, GET/list still work for historical rows (seeded directly via the DB session since creation is retired -- there's no other way to get historical data into the table anymore), 404 still behaves normally on unknown ids. Live-verified against the running dev API: POST 410s, GET with include_terminal=true still returns all 10 migrated-and-declined historical rows. No regressions: full repo suite green (563 tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:59 +02:00
import pytest
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
from tests.test_capability_requests import _create_domain
async def _seed_suggestion(test_engine, *, domain_slug: str, **overrides) -> str:
"""Insert a Suggestion row directly, bypassing the retired POST
endpoint this is now the only way historical rows get created."""
from api.models.domain import Domain
from api.models.suggestion import Suggestion
from sqlalchemy import select
factory = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
async with factory() as session:
domain_id = (
await session.execute(select(Domain.id).where(Domain.slug == domain_slug))
).scalar_one()
suggestion = Suggestion(
domain_id=domain_id,
title=overrides.get("title", "Legacy suggestion"),
description=overrides.get("description"),
origin=overrides.get("origin"),
origin_ref=overrides.get("origin_ref"),
base_value=overrides.get("base_value", 3.0),
job_size=overrides.get("job_size", 3.0),
)
session.add(suggestion)
await session.commit()
await session.refresh(suggestion)
return str(suggestion.id)
@pytest.mark.asyncio
CUST-WP-0061-T06: retire suggestions to read-only legacy Founder-reviewed decision (WorkOrchestrationArchitectureDraft.md v0.2 section 8 item 6): the fresh intake work-record entity replaces suggestions, not a rename-bridge. All 5 mutation endpoints (create, vet, decline, promote, bump-relevance) now 410 with a pointer to POST /intakes/ and the migration doc; GET/list stay live for the historical record (10 rows migrated to file-backed intake records in the-custodian, see that repo's intake-legacy-suggestions-migration.md and CUST-IN-0001 through CUST-IN-0010). Removed dead code the retirement makes unreachable: Task/TaskPriority/ TaskStatus/normalize_task_status imports (only used by the deleted promote body), the suggestion_relevance.bump_relevance import, and the _ALLOWED_*_FROM stage-guard sets + _reject_stage helper (only used by the deleted vet/decline/promote bodies). WSJF ranking (compute_wsjf, cost_of_delay, suggestion_sort_key) stays -- still exercised by the surviving GET /suggestions/?rank=wsjf read path. MCP tool docstrings (create_suggestion, vet_suggestion, decline_suggestion, promote_suggestion_to_task, bump_suggestion_relevance) updated to point at the replacement (create_intake/route_intake/close_intake) rather than silently 410ing with no guidance. tests/test_suggestions.py rewritten: verifies all 5 mutations 410, GET/list still work for historical rows (seeded directly via the DB session since creation is retired -- there's no other way to get historical data into the table anymore), 404 still behaves normally on unknown ids. Live-verified against the running dev API: POST 410s, GET with include_terminal=true still returns all 10 migrated-and-declined historical rows. No regressions: full repo suite green (563 tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 01:51:59 +02:00
class TestSuggestionsRetired:
async def test_create_returns_410(self, client):
await _create_domain(client, "custodian", "Custodian")
r = await client.post("/suggestions/", json={
"domain": "custodian",
"title": "New suggestion attempt",
})
assert r.status_code == 410
assert "intake" in r.json()["detail"].lower()
async def test_vet_returns_410(self, client, test_engine):
await _create_domain(client, "custodian", "Custodian")
sid = await _seed_suggestion(test_engine, domain_slug="custodian")
r = await client.post(f"/suggestions/{sid}/vet", json={"note": "x"})
assert r.status_code == 410
async def test_decline_returns_410(self, client, test_engine):
await _create_domain(client, "custodian", "Custodian")
sid = await _seed_suggestion(test_engine, domain_slug="custodian")
r = await client.post(f"/suggestions/{sid}/decline", json={"note": "x"})
assert r.status_code == 410
async def test_promote_returns_410(self, client, test_engine):
await _create_domain(client, "custodian", "Custodian")
sid = await _seed_suggestion(test_engine, domain_slug="custodian")
r = await client.post(f"/suggestions/{sid}/promote", json={})
assert r.status_code == 410
async def test_bump_relevance_returns_410(self, client, test_engine):
await _create_domain(client, "custodian", "Custodian")
sid = await _seed_suggestion(test_engine, domain_slug="custodian")
r = await client.post(f"/suggestions/{sid}/bump-relevance", json={})
assert r.status_code == 410
async def test_get_and_list_still_work_for_historical_rows(self, client, test_engine):
await _create_domain(client, "custodian", "Custodian")
sid = await _seed_suggestion(
test_engine, domain_slug="custodian",
title="Historical gated-need signal",
origin="WARDEN-WP-0012", origin_ref="example",
)
r = await client.get(f"/suggestions/{sid}")
assert r.status_code == 200
assert r.json()["title"] == "Historical gated-need signal"
r2 = await client.get("/suggestions/")
assert r2.status_code == 200
assert any(s["id"] == sid for s in r2.json())
async def test_unknown_id_still_404s_not_410(self, client):
"""Read paths keep their normal semantics; only mutations are
blanket-retired."""
r = await client.get(f"/suggestions/{uuid.uuid4()}")
assert r.status_code == 404