dodil/erp-brick-ar

DODIL ERP brick: accounts receivable

tests/test_doors.py

"""The same outcome through the API and through MCP: tools are use cases, the doors are generated from one declaration."""
import json

from conftest import BILLER, BP, OWNER, SYSTEM, api, billing, issued, line, must, person, tool, tools

from brick_ar.application.use_cases import AR


def test_every_use_case_is_a_route_and_a_tool_except_the_process_view():
    names = tools(person("all@x", ["*:*:*"]))
    expected = sorted(uc.name for uc in AR.use_cases if uc.tool)
    assert names == expected and len(expected) >= 50


def test_a_draft_is_raised_issued_and_read_the_same_way_through_both_doors(world):
    billing()
    s, d1 = tool(BILLER, "draft_invoice", {"bp_id": str(BP), "currency": "GBP", "lines": [line()]})
    assert s == "ok" and d1["total"] == "240.00" and d1["lifecycle"] == "draft"
    s, gates = tool(BILLER, "invoice_readiness", {"invoice_id": d1["invoice_id"]})
    assert s == "ok" and gates["ready"] is True and {"gate", "met", "reason", "fix"} == set(gates["gates"][0])
    s, issued_ = tool(BILLER, "issue_invoice", {"invoice_id": d1["invoice_id"]})
    assert s == "ok" and issued_["invoice_no"] == "INV-2026-0001"
    via_api = must(api("GET", f"/invoices/{d1['invoice_id']}", BILLER))
    s, via_tool = tool(BILLER, "get_invoice", {"invoice_id": d1["invoice_id"]})
    assert s == "ok" and json.loads(json.dumps(via_api)) == via_tool
    s, why = tool(BILLER, "edit_draft", {"invoice_id": d1["invoice_id"], "customer_po_ref": "PO-1"})
    assert s == "refused" and "credit it instead" in why


def test_a_source_asks_through_the_machine_tool_and_asking_twice_returns_the_same_invoice(world):
    billing()
    args = {"source_kind": "sales_order", "source_id": "so:SO-1:1", "bp_id": str(BP), "currency": "GBP",
            "lines": [line(sales_order_id="SO-1", so_line_no=1)]}
    s, a = tool(SYSTEM, "raise_draft", args)
    s2, b = tool(SYSTEM, "raise_draft", args)
    assert (s, s2) == ("ok", "ok") and a["version"] == b["version"] and a["invoice_id"] == "so:SO-1:1"
    s, why = tool(BILLER, "raise_draft", args)                      # a machine permission: refused to a person
    assert s == "refused"


def test_a_money_amount_leaves_as_an_exact_string_and_an_id_as_a_string(world):
    inv = issued(world)
    out = must(api("GET", f"/invoices/{inv['invoice_id']}", BILLER))
    assert out["total"] == "240.00" and out["bp_id"] == str(BP) and isinstance(out["lines"][0]["unit_price"], str)
    s, body = tool(BILLER, "get_invoice", {"invoice_id": inv["invoice_id"]})
    assert body["bp_id"] == str(BP) and body["subtotal"] == "200.00"


def test_the_process_is_served_as_bpmn():
    s, body = api("GET", "/process/bpmn", OWNER)
    assert s == 200 and "bpmn" in json.dumps(body).lower() and "Task_issue" in json.dumps(body)


def test_a_command_that_makes_something_new_takes_an_idempotency_key(world):
    billing()
    body = {"bp_id": str(BP), "currency": "GBP", "lines": [line()]}
    h = {**BILLER, "Idempotency-Key": "k-1"}
    a = must(api("POST", "/invoices", h, body))
    b = must(api("POST", "/invoices", h, body))
    assert a["invoice_id"] == b["invoice_id"] and len(must(api("GET", "/invoices", BILLER))["invoices"]) == 1