@@ -0,0 +1,122 @@
|
||||
"""Чистая установка: пустой 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()
|
||||
Reference in New Issue
Block a user