"""Доступ к модулю Lab — per-user lab_access и server-side guards.""" from __future__ import annotations import unittest from app import create_app, db from app.models import Recipe, WebUser from app.models.base import default_uuid from app.services.setup_state import mark_setup_complete from tests.helpers.zootech_test_helpers import ZootechTestConfig from werkzeug.security import generate_password_hash class LabAccessControlTests(unittest.TestCase): def setUp(self) -> None: self.app = create_app(ZootechTestConfig) self.client = self.app.test_client() self.ctx = self.app.app_context() self.ctx.push() db.create_all() mark_setup_complete(self.app) recipe = Recipe(id=default_uuid(), name="Lab access test", heads_per_trip=10, mixing_time=5) db.session.add(recipe) db.session.add( WebUser( login="zootech-test-admin", password_hash=generate_password_hash("zootech-test-secret"), is_superuser=True, ) ) db.session.add( WebUser( login="zootech-no-lab", password_hash=generate_password_hash("zootech-no-lab-secret"), is_superuser=False, lab_access=False, ) ) db.session.add( WebUser( login="zootech-with-lab", password_hash=generate_password_hash("zootech-with-lab-secret"), is_superuser=False, lab_access=True, ) ) db.session.commit() self.recipe_id = recipe.id def tearDown(self) -> None: db.session.remove() db.drop_all() self.ctx.pop() def _login(self, login: str, password: str) -> None: r = self.client.post("/api/auth/login", json={"login": login, "password": password}) self.assertEqual(r.status_code, 200, r.get_data(as_text=True)) def test_auth_check_can_lab_for_superuser(self) -> None: self._login("zootech-test-admin", "zootech-test-secret") body = self.client.get("/api/auth/check").get_json() self.assertTrue(body.get("can_lab")) def test_auth_check_can_lab_disabled_for_zootech(self) -> None: self._login("zootech-no-lab", "zootech-no-lab-secret") body = self.client.get("/api/auth/check").get_json() self.assertFalse(body.get("can_lab")) def test_auth_check_can_lab_enabled_for_zootech(self) -> None: self._login("zootech-with-lab", "zootech-with-lab-secret") body = self.client.get("/api/auth/check").get_json() self.assertTrue(body.get("can_lab")) def test_lab_page_redirect_without_access(self) -> None: self._login("zootech-no-lab", "zootech-no-lab-secret") r = self.client.get("/lab", follow_redirects=False) self.assertEqual(r.status_code, 302) self.assertIn("/recipes", r.headers.get("Location", "")) def test_lab_page_ok_with_access(self) -> None: self._login("zootech-with-lab", "zootech-with-lab-secret") r = self.client.get("/lab") self.assertEqual(r.status_code, 200) def test_lab_api_forbidden_without_access(self) -> None: self._login("zootech-no-lab", "zootech-no-lab-secret") r = self.client.get("/api/lab/recipes") self.assertEqual(r.status_code, 403, r.get_data(as_text=True)) def test_lab_api_ok_with_access(self) -> None: self._login("zootech-with-lab", "zootech-with-lab-secret") r = self.client.get("/api/lab/recipes") self.assertEqual(r.status_code, 200, r.get_data(as_text=True)) def test_components_nutrients_hidden_without_lab_access(self) -> None: self._login("zootech-no-lab", "zootech-no-lab-secret") create = self.client.post( "/api/components", json={ "name": "Сено", "type": "Грубые корма", "dry_matter": 88, "price": 10, "nutrients": {"Сырая зола": 50}, }, ) self.assertEqual(create.status_code, 403, create.get_data(as_text=True)) create_ok = self.client.post( "/api/components", json={"name": "Сено", "type": "Грубые корма", "dry_matter": 88, "price": 10}, ) self.assertEqual(create_ok.status_code, 201, create_ok.get_data(as_text=True)) comp_id = create_ok.get_json()["id"] get_one = self.client.get(f"/api/components/{comp_id}").get_json() self.assertEqual(get_one.get("nutrients"), {}) def test_admin_can_toggle_lab_access(self) -> None: self._login("zootech-test-admin", "zootech-test-secret") user = db.session.query(WebUser).filter_by(login="zootech-no-lab").one() patch = self.client.patch( f"/api/admin/users/{user.id}", json={"lab_access": True}, ) self.assertEqual(patch.status_code, 200, patch.get_data(as_text=True)) db.session.refresh(user) self.assertTrue(user.lab_access) self.client.post("/api/auth/logout") self._login("zootech-no-lab", "zootech-no-lab-secret") r = self.client.get("/api/lab/recipes") self.assertEqual(r.status_code, 200, r.get_data(as_text=True)) if __name__ == "__main__": unittest.main()