Обновление возит только папку app, поэтому переход на детектор речи потребовал файла, которого на машине нет - и сервис узнал об этом на первой же задаче: отказ выглядел сбоем обработки, хотя это незаконченная установка. Причём download_models.py лежит вне app и тоже был старым, так что штатный способ докачки не помог бы. Список моделей и адреса переехали в app/models.py. Мелкие докачиваются при запуске, крупная модель распознавания - нет: сотни мегабайт без спроса тянуть нельзя. Нехватка видна в /health полем missing_models. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
119 lines
5.7 KiB
Python
119 lines
5.7 KiB
Python
"""Проверки генерации Caddyfile и запуска."""
|
|
from app.launcher import MARK, ensure_caddyfile, render_caddyfile
|
|
|
|
|
|
class TestRender:
|
|
def test_domains_go_into_site_block(self):
|
|
text = render_caddyfile("a.example.com, b.example.com", 8756, 500)
|
|
assert "a.example.com, b.example.com {" in text
|
|
|
|
def test_extra_spaces_and_empty_entries_are_dropped(self):
|
|
text = render_caddyfile(" a.example.com ,, b.example.com ,", 8756, 500)
|
|
assert "a.example.com, b.example.com {" in text
|
|
|
|
def test_port_and_limit_come_from_settings(self):
|
|
text = render_caddyfile("a.example.com", 9000, 250)
|
|
assert "reverse_proxy 127.0.0.1:9000" in text
|
|
assert "max_size 250MB" in text
|
|
|
|
def test_xff_is_left_to_caddy_default(self):
|
|
"""Caddy сам игнорирует X-Forwarded-For от клиента и ставит реальный адрес.
|
|
|
|
Свой header_up был лишним - Caddy предупреждал об этом при каждом
|
|
запуске. Важнее обратное: trusted_proxies появляться не должен, иначе
|
|
Caddy начнёт верить заголовку клиента и адрес станет подделываемым.
|
|
"""
|
|
# Комментарии не в счёт: в них эти слова упомянуты как предостережение.
|
|
directives = "\n".join(
|
|
line for line in render_caddyfile("a.example.com", 8756, 500).splitlines()
|
|
if not line.strip().startswith("#"))
|
|
assert "header_up" not in directives
|
|
assert "trusted_proxies" not in directives
|
|
|
|
def test_caddy_log_goes_to_file(self):
|
|
"""Иначе Caddy забивает консоль сообщениями про сертификаты."""
|
|
text = render_caddyfile("a.example.com", 8756, 500)
|
|
assert "output file logs/caddy.log" in text
|
|
|
|
def test_generated_file_is_marked(self):
|
|
assert render_caddyfile("a.example.com", 8756, 500).startswith(MARK)
|
|
|
|
def test_no_blank_line_before_global_block(self):
|
|
"""caddy fmt считает такой файл неформатированным и ругается при запуске."""
|
|
lines = render_caddyfile("a.example.com", 8756, 500).splitlines()
|
|
assert lines[lines.index("{") - 1].strip() != ""
|
|
|
|
|
|
class TestEnsure:
|
|
def test_creates_file_when_missing(self, tmp_path):
|
|
content = render_caddyfile("a.example.com", 8756, 500)
|
|
written = ensure_caddyfile(tmp_path, content)
|
|
assert written == tmp_path / "Caddyfile"
|
|
assert written.read_text(encoding="utf-8") == content
|
|
|
|
def test_overwrites_own_previous_version(self, tmp_path):
|
|
ensure_caddyfile(tmp_path, render_caddyfile("old.example.com", 8756, 500))
|
|
fresh = render_caddyfile("new.example.com", 8756, 500)
|
|
ensure_caddyfile(tmp_path, fresh)
|
|
assert (tmp_path / "Caddyfile").read_text(encoding="utf-8") == fresh
|
|
assert not (tmp_path / "Caddyfile.new").exists()
|
|
|
|
def test_keeps_hand_written_file(self, tmp_path):
|
|
"""Чужой файл не трогаем: молча стереть настройку хуже, чем оставить старую."""
|
|
handmade = "site.example.com {\n\treverse_proxy 127.0.0.1:1234\n}\n"
|
|
(tmp_path / "Caddyfile").write_text(handmade, encoding="utf-8")
|
|
written = ensure_caddyfile(tmp_path, render_caddyfile("a.example.com", 8756, 500))
|
|
assert (tmp_path / "Caddyfile").read_text(encoding="utf-8") == handmade
|
|
assert written == tmp_path / "Caddyfile.new"
|
|
assert MARK in written.read_text(encoding="utf-8")
|
|
|
|
def test_no_rewrite_when_nothing_changed(self, tmp_path):
|
|
content = render_caddyfile("a.example.com", 8756, 500)
|
|
ensure_caddyfile(tmp_path, content)
|
|
before = (tmp_path / "Caddyfile").stat().st_mtime_ns
|
|
assert ensure_caddyfile(tmp_path, content) is None
|
|
assert (tmp_path / "Caddyfile").stat().st_mtime_ns == before
|
|
|
|
|
|
class TestStartupOrder:
|
|
def test_updater_runs_before_service(self, tmp_path, monkeypatch):
|
|
"""Обновиться нужно до запуска, иначе обновление бессмысленно."""
|
|
import app.launcher as launcher
|
|
|
|
calls: list[str] = []
|
|
|
|
class FakeSettings:
|
|
base_dir = tmp_path
|
|
models_dir = tmp_path / "models"
|
|
diarize = False
|
|
port = 8756
|
|
max_upload_mb = 500
|
|
https_domains = ""
|
|
|
|
monkeypatch.setattr("app.config.load_settings", lambda: FakeSettings())
|
|
monkeypatch.setattr("app.updater.main", lambda: calls.append("updater"))
|
|
monkeypatch.setattr("app.models.ensure_models", lambda *a: [])
|
|
monkeypatch.setitem(
|
|
__import__("sys").modules, "app.main",
|
|
type("M", (), {"run": staticmethod(lambda: calls.append("run"))}))
|
|
|
|
assert launcher.main([]) == 0
|
|
assert calls == ["updater", "run"]
|
|
|
|
def test_https_without_domains_stops_with_explanation(self, tmp_path, monkeypatch):
|
|
"""Пустые домены - это ошибка настройки, а не повод падать внутри Caddy."""
|
|
import app.launcher as launcher
|
|
|
|
class FakeSettings:
|
|
base_dir = tmp_path
|
|
models_dir = tmp_path / "models"
|
|
diarize = False
|
|
port = 8756
|
|
max_upload_mb = 500
|
|
https_domains = " "
|
|
|
|
monkeypatch.setattr("app.config.load_settings", lambda: FakeSettings())
|
|
monkeypatch.setattr("app.updater.main", lambda: None)
|
|
monkeypatch.setattr("app.models.ensure_models", lambda *a: [])
|
|
assert launcher.main(["https"]) == 1
|