games-development-ai/cheap-worker/tests/test_match3_gold_batch.py
lili cbfd4d871b
Some checks failed
contract-gates / contract-gates (push) Has been cancelled
docs-gate / docs-gate (push) Has been cancelled
feat(acceptance): 闭合 playtest v3 与 A+ 可信消费链
固化 Match-3 生产者、视觉、音频与双 Judge 证据闭包。

将《山海行纪》r1.1 绑定新的不可变 release,并以生产预检现场核验 bundle、Registry/2 和 25 项 Writer 快照。

同步地图1平衡锁值、跨游戏回归修复、验收契约与 SoT 证据。
2026-07-28 20:16:13 -07:00

85 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""fresh Match-3 金标生成批次的静态边界测试。"""
import sys
import hashlib
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import match3_gold_batch as batch # noqa: E402
REPO_ROOT = Path(__file__).resolve().parents[2]
def test_candidates_are_five_distinct_fresh_games():
"""五款必须独立 gameId,不能把同一 fixture 的多轮冒充多款。"""
ids = [candidate["gameId"] for candidate in batch.CANDIDATES]
assert len(ids) == 5
assert len(set(ids)) == 5
assert all(game_id.startswith("gold-m3-") for game_id in ids)
assert ids == [
"gold-m3-gem-r3", "gold-m3-candy-r3", "gold-m3-porcelain-r3",
"gold-m3-fruit-r3", "gold-m3-rune-r3",
]
def test_candidates_keep_canonical_match3_requirements():
"""每份题面都必须明确 8×8、相邻交换、真实三连、终态和原生事件。"""
required = (
"8×8", "上下左右相邻", "无效交换必须换回", "20 步", "gameover",
"ctx.event", "puzzle.move-applied", "puzzle.swap-committed", "puzzle.match-formed",
"boardRevisionBefore", "boardRevisionAfter", "matchedCells", "matchedCount",
"puzzle.goal-reached", "puzzle.level-completed", "1-based row/column",
)
for candidate in batch.CANDIDATES:
assert all(fragment in candidate["brief"] for fragment in required)
assert candidate["brief"].count("_forensicsView") == 1
assert "通过 ctx.log 写原生 game 业务事件" not in candidate["brief"]
assert "禁止创建或启动 menu" in candidate["brief"]
assert "match3ProducerProfile" in candidate["brief"]
assert "drawSelectionOverlay" in candidate["brief"]
assert "第一次点选只更新 selected" in candidate["brief"]
assert "同一次 handleTap 同步调用栈" in candidate["brief"]
assert "全部确定性连锁、重力补新、计分和目标/通关判定" in candidate["brief"]
assert "这些业务事实不得延迟" in candidate["brief"]
assert "立即调用 match3VisualTimeline.begin(...) 封存视觉事务" in candidate["brief"]
assert "update(dt) 必须调用 match3VisualTimeline.update(dt)" in candidate["brief"]
assert "render(g) 必须读取 match3VisualTimeline.view().tiles" in candidate["brief"]
assert "禁止读取已经完成全部补位的业务终盘伪装动画" in candidate["brief"]
assert "业务终态可以同步 latch,但成功/失败场景只能在 timeline settle 后显示" in candidate["brief"]
assert "若耗尽最后一步,失败结果仍同步封存但延迟显示" in candidate["brief"]
def test_producer_api_exposes_machine_perceivable_theme_contract():
"""Writer 被要求先读的 API 必须写清图标感知约束,不能只给安全区坐标。"""
source = (REPO_ROOT / "game-runtime/src/plugins/match3-producer-profile/api.d.ts").read_text(
encoding="utf-8",
)
required = (
"覆盖格心的单个连续实心前景",
"禁止空心中心、分离装饰、文字和多块碎片",
"70..200 千分比",
"同一类别跨棋格逐像素保持同构",
"不叠加渐变、半透明柔光、随机纹理或位置相关高光",
)
assert all(fragment in source for fragment in required)
def test_generation_batch_never_uses_fixture_as_target():
"""生成批只允许从黄金 puzzle 起点生成 fresh ID,不得覆盖历史 match3 control。"""
assert batch.TEMPLATE == "_template-puzzle"
assert all(candidate["gameId"] != "match3" for candidate in batch.CANDIDATES)
assert batch.HARD_STOP_RMB == 75.0
def test_batch_manifest_keeps_full_briefs_and_hashes():
"""通用 brief 取证即使限长,合成金标批仍必须保留完整可复核原文。"""
manifest = batch._batch_manifest()
assert manifest["schemaVersion"] == "Match3GoldGenerationBatch/3"
assert len(manifest["candidates"]) == 5
for source, recorded in zip(batch.CANDIDATES, manifest["candidates"], strict=True):
assert recorded["brief"] == source["brief"]
assert len(recorded["brief"]) > 1000
assert recorded["briefSha256"] == hashlib.sha256(source["brief"].encode()).hexdigest()