from __future__ import annotations from collections.abc import Callable import pytest from fastapi.testclient import TestClient from models.navigation import NavigationGroup, NavigationLink from services.unit_of_work import UnitOfWork @pytest.fixture() def seed_calculation_navigation( unit_of_work_factory: Callable[[], UnitOfWork] ) -> Callable[[], None]: def _seed() -> None: with unit_of_work_factory() as uow: repo = uow.navigation assert repo is not None workspace = repo.add_group( NavigationGroup( slug="workspace", label="Workspace", sort_order=10, ) ) projects_link = repo.add_link( NavigationLink( group_id=workspace.id, slug="projects", label="Projects", href_override="/projects", sort_order=5, required_roles=[], ) ) repo.add_link( NavigationLink( group_id=workspace.id, parent_link_id=projects_link.id, slug="profitability", label="Profitability Calculator", route_name="calculations.profitability_form", href_override="/calculations/profitability", match_prefix="/calculations/profitability", sort_order=8, required_roles=["analyst", "admin"], ) ) repo.add_link( NavigationLink( group_id=workspace.id, parent_link_id=projects_link.id, slug="opex", label="Opex Planner", route_name="calculations.opex_form", href_override="/calculations/opex", match_prefix="/calculations/opex", sort_order=10, required_roles=["analyst", "admin"], ) ) repo.add_link( NavigationLink( group_id=workspace.id, parent_link_id=projects_link.id, slug="capex", label="Capex Planner", route_name="calculations.capex_form", href_override="/calculations/capex", match_prefix="/calculations/capex", sort_order=15, required_roles=["analyst", "admin"], ) ) return _seed def test_navigation_sidebar_includes_calculation_links_for_admin( client: TestClient, seed_calculation_navigation: Callable[[], None], ) -> None: seed_calculation_navigation() response = client.get("/navigation/sidebar") assert response.status_code == 200 payload = response.json() groups = payload["groups"] assert groups workspace = next( group for group in groups if group["label"] == "Workspace") workspace_links = workspace["links"] assert [link["label"] for link in workspace_links] == ["Projects"] projects_children = workspace_links[0]["children"] child_labels = [link["label"] for link in projects_children] assert child_labels == [ "Profitability Calculator", "Opex Planner", "Capex Planner", ] profitability_link = next( link for link in projects_children if link["label"] == "Profitability Calculator") assert profitability_link["href"] == "/calculations/profitability" assert profitability_link["match_prefix"] == "/calculations/profitability" opex_link = next( link for link in projects_children if link["label"] == "Opex Planner") assert opex_link["href"] == "/calculations/opex" assert opex_link["match_prefix"] == "/calculations/opex" capex_link = next( link for link in projects_children if link["label"] == "Capex Planner") assert capex_link["href"] == "/calculations/capex" assert capex_link["match_prefix"] == "/calculations/capex" assert payload["roles"] == ["admin"] def test_navigation_sidebar_hides_calculation_links_for_viewer_without_role( client: TestClient, seed_calculation_navigation: Callable[[], None], test_user_headers: Callable[[str | None], dict[str, str]], ) -> None: seed_calculation_navigation() response = client.get( "/navigation/sidebar", headers=test_user_headers("viewer"), ) assert response.status_code == 200 payload = response.json() groups = payload["groups"] assert groups workspace = next( group for group in groups if group["label"] == "Workspace") workspace_links = workspace["links"] assert [link["label"] for link in workspace_links] == ["Projects"] assert workspace_links[0]["children"] == [] assert payload["roles"] == ["viewer"] def test_navigation_sidebar_includes_contextual_urls_when_ids_provided( client: TestClient, seed_calculation_navigation: Callable[[], None], ) -> None: seed_calculation_navigation() response = client.get( "/navigation/sidebar", params={"project_id": "5", "scenario_id": "11"}, ) assert response.status_code == 200 payload = response.json() workspace = next( group for group in payload["groups"] if group["label"] == "Workspace") projects = workspace["links"][0] capex_link = next( link for link in projects["children"] if link["label"] == "Capex Planner") assert capex_link["href"].endswith( "/calculations/projects/5/scenarios/11/calculations/capex" ) assert capex_link["match_prefix"] == "/calculations/capex"