86 lines
3.2 KiB
Python
86 lines
3.2 KiB
Python
"""预算时间与金额、配置引用的确定性合同。"""
|
|
|
|
from datetime import datetime, timedelta
|
|
from decimal import Decimal
|
|
from zoneinfo import ZoneInfo
|
|
|
|
import pytest
|
|
|
|
from muse.任务运行.配置版本 import 凭据引用, 提供方配置, 运行配置内容, 配置版本错误
|
|
from muse.任务运行.预算管理 import 任务预算计划, 归属窗口, 角色预算, 预算错误, 额度策略
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"hour,expected",
|
|
[(0, 0), (4, 0), (5, 5), (9, 5), (10, 10), (14, 10), (15, 15), (19, 15), (20, 20), (23, 20)],
|
|
ids=["00", "04", "05", "09", "10", "14", "15", "19", "20", "23"],
|
|
)
|
|
def test_五小时额度窗口承接每日边界__2852b5(hour: int, expected: int) -> None:
|
|
当前 = datetime(2026, 7, 16, hour, 30, tzinfo=ZoneInfo("Asia/Shanghai"))
|
|
窗口 = 归属窗口(当前)
|
|
assert 窗口.起点.strftime("%Y-%m-%dT%H") == f"2026-07-16T{expected:02}"
|
|
assert 窗口.终点 - 窗口.起点 == timedelta(hours=4 if hour >= 20 else 5)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"hour,minute,seconds",
|
|
[(3, 0, 7200), (19, 30, 1800), (21, 0, 10800), (23, 59, 60)],
|
|
ids=["morning", "20-boundary", "midnight", "last-minute"],
|
|
)
|
|
def test_下一窗口等待秒数按原日界线计算__7790f8(hour: int, minute: int, seconds: int) -> None:
|
|
当前 = datetime(2026, 9, 9, hour, minute, tzinfo=ZoneInfo("Asia/Shanghai"))
|
|
assert (归属窗口(当前).终点 - 当前).total_seconds() == seconds
|
|
|
|
|
|
def test_预算计划保留调用计划并按单次上限预留__a60003() -> None:
|
|
角色 = (
|
|
角色预算("writer", 45, 150, Decimal("5")),
|
|
角色预算("detector", 24, 150, Decimal("5")),
|
|
角色预算("judge", 45, 150, Decimal("5")),
|
|
)
|
|
计划 = 任务预算计划(
|
|
Decimal("570"), 角色, "approval-1", datetime(2030, 1, 1, tzinfo=ZoneInfo("UTC"))
|
|
)
|
|
assert 计划.最坏预留 == Decimal("570.000000")
|
|
assert 计划.冻结()["角色"][0]["计划次数"] == 45
|
|
with pytest.raises(预算错误):
|
|
任务预算计划(Decimal("569.99"), 角色, "approval-1", 计划.截止时间)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"amount",
|
|
[Decimal("NaN"), Decimal("-1"), Decimal("Infinity")],
|
|
ids=["nan", "negative", "infinite"],
|
|
)
|
|
def test_预算拒绝不确定或负数金额__a60001(amount: Decimal) -> None:
|
|
with pytest.raises(预算错误):
|
|
额度策略("quota", "1", amount, 6000)
|
|
|
|
|
|
def test_配置凭据只保留引用且调用参数不可藏入明文__a60002() -> None:
|
|
引用 = 凭据引用("api", "环境变量", "MUSE_PROVIDER_KEY")
|
|
内容 = 运行配置内容(
|
|
"direct",
|
|
"1",
|
|
"role-policy-1",
|
|
"release-1",
|
|
"quota-1",
|
|
{"writer": {"provider": "provider", "model": "model", "thinking": "high"}},
|
|
(引用,),
|
|
(提供方配置("provider", "responses", "https://provider.invalid/v1/responses", "api"),),
|
|
"price-1",
|
|
)
|
|
assert 内容.冻结()["凭据"][0]["位置"] == "MUSE_PROVIDER_KEY"
|
|
with pytest.raises(配置版本错误):
|
|
运行配置内容(
|
|
"direct",
|
|
"1",
|
|
"policy",
|
|
"release",
|
|
"quota",
|
|
{"writer": {"api_key": "synthetic-secret"}},
|
|
(),
|
|
(),
|
|
"price-1",
|
|
)
|