From 11436f07ccc6bb4eddac21d26c59ee6b6ae21f56 Mon Sep 17 00:00:00 2001 From: lili Date: Fri, 3 Jul 2026 21:11:31 -0700 Subject: [PATCH] =?UTF-8?q?test(cheap):=20test=5Fa11=5Fm3/m4=20=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E7=BA=A7=20game=5Fdir/dedup=20=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E6=94=B6=E8=BF=9B=E5=8F=AF=E6=81=A2=E5=A4=8D=20autouse=20fixtu?= =?UTF-8?q?re=E2=80=94=E2=80=94=E6=A0=B9=E9=99=A4=20game=5Fdir=20=E8=B7=A8?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E9=9A=94=E7=A6=BB=E6=B1=A1=E6=9F=93(?= =?UTF-8?q?=E5=9F=BA=E7=BA=BF=E4=BA=8C=E5=88=86=E9=92=89=E6=AD=BB:5=20?= =?UTF-8?q?=E4=B8=AA=E5=A4=B1=E8=B4=A5,=E9=9D=9E=2012)=20(=E5=B0=BE?= =?UTF-8?q?=E6=B3=A2=E5=B0=8F=E5=8D=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_a11_m3_deterministic 与 test_a11_m4_regenerate 在 import 时无恢复地替换 cheap_run.game_dir(两文件同构),污染同会话后续测试模块:受害者按真 games/ 根校验 write 边界,game_dir 被改指临时目录后一写即判越界。改为 module 级 autouse fixture (yield 后恢复原值),__main__ 独立运行路径同装(单进程无需恢复),dedup 登记表一并收拢。 证据:全套 12→7 failed、三种文件顺序结果恒定(7 failed / 322 passed);二分钉死纯 game_dir 污染 = 5 个,均已恢复 = test_run::test_scaffold + test_toolkit::{write_l1_rejected, write_l3_ok,check_shape_drawbutton_ok,check_shape_define_fail}。 余 7 个非本污染、隔离下同红,系环境前置:worktree 无 esbuild → test_run::{test_stage,test_play_real}、test_toolkit::test_check_and_build_clean_pass; 缺真实 e2e 产物 → test_studio_smoke×4(须先跑 cheap_studio 单局)。须集成段 mini-desktop 才具备,未纳入本单。同族但未致失败的 test_worker_service 模块级 dedup 泄漏按最小改动未动。 Co-Authored-By: Claude Fable 5 --- .../tests/test_a11_m3_deterministic.py | 30 +++++++++++++++---- cheap-worker/tests/test_a11_m4_regenerate.py | 29 ++++++++++++++---- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/cheap-worker/tests/test_a11_m3_deterministic.py b/cheap-worker/tests/test_a11_m3_deterministic.py index 0b58bca5..bf52b886 100644 --- a/cheap-worker/tests/test_a11_m3_deterministic.py +++ b/cheap-worker/tests/test_a11_m3_deterministic.py @@ -20,13 +20,30 @@ import cheap_run # noqa: E402 import cheap_modify as M # noqa: E402 import worker_service as W # noqa: E402 import dedup # noqa: E402 +import pytest # noqa: E402 -# 把 game_dir 重定向到临时目录:materialize / worker 接线测试落盘免污染真 games/(与 cheap_modify、worker_service -# 同引用一个 cheap_run 模块对象 → patch 模块属性即对二者生效)。 -_TMP_GAMES = Path(tempfile.mkdtemp(prefix="a11-m3-games-")) -cheap_run.game_dir = lambda gid: _TMP_GAMES / f"amgen-{gid}" # noqa: E731 -# create 路会调 D9 dedup.check_similarity(写 FS 登记表)→ 重定向防污染真登记表(与 test_worker_service 同范式)。 -dedup.DEDUP_REGISTRY = Path(tempfile.mkdtemp(prefix="a11m3-dedup-")) / "_dedup_registry.jsonl" +# game_dir 与 dedup 登记表在本模块测试期间重定向到临时目录:materialize、worker 接线、create 路(D9 dedup) +# 的落盘都进临时目录,不碰真 games/ 与真登记表(cheap_modify、worker_service 与本模块同引用一个 cheap_run +# 模块对象,改模块属性即对三者同时生效)。重定向收进 autouse fixture:只在本模块测试期间生效、跑完即恢复 +# 原值,不再像早先那样在 import 时无恢复地替换 cheap_run.game_dir——那样会污染同会话后续的 test_run / +# test_studio_smoke / test_toolkit(它们按真 games/ 根校验 write 边界,game_dir 被换后一写就判越界)。 +def _install_tmp_redirect(): + """把 cheap_run.game_dir、dedup.DEDUP_REGISTRY 指向新建临时目录,返回二者原值供恢复。""" + orig = (cheap_run.game_dir, dedup.DEDUP_REGISTRY) + tmp_games = Path(tempfile.mkdtemp(prefix="a11-m3-games-")) + cheap_run.game_dir = lambda gid: tmp_games / f"amgen-{gid}" # noqa: E731 + dedup.DEDUP_REGISTRY = Path(tempfile.mkdtemp(prefix="a11m3-dedup-")) / "_dedup_registry.jsonl" + return orig + + +@pytest.fixture(autouse=True, scope="module") +def _tmp_redirect(): + orig_game_dir, orig_registry = _install_tmp_redirect() + try: + yield + finally: + cheap_run.game_dir = orig_game_dir + dedup.DEDUP_REGISTRY = orig_registry _GOOD_BUNDLE = "var __GameBundle=(function(){return{bootGameHost(){}}})();" @@ -287,6 +304,7 @@ def test_create_path_unaffected_by_modify_wiring(): if __name__ == "__main__": + _install_tmp_redirect() # 独立运行(非 pytest,autouse fixture 不触发):单进程一次性装重定向,退出即释放、无需恢复 _fns = [v for k, v in sorted(globals().items()) if k.startswith("test_") and callable(v)] _failed = 0 for _fn in _fns: diff --git a/cheap-worker/tests/test_a11_m4_regenerate.py b/cheap-worker/tests/test_a11_m4_regenerate.py index 9b8984dc..16dbab32 100644 --- a/cheap-worker/tests/test_a11_m4_regenerate.py +++ b/cheap-worker/tests/test_a11_m4_regenerate.py @@ -22,13 +22,29 @@ import cheap_run # noqa: E402 import cheap_modify as M # noqa: E402 import worker_service as W # noqa: E402 import dedup # noqa: E402 +import pytest # noqa: E402 -# game_dir 重定向到临时目录:materialize / 接线落盘免污染真 games/(cheap_modify、worker_service 同引用一个 -# cheap_run 模块对象 → patch 模块属性即对二者生效;与 test_a11_m3 同范式)。 -_TMP_GAMES = Path(tempfile.mkdtemp(prefix="a11-m4-games-")) -cheap_run.game_dir = lambda gid: _TMP_GAMES / f"amgen-{gid}" # noqa: E731 -# 防 D9 dedup 写真登记表(本测不走 create 路、稳妥起见仍重定向)。 -dedup.DEDUP_REGISTRY = Path(tempfile.mkdtemp(prefix="a11m4-dedup-")) / "_dedup_registry.jsonl" +# game_dir 与 dedup 登记表在本模块测试期间重定向到临时目录:materialize、worker 接线的落盘都进临时目录, +# 不碰真 games/(cheap_modify、worker_service 与本模块同引用一个 cheap_run 模块对象,改模块属性即对三者同时 +# 生效);本测虽不走 create 路,dedup 登记表仍一并重定向以防万一。重定向收进 autouse fixture:只在本模块测试 +# 期间生效、跑完即恢复原值,不再在 import 时无恢复地替换 cheap_run.game_dir 而污染同会话后续测试模块。 +def _install_tmp_redirect(): + """把 cheap_run.game_dir、dedup.DEDUP_REGISTRY 指向新建临时目录,返回二者原值供恢复。""" + orig = (cheap_run.game_dir, dedup.DEDUP_REGISTRY) + tmp_games = Path(tempfile.mkdtemp(prefix="a11-m4-games-")) + cheap_run.game_dir = lambda gid: tmp_games / f"amgen-{gid}" # noqa: E731 + dedup.DEDUP_REGISTRY = Path(tempfile.mkdtemp(prefix="a11m4-dedup-")) / "_dedup_registry.jsonl" + return orig + + +@pytest.fixture(autouse=True, scope="module") +def _tmp_redirect(): + orig_game_dir, orig_registry = _install_tmp_redirect() + try: + yield + finally: + cheap_run.game_dir = orig_game_dir + dedup.DEDUP_REGISTRY = orig_registry _GOOD_BUNDLE = "var __GameBundle=(function(){return{bootGameHost(){}}})();" _BASE_LOGIC = "// base game-logic\nexport function createGame({ plugins, bundle, viewport }) { return {}; }\n" @@ -262,6 +278,7 @@ def test_worker_unknown_mode_still_failed(): if __name__ == "__main__": + _install_tmp_redirect() # 独立运行(非 pytest,autouse fixture 不触发):单进程一次性装重定向,退出即释放、无需恢复 _fns = [v for k, v in sorted(globals().items()) if k.startswith("test_") and callable(v)] _failed = 0 for _fn in _fns: