131 lines
4.2 KiB
Python
131 lines
4.2 KiB
Python
"""Served HTML /recipes: auth, структура, скрипты, preload."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import re
|
|
import tempfile
|
|
import unittest
|
|
|
|
from app import create_app, db
|
|
from app.models import Component, FeedDispenser, Recipe
|
|
from app.services.setup_state import mark_setup_complete
|
|
from config import TestingConfig
|
|
from tests.helpers.recipes_ui_inventory import DOM_IDS, RECIPES_PAGE_SCRIPTS
|
|
|
|
|
|
class RecipesPageServedConfig(TestingConfig):
|
|
_TMP_DIR = tempfile.mkdtemp(prefix="wesp-recipes-served-")
|
|
SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.join(_TMP_DIR, 'recipes_test.db')}"
|
|
SQLALCHEMY_BINDS = {"reports": f"sqlite:///{os.path.join(_TMP_DIR, 'reports_test.db')}"}
|
|
AUTH_LOGIN = "recipes-served-admin"
|
|
AUTH_PASSWORD = "recipes-served-secret"
|
|
|
|
|
|
def _drain(resp) -> None:
|
|
try:
|
|
resp.get_data()
|
|
finally:
|
|
close = getattr(resp, "close", None)
|
|
if callable(close):
|
|
close()
|
|
|
|
|
|
class RecipesPageServedTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.app = create_app(RecipesPageServedConfig)
|
|
self.client = self.app.test_client()
|
|
self.ctx = self.app.app_context()
|
|
self.ctx.push()
|
|
db.create_all()
|
|
mark_setup_complete(self.app)
|
|
|
|
def tearDown(self) -> None:
|
|
db.session.remove()
|
|
db.drop_all()
|
|
self.ctx.pop()
|
|
|
|
def _login(self) -> None:
|
|
resp = self.client.post(
|
|
"/api/auth/login",
|
|
json={"login": "recipes-served-admin", "password": "recipes-served-secret"},
|
|
)
|
|
self.assertEqual(resp.status_code, 200)
|
|
_drain(resp)
|
|
|
|
def test_recipes_redirects_without_session(self) -> None:
|
|
resp = self.client.get("/recipes", follow_redirects=False)
|
|
self.assertEqual(resp.status_code, 302)
|
|
self.assertIn("/login", resp.headers.get("Location", ""))
|
|
_drain(resp)
|
|
|
|
def test_recipes_served_with_key_dom_after_login(self) -> None:
|
|
self._login()
|
|
resp = self.client.get("/recipes")
|
|
self.assertEqual(resp.status_code, 200)
|
|
body = resp.get_data(as_text=True)
|
|
for dom_id in DOM_IDS[:12]:
|
|
with self.subTest(dom_id=dom_id):
|
|
self.assertIn(f'id="{dom_id}"', body)
|
|
_drain(resp)
|
|
|
|
def test_recipes_page_includes_module_scripts(self) -> None:
|
|
self._login()
|
|
body = self.client.get("/recipes").get_data(as_text=True)
|
|
for script in RECIPES_PAGE_SCRIPTS:
|
|
with self.subTest(script=script):
|
|
self.assertRegex(body, re.escape(script))
|
|
|
|
def test_recipes_page_preload_module_chain(self) -> None:
|
|
self._login()
|
|
body = self.client.get("/recipes").get_data(as_text=True)
|
|
self.assertIn("recipes-boot.js", body)
|
|
self.assertIn("recipes-data-controller.js", body)
|
|
|
|
def test_api_feed_dispensers_returns_seeded_mill_for_page(self) -> None:
|
|
self._login()
|
|
mill = FeedDispenser(
|
|
id="mill-served-1",
|
|
name="Цех served",
|
|
farm="F",
|
|
operator="O",
|
|
type="mill",
|
|
content_hash="",
|
|
)
|
|
comp = Component(
|
|
id="comp-served-1",
|
|
name="Зерно",
|
|
type="grain",
|
|
dry_matter=50.0,
|
|
protein=0.0,
|
|
energy=0.0,
|
|
price=0.0,
|
|
)
|
|
recipe = Recipe(
|
|
id="recipe-served-1",
|
|
name="Рецепт served",
|
|
heads_per_trip=5,
|
|
mixing_time=3,
|
|
trip_percent=100.0,
|
|
target_component_id=comp.id,
|
|
content_hash="",
|
|
)
|
|
db.session.add_all([mill, comp, recipe])
|
|
db.session.commit()
|
|
|
|
dispensers = self.client.get("/api/feed_dispensers")
|
|
self.assertEqual(dispensers.status_code, 200)
|
|
rows = dispensers.get_json()
|
|
mill_rows = [r for r in rows if r.get("id") == mill.id]
|
|
self.assertEqual(len(mill_rows), 1)
|
|
self.assertEqual(mill_rows[0]["type"], "mill")
|
|
|
|
recipes = self.client.get(f"/api/feed_dispensers/{mill.id}/recipes")
|
|
self.assertEqual(recipes.status_code, 200)
|
|
recipe_ids = [r["id"] for r in recipes.get_json()]
|
|
self.assertIn(recipe.id, recipe_ids)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|