CI/CD: Gitea Actions — тесты CRM (sqlite) и автодеплой на VPS
ci.yml (push) Invalid workflow file

- 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 репозитория
This commit is contained in:
merrow committed 2026-10-11 23:00:20 +05:00
1 parent da5b259d06
commit 881ef45be5
8 files changed
+469

No files matched your search

+73
View File
@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
"""Чистые функции без сервера: гигиена гостевого текста и гейт «бар открыт»."""
import sys
from datetime import datetime, timedelta
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from server import BAR_TZ, bar_is_open, next_bar_opening # noqa: E402
from storage import clean_guest_text # noqa: E402
# 2026-10-05 — понедельник; от него строим любой день недели
MONDAY = datetime(2026, 10, 5, 12, 0, tzinfo=BAR_TZ)
def dt(day: str, hour: int, minute: int = 0) -> datetime:
shift = "mon tue wed thu fri sat sun".split().index(day)
return MONDAY.replace(hour=hour, minute=minute) + timedelta(days=shift)
# ------------------------------------------------------------- clean_guest_text
def test_clean_text_removes_hidden_chars():
assert clean_guest_text("Кола\u200b", 60) == "Кола" # zero-width space
assert clean_guest_text("\ufeffпиво\u200e", 60) == "пиво" # BOM + LRM
assert clean_guest_text("a\x00b\x1f[c]\x7f", 60) == "ab[c]" # control chars
assert clean_guest_text("ab\u202edc", 60) == "abdc" # bidi override
def test_clean_text_keeps_normal_text_and_emoji():
text = "Столик у окна, пиво 🍺 и — тире"
assert clean_guest_text(text, 60) == text
def test_clean_text_truncates_to_limit():
assert len(clean_guest_text("х" * 500, 60)) == 60
# ------------------------------------------------------------------ бар открыт?
def test_bar_open_in_the_evening():
assert bar_is_open(dt("fri", 21)) is True
assert bar_is_open(dt("sat", 21)) is True
assert bar_is_open(dt("sun", 20)) is True
def test_bar_closed_monday_evening():
# понедельник — вечерних сессий нет (BAR_CLOSED_WEEKDAY)
assert bar_is_open(dt("mon", 21)) is False
def test_bar_tail_after_midnight():
assert bar_is_open(dt("sat", 2)) is True # хвост пятницы до 03:00
assert bar_is_open(dt("sun", 2)) is True # хвост субботы до 03:00
assert bar_is_open(dt("tue", 0, 30)) is False # во вторник хвоста нет
assert bar_is_open(dt("sat", 3, 30)) is False # хвост закончился
def test_preorder_window_is_one_hour():
now = dt("fri", 18, 30)
opening = next_bar_opening(now)
assert opening is not None and (opening - now) <= timedelta(hours=1)
now2 = dt("fri", 17, 0)
opening2 = next_bar_opening(now2)
assert (opening2 - now2) > timedelta(hours=1)
def test_next_opening_skips_monday():
# воскресенье 21:00 → ближайшее открытие во вторник 19:00, не в понедельник
opening = next_bar_opening(dt("sun", 21))
assert opening is not None
assert opening.weekday() == 1 # вторник