9ca07a3f81
- collections.py resolution helpers (default_collection_id, type, initial_state) - registry mirror writes project grouping fields + default-collection corpus fields - auth.project_of_rfc joins collections; project_member_role reads memberships - cache/api_*/funder writers+readers re-keyed to collection_id (cached_prs + denormalised project_id tags unchanged); api_deployment reads type/initial_state from the default collection - projects.py restamp detects bootstrap via collections; initial_state via collection - tests updated to the three-tier schema; test_migration_028 retired (superseded by 029) - add @S1 acceptance test (collection grain + N=1 serving) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
79 lines
3.2 KiB
Python
79 lines
3.2 KiB
Python
"""@S1 acceptance — the collection grain exists (invisible default) and N=1 is
|
|
unchanged.
|
|
|
|
Part C scenarios C3.7 (single-collection project skips the directory) and C3.8
|
|
(single-project deployment skips the directory) are the client-side redirect
|
|
contract asserted in the frontend; this module asserts the backend N=1
|
|
invariants behind the slice: every entry keys on a real collection_id, the
|
|
shipped project-scoped serving still resolves through the default collection,
|
|
and the legacy /rfc/<slug> URL 308-redirects through /c/<default>/.
|
|
|
|
Binding: docs/design/2026-06-05-three-tier-projects-collections.md §A.6 / Part E.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
from test_propose_vertical import ( # noqa: F401
|
|
app_with_fake_gitea, tmp_env, provision_user_row, sign_in_as,
|
|
)
|
|
|
|
|
|
def _seed_entry(slug, title, collection_id="default", state="active"):
|
|
from app import db
|
|
db.conn().execute(
|
|
"INSERT INTO cached_rfcs (slug, title, state, collection_id) VALUES (?, ?, ?, ?)",
|
|
(slug, title, state, collection_id),
|
|
)
|
|
|
|
|
|
def test_s1_migration_seeds_one_default_collection_for_the_default_project(app_with_fake_gitea):
|
|
from app import db
|
|
app, _ = app_with_fake_gitea
|
|
with TestClient(app):
|
|
row = db.conn().execute(
|
|
"SELECT id FROM collections WHERE project_id = 'default'"
|
|
).fetchall()
|
|
assert len(row) == 1
|
|
assert row[0]["id"] == "default"
|
|
|
|
|
|
def test_s1_entry_served_under_default_collection(app_with_fake_gitea):
|
|
"""N=1 unchanged: an entry is keyed by collection_id under the hood and the
|
|
shipped project-scoped serving endpoint still resolves it."""
|
|
from app import db
|
|
app, _ = app_with_fake_gitea
|
|
with TestClient(app) as client:
|
|
_seed_entry("human", "Human")
|
|
# the row carries a real collection grain (the default collection)
|
|
cid = db.conn().execute(
|
|
"SELECT collection_id FROM cached_rfcs WHERE slug='human'"
|
|
).fetchone()["collection_id"]
|
|
assert cid == "default"
|
|
# project-scoped serving (collection = default) still returns it
|
|
r = client.get("/api/projects/default/rfcs/human")
|
|
assert r.status_code == 200, r.text
|
|
assert r.json()["slug"] == "human"
|
|
# and it appears in the project catalog
|
|
slugs = [i["slug"] for i in client.get("/api/projects/default/rfcs").json()["items"]]
|
|
assert "human" in slugs
|
|
|
|
|
|
def test_s1_legacy_rfc_url_redirects_through_collection(app_with_fake_gitea):
|
|
"""The shipped /rfc/<slug> now 308s through the default collection segment."""
|
|
app, _ = app_with_fake_gitea
|
|
with TestClient(app) as client:
|
|
r = client.get("/rfc/human", follow_redirects=False)
|
|
assert r.status_code == 308
|
|
assert r.headers["location"] == "/p/default/c/default/e/human"
|
|
|
|
|
|
def test_s1_deployment_reports_single_project(app_with_fake_gitea):
|
|
"""C3.8 precondition: the N=1 deployment reports exactly one visible project
|
|
and its default id (the frontend uses this to skip the directory)."""
|
|
app, _ = app_with_fake_gitea
|
|
with TestClient(app) as client:
|
|
body = client.get("/api/deployment").json()
|
|
assert body["default_project_id"] == "default"
|
|
assert [p["id"] for p in body["projects"]] == ["default"]
|