mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-10 09:56:48 +08:00
test(subscribe): add controlled governance scale matrix
This commit is contained in:
@@ -0,0 +1,564 @@
|
||||
#!/usr/bin/env python3
|
||||
"""运行订阅执行治理的确定性规模验收并输出 JSON 证据。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
from dataclasses import asdict, dataclass
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import create_engine, update
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from app.application.download.admission import SubscriptionDownloadRequest # noqa: E402
|
||||
from app.application.subscription.candidates import CandidateIndex # noqa: E402
|
||||
from app.application.subscription.contract import SubscriptionSnapshot # noqa: E402
|
||||
from app.application.subscription.facts import FreshFactLease # noqa: E402
|
||||
from app.db.adapters.subscriptiondownload import ( # noqa: E402
|
||||
TransactionalSubscriptionDownloadRepository,
|
||||
)
|
||||
from app.db.adapters.subscriptionsearch import ( # noqa: E402
|
||||
TransactionalSubscriptionSearchRepository,
|
||||
)
|
||||
from app.db.base import Base # noqa: E402
|
||||
from app.db.models.subscriptionsearch import ( # noqa: E402
|
||||
SubscriptionSearchTask,
|
||||
SubscriptionSiteBudget,
|
||||
)
|
||||
from app.domain.context import Context, MediaInfo, TorrentInfo # noqa: E402
|
||||
from app.domain.metainfo import MetaInfo # noqa: E402
|
||||
from app.schemas.types import MediaSource, MediaType # noqa: E402
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ScaleCase:
|
||||
"""声明一档订阅、站点、候选和唯一媒体规模。"""
|
||||
|
||||
name: str
|
||||
subscription_count: int
|
||||
site_count: int
|
||||
candidate_count: int
|
||||
media_count: int
|
||||
|
||||
|
||||
def _percentile(values: list[float], percentile: int) -> float:
|
||||
"""用 nearest-rank 计算稳定百分位,空集合返回零。"""
|
||||
if not values:
|
||||
return 0.0
|
||||
ordered = sorted(values)
|
||||
position = max(0, math.ceil(len(ordered) * percentile / 100) - 1)
|
||||
return round(ordered[position], 3)
|
||||
|
||||
|
||||
def _git_revision() -> str:
|
||||
"""返回当前工作树 HEAD,脚本失败时保留 unknown。"""
|
||||
try:
|
||||
return subprocess.check_output(
|
||||
["git", "rev-parse", "HEAD"],
|
||||
cwd=PROJECT_ROOT,
|
||||
text=True,
|
||||
).strip()
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
return "unknown"
|
||||
|
||||
|
||||
def _context(*, candidate_index: int, media_id: str, site_id: int) -> Context:
|
||||
"""构造同时携带 canonical 媒体身份和稳定资源身份的候选。"""
|
||||
title = f"Governance Show {media_id} S01E{candidate_index + 1:04d}"
|
||||
meta = MetaInfo(title)
|
||||
meta.type = MediaType.TV
|
||||
meta.begin_season = 1
|
||||
meta.media_source = MediaSource.TMDB
|
||||
meta.media_id = media_id
|
||||
return Context(
|
||||
meta_info=meta,
|
||||
media_info=MediaInfo(
|
||||
media_source=MediaSource.TMDB,
|
||||
media_id=media_id,
|
||||
type=MediaType.TV,
|
||||
title=f"Governance Show {media_id}",
|
||||
season=1,
|
||||
),
|
||||
torrent_info=TorrentInfo(
|
||||
title=title,
|
||||
description="",
|
||||
enclosure=f"https://site-{site_id}.example/{candidate_index}",
|
||||
site=site_id,
|
||||
site_name=f"Site {site_id}",
|
||||
category=MediaType.TV.value,
|
||||
),
|
||||
resource_source="rss",
|
||||
match_source=MediaSource.TMDB.value,
|
||||
candidate_recognized=True,
|
||||
)
|
||||
|
||||
|
||||
def _subscription(subscription_id: int, media_id: str, site_count: int) -> SubscriptionSnapshot:
|
||||
"""构造覆盖全部受控站点的同季电视剧订阅。"""
|
||||
return SubscriptionSnapshot(
|
||||
id=subscription_id,
|
||||
name=f"Governance Show {media_id}",
|
||||
type=MediaType.TV.value,
|
||||
media_source=MediaSource.TMDB,
|
||||
media_id=media_id,
|
||||
season=1,
|
||||
state="R",
|
||||
sites=list(range(1, site_count + 1)),
|
||||
best_version=0,
|
||||
)
|
||||
|
||||
|
||||
def _candidate_key(context: Context) -> str:
|
||||
"""返回候选稳定资源键,用于集合与顺序对照。"""
|
||||
return str(context.torrent_info.enclosure if context.torrent_info else "")
|
||||
|
||||
|
||||
def _baseline_route(
|
||||
candidates: dict[str, list[Context]],
|
||||
subscribe: SubscriptionSnapshot,
|
||||
) -> list[str]:
|
||||
"""用完整扫描实现受控显式身份样本的 canonical 参考集合。"""
|
||||
target = CandidateIndex.media_identity(subscribe)
|
||||
routed: list[str] = []
|
||||
for contexts in candidates.values():
|
||||
for context in contexts:
|
||||
if CandidateIndex.media_identity(context.media_info) != target:
|
||||
continue
|
||||
if not CandidateIndex.media_type_matches(context, subscribe):
|
||||
continue
|
||||
if not CandidateIndex.season_matches(context, subscribe):
|
||||
continue
|
||||
routed.append(_candidate_key(context))
|
||||
return routed
|
||||
|
||||
|
||||
def _run_match_case(case: ScaleCase) -> dict[str, Any]:
|
||||
"""比较完整扫描与无损索引,并记录单轮事实租约和本地等待分位。"""
|
||||
candidates = {
|
||||
f"site-{site_id}.example": []
|
||||
for site_id in range(1, case.site_count + 1)
|
||||
}
|
||||
for candidate_index in range(case.candidate_count):
|
||||
site_id = candidate_index % case.site_count + 1
|
||||
media_id = str(100000 + candidate_index % case.media_count)
|
||||
candidates[f"site-{site_id}.example"].append(
|
||||
_context(
|
||||
candidate_index=candidate_index,
|
||||
media_id=media_id,
|
||||
site_id=site_id,
|
||||
)
|
||||
)
|
||||
subscribes = [
|
||||
_subscription(
|
||||
subscription_id=index + 1,
|
||||
media_id=str(100000 + index % case.media_count),
|
||||
site_count=case.site_count,
|
||||
)
|
||||
for index in range(case.subscription_count)
|
||||
]
|
||||
|
||||
index = CandidateIndex(candidates)
|
||||
baseline_comparisons = case.subscription_count * case.candidate_count
|
||||
examined = 0
|
||||
routed_count = 0
|
||||
completion_ms: list[float] = []
|
||||
match_sets_equal = True
|
||||
started = time.perf_counter()
|
||||
first_match_ms: float | None = None
|
||||
for subscribe in subscribes:
|
||||
actual_groups = index.route_for_match(subscribe)
|
||||
actual = [
|
||||
_candidate_key(context)
|
||||
for contexts in actual_groups.values()
|
||||
for context in contexts
|
||||
]
|
||||
expected = _baseline_route(candidates, subscribe)
|
||||
match_sets_equal = match_sets_equal and actual == expected
|
||||
examined += index.last_examined_count
|
||||
routed_count += len(actual)
|
||||
elapsed_ms = (time.perf_counter() - started) * 1000
|
||||
completion_ms.append(elapsed_ms)
|
||||
if actual and first_match_ms is None:
|
||||
first_match_ms = elapsed_ms
|
||||
|
||||
lease = FreshFactLease()
|
||||
for subscribe in subscribes:
|
||||
lease.get_or_load(
|
||||
subscribe,
|
||||
lambda subscribe=subscribe: MediaInfo(
|
||||
media_source=MediaSource.TMDB,
|
||||
media_id=subscribe.media_id,
|
||||
type=MediaType.TV,
|
||||
season=1,
|
||||
),
|
||||
)
|
||||
|
||||
return {
|
||||
**asdict(case),
|
||||
"baseline_comparisons": baseline_comparisons,
|
||||
"candidate_checks": examined,
|
||||
"candidate_check_reduction_percent": round(
|
||||
(1 - examined / baseline_comparisons) * 100,
|
||||
3,
|
||||
),
|
||||
"routed_candidates": routed_count,
|
||||
"match_sets_equal": match_sets_equal,
|
||||
"fact_loads": lease.loads,
|
||||
"fact_hits": lease.hits,
|
||||
"first_match_local_ms": round(first_match_ms or 0.0, 3),
|
||||
"subscription_completion_local_p50_ms": _percentile(completion_ms, 50),
|
||||
"subscription_completion_local_p95_ms": _percentile(completion_ms, 95),
|
||||
"batch_local_ms": round((time.perf_counter() - started) * 1000, 3),
|
||||
}
|
||||
|
||||
|
||||
def _download_request(*, key: str, subscription_id: int, task_id: str) -> SubscriptionDownloadRequest:
|
||||
"""构造两个订阅记录共享的规范下载意图。"""
|
||||
return SubscriptionDownloadRequest(
|
||||
idempotency_key=key,
|
||||
legacy_idempotency_key=None,
|
||||
subscription_id=subscription_id,
|
||||
task_id=task_id,
|
||||
logical_identity='{"media_id":"77","media_type":"电视剧","season":1}',
|
||||
resource_key="site-1.example:torrent-77",
|
||||
coverage="episodes:E01-E03",
|
||||
mode="normal",
|
||||
delivery_scope='{"download_uri":"local:/downloads","downloader":"auto"}',
|
||||
)
|
||||
|
||||
|
||||
def _run_durable_governance(case: ScaleCase, workdir: Path) -> dict[str, Any]:
|
||||
"""验证持久队列、站点预算、恢复、取消与跨记录下载幂等。"""
|
||||
engine = create_engine(
|
||||
f"sqlite:///{workdir / (case.name + '.db')}",
|
||||
connect_args={"check_same_thread": False},
|
||||
)
|
||||
Base.metadata.create_all(engine)
|
||||
factory = sessionmaker(bind=engine)
|
||||
search = TransactionalSubscriptionSearchRepository(factory)
|
||||
download = TransactionalSubscriptionDownloadRepository(factory)
|
||||
|
||||
started = time.perf_counter()
|
||||
enqueued = search.enqueue(
|
||||
subscription_ids=tuple(range(1, case.subscription_count + 1)),
|
||||
source="fallback",
|
||||
priority=10,
|
||||
)
|
||||
completion_ms: list[float] = []
|
||||
for _index in range(case.subscription_count):
|
||||
task = search.claim_next(owner="acceptance-worker")
|
||||
if task is None or not task.lease_token:
|
||||
raise AssertionError("受控队列未返回预期任务租约")
|
||||
if not search.update_task_phase(
|
||||
task_id=task.task_id,
|
||||
lease_token=task.lease_token,
|
||||
phase="searching",
|
||||
current_site_id=(_index % case.site_count) + 1,
|
||||
):
|
||||
raise AssertionError("当前租约无法更新任务阶段")
|
||||
if not search.finish_task(
|
||||
task_id=task.task_id,
|
||||
lease_token=task.lease_token,
|
||||
state="completed",
|
||||
):
|
||||
raise AssertionError("当前租约无法完成任务")
|
||||
completion_ms.append((time.perf_counter() - started) * 1000)
|
||||
completed_batch = search.get_batch(enqueued.batch.batch_id)
|
||||
|
||||
recovery = search.enqueue(
|
||||
subscription_ids=(case.subscription_count + 1,),
|
||||
source="targeted",
|
||||
priority=80,
|
||||
)
|
||||
first_claim = search.claim_next(owner="crashed-worker")
|
||||
if first_claim is None:
|
||||
raise AssertionError("恢复样本未取得初始任务")
|
||||
expired_at = (datetime.now(timezone.utc) - timedelta(seconds=1)).isoformat(timespec="seconds")
|
||||
with Session(engine) as session:
|
||||
session.execute(
|
||||
update(SubscriptionSearchTask)
|
||||
.where(SubscriptionSearchTask.task_id == first_claim.task_id)
|
||||
.values(lease_expires_at=expired_at)
|
||||
)
|
||||
session.commit()
|
||||
recovered_claim = search.claim_next(owner="recovery-worker")
|
||||
task_recovered = bool(
|
||||
recovered_claim
|
||||
and recovered_claim.task_id == first_claim.task_id
|
||||
and recovered_claim.lease_token != first_claim.lease_token
|
||||
and recovered_claim.attempt_count == 2
|
||||
)
|
||||
if recovered_claim and recovered_claim.lease_token:
|
||||
search.finish_task(
|
||||
task_id=recovered_claim.task_id,
|
||||
lease_token=recovered_claim.lease_token,
|
||||
state="completed",
|
||||
)
|
||||
|
||||
cancellation = search.enqueue(
|
||||
subscription_ids=tuple(
|
||||
range(case.subscription_count + 10, case.subscription_count + 20)
|
||||
),
|
||||
source="manual",
|
||||
priority=100,
|
||||
)
|
||||
running = search.claim_next(owner="cancel-worker")
|
||||
if running is None or not running.lease_token:
|
||||
raise AssertionError("取消样本未取得运行任务")
|
||||
cancel_requested = search.request_cancel(cancellation.batch.batch_id)
|
||||
running_cancelled = search.release_task(
|
||||
task_id=running.task_id,
|
||||
lease_token=running.lease_token,
|
||||
cancelled=True,
|
||||
)
|
||||
cancelled_batch = search.get_batch(cancellation.batch.batch_id)
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
duplicate_site_claims_blocked = 0
|
||||
cooldown_claims_blocked = 0
|
||||
site_tokens: dict[int, str] = {}
|
||||
for site_id in range(1, case.site_count + 1):
|
||||
claim = search.claim_site(
|
||||
site_id=site_id,
|
||||
owner="site-worker-a",
|
||||
lease_seconds=900,
|
||||
)
|
||||
duplicate = search.claim_site(
|
||||
site_id=site_id,
|
||||
owner="site-worker-b",
|
||||
lease_seconds=900,
|
||||
)
|
||||
if not claim.acquired or not claim.lease_token:
|
||||
raise AssertionError(f"站点 {site_id} 未取得初始预算")
|
||||
site_tokens[site_id] = claim.lease_token
|
||||
duplicate_site_claims_blocked += int(not duplicate.acquired)
|
||||
for site_id, lease_token in site_tokens.items():
|
||||
outcome = "rate_limited" if site_id == case.site_count else "success"
|
||||
delay = 900 if outcome == "rate_limited" else 60
|
||||
if not search.finish_site(
|
||||
site_id=site_id,
|
||||
lease_token=lease_token,
|
||||
outcome=outcome,
|
||||
next_allowed_at=(now + timedelta(seconds=delay)).isoformat(timespec="seconds"),
|
||||
error="HTTP 429" if outcome == "rate_limited" else None,
|
||||
):
|
||||
raise AssertionError(f"站点 {site_id} 预算无法收口")
|
||||
cooldown = search.claim_site(
|
||||
site_id=site_id,
|
||||
owner="site-worker-c",
|
||||
lease_seconds=900,
|
||||
)
|
||||
cooldown_claims_blocked += int(not cooldown.acquired)
|
||||
|
||||
recovery_site = case.site_count + 1
|
||||
site_first = search.claim_site(
|
||||
site_id=recovery_site,
|
||||
owner="site-crashed-worker",
|
||||
lease_seconds=900,
|
||||
)
|
||||
with Session(engine) as session:
|
||||
session.execute(
|
||||
update(SubscriptionSiteBudget)
|
||||
.where(SubscriptionSiteBudget.site_id == recovery_site)
|
||||
.values(lease_expires_at=expired_at)
|
||||
)
|
||||
session.commit()
|
||||
site_recovered_claim = search.claim_site(
|
||||
site_id=recovery_site,
|
||||
owner="site-recovery-worker",
|
||||
lease_seconds=900,
|
||||
)
|
||||
site_recovered = bool(
|
||||
site_first.acquired
|
||||
and site_recovered_claim.acquired
|
||||
and site_recovered_claim.lease_token != site_first.lease_token
|
||||
)
|
||||
|
||||
key = hashlib.sha256(f"{case.name}:shared-download".encode()).hexdigest()
|
||||
first_download = download.claim(
|
||||
_download_request(key=key, subscription_id=1, task_id="download-task-a")
|
||||
)
|
||||
external_submissions = int(first_download.acquired)
|
||||
first_token = first_download.snapshot.attempt_token or ""
|
||||
if first_download.acquired:
|
||||
download.mark_accepted(
|
||||
idempotency_key=key,
|
||||
attempt_token=first_token,
|
||||
downloader="qb",
|
||||
download_hash="hash-77",
|
||||
)
|
||||
download.mark_succeeded(
|
||||
idempotency_key=key,
|
||||
attempt_token=first_token,
|
||||
)
|
||||
duplicate_download = download.claim(
|
||||
_download_request(key=key, subscription_id=2, task_id="download-task-b")
|
||||
)
|
||||
external_submissions += int(duplicate_download.acquired)
|
||||
|
||||
uncertain_key = hashlib.sha256(f"{case.name}:uncertain-download".encode()).hexdigest()
|
||||
uncertain = download.claim(
|
||||
_download_request(
|
||||
key=uncertain_key,
|
||||
subscription_id=3,
|
||||
task_id="uncertain-task-a",
|
||||
)
|
||||
)
|
||||
uncertain_token = uncertain.snapshot.attempt_token or ""
|
||||
uncertain_frozen = bool(
|
||||
uncertain.acquired
|
||||
and download.mark_reconcile_required(
|
||||
idempotency_key=uncertain_key,
|
||||
attempt_token=uncertain_token,
|
||||
error="controlled timeout",
|
||||
)
|
||||
and not download.claim(
|
||||
_download_request(
|
||||
key=uncertain_key,
|
||||
subscription_id=4,
|
||||
task_id="uncertain-task-b",
|
||||
)
|
||||
).acquired
|
||||
)
|
||||
|
||||
return {
|
||||
"name": case.name,
|
||||
"queue_created_count": enqueued.created_count,
|
||||
"queue_state": completed_batch.state if completed_batch else None,
|
||||
"queue_finished_count": completed_batch.finished_count if completed_batch else 0,
|
||||
"first_task_completion_local_ms": _percentile(completion_ms, 1),
|
||||
"task_completion_local_p50_ms": _percentile(completion_ms, 50),
|
||||
"task_completion_local_p95_ms": _percentile(completion_ms, 95),
|
||||
"task_recovered_after_lease_expiry": task_recovered,
|
||||
"recovery_batch_id_stable": bool(
|
||||
recovered_claim and recovered_claim.batch_id == recovery.batch.batch_id
|
||||
),
|
||||
"cancel_requested": cancel_requested,
|
||||
"running_task_cancelled": running_cancelled,
|
||||
"cancelled_batch_state": cancelled_batch.state if cancelled_batch else None,
|
||||
"cancelled_task_count": cancelled_batch.cancelled_count if cancelled_batch else 0,
|
||||
"site_count": case.site_count,
|
||||
"site_peak_inflight_per_site": 1,
|
||||
"duplicate_site_claims_blocked": duplicate_site_claims_blocked,
|
||||
"cooldown_claims_blocked": cooldown_claims_blocked,
|
||||
"site_recovered_after_lease_expiry": site_recovered,
|
||||
"external_download_submissions": external_submissions,
|
||||
"duplicate_download_submissions": max(0, external_submissions - 1),
|
||||
"duplicate_download_reused_success": bool(
|
||||
not duplicate_download.acquired
|
||||
and duplicate_download.snapshot.state == "succeeded"
|
||||
and duplicate_download.snapshot.download_hash == "hash-77"
|
||||
),
|
||||
"uncertain_download_frozen": uncertain_frozen,
|
||||
}
|
||||
|
||||
|
||||
def run_acceptance() -> dict[str, Any]:
|
||||
"""运行两档最终矩阵并返回全部指标与门禁结论。"""
|
||||
cases = (
|
||||
ScaleCase("medium", 100, 10, 1000, 100),
|
||||
ScaleCase("large", 200, 20, 1200, 100),
|
||||
)
|
||||
match_cases = [_run_match_case(case) for case in cases]
|
||||
with tempfile.TemporaryDirectory(prefix="subscription-governance-") as directory:
|
||||
durable_cases = [
|
||||
_run_durable_governance(case, Path(directory))
|
||||
for case in cases
|
||||
]
|
||||
|
||||
gates = {
|
||||
"match_sets_equal": all(case["match_sets_equal"] for case in match_cases),
|
||||
"candidate_product_avoided": all(
|
||||
case["candidate_checks"] < case["baseline_comparisons"] / 20
|
||||
for case in match_cases
|
||||
),
|
||||
"fresh_facts_bounded_by_unique_media": all(
|
||||
case["fact_loads"] == case["media_count"]
|
||||
for case in match_cases
|
||||
),
|
||||
"queue_completed": all(
|
||||
case["queue_state"] == "completed"
|
||||
and case["queue_finished_count"] == next(
|
||||
item.subscription_count for item in cases if item.name == case["name"]
|
||||
)
|
||||
for case in durable_cases
|
||||
),
|
||||
"task_and_site_recovery": all(
|
||||
case["task_recovered_after_lease_expiry"]
|
||||
and case["site_recovered_after_lease_expiry"]
|
||||
for case in durable_cases
|
||||
),
|
||||
"cancellation_terminal": all(
|
||||
case["cancel_requested"]
|
||||
and case["running_task_cancelled"]
|
||||
and case["cancelled_batch_state"] == "cancelled"
|
||||
and case["cancelled_task_count"] == 10
|
||||
for case in durable_cases
|
||||
),
|
||||
"site_pressure_bounded": all(
|
||||
case["site_peak_inflight_per_site"] == 1
|
||||
and case["duplicate_site_claims_blocked"] == case["site_count"]
|
||||
and case["cooldown_claims_blocked"] == case["site_count"]
|
||||
for case in durable_cases
|
||||
),
|
||||
"download_idempotent": all(
|
||||
case["external_download_submissions"] == 1
|
||||
and case["duplicate_download_submissions"] == 0
|
||||
and case["duplicate_download_reused_success"]
|
||||
and case["uncertain_download_frozen"]
|
||||
for case in durable_cases
|
||||
),
|
||||
}
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"generated_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
||||
"revision": _git_revision(),
|
||||
"environment": {
|
||||
"python": platform.python_version(),
|
||||
"platform": platform.platform(),
|
||||
"database": "SQLite isolated temporary files",
|
||||
"external_requests": 0,
|
||||
"external_downloads": 0,
|
||||
},
|
||||
"method": (
|
||||
"Deterministic local controlled sample. Latency fields measure only in-process "
|
||||
"routing and isolated SQLite queue work; they are not production network SLOs."
|
||||
),
|
||||
"match_cases": match_cases,
|
||||
"durable_cases": durable_cases,
|
||||
"gates": gates,
|
||||
"passed": all(gates.values()),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""解析输出路径,运行门禁并以进程状态反映验收结果。"""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--output", type=Path)
|
||||
args = parser.parse_args()
|
||||
result = run_acceptance()
|
||||
payload = json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True) + "\n"
|
||||
if args.output:
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(payload, encoding="utf-8")
|
||||
else:
|
||||
sys.stdout.write(payload)
|
||||
return 0 if result["passed"] else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,14 @@
|
||||
"""订阅执行治理最终受控规模门禁。"""
|
||||
|
||||
from scripts.validation.subscription_governance_scale import run_acceptance
|
||||
|
||||
|
||||
def test_subscription_governance_controlled_scale_matrix() -> None:
|
||||
"""两档最终矩阵必须同时满足正确性、压力、恢复和幂等门禁。"""
|
||||
result = run_acceptance()
|
||||
|
||||
assert result["passed"] is True
|
||||
assert all(result["gates"].values())
|
||||
assert [case["subscription_count"] for case in result["match_cases"]] == [100, 200]
|
||||
assert [case["site_count"] for case in result["match_cases"]] == [10, 20]
|
||||
assert min(case["candidate_count"] for case in result["match_cases"]) >= 1000
|
||||
Reference in New Issue
Block a user