# -*- coding: utf-8 -*- """Публичное оформление заказа с сайта: валидация состава, honeypot, цена только из каталога, трекинг и отмена гостем. Барное время зафиксировано на пятнице 21:00 (см. tests/_server_main.py), поэтому гейт «бар закрыт» не мешает проверять валидацию в любое время суток. """ import pytest @pytest.fixture() def table(api): status, body = api.request("GET", "/api/halls/names") assert status == 200 and body.get("tables") return body["tables"][0] _ORDER_SEQ = [0] def _order(api, table, items, **extra): # уникальный guest_phone на каждый заказ: дедупликация (тот же состав за # минуту → 409) не должна превращать быстрые тесты в ложные конфликты _ORDER_SEQ[0] += 1 payload = { "table_name": table, "items": items, "guest_phone": f"+7900000{_ORDER_SEQ[0]:04d}", } payload.update(extra) return api.request("POST", "/api/orders", payload) def test_order_requires_items(api, table): status, body = api.request("POST", "/api/orders", {"table_name": table}) assert status == 400 assert "items" in body["error"] def test_order_unknown_item_rejected(api, table): # состав заказа валидируется строго по каталогу CRM — клиентские # name/price игнорируются, неизвестные позиции отклоняют заказ status, body = _order(api, table, [{"id": "no-such-item", "qty": 1}]) assert status == 400 def test_order_unknown_table_rejected(api): status, body = _order(api, "несуществующий-стол-12345", [{"id": "blackberry-mint", "qty": 1}]) assert status == 400 def test_honeypot_swallows_bot_order(api, table): # скрытое поле hp_check: отвечаем боту успехом, заказ не создаём status, body = _order(api, table, [{"id": "blackberry-mint", "qty": 1}], hp_check="spam") assert status == 200 and body.get("ok") is True def test_order_price_comes_from_catalog(api, table): # 2 × 350 ₽ из crm/seed/menu.json: подмена цены клиентом невозможна status, order = _order(api, table, [{"id": "blackberry-mint", "qty": 2, "price": 1}]) assert status == 201 assert order["status"] == "new" assert order["price"] == 700 assert order["code"] def test_qty_clamped_to_50(api, table): status, order = _order(api, table, [{"id": "blackberry-mint", "qty": 999}]) assert status == 201 item = next(i for i in order["items"] if i["id"] == "blackberry-mint") assert item["qty"] == 50 def test_track_then_guest_cancel(api, table): status, order = _order(api, table, [{"id": "blackberry-mint", "qty": 1}]) assert status == 201 code = order["code"] status, tracked = api.request("GET", f"/api/track/{code}") assert status == 200 assert tracked["status"] == "new" # гость отменяет случайный заказ, пока он «new» status, _ = api.request("POST", f"/api/track/{code}/cancel") assert status in (200, 201) status, tracked = api.request("GET", f"/api/track/{code}") assert status == 200 assert tracked["status"] == "cancelled" def test_double_cancel_conflict(api, table): status, order = _order(api, table, [{"id": "blackberry-mint", "qty": 1}]) assert status == 201 code = order["code"] status, _ = api.request("POST", f"/api/track/{code}/cancel") assert status in (200, 201) status, body = api.request("POST", f"/api/track/{code}/cancel") assert status == 409 def test_stoplisted_item_rejects_order(api, table, owner_token): # владелец уводит позицию в стоп-лист → заказ с ней отклоняется целиком status, _ = api.request( "PATCH", "/api/menu/blackberry-mint", {"stopped": True}, token=owner_token, ) assert status in (200, 201) try: status, body = _order(api, table, [{"id": "blackberry-mint", "qty": 1}]) assert status == 400 assert "стоп" in body["error"] finally: status, _ = api.request( "PATCH", "/api/menu/blackberry-mint", {"stopped": False}, token=owner_token, ) assert status in (200, 201)