dodil/erp-method

node_modules/.cache/erp-method/v0.1.21/modules/test_composition.py

"""The composition boots, its bricks answer through HTTP together, and a signed-in caller can read each one."""
import inspect
import pathlib
from contextlib import ExitStack

import anyio
import httpx
from sqlalchemy import create_engine
from erp_platform import compose, seam
from erp_platform.scenarios import Catalogue
from erp_platform.scenarios.inprocess import stage

COMPOSE = pathlib.Path(__file__).resolve().parent.parent / "compose.yaml"


def test_composition_boots_and_every_brick_answers_a_signed_in_read(tmp_path):
    comp = compose.load(COMPOSE)
    assert compose.check(comp)["problems"] == []
    engine = create_engine(f"sqlite:///{tmp_path / 'bucket.db'}", connect_args={"check_same_thread": False})
    perms = sorted({u.permission for m in comp.members for u in m.brick.use_cases if u.permission})
    me = {"sub": "tester.1", "email": "tester@test", "app_roles": ["tester"], "permissions": perms}

    async def get(app, path):
        async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app), base_url="http://t") as c:
            return await c.get(path, headers=seam.gateway_headers(me))

    with ExitStack() as st:
        app = stage(Catalogue("t", compose=comp, roles=None), st, lambda name: engine).app
        assert anyio.run(get, app, "/healthz").status_code == 200
        for m in comp.members:
            reads = [u for u in m.brick.use_cases if u.kind == "query" and u.method == "GET" and u.tool
                     and not u.path_params and all(d is not inspect.Parameter.empty for _, _, d in u.inputs)]
            assert reads, f"{m.name}: no plain read use case to call"
            # The first plain read may refuse an empty question (activity's GET /contacts wants a bp_id or an
            # email: 422). A refusal is the use case answering; a 5xx is the composition broken. So: no read
            # may 5xx, and at least one must answer 200.
            answers = []
            for u in reads:
                r = anyio.run(get, app, f"/{m.name}{u.path}")
                assert r.status_code < 500, f"{m.name} {u.path}: {r.status_code} {r.text[:200]}"
                answers.append(f"{u.path} {r.status_code}")
                if r.status_code == 200:
                    break
            else:
                raise AssertionError(f"{m.name}: no plain read answered 200 ({', '.join(answers)})")