- Engine: Add ppa_revenue_cr, mcp_revenue_cr, tariff, units to PnLRow - Engine: Split PPA vs MCP revenue in P&L computation - Web: Collapsible rows for PPA/MCP Revenue and Opex - Web: Highlighted rows (Total Revenue, EBITDA, EBIT, PBT, PAT) - Web: Units above Tariff in breakdown, bg-blue-50 highlight - Fix sticky column z-index for horizontal scroll - CLAUDE.md: Add project documentation Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
76 lines
2 KiB
Python
76 lines
2 KiB
Python
"""CLI smoke tests."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from typer.testing import CliRunner
|
|
|
|
from remodel_engine.cli import app
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
@pytest.fixture()
|
|
def solar_scenario(tmp_path: Path) -> Path:
|
|
data = {
|
|
"solar": {
|
|
"location_id": "RJ",
|
|
"capacity_dc_mwp": 10.0,
|
|
"capacity_ac_mw": 8.0,
|
|
}
|
|
}
|
|
p = tmp_path / "scenario.json"
|
|
p.write_text(json.dumps(data))
|
|
return p
|
|
|
|
|
|
@pytest.fixture()
|
|
def wind_scenario(tmp_path: Path) -> Path:
|
|
data = {
|
|
"wind": {
|
|
"location_id": "RJ",
|
|
"capacity_mw": 10.0,
|
|
}
|
|
}
|
|
p = tmp_path / "scenario.json"
|
|
p.write_text(json.dumps(data))
|
|
return p
|
|
|
|
|
|
def _invoke(scenario: Path, out: Path) -> object:
|
|
return runner.invoke(app, ["simulate-gen", "--input", str(scenario), "--output", str(out)])
|
|
|
|
|
|
def test_simulate_gen_solar(solar_scenario: Path, tmp_path: Path) -> None:
|
|
out = tmp_path / "gen.parquet"
|
|
result = _invoke(solar_scenario, out)
|
|
assert result.exit_code == 0, result.output # type: ignore[union-attr]
|
|
assert out.exists()
|
|
assert "Solar Y1 CUF" in result.output # type: ignore[union-attr]
|
|
|
|
|
|
def test_simulate_gen_wind(wind_scenario: Path, tmp_path: Path) -> None:
|
|
out = tmp_path / "gen.parquet"
|
|
result = _invoke(wind_scenario, out)
|
|
assert result.exit_code == 0, result.output # type: ignore[union-attr]
|
|
assert out.exists()
|
|
assert "Wind Y1 PLF" in result.output # type: ignore[union-attr]
|
|
|
|
|
|
def test_simulate_gen_empty_input(tmp_path: Path) -> None:
|
|
p = tmp_path / "empty.json"
|
|
p.write_text("{}")
|
|
out = tmp_path / "gen.parquet"
|
|
result = _invoke(p, out)
|
|
assert result.exit_code != 0 # type: ignore[union-attr]
|
|
|
|
|
|
def test_simulate_gen_parquet_has_rows(solar_scenario: Path, tmp_path: Path) -> None:
|
|
import pandas as pd
|
|
|
|
out = tmp_path / "gen.parquet"
|
|
result = _invoke(solar_scenario, out)
|
|
assert result.exit_code == 0, result.output # type: ignore[union-attr]
|
|
df = pd.read_parquet(out)
|
|
assert len(df) == 25 * 8760
|