mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
170 lines
5.8 KiB
Python
170 lines
5.8 KiB
Python
import pytest
|
||
|
||
from src.services.billing.formula_engine import (
|
||
BillingIncompleteError,
|
||
FormulaEngine,
|
||
UnsafeExpressionError,
|
||
)
|
||
|
||
|
||
class TestSafeExpression:
|
||
def test_reject_attribute_access(self) -> None:
|
||
engine = FormulaEngine()
|
||
with pytest.raises(UnsafeExpressionError):
|
||
engine.evaluate(
|
||
expression="(1).__class__",
|
||
variables={},
|
||
dimensions={},
|
||
dimension_mappings={},
|
||
strict_mode=True, # 确保抛出异常,便于断言类型
|
||
)
|
||
|
||
def test_reject_import(self) -> None:
|
||
engine = FormulaEngine()
|
||
with pytest.raises(UnsafeExpressionError):
|
||
engine.evaluate(
|
||
expression="__import__('os').system('echo hacked')",
|
||
variables={},
|
||
dimensions={},
|
||
dimension_mappings={},
|
||
strict_mode=True,
|
||
)
|
||
|
||
|
||
class TestFormulaEngine:
|
||
def test_basic_video_formula(self) -> None:
|
||
engine = FormulaEngine()
|
||
result = engine.evaluate(
|
||
expression="(base_price + duration_seconds * price_per_second) * resolution_multiplier",
|
||
variables={"base_price": 0.05, "price_per_second": 0.02},
|
||
dimensions={"duration_seconds": 10, "resolution": "720p"},
|
||
dimension_mappings={
|
||
"duration_seconds": {
|
||
"source": "dimension",
|
||
"key": "duration_seconds",
|
||
"required": True,
|
||
},
|
||
"resolution_multiplier": {
|
||
"source": "matrix",
|
||
"key": "resolution",
|
||
"map": {"720p": 1.0, "1080p": 1.5},
|
||
"default": 1.0,
|
||
},
|
||
},
|
||
)
|
||
assert result.status == "complete"
|
||
assert result.missing_required == []
|
||
assert abs(float(result.cost) - 0.25) < 1e-9
|
||
|
||
def test_required_dimension_missing_non_strict(self) -> None:
|
||
engine = FormulaEngine()
|
||
result = engine.evaluate(
|
||
expression="base_price + duration_seconds * price_per_second",
|
||
variables={"base_price": 0.05, "price_per_second": 0.02},
|
||
dimensions={"duration_seconds": None},
|
||
dimension_mappings={
|
||
"duration_seconds": {
|
||
"source": "dimension",
|
||
"key": "duration_seconds",
|
||
"required": True,
|
||
},
|
||
},
|
||
strict_mode=False,
|
||
)
|
||
assert result.status == "incomplete"
|
||
assert float(result.cost) == 0.0
|
||
assert result.missing_required == ["duration_seconds"]
|
||
|
||
def test_required_dimension_missing_strict_raises(self) -> None:
|
||
engine = FormulaEngine()
|
||
with pytest.raises(BillingIncompleteError) as exc:
|
||
engine.evaluate(
|
||
expression="base_price + duration_seconds * price_per_second",
|
||
variables={"base_price": 0.05, "price_per_second": 0.02},
|
||
dimensions={"duration_seconds": None},
|
||
dimension_mappings={
|
||
"duration_seconds": {
|
||
"source": "dimension",
|
||
"key": "duration_seconds",
|
||
"required": True,
|
||
},
|
||
},
|
||
strict_mode=True,
|
||
)
|
||
assert exc.value.missing_required == ["duration_seconds"]
|
||
|
||
def test_allow_zero(self) -> None:
|
||
engine = FormulaEngine()
|
||
|
||
# allow_zero=false(默认):0 视为缺失
|
||
result = engine.evaluate(
|
||
expression="duration_seconds * 1",
|
||
variables={},
|
||
dimensions={"duration_seconds": 0},
|
||
dimension_mappings={
|
||
"duration_seconds": {
|
||
"source": "dimension",
|
||
"key": "duration_seconds",
|
||
"required": True,
|
||
},
|
||
},
|
||
)
|
||
assert result.status == "incomplete"
|
||
assert result.missing_required == ["duration_seconds"]
|
||
|
||
# allow_zero=true:0 合法
|
||
result = engine.evaluate(
|
||
expression="duration_seconds * 1",
|
||
variables={},
|
||
dimensions={"duration_seconds": 0},
|
||
dimension_mappings={
|
||
"duration_seconds": {
|
||
"source": "dimension",
|
||
"key": "duration_seconds",
|
||
"required": True,
|
||
"allow_zero": True,
|
||
},
|
||
},
|
||
)
|
||
assert result.status == "complete"
|
||
assert float(result.cost) == 0.0
|
||
|
||
def test_tiered_mapping(self) -> None:
|
||
engine = FormulaEngine()
|
||
|
||
result = engine.evaluate(
|
||
expression="input_price",
|
||
variables={},
|
||
dimensions={"total_input_tokens": 100_000},
|
||
dimension_mappings={
|
||
"input_price": {
|
||
"source": "tiered",
|
||
"tier_key": "total_input_tokens",
|
||
"tiers": [
|
||
{"up_to": 128_000, "value": 3.0},
|
||
{"up_to": None, "value": 1.5},
|
||
],
|
||
}
|
||
},
|
||
)
|
||
assert result.status == "complete"
|
||
assert float(result.cost) == 3.0
|
||
|
||
result = engine.evaluate(
|
||
expression="input_price",
|
||
variables={},
|
||
dimensions={"total_input_tokens": 200_000},
|
||
dimension_mappings={
|
||
"input_price": {
|
||
"source": "tiered",
|
||
"tier_key": "total_input_tokens",
|
||
"tiers": [
|
||
{"up_to": 128_000, "value": 3.0},
|
||
{"up_to": None, "value": 1.5},
|
||
],
|
||
}
|
||
},
|
||
)
|
||
assert result.status == "complete"
|
||
assert float(result.cost) == 1.5
|