from pathlib import Path import sys ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from apps.cli.run_config_autofill import build_rows, merge_rows from apps.cli.autofill_common import scan_files from gfa_core.mas.agents.router_runtime import RouterRuntime def test_direct_config_open_commands_map_to_config_dialog_action(): for query in ( "打开 config", "打开 config.xlsx", "重新打开配置表", "帮我打开当前 job 的配置", ): assert RouterRuntime._config_dialog_ui_action(query) == "open_config_dialog" def test_config_discussion_does_not_map_to_config_dialog_action(): for query in ( "读取 config.xlsx 中有哪些物种", "检查 config 是否正确", "我已经打开 config,下一步做什么", "config 为什么打不开", "config 已确认,执行数据准备", ): assert RouterRuntime._config_dialog_ui_action(query) is None def test_at_spl_pep_upload_is_detected_as_seed_reference(tmp_path): input_dir = tmp_path / "input" input_dir.mkdir() (input_dir / "At.SPL.pep.fa").write_text(">AT1G01010\nMAAA\n", encoding="utf-8") rows = build_rows( input_dir=input_dir, source_dir=input_dir, description="", species_code="", gene_family="SPL", ) row = next(item for item in rows if item["Original_File_Name"] == "At.SPL.pep.fa") assert row["Species_Code"] == "At" assert row["Gene_Family"] == "SPL" assert row["File_Type"] == "pep" assert row["Ref"] == "TRUE" assert row["Species_Role"] == "seed_reference" assert row["File_Scope"] == "known_family" assert row["confidence"] == "high" def test_scan_files_uses_job_input_but_ignores_generated_or_archive_dirs(tmp_path): input_dir = tmp_path / "input" input_dir.mkdir() (input_dir / "At.SPL.pep.fa").write_text(">AT\nMA\n", encoding="utf-8") (input_dir / "final").mkdir() (input_dir / "final" / "At.SPL.pep.fa").write_text(">generated\nMA\n", encoding="utf-8") (input_dir / "uploaded_configs").mkdir() (input_dir / "uploaded_configs" / "At.SPL.pep.fa").write_text(">archive\nMA\n", encoding="utf-8") scanned = [path.relative_to(input_dir).as_posix() for path in scan_files(input_dir)] assert scanned == ["At.SPL.pep.fa"] def test_regenerated_draft_merges_new_upload_without_dropping_existing_rows(tmp_path): input_dir = tmp_path / "input" input_dir.mkdir() (input_dir / "At.SPL.pep.fa").write_text(">AT1G01010\nMAAA\n", encoding="utf-8") existing_rows = [ { "Original_File_Name": "Arabidopsis_thaliana.TAIR10.pep.all.fa", "Species_Code": "At", "Gene_Family": "", "File_Type": "pep", "Ref": "TRUE", "Species_Role": "seed_reference", "File_Scope": "known_family", "confidence": "high", "notes": "", } ] detected_rows = build_rows(input_dir, input_dir, "", "", "SPL") merged = merge_rows(existing_rows, detected_rows) names = [row["Original_File_Name"] for row in merged] assert "Arabidopsis_thaliana.TAIR10.pep.all.fa" in names assert "At.SPL.pep.fa" in names assert len(names) == len(set(names)) def test_frontend_autofill_entrypoints_keep_open_and_regenerate_separate(): html = (ROOT / "apps/web/vue3/index.html").read_text(encoding="utf-8") queue_start = html.index("const queueAutofillFromPayload") queue_end = html.index("const handleUploadSuccess", queue_start) queue_block = html[queue_start:queue_end] assert queue_block.count("sourceDir: ''") >= 2 assert "await openAutofillDialog('config'" in queue_block assert "await openAutofillDialog('samples'" in queue_block assert "const openExistingAutofillDraft" in html assert "options.keepCurrentRows && !(autofillState.rows && autofillState.rows.length)" in html assert "autoGenerate: wantsAutofillRegenerate" in html def test_backend_open_endpoints_prefer_draft_then_confirmed_file(): main_py = (ROOT / "apps/web/vue3/main.py").read_text(encoding="utf-8") assert '@app.post("/api/autofill/config/open")' in main_py assert 'workspace_dir / "final" / "config.autofill.draft.xlsx"' in main_py assert 'input_dir / "config.xlsx"' in main_py assert '@app.post("/api/autofill/samples/open")' in main_py assert 'workspace_dir / "final" / "samples.autofill.draft.yaml"' in main_py assert 'input_dir / "samples.yaml"' in main_py def test_chat_config_dialog_action_is_transmitted_and_opens_current_job_config(): main_py = (ROOT / "apps/web/vue3/main.py").read_text(encoding="utf-8") html = (ROOT / "apps/web/vue3/index.html").read_text(encoding="utf-8") assert '"ui_action": outcome.get("ui_action")' in main_py assert "data.intent.ui_action === 'open_config_dialog'" in html assert "await openAutofillDialog('config'" in html assert "await openExistingAutofillDraft('config')" in html def test_samples_confirm_no_longer_blocks_on_pending_de_roles_and_closes_dialog(): html = (ROOT / "apps/web/vue3/index.html").read_text(encoding="utf-8") confirm_start = html.index("const confirmAutofillDraft") confirm_end = html.index("const loadFromPath", confirm_start) confirm_block = html[confirm_start:confirm_end] assert "链特异性为必填" in confirm_block assert "分组角色为必填" not in confirm_block assert "请在样本表中标记唯一对照组" not in confirm_block assert "请在样本表中至少标记一个比较组" not in confirm_block assert "autofillDialogVisible.value = false" in confirm_block