test(#1743): 修复 smart-match 随机噪声引发的确定性排序断言
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 14s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 15s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m22s
AI Code Review / AI Code Review (pull_request) Successful in 1m31s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m35s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 1m53s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m54s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m49s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 3m7s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m31s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 14m41s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 9s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 7s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m30s

CI Unit Tests 暴露(沙箱 httpx2 无法 collect test_smart_match*.py 未本地复现):
- test_smart_match_fallback test_limit_backfills: second(quality=40)/third(30)
  质量项仅差 4 分 < 噪声 20,third 可翻到首位 → second 质量提至 90(差 24>20),
  200 seeds 验证零翻转
- test_smart_match test_returns_sorted_by_score_descending / test_image_assets:
  smart_select_assets 直接调用处注入零噪声 rng(_ZeroNoiseRandom),验证纯评分排序
- 验证生产噪声行为:大分差素材排名稳定、同分素材组合随机
This commit is contained in:
saas-backend-agent
2026-09-06 19:58:59 +08:00
parent ed09794f4d
commit 6cdb70bb61
2 changed files with 23 additions and 3 deletions
+20 -2
View File
@@ -1,5 +1,6 @@
"""Tests for packages/domain/smart_match.py — 统一智能选素材算法。"""
import random
from dataclasses import dataclass, field
from datetime import datetime, timedelta, timezone
from typing import Any
@@ -7,6 +8,7 @@ from typing import Any
import pytest
from packages.domain.smart_match import (
SCORE_RANDOM_NOISE_MAX,
SmartMatchResult,
_diversity_select,
_duration_bucket,
@@ -46,6 +48,22 @@ class FakeAsset:
NOW = datetime(2026, 8, 5, 12, 0, 0, tzinfo=timezone.utc)
class _ZeroNoiseRandom(random.Random):
"""零噪声随机源:uniform(0, NOISE_MAX) 恒返回 0,使「按评分降序」类断言确定可复现。
smart_select_assets 生产环境注入随机噪声(同分素材每次选出不同组合,#1743);
验证纯评分排序的单测用本随机源消除排序随机性。
"""
def uniform(self, a, b):
if a == 0.0 and b == SCORE_RANDOM_NOISE_MAX:
return 0.0
return super().uniform(a, b)
_ZERO_NOISE = _ZeroNoiseRandom(0)
# ── score_asset tests ────────────────────────────────────────────────────────
@@ -185,7 +203,7 @@ class TestSmartSelectAssets:
FakeAsset(id="high", quality_score=95, duration=15),
FakeAsset(id="mid", quality_score=60, duration=15),
]
results = smart_select_assets(assets)
results = smart_select_assets(assets, rng=_ZERO_NOISE)
scores = [r.score for r in results]
assert scores == sorted(scores, reverse=True)
assert results[0].asset.id == "high"
@@ -234,7 +252,7 @@ class TestSmartSelectAssets:
FakeAsset(id="img1", mime_type="image/jpeg", quality_score=90, duration=None),
FakeAsset(id="img2", mime_type="image/png", quality_score=70, duration=None),
]
results = smart_select_assets(assets, kind="image")
results = smart_select_assets(assets, kind="image", rng=_ZERO_NOISE)
assert len(results) == 2
assert results[0].asset.id == "img1"
+3 -1
View File
@@ -155,7 +155,9 @@ class TestSmartMatchAvailabilityFallback:
project = Project(id="proj-1", name="Test", owner_user_id="user-1")
assets = [
_video_asset("top-exhausted.mp4", quality=100, used_ranges=_exhausted_ranges(15)),
_video_asset("second-fresh.mp4", quality=40, used_ranges=None),
# second 质量分显著高于 third(质量项差 (90-30)*0.4=24 > 噪声上限 20),
# 排除耗尽素材后 second 稳定排首位回补(噪声不影响大分差排名)
_video_asset("second-fresh.mp4", quality=90, used_ranges=None),
_video_asset("third-fresh.mp4", quality=30, used_ranges=None),
]
repo = _StubAssetRepo(assets)