"""预算的核心保护:并发不超额、重复调用不重复计费。 按简化决策删除其余变体用例;夹具从按例克隆改为共享库以消除建库开销。 """ import uuid from concurrent.futures import ThreadPoolExecutor from datetime import UTC, datetime, timedelta from decimal import Decimal from pathlib import Path import pytest from muse.任务运行.接口 import ( 任务服务, 任务请求, 步骤处理器, 步骤结果, 步骤计划, ) from muse.任务运行.预算管理 import ( 任务预算计划, 角色预算, 预算不足, 预算状态冲突, 预算管理, 额度策略, ) from muse.共享.调用身份 import 内容用途, 用途 from muse.基础设施.数据库.连接 import 数据库工厂 from muse.编排.接口 import 流程定义, 流程服务, 流程登记 pytestmark = pytest.mark.数据库 @pytest.fixture def 预算环境(数据库底座, monkeypatch: pytest.MonkeyPatch, tmp_path: Path): with 数据库底座.共享库(tmp_path / "连接引用") as 工厂: for 声明, 当前 in 工厂.items(): monkeypatch.setenv(f"MUSE_BUDGET_{声明.name}_URL", Path(当前.引用.位置).read_text()) yield 工厂 def 新任务(工厂: 数据库工厂): 登记 = 流程登记() 登记.登记处理器(步骤处理器("call", "1", lambda _: 步骤结果({}), "v1", "v1")) 登记.登记类型("budget-test", 必需保护=()) 运行 = 任务服务(工厂, 登记) 编排 = 流程服务(运行, 登记) 编排.发布(流程定义("budget-test", "1", (步骤计划("call", "call", "1"),))) 请求 = 任务请求( "budget-test", uuid.uuid4().hex, "author", 工厂.用途, 内容用途.生成, {}, "policy-1", "release-1", { "source_scope": {}, "schema_versions": {}, "authorization": "grant", "budget": {"approved": True}, "stop_conditions": ["cancelled"], }, ) 身份 = 运行.创建任务(请求, "budget-test", "1") 领取 = 运行.领取步骤("worker", ["call"]) assert 领取 is not None and 领取.任务ID == 身份 return 身份, 领取 def 建预算(工厂: 数据库工厂, 任务ID: str, *, 窗口金额="24", 单次="6", 次数=3, 窗口次数=6000): 管理 = 预算管理(工厂, "quota") 管理.登记策略(额度策略("quota", "1", Decimal(窗口金额), 窗口次数)) 管理.登记任务预算( 任务ID, 任务预算计划( Decimal(单次) * 次数, (角色预算("writer", 次数, 次数, Decimal(单次)),), "approval", datetime.now(UTC) + timedelta(minutes=10), ), ) return 管理 @pytest.mark.case_id( "NC-budget-concurrent-reserve", environment="隔离 PostgreSQL;配置验证器为显式协议替身", given="两个真实任务共用10额度账户", when="两个独立连接同时预留6", then=["只有一笔成功且余额为4"], contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md", ) def test_并发预留共享窗口且不超额__a61001(预算环境) -> None: from threading import Barrier A, 领取A = 新任务(预算环境[用途.生产]) B, 领取B = 新任务(预算环境[用途.生产]) 预算A = 建预算(预算环境[用途.生产], A, 窗口金额="10") 预算B = 建预算(预算环境[用途.生产], B, 窗口金额="10") 栅栏 = Barrier(2) def 预留(输入): 管理, 领取 = 输入 栅栏.wait() try: return 管理.预留(领取, 领取.任务ID, "writer") except 预算不足: return None with ThreadPoolExecutor(max_workers=2) as 线程: 结果 = list(线程.map(预留, [(预算A, 领取A), (预算B, 领取B)])) assert sum(r is not None for r in 结果) == 1 assert 预算A.窗口余额()["在途预留"] == Decimal("6") assert 预算A.窗口余额()["可用金额"] == Decimal("4") @pytest.mark.case_id( "NC-budget-idempotent-settlement", environment="隔离 PostgreSQL;配置验证器为显式协议替身", given="同调用身份和失败调用可信成本回执", when="重复预留、外发申请与结算", then=["只授权外发一次、只计费一次,冲突成本拒绝"], contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md", ) def test_重复预留发送和结算不重复计费__a61002(预算环境) -> None: 身份, 领取 = 新任务(预算环境[用途.生产]) 预算 = 建预算(预算环境[用途.生产], 身份) 初次 = 预算.预留(领取, "call-1", "writer") assert 预算.预留(领取, "call-1", "writer") == 初次 assert 预算.标记已发送(领取, "call-1", 最长秒=30).允许外发 assert not 预算.标记已发送(领取, "call-1", 最长秒=30).允许外发 结果 = 预算.结算("call-1", Decimal("0.125"), 回执ID="failed-call-receipt") assert 预算.结算("call-1", Decimal("0.125"), 回执ID="failed-call-receipt") == 结果 with pytest.raises(预算状态冲突): 预算.结算("call-1", Decimal("0.2"), 回执ID="changed") assert 预算.窗口余额()["已知成本"] == Decimal("0.125") assert 预算.窗口余额()["已占次数"] == 1