from __future__ import annotations from collections.abc import Callable from fastapi.testclient import TestClient class TestScenarioLifecycle: def test_scenario_lifecycle_flow( self, client: TestClient, scenario_calculation_url: Callable[[str, int, int], str], ) -> None: # Create a project to attach scenarios to project_response = client.post( "/projects", json={ "name": "Scenario Host Project", "location": "Ontario", "operation_type": "open_pit", "description": "Project for scenario lifecycle testing", }, ) assert project_response.status_code == 201 project_id = project_response.json()["id"] # Create a scenario via the API for the project scenario_response = client.post( f"/projects/{project_id}/scenarios", json={ "name": "Lifecycle Scenario", "description": "Initial scenario description", "status": "draft", "currency": "usd", "primary_resource": "diesel", }, ) assert scenario_response.status_code == 201 scenario_id = scenario_response.json()["id"] # Project detail page should list the new scenario in draft state project_detail = client.get(f"/projects/{project_id}/view") assert project_detail.status_code == 200 assert "Lifecycle Scenario" in project_detail.text assert 'Draft' in project_detail.text # Update the scenario through the HTML form form_response = client.post( f"/scenarios/{scenario_id}/edit", data={ "name": "Lifecycle Scenario Revised", "description": "Revised scenario assumptions", "status_value": "active", "start_date": "2025-01-01", "end_date": "2025-12-31", "discount_rate": "5.5", "currency": "cad", "primary_resource": "electricity", }, follow_redirects=False, ) assert form_response.status_code == 303 assert form_response.headers["Location"].endswith( f"/scenarios/{scenario_id}/view") # Scenario detail page should reflect the updated information scenario_detail = client.get(f"/scenarios/{scenario_id}/view") assert scenario_detail.status_code == 200 assert "Lifecycle Scenario Revised" in scenario_detail.text assert "

Active

" in scenario_detail.text assert "CAD" in scenario_detail.text assert "Electricity" in scenario_detail.text assert "Revised scenario assumptions" in scenario_detail.text scenario_opex_url = scenario_calculation_url( "calculations.scenario_opex_form", project_id, scenario_id, ) scenario_capex_url = scenario_calculation_url( "calculations.scenario_capex_form", project_id, scenario_id, ) assert f'href="{scenario_opex_url}"' in scenario_detail.text assert f'href="{scenario_capex_url}"' in scenario_detail.text # Project detail page should show the scenario as active with updated currency/resource project_detail = client.get(f"/projects/{project_id}/view") assert 'Active' in project_detail.text assert 'CAD' in project_detail.text assert 'Electricity' in project_detail.text # Attempt to update the scenario with invalid currency to trigger validation error invalid_update = client.put( f"/scenarios/{scenario_id}", json={"currency": "ca"}, ) assert invalid_update.status_code == 422 assert "Invalid currency code" in invalid_update.json()[ "detail"][0]["msg"] # Scenario detail should still show the previous (valid) currency scenario_detail = client.get(f"/scenarios/{scenario_id}/view") assert "CAD" in scenario_detail.text assert f'href="{scenario_opex_url}"' in scenario_detail.text assert f'href="{scenario_capex_url}"' in scenario_detail.text # Archive the scenario through the API archive_response = client.put( f"/scenarios/{scenario_id}", json={"status": "archived"}, ) assert archive_response.status_code == 200 assert archive_response.json()["status"] == "archived" # Scenario detail reflects archived status scenario_detail = client.get(f"/scenarios/{scenario_id}/view") assert '

Archived

' in scenario_detail.text assert f'href="{scenario_opex_url}"' in scenario_detail.text assert f'href="{scenario_capex_url}"' in scenario_detail.text # Project detail metrics and table entries reflect the archived state project_detail = client.get(f"/projects/{project_id}/view") assert "

Archived

" in project_detail.text assert '

1

' in project_detail.text assert "Archived" in project_detail.text # Scenario portfolio view includes calculator links for each scenario entry scenario_portfolio = client.get(f"/projects/{project_id}/scenarios/ui") assert scenario_portfolio.status_code == 200 assert f'href="{scenario_opex_url}"' in scenario_portfolio.text assert f'href="{scenario_capex_url}"' in scenario_portfolio.text