"""Hospitality API foundation flow — Phase 12.0.""" from __future__ import annotations import pytest from app.events.publisher import get_event_publisher from app.events.types import HospitalityEventType from app.tests.conftest import TENANT_A, TENANT_B, tenant_headers @pytest.mark.asyncio async def test_health(client): resp = await client.get("/health") assert resp.status_code == 200 body = resp.json() assert body["service"] == "hospitality-service" assert body["status"] == "ok" assert body["version"] == "0.12.8.0" @pytest.mark.asyncio async def test_capabilities(client): resp = await client.get("/capabilities") assert resp.status_code == 200 body = resp.json() assert body["phase"] == "12.8" assert body["product"] == "Torbat Food" assert body["features"]["foundation"] is True assert body["features"]["bundle_based_licensing"] is True assert body["features"]["digital_menu_catalog"] is True assert body["features"]["qr_menu"] is True assert body["features"]["qr_ordering"] is True assert body["features"]["table_service"] is True assert body["features"]["reservation"] is True assert body["features"]["pos_lite"] is True assert body["features"]["pos_pro"] is True assert body["features"]["kitchen"] is True assert body["features"]["delivery_integration"] is True assert body["features"]["accounting_integration"] is True assert body["features"]["analytics"] is True assert body["features"]["pos_engine"] is False assert "digital_menu" in body["bundles"] assert "cafe" in body["venue_formats"] assert body["independence"]["integration_mode"] == "api_and_events_only" @pytest.mark.asyncio async def test_foundation_flow_and_events(client): venue = await client.post( "/api/v1/venues", json={ "code": "main", "name": "Main Cafe", "status": "active", "venue_format": "cafe", }, headers=tenant_headers(TENANT_A), ) assert venue.status_code == 201, venue.text venue_id = venue.json()["id"] assert venue.json()["code"] == "MAIN" assert venue.json()["venue_format"] == "cafe" branch = await client.post( "/api/v1/branches", json={"venue_id": venue_id, "code": "b1", "name": "Branch 1"}, headers=tenant_headers(TENANT_A), ) assert branch.status_code == 201, branch.text area = await client.post( "/api/v1/dining-areas", json={"venue_id": venue_id, "code": "hall", "name": "Main Hall"}, headers=tenant_headers(TENANT_A), ) assert area.status_code == 201, area.text area_id = area.json()["id"] table = await client.post( "/api/v1/tables", json={ "venue_id": venue_id, "dining_area_id": area_id, "code": "t1", "name": "Table 1", "capacity": 4, }, headers=tenant_headers(TENANT_A), ) assert table.status_code == 201, table.text menu = await client.post( "/api/v1/menus", json={"venue_id": venue_id, "code": "day", "name": "Day Menu"}, headers=tenant_headers(TENANT_A), ) assert menu.status_code == 201, menu.text menu_id = menu.json()["id"] category = await client.post( "/api/v1/menu-categories", json={ "venue_id": venue_id, "menu_id": menu_id, "code": "drinks", "name": "Drinks", }, headers=tenant_headers(TENANT_A), ) assert category.status_code == 201, category.text category_id = category.json()["id"] item = await client.post( "/api/v1/menu-items", json={ "venue_id": venue_id, "menu_id": menu_id, "category_id": category_id, "code": "latte", "name": "Latte", "base_price": 150000, }, headers=tenant_headers(TENANT_A), ) assert item.status_code == 201, item.text bundle = await client.post( "/api/v1/bundle-definitions", json={ "code": "dm", "bundle_key": "digital_menu", "name": "Digital Menu", "feature_keys": ["hospitality.digital_menu"], "permission_prefixes": ["hospitality.digital_menu."], "api_prefixes": ["/api/v1/menus"], }, headers=tenant_headers(TENANT_A), ) assert bundle.status_code == 201, bundle.text bundle_id = bundle.json()["id"] activated = await client.post( "/api/v1/tenant-bundles/activate", json={"bundle_definition_id": bundle_id, "venue_id": venue_id}, headers=tenant_headers(TENANT_A), ) assert activated.status_code == 201, activated.text assert activated.json()["bundle_key"] == "digital_menu" assert activated.json()["status"] == "active" toggle = await client.post( "/api/v1/feature-toggles/upsert", json={ "feature_key": "hospitality.digital_menu", "enabled": True, "venue_id": venue_id, "bundle_key": "digital_menu", }, headers=tenant_headers(TENANT_A), ) assert toggle.status_code == 200, toggle.text assert toggle.json()["enabled"] is True role = await client.post( "/api/v1/roles", json={"code": "manager", "name": "Manager", "venue_id": venue_id}, headers=tenant_headers(TENANT_A), ) assert role.status_code == 201, role.text cfg = await client.post( "/api/v1/configurations", json={ "code": "hours", "name": "Working Hours", "venue_id": venue_id, "config": {"timezone": "Asia/Tehran"}, }, headers=tenant_headers(TENANT_A), ) assert cfg.status_code == 201, cfg.text published = {e.event_type for e in get_event_publisher().published} assert HospitalityEventType.VENUE_CREATED.value in published assert HospitalityEventType.MENU_CREATED.value in published assert HospitalityEventType.TENANT_BUNDLE_ACTIVATED.value in published assert HospitalityEventType.FEATURE_TOGGLE_UPSERTED.value in published @pytest.mark.asyncio async def test_tenant_isolation(client): venue = await client.post( "/api/v1/venues", json={"code": "a1", "name": "Tenant A Venue", "status": "active"}, headers=tenant_headers(TENANT_A), ) assert venue.status_code == 201 venue_id = venue.json()["id"] other = await client.get( f"/api/v1/venues/{venue_id}", headers=tenant_headers(TENANT_B), ) assert other.status_code == 404 missing = await client.get("/api/v1/venues") assert missing.status_code in (400, 422)