dodil/erp-brick-pos
DODIL ERP brick: point of sale
tests/test_doors.py
"""Both doors, one outcome: the same use case through the API and through MCP - the same answer, the same refusal, the same 403."""
from conftest import ACCOUNTANT, BOSS, CASHIER, CASHIER2, OPS, SYSTEM, api, make_shop, must, tool, tools
OPEN = {"shop_id": "LON-SOHO", "register_id": "R1", "opening_float": "150.00"}
EXPECTED = {"list_shops", "get_shop", "keep_shop", "keep_register", "assign_shop_staff", "list_pos_settings", "set_pos_setting",
"set_pos_control", "list_sessions", "get_session", "open_session", "record_cash_move", "count_session", "approve_difference",
"list_receipts", "get_receipt", "ring_up", "take_payment", "finalise_receipt", "dispatch_outbox", "list_outbox",
"force_close_session", "post_session", "requeue_outbox_row", "seal_cash_bag", "list_cash_bags", "void_receipt", "refund_receipt", "list_category_rules", "set_category_rule", "get_voucher", "resolve_card_payment", "list_recipes", "get_recipe", "keep_recipe", "get_register_snapshot"}
def test_every_use_case_is_a_route_and_a_tool_with_the_same_name(nb):
assert EXPECTED <= set(tools(BOSS)) | {"dispatch_outbox"}
assert "dispatch_outbox" in tools(SYSTEM)
def test_open_a_session_through_both_doors_gives_the_same_answer(nb):
make_shop()
status, via_api = api("POST", "/sessions", CASHIER, OPEN)
kind, via_tool = tool(CASHIER2, "open_session", {**OPEN, "register_id": "R2"})
assert status == 200 and kind == "ok"
strip = lambda d: {k: v for k, v in d.items() if k not in ("session_id", "register_id", "session_no", "opened_by", "opened_sub", "opened_at")} # noqa: E731
assert strip(via_api) == strip(via_tool) and via_tool["session_id"] == "LON-SOHO-R2-0001"
def test_a_sale_through_the_tools_and_a_refusal_in_the_same_words(nb):
make_shop()
sid = must(api("POST", "/sessions", CASHIER, OPEN))["session_id"]
kind, r = tool(CASHIER, "ring_up", {"session_id": sid, "lines": [{"product_id": "LATTE-REG", "quantity": "2"}]})
assert kind == "ok" and r["gross"] == "7.20"
kind, r = tool(CASHIER, "take_payment", {"receipt_id": r["receipt_id"], "method": "cash", "amount": "10.00", "version": r["version"]})
assert kind == "ok" and r["payments"][0]["change_amount"] == "2.80"
kind, done = tool(CASHIER, "finalise_receipt", {"receipt_id": r["receipt_id"], "version": r["version"]})
assert (kind, done["status"]) == ("ok", "finalised")
status, body = api("POST", f"/sessions/{sid}/count", CASHIER, {"counted_cash": "150.00", "register_last_receipt_no": 9})
kind2, text = tool(CASHIER, "count_session", {"session_id": sid, "counted_cash": "150.00", "register_last_receipt_no": 9})
assert status == 409 and kind2 == "refused" and "0002 to 0009" in text and body["detail"] in text
def test_a_missing_permission_is_refused_through_both_doors(nb):
make_shop()
assert api("POST", "/sessions", ACCOUNTANT, OPEN)[0] == 403
kind, text = tool(ACCOUNTANT, "open_session", OPEN)
assert kind == "refused" and "pos:session:open" in text
kind, text = tool(BOSS, "dispatch_outbox", {}) # a wildcard never reaches a machine permission
assert kind == "refused" and "pos:outbox:dispatch" in text
assert tool(SYSTEM, "dispatch_outbox", {})[0] == "ok"
def test_the_shop_setup_through_the_tools(nb):
kind, shop = tool(OPS, "keep_shop", {"shop_id": "NEW-1", "company_code": "UK01", "price_list_id": "LIST-UK-SHOP", "country": "gb"})
assert kind == "ok" and shop["country"] == "GB" and shop["fiscal_profile"] == "none" and shop["version"] == 1
kind, shop = tool(OPS, "keep_register", {"shop_id": "NEW-1", "register_id": "R1", "version": 1})
assert kind == "ok" and shop["registers"][0]["register_id"] == "R1" and shop["version"] == 2
kind, text = tool(OPS, "keep_register", {"shop_id": "NEW-1", "register_id": "R1", "version": 1})
assert kind == "refused" and "changed since you loaded it" in text
status, shop = api("POST", "/shops/NEW-1/registers", OPS, {"register_id": "R1", "status": "retired"})
assert status == 200
status, body = api("POST", "/shops/NEW-1/registers", OPS, {"register_id": "R1", "status": "active"})
assert status == 409 and body["gate"] == "register_retired" # a retired register's id is never reused
assert api("POST", "/shops", OPS, {"shop_id": "bad code"})[0] == 422
assert api("POST", "/shops", OPS, {"shop_id": "X", "fiscal_profile": "klingon"})[0] == 422