Files
merrow 881ef45be5
ci.yml (push) Invalid workflow file
CI/CD: Gitea Actions — тесты CRM (sqlite) и автодеплой на VPS
- crm/tests: 27 тестов через настоящий server.main на sqlite-бэкенде
  (валидация заказов, цена только из каталога, honeypot, дедупликация,
  трекинг и отмена гостем, стоп-лист, аутентификация, гигиена текста,
  гейт расписания бара)
- .gitea/workflows/ci.yml: test-джоба + deploy (только push в main,
  после тестов): rsync кода -> pre-flight docker compose config ->
  pg_dump бэкап БД (ротация 7) -> up -d --build -> health-check
- деплой на neon-bar86.ru идёт деплой-ключом из secrets репозитория
2026-10-11 23:00:20 +05:00

120 lines
4.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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)