Module and test docstrings state what is pinned without the record number or step that introduced it. Incident accounts become the failure shape in the present tense: the pipefail head, the code_shapes import, the export order, the facet dialects, the cross-kind repeat, the write-once kind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
99 lines
4.2 KiB
Python
99 lines
4.2 KiB
Python
"""Real-Postgres integration tests for project inception.
|
|
|
|
What mocks can't prove: a decision's effects land through the real services
|
|
(the design system points, the standard Systems seed once), the record is
|
|
written last, a bad target applies nothing.
|
|
|
|
Rules are not part of inception: a rule in a rulebook is
|
|
global and applies to every project, so there is nothing to subscribe to.
|
|
"""
|
|
import pytest
|
|
import pytest_asyncio
|
|
|
|
from scribe.models import async_session
|
|
from scribe.models.project import Project
|
|
from scribe.services import inception as inception_svc
|
|
from scribe.services import canonical_systems as canonical_svc
|
|
from scribe.services import systems as systems_svc
|
|
from tests.helpers import ensure_user
|
|
|
|
pytestmark = [pytest.mark.integration, pytest.mark.usefixtures("_dispose_engine")]
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def seeded():
|
|
"""Owner and a fresh, undecided project."""
|
|
async with async_session() as s:
|
|
owner = await ensure_user(s, "inception_owner")
|
|
project = Project(user_id=owner.id, title="Inception target")
|
|
s.add(project)
|
|
await s.flush()
|
|
ids = {"owner": owner.id, "pid": project.id}
|
|
await s.commit()
|
|
return ids
|
|
|
|
|
|
@pytest.mark.integration
|
|
async def test_decide_applies_every_effect_and_records_last(seeded):
|
|
owner, pid = seeded["owner"], seeded["pid"]
|
|
defaults = await inception_svc.current_defaults(owner, pid)
|
|
assert set(defaults) == {
|
|
"design_system_id", "design_systems", "systems", "platforms", "project_platforms",
|
|
}
|
|
assert defaults["systems"] == 0 and defaults["design_system_id"] is None
|
|
|
|
out = await inception_svc.decide(owner, pid, via="mcp", choices={
|
|
"design_system_id": None,
|
|
"seed_systems": True,
|
|
})
|
|
assert set(out["effects"]) == {"design_system_id", "systems_seeded", "platforms"}
|
|
catalog = await canonical_svc.list_canonical_systems()
|
|
assert len(out["effects"]["systems_seeded"]) == len(catalog)
|
|
# Seeded Systems come out mapped, not needing a later reconciliation.
|
|
seeded_systems = await systems_svc.list_systems(owner, pid)
|
|
assert all(s.canonical_id is not None for s in seeded_systems)
|
|
|
|
# The record, written last, says why.
|
|
async with async_session() as s:
|
|
project = await s.get(Project, pid)
|
|
assert inception_svc.is_decided(project)
|
|
assert project.inception["via"] == "mcp" and project.inception["decided_by"] == owner
|
|
assert project.inception["choices"] == {
|
|
"design_system_id": None, "seed_systems": True, "platforms": None,
|
|
}
|
|
# Re-deciding with seed again mints nothing twice.
|
|
again = await inception_svc.decide(owner, pid, via="ui", choices={"seed_systems": True})
|
|
assert again["effects"]["systems_seeded"] == []
|
|
assert len(await systems_svc.list_systems(owner, pid)) == len(catalog)
|
|
|
|
|
|
@pytest.mark.integration
|
|
async def test_a_bad_decision_applies_nothing(seeded):
|
|
owner, pid = seeded["owner"], seeded["pid"]
|
|
# A design system that is not readable is refused BEFORE any effect
|
|
# lands — the seed must not happen on a decision that fails.
|
|
with pytest.raises(ValueError, match="not found"):
|
|
await inception_svc.decide(owner, pid, via="mcp", choices={
|
|
"design_system_id": 999999, "seed_systems": True,
|
|
})
|
|
assert await systems_svc.list_systems(owner, pid) == []
|
|
# The retired rulebook choice is an unknown key, refused whole: a caller
|
|
# still passing it must not believe a rulebook was subscribed.
|
|
with pytest.raises(ValueError, match="unknown inception choice"):
|
|
await inception_svc.decide(owner, pid, via="mcp", choices={
|
|
"subscribe_rulebooks": [1], "seed_systems": True,
|
|
})
|
|
assert await systems_svc.list_systems(owner, pid) == []
|
|
with pytest.raises(ValueError, match="legacy"):
|
|
await inception_svc.decide(owner, pid, via="legacy", choices={})
|
|
async with async_session() as s:
|
|
project = await s.get(Project, pid)
|
|
assert not inception_svc.is_decided(project)
|
|
# An outsider cannot decide someone else's project.
|
|
async with async_session() as s:
|
|
other = await ensure_user(s, "inception_other")
|
|
other_id = other.id
|
|
await s.commit()
|
|
with pytest.raises(ValueError, match="not found"):
|
|
await inception_svc.decide(other_id, pid, via="mcp", choices={})
|