from __future__ import annotations from io import BytesIO import pandas as pd import pytest from fastapi.testclient import TestClient from models import ( MiningOperationType, Project, Scenario, ScenarioStatus, ) from models.import_export_log import ImportExportLog @pytest.fixture() def project_seed(unit_of_work_factory): with unit_of_work_factory() as uow: assert uow.projects is not None project = Project(name="Seed Project", operation_type=MiningOperationType.OPEN_PIT) uow.projects.create(project) yield project def test_project_import_preview_and_commit(client: TestClient, unit_of_work_factory) -> None: csv_content = ( "name,location,operation_type\n" "Project Import A,Chile,open pit\n" "Project Import B,Canada,underground\n" ) files = {"file": ("projects.csv", csv_content, "text/csv")} preview_response = client.post("/imports/projects/preview", files=files) assert preview_response.status_code == 200 preview_payload = preview_response.json() assert preview_payload["summary"]["accepted"] == 2 assert preview_payload["stage_token"] token = preview_payload["stage_token"] commit_response = client.post("/imports/projects/commit", json={"token": token}) assert commit_response.status_code == 200 commit_payload = commit_response.json() assert commit_payload["summary"]["created"] == 2 with unit_of_work_factory() as uow: assert uow.projects is not None names = {project.name for project in uow.projects.list()} assert {"Project Import A", "Project Import B"}.issubset(names) # ensure audit logs recorded preview and commit events assert uow.session is not None logs = ( uow.session.query(ImportExportLog) .filter(ImportExportLog.dataset == "projects") .order_by(ImportExportLog.created_at) .all() ) actions = [log.action for log in logs] assert "preview" in actions assert "commit" in actions def test_scenario_import_preview_and_commit(client: TestClient, unit_of_work_factory, project_seed) -> None: csv_content = ( "project_name,name,status\n" "Seed Project,Scenario Import A,Draft\n" "Seed Project,Scenario Import B,Active\n" ) files = {"file": ("scenarios.csv", csv_content, "text/csv")} preview_response = client.post("/imports/scenarios/preview", files=files) assert preview_response.status_code == 200 preview_payload = preview_response.json() assert preview_payload["summary"]["accepted"] == 2 token = preview_payload["stage_token"] commit_response = client.post("/imports/scenarios/commit", json={"token": token}) assert commit_response.status_code == 200 commit_payload = commit_response.json() assert commit_payload["summary"]["created"] == 2 with unit_of_work_factory() as uow: assert uow.projects is not None and uow.scenarios is not None project = uow.projects.list()[0] scenarios = uow.scenarios.list_for_project(project.id) names = {scenario.name for scenario in scenarios} assert {"Scenario Import A", "Scenario Import B"}.issubset(names) assert uow.session is not None logs = ( uow.session.query(ImportExportLog) .filter(ImportExportLog.dataset == "scenarios") .order_by(ImportExportLog.created_at) .all() ) actions = [log.action for log in logs] assert "preview" in actions assert "commit" in actions def test_project_export_endpoint(client: TestClient, unit_of_work_factory) -> None: with unit_of_work_factory() as uow: assert uow.projects is not None uow.projects.create(Project(name="Export Project", operation_type=MiningOperationType.OPEN_PIT)) response = client.post("/exports/projects", json={"format": "csv"}) assert response.status_code == 200 assert response.headers["Content-Type"].startswith("text/csv") assert "attachment; filename=" in response.headers["Content-Disposition"] body = response.content.decode("utf-8") assert "Export Project" in body with unit_of_work_factory() as uow: assert uow.session is not None logs = ( uow.session.query(ImportExportLog) .filter(ImportExportLog.dataset == "projects", ImportExportLog.action == "export") .order_by(ImportExportLog.created_at.desc()) .first() ) assert logs is not None assert logs.status == "success" assert logs.row_count >= 1