Files
calminer/tests/integration/test_navigation_sidebar_calculations.py
zwitschi 4d0e1a9989
Some checks failed
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / lint (push) Failing after 14s
CI / deploy (push) Has been skipped
feat(navigation): Enhance navigation links and add legacy route redirects
- Updated navigation links in `init_db.py` to include href overrides and parent slugs for profitability, opex, and capex planners.
- Modified `NavigationService` to handle child links and href overrides, ensuring proper routing when context is missing.
- Adjusted scenario detail and list templates to use new route names for opex and capex forms, with legacy fallbacks.
- Introduced integration tests for legacy calculation routes to ensure proper redirection and error handling.
- Added tests for navigation sidebar to validate role-based access and link visibility.
- Enhanced navigation sidebar tests to include calculation links and contextual URLs based on project and scenario IDs.
2025-11-13 20:23:53 +01:00

171 lines
5.7 KiB
Python

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"