From ac115d96fbe1dc1730b3839091738b71a17e694c Mon Sep 17 00:00:00 2001 From: iomgaa Date: Tue, 14 Jul 2026 15:44:43 -0400 Subject: [PATCH] test: cover backfill params end-to-end (mutation isolation + seq_offset) --- tests/integration/test_pipeline_v2.py | 84 +++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/tests/integration/test_pipeline_v2.py b/tests/integration/test_pipeline_v2.py index fbaf5dc..f089245 100644 --- a/tests/integration/test_pipeline_v2.py +++ b/tests/integration/test_pipeline_v2.py @@ -580,6 +580,90 @@ class TestPipelineV2: if result.accepted: assert len(result.heavy_sampled) == len(result.accepted) + @pytest.mark.asyncio + async def test_backfill_params_do_not_mutate_caller_objects( + self, tree, default_config, store, tmp_path + ): + """补生成三参:传入的 used_node_ids / embed_pool 对象不被就地修改。 + + 内部应复制而非别名 —— run 完之后调用方传入的容器长度/内容保持原样。 + """ + vlm = MockVLM([_make_candidate_json(f"Q{i}?") for i in range(20)]) + llm = MockLLM([_make_gate_pass_response()] * 100) + + config = PipelineConfig( + per_type=2, + retry_limit=2, + heavy_sample_rate=0.0, + dedup_threshold=0.85, + concurrency=1, + seed=42, + output_dir=tmp_path / "out", + ) + + initial_used_node_ids = {"some_node"} + seed_vec = _mock_embed_fn("seed embedding text") + initial_embed_pool = [seed_vec] + + result = await run_pipeline_v2( + video_ids=["vid_L1_000"], + trees={"vid_L1_000": tree}, + vlm=vlm, + llm=llm, + embed_fn=_mock_embed_fn, + store=store, + config=config, + task_types=["Action Recognition"], + initial_used_node_ids=initial_used_node_ids, + initial_embed_pool=initial_embed_pool, + ) + + # 至少接受一题,确保内部确实往副本里追加了 node/embedding + assert len(result.accepted) > 0 + + # 调用方对象未被 mutation:集合仍只含原始节点 + assert initial_used_node_ids == {"some_node"} + # embed_pool 仍只含最初的一个向量,且内容未变 + assert len(initial_embed_pool) == 1 + assert np.array_equal(initial_embed_pool[0], seed_vec) + + @pytest.mark.asyncio + async def test_seq_offset_continues_question_ids( + self, tree, default_config, store, tmp_path + ): + """补生成三参:seq_offset 端到端续编 question_id 的 seq,避开旧 run 号段。""" + vlm = MockVLM([_make_candidate_json(f"Q{i}?") for i in range(20)]) + llm = MockLLM([_make_gate_pass_response()] * 100) + + config = PipelineConfig( + per_type=2, + retry_limit=2, + heavy_sample_rate=0.0, + dedup_threshold=0.85, + concurrency=1, + seed=42, + output_dir=tmp_path / "out", + ) + + result = await run_pipeline_v2( + video_ids=["vid_L1_000"], + trees={"vid_L1_000": tree}, + vlm=vlm, + llm=llm, + embed_fn=_mock_embed_fn, + store=store, + config=config, + task_types=["Action Recognition"], + seq_offset=30, + ) + + assert len(result.accepted) > 0 + # question_id 格式 "{video_id}_{task_type}_{seq:04d}",seq 是最后一段 4 位数字 + seqs = [int(q.question_id.rsplit("_", 1)[1]) for q in result.accepted] + assert all(s > 30 for s in seqs), seqs + # per_type=2 → seq 从 31 起续编,不与 0-30 撞 + assert min(seqs) == 31 + @pytest.mark.asyncio async def test_store_records_all(self, tree, default_config, store, tmp_path): """验证 store 中记录了每道题的生成与门控结果。"""