99 lines
3.6 KiB
Python
99 lines
3.6 KiB
Python
"""Фоновая инициализация и /api/startup/status."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
from app import create_app, db
|
|
from app.services.startup_background import is_reloader_parent_process, schedule_background_startup
|
|
from app.services.startup_state import mark_startup_ready, reset_startup_state
|
|
from config import TestingConfig
|
|
|
|
|
|
class StartupBgConfig(TestingConfig):
|
|
_TMP = tempfile.mkdtemp(prefix="wesp-startup-bg-")
|
|
BASE_DIR = _TMP
|
|
DATA_DIR = os.path.join(_TMP, "data")
|
|
SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.join(_TMP, 'recipes_test.db')}"
|
|
SQLALCHEMY_BINDS = {"reports": f"sqlite:///{os.path.join(_TMP, 'reports_test.db')}"}
|
|
WESP_BACKGROUND_STARTUP = False
|
|
|
|
|
|
class StartupBackgroundTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
reset_startup_state()
|
|
self.app = create_app(StartupBgConfig)
|
|
self.client = self.app.test_client()
|
|
self.ctx = self.app.app_context()
|
|
self.ctx.push()
|
|
mark_startup_ready()
|
|
db.create_all()
|
|
|
|
def tearDown(self) -> None:
|
|
db.session.remove()
|
|
db.drop_all()
|
|
self.ctx.pop()
|
|
reset_startup_state()
|
|
|
|
def test_startup_status_ready(self) -> None:
|
|
r = self.client.get("/api/startup/status")
|
|
self.assertEqual(r.status_code, 200)
|
|
body = r.get_json()
|
|
self.assertTrue(body.get("ready"))
|
|
|
|
def test_reloader_parent_skips_blocking_startup(self) -> None:
|
|
app = create_app(StartupBgConfig)
|
|
app.debug = True
|
|
with patch.dict(os.environ, {}, clear=False):
|
|
os.environ.pop("WERKZEUG_RUN_MAIN", None)
|
|
with patch(
|
|
"app.services.startup_background.is_running_from_reloader",
|
|
create=True,
|
|
return_value=False,
|
|
):
|
|
self.assertTrue(is_reloader_parent_process(app))
|
|
with patch("app.services.startup_background.run_blocking_startup_tasks") as blocked:
|
|
self.assertFalse(schedule_background_startup(app))
|
|
blocked.assert_not_called()
|
|
|
|
def test_health_during_starting(self) -> None:
|
|
reset_startup_state()
|
|
from app.services.startup_state import set_startup_phase
|
|
|
|
set_startup_phase("migrations", "Тест")
|
|
with self.app.app_context():
|
|
self.app.config["WESP_DEFERRED_STARTUP"] = True
|
|
from app.services.update_health import build_health_payload
|
|
|
|
payload = build_health_payload(self.app)
|
|
self.assertTrue(payload.get("starting"))
|
|
self.assertFalse(payload.get("ok"))
|
|
|
|
def test_scales_not_redirected_to_starting_while_migrations(self) -> None:
|
|
reset_startup_state()
|
|
from app.services.startup_state import set_startup_phase
|
|
|
|
set_startup_phase("migrations", "Тест")
|
|
self.app.config["WESP_DEFERRED_STARTUP"] = True
|
|
r = self.client.get("/scales", follow_redirects=False)
|
|
loc = r.headers.get("Location") or ""
|
|
self.assertNotIn("/starting", loc)
|
|
|
|
def test_stream_weight_not_redirected_while_migrations(self) -> None:
|
|
reset_startup_state()
|
|
from app.services.startup_state import set_startup_phase
|
|
|
|
set_startup_phase("migrations", "Тест")
|
|
self.app.config["WESP_DEFERRED_STARTUP"] = True
|
|
r = self.client.get("/stream_weight")
|
|
self.assertNotEqual(r.status_code, 302)
|
|
loc = r.headers.get("Location") or ""
|
|
self.assertNotIn("/starting", loc)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|