Files
calminer/tests/test_pricing.py
zwitschi 795a9f99f4 feat: Enhance currency handling and validation across scenarios
- 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.
2025-11-11 18:29:59 +01:00

156 lines
4.6 KiB
Python

from __future__ import annotations
import math
import pytest
from services.pricing import (
PricingInput,
PricingMetadata,
PricingResult,
calculate_pricing,
)
def test_calculate_pricing_with_explicit_penalties() -> None:
pricing_input = PricingInput(
metal="copper",
ore_tonnage=100_000,
head_grade_pct=1.2,
recovery_pct=90,
payable_pct=96,
reference_price=8_500,
treatment_charge=100_000,
smelting_charge=0,
moisture_pct=10,
moisture_threshold_pct=8,
moisture_penalty_per_pct=3_000,
impurity_ppm={"As": 100},
impurity_thresholds={"As": 0},
impurity_penalty_per_ppm={"As": 2},
premiums=50_000,
fx_rate=1.0,
currency_code="usd",
)
result = calculate_pricing(pricing_input)
assert isinstance(result, PricingResult)
assert math.isclose(result.payable_metal_tonnes, 1_036.8, rel_tol=1e-6)
assert math.isclose(result.gross_revenue, 1_036.8 * 8_500, rel_tol=1e-6)
assert math.isclose(result.moisture_penalty, 6_000, rel_tol=1e-6)
assert math.isclose(result.impurity_penalty, 200, rel_tol=1e-6)
assert math.isclose(result.net_revenue, 8_756_600, rel_tol=1e-6)
assert result.treatment_smelt_charges == pytest.approx(100_000)
assert result.currency == "USD"
def test_calculate_pricing_with_metadata_defaults() -> None:
metadata = PricingMetadata(
default_payable_pct=95,
default_currency="EUR",
moisture_threshold_pct=7,
moisture_penalty_per_pct=2_000,
impurity_thresholds={"Pb": 50},
impurity_penalty_per_ppm={"Pb": 1.5},
)
pricing_input = PricingInput(
metal="lead",
ore_tonnage=50_000,
head_grade_pct=5,
recovery_pct=85,
payable_pct=None,
reference_price=2_000,
treatment_charge=30_000,
smelting_charge=20_000,
moisture_pct=9,
moisture_threshold_pct=None,
moisture_penalty_per_pct=None,
impurity_ppm={"Pb": 120},
premiums=12_000,
fx_rate=1.2,
currency_code=None,
)
result = calculate_pricing(pricing_input, metadata=metadata)
expected_payable = 50_000 * 0.05 * 0.85 * 0.95
assert math.isclose(result.payable_metal_tonnes,
expected_payable, rel_tol=1e-6)
assert result.moisture_penalty == pytest.approx((9 - 7) * 2_000)
assert result.impurity_penalty == pytest.approx((120 - 50) * 1.5)
assert result.treatment_smelt_charges == pytest.approx(50_000)
assert result.currency == "EUR"
assert result.net_revenue > 0
def test_calculate_pricing_currency_override() -> None:
pricing_input = PricingInput(
metal="gold",
ore_tonnage=10_000,
head_grade_pct=2.5,
recovery_pct=92,
payable_pct=98,
reference_price=60_000,
treatment_charge=40_000,
smelting_charge=10_000,
moisture_pct=5,
moisture_threshold_pct=7,
moisture_penalty_per_pct=1_000,
premiums=25_000,
fx_rate=1.0,
currency_code="cad",
)
metadata = PricingMetadata(default_currency="USD")
result = calculate_pricing(
pricing_input, metadata=metadata, currency="CAD")
assert result.currency == "CAD"
assert result.net_revenue > 0
def test_calculate_pricing_multiple_inputs_aggregate() -> None:
metadata = PricingMetadata(default_currency="USD")
inputs = [
PricingInput(
metal="copper",
ore_tonnage=10_000,
head_grade_pct=1.5,
recovery_pct=88,
payable_pct=95,
reference_price=8_000,
treatment_charge=20_000,
smelting_charge=5_000,
moisture_pct=7,
moisture_threshold_pct=8,
moisture_penalty_per_pct=1_000,
premiums=0,
fx_rate=1.0,
currency_code=None,
),
PricingInput(
metal="copper",
ore_tonnage=8_000,
head_grade_pct=1.1,
recovery_pct=90,
payable_pct=96,
reference_price=8_000,
treatment_charge=18_000,
smelting_charge=4_000,
moisture_pct=9,
moisture_threshold_pct=8,
moisture_penalty_per_pct=1_000,
premiums=0,
fx_rate=1.0,
currency_code="usd",
),
]
results = [calculate_pricing(i, metadata=metadata) for i in inputs]
assert all(result.currency == "USD" for result in results)
assert sum(result.net_revenue for result in results) > 0