"""UI-контракты кормоцеха: HTML/JS и страницы (без браузерного раннера).""" from __future__ import annotations import os import re import tempfile import unittest from pathlib import Path from app import create_app, db from app.services.setup_state import mark_setup_complete from config import TestingConfig PROJECT_ROOT = Path(__file__).resolve().parent.parent STATIC = PROJECT_ROOT / "static" class FeedMillUiConfig(TestingConfig): _TMP_DIR = tempfile.mkdtemp(prefix="wesp-feed-mill-ui-") 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 = "mill-ui-admin" AUTH_PASSWORD = "mill-ui-secret" def _drain_response(resp) -> None: try: resp.get_data() finally: close = getattr(resp, "close", None) if callable(close): close() class FeedMillUiTests(unittest.TestCase): def setUp(self) -> None: self.app = create_app(FeedMillUiConfig) 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": "mill-ui-admin", "password": "mill-ui-secret"} ) self.assertEqual(resp.status_code, 200) def test_feed_dispensers_page_requires_auth(self) -> None: resp = self.client.get("/feed_dispensers", follow_redirects=False) self.assertIn(resp.status_code, (302, 401, 403)) def test_feed_dispensers_and_recipes_pages_ok_when_authed(self) -> None: self._login() for path in ("/feed_dispensers", "/recipes"): resp = self.client.get(path) _drain_response(resp) self.assertEqual(resp.status_code, 200, path) self.assertIn(b"", resp.data or b"") def test_feed_dispensers_html_mill_modal_and_create_payload(self) -> None: html = (STATIC / "feed_dispensers.html").read_text(encoding="utf-8") self.assertIn("Добавить кормоцех", html) self.assertIn('id="addMillModalLabel"', html) self.assertIn("type: 'mill'", html) self.assertIn("dispenser-card--mill", html) self.assertIn("!isMill", html) self.assertIn("managePeriods", html) def test_recipes_data_controller_mill_branch(self) -> None: js = (STATIC / "js" / "pages" / "recipes-data-controller.js").read_text(encoding="utf-8") self.assertIn('getCurrentDispenserType() === "mill"', js) self.assertIn("loadMillRecipes", js) self.assertIn('millRecipesList', js) self.assertIn('dispenser.type === "mill"', js) self.assertIn('? "recipes" : "periods"', js) def test_recipes_operations_mill_uses_global_delete(self) -> None: js = (STATIC / "js" / "pages" / "recipes-operations-controller.js").read_text( encoding="utf-8" ) self.assertIn('getCurrentDispenserType() === "mill"', js) self.assertIn("unlinkFromPeriodOnly", js) self.assertIn("/api/recipes/", js) # mill: DELETE глобальный, не unlink из периода self.assertRegex( js, r"unlinkFromPeriodOnly\s*=\s*Boolean\(periodId\s*&&\s*dispenserId\s*&&\s*!isMill\)", ) def test_recipes_editor_mill_target_component(self) -> None: js = (STATIC / "js" / "pages" / "recipes-editor-controller.js").read_text(encoding="utf-8") self.assertIn('getCurrentDispenserType() === "mill"', js) self.assertIn("target_component", js) def test_recipe_period_transfer_excludes_mill(self) -> None: js = (STATIC / "js" / "modules" / "recipes" / "recipe-period-transfer.js").read_text( encoding="utf-8" ) self.assertIn('dataset.dispenserType === "mill"', js) mill_skips = len(re.findall(r'dispenserType\s*===\s*["\']mill["\']', js)) self.assertGreaterEqual(mill_skips, 2) def test_recipes_html_legacy_mill_flow_present(self) -> None: html = (STATIC / "recipes.html").read_text(encoding="utf-8") self.assertIn("currentDispenserType = 'dispenser'", html) self.assertIn("currentDispenserType === 'mill'", html) self.assertIn("target_component_id", html) self.assertIn("Загрузка рецептов кормоцеха", html) def test_recipe_skeleton_mill_label(self) -> None: js = (STATIC / "js" / "modules" / "recipes" / "recipe-skeleton.js").read_text(encoding="utf-8") self.assertIn("mill:", js) self.assertIn("Загрузка рецептов", js) if __name__ == "__main__": unittest.main()