Files
site/WESP_REL/tests/test_fresh_install.py
T
2026-07-17 12:57:18 +03:00

123 lines
4.5 KiB
Python

"""Чистая установка: пустой data/, Alembic, bootstrap, health."""
from __future__ import annotations
import os
import shutil
import tempfile
import unittest
from pathlib import Path
from app import create_app, db
from app.models.auto_update_settings import AutoUpdateSettings
from app.services.update_health import migrations_ok_for_app
from config import Config, ProductionConfig
class FreshInstallTest(unittest.TestCase):
def setUp(self) -> None:
self._tmp = tempfile.mkdtemp(prefix="wesp-fresh-install-")
self._base = Path(self._tmp) / "wesp_home"
self._base.mkdir()
data = self._base / "data"
data.mkdir()
recipes = data / "recipes.db"
reports = data / "reports.db"
class FreshConfig(ProductionConfig):
BASE_DIR = str(self._base)
DATA_DIR = str(data)
SQLALCHEMY_DATABASE_URI = f"sqlite:///{recipes}"
SQLALCHEMY_BINDS = {"reports": f"sqlite:///{reports}"}
SECRET_KEY = "fresh-install-test-secret"
AUTH_LOGIN = "install-admin"
AUTH_PASSWORD = "install-secret"
SYNC_BACKGROUND_REQUEUE = False
SYNC_CLIENT_AUTOSTART = False
WESP_ADMIN_LOG_PATH = ""
WESP_ADMIN_HARDWARE_LOG_PATH = ""
WESP_ADMIN_UI_ACTIVITY_LOG_PATH = ""
WESP_LLM_AUTOSTART = False
WESP_BACKGROUND_STARTUP = False
self.app = create_app(FreshConfig, run_migrations=True)
self.client = self.app.test_client()
self._ctx = self.app.app_context()
self._ctx.push()
def tearDown(self) -> None:
self._ctx.pop()
shutil.rmtree(self._tmp, ignore_errors=True)
def test_ensure_auto_update_row_idempotent(self) -> None:
from sqlalchemy import func, select
from app.services.auto_update_settings_service import ensure_auto_update_row
ensure_auto_update_row()
ensure_auto_update_row()
n = db.session.scalar(select(func.count()).select_from(AutoUpdateSettings))
self.assertEqual(int(n or 0), 1)
def test_databases_and_auto_update_bootstrap(self) -> None:
from app.services.auto_update_settings_service import bootstrap_update_environment
recipes = Path(self.app.config["DATA_DIR"]) / "recipes.db"
reports = Path(self.app.config["DATA_DIR"]) / "reports.db"
self.assertTrue(recipes.is_file())
self.assertTrue(reports.is_file())
# Как при prod-старте, но синхронно: отложенный поток под нагрузкой pytest может не успеть за 5 с.
bootstrap_update_environment(self.app)
row = db.session.get(AutoUpdateSettings, 1)
self.assertIsNotNone(row)
config_json = Path(self.app.config["DATA_DIR"]) / "config.json"
self.assertTrue(config_json.is_file())
def test_migrations_at_head(self) -> None:
self.assertTrue(migrations_ok_for_app(self.app))
def test_health_after_fresh_install(self) -> None:
r = self.client.get("/api/health")
self.assertEqual(r.status_code, 200)
body = r.get_json()
self.assertTrue(body.get("ok"))
self.assertTrue(body.get("db_ok"))
self.assertTrue(body.get("migrations_ok"))
self.assertEqual(body.get("migration_head"), body.get("migration_revision"))
def test_setup_not_completed_by_default(self) -> None:
from app.services.setup_state import is_setup_completed
self.assertFalse(is_setup_completed(self.app))
def test_alembic_env_does_not_bootstrap_before_upgrade(self) -> None:
"""create_app(run_migrations=False) не обращается к таблицам до upgrade (как migrations/env.py)."""
empty = Path(self._tmp) / "empty"
empty.mkdir()
data = empty / "data"
data.mkdir()
recipes = data / "recipes.db"
reports = data / "reports.db"
class EmptyConfig(Config):
BASE_DIR = str(empty)
DATA_DIR = str(data)
SQLALCHEMY_DATABASE_URI = f"sqlite:///{recipes}"
SQLALCHEMY_BINDS = {"reports": f"sqlite:///{reports}"}
SECRET_KEY = "empty"
SYNC_BACKGROUND_REQUEUE = False
WESP_ADMIN_LOG_PATH = ""
os.environ["WESP_ALEMBIC"] = "1"
try:
app = create_app(EmptyConfig, run_migrations=False)
self.assertIsNotNone(app)
finally:
os.environ.pop("WESP_ALEMBIC", None)
if __name__ == "__main__":
unittest.main()