- Updated form template to prefill currency input with default value and added help text for clarity. - Modified integration tests to assert more descriptive error messages for invalid currency codes. - Introduced new tests for currency normalization and validation in various scenarios, including imports and exports. - Added comprehensive tests for pricing calculations, ensuring defaults are respected and overrides function correctly. - Implemented unit tests for pricing settings repository, ensuring CRUD operations and default settings are handled properly. - Enhanced scenario pricing evaluation tests to validate currency handling and metadata defaults. - Added simulation tests to ensure Monte Carlo runs are accurate and handle various distribution scenarios.
121 lines
3.5 KiB
Python
121 lines
3.5 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from models import MiningOperationType, Project, Scenario, ScenarioStatus
|
|
from services.pricing import PricingInput, PricingMetadata
|
|
from services.scenario_evaluation import ScenarioPricingConfig, ScenarioPricingEvaluator
|
|
|
|
|
|
def build_scenario() -> Scenario:
|
|
project = Project(name="Test Project",
|
|
operation_type=MiningOperationType.OPEN_PIT)
|
|
scenario = Scenario(
|
|
project=project,
|
|
project_id=1,
|
|
name="Scenario A",
|
|
status=ScenarioStatus.ACTIVE,
|
|
currency="USD",
|
|
)
|
|
scenario.id = 1 # simulate persisted entity
|
|
return scenario
|
|
|
|
|
|
def test_scenario_pricing_evaluator_uses_metadata_defaults() -> None:
|
|
scenario = build_scenario()
|
|
evaluator = ScenarioPricingEvaluator(
|
|
ScenarioPricingConfig(
|
|
metadata=PricingMetadata(
|
|
default_currency="USD", default_payable_pct=95)
|
|
)
|
|
)
|
|
inputs = [
|
|
PricingInput(
|
|
metal="copper",
|
|
ore_tonnage=50_000,
|
|
head_grade_pct=1.0,
|
|
recovery_pct=90,
|
|
payable_pct=None,
|
|
reference_price=9_000,
|
|
treatment_charge=50_000,
|
|
smelting_charge=10_000,
|
|
moisture_pct=9,
|
|
moisture_threshold_pct=None,
|
|
moisture_penalty_per_pct=None,
|
|
impurity_ppm={"As": 120},
|
|
premiums=10_000,
|
|
fx_rate=1.0,
|
|
currency_code=None,
|
|
)
|
|
]
|
|
|
|
snapshot = evaluator.evaluate(scenario, inputs=inputs)
|
|
|
|
assert snapshot.scenario_id == scenario.id
|
|
assert len(snapshot.results) == 1
|
|
result = snapshot.results[0]
|
|
assert result.currency == "USD"
|
|
assert result.net_revenue > 0
|
|
|
|
|
|
def test_scenario_pricing_evaluator_override_metadata() -> None:
|
|
scenario = build_scenario()
|
|
evaluator = ScenarioPricingEvaluator(ScenarioPricingConfig())
|
|
metadata_override = PricingMetadata(
|
|
default_currency="CAD",
|
|
default_payable_pct=90,
|
|
moisture_threshold_pct=5,
|
|
moisture_penalty_per_pct=500,
|
|
)
|
|
|
|
inputs = [
|
|
PricingInput(
|
|
metal="copper",
|
|
ore_tonnage=20_000,
|
|
head_grade_pct=1.2,
|
|
recovery_pct=88,
|
|
payable_pct=None,
|
|
reference_price=8_200,
|
|
treatment_charge=15_000,
|
|
smelting_charge=6_000,
|
|
moisture_pct=6,
|
|
moisture_threshold_pct=None,
|
|
moisture_penalty_per_pct=None,
|
|
premiums=5_000,
|
|
fx_rate=1.0,
|
|
currency_code="cad",
|
|
),
|
|
PricingInput(
|
|
metal="gold",
|
|
ore_tonnage=5_000,
|
|
head_grade_pct=2.0,
|
|
recovery_pct=90,
|
|
payable_pct=None,
|
|
reference_price=60_000,
|
|
treatment_charge=10_000,
|
|
smelting_charge=5_000,
|
|
moisture_pct=4,
|
|
moisture_threshold_pct=None,
|
|
moisture_penalty_per_pct=None,
|
|
premiums=15_000,
|
|
fx_rate=1.0,
|
|
currency_code="cad",
|
|
),
|
|
]
|
|
|
|
snapshot = evaluator.evaluate(
|
|
scenario,
|
|
inputs=inputs,
|
|
metadata_override=metadata_override,
|
|
)
|
|
|
|
assert len(snapshot.results) == 2
|
|
assert all(result.currency ==
|
|
scenario.currency for result in snapshot.results)
|
|
|
|
copper_result = snapshot.results[0]
|
|
expected_payable = 20_000 * 0.012 * 0.88 * 0.90
|
|
assert copper_result.payable_metal_tonnes == pytest.approx(
|
|
expected_payable)
|
|
assert sum(result.net_revenue for result in snapshot.results) > 0
|