107 lines
3.8 KiB
Python
Executable File
107 lines
3.8 KiB
Python
Executable File
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from gfa_core.extension_stage.api_converter import (
|
|
ApiConversionError,
|
|
_build_repair_prompt,
|
|
_validate_spec,
|
|
)
|
|
|
|
|
|
def test_validate_spec_preserves_input_roles_and_param_placeholders():
|
|
spec = _validate_spec({
|
|
"original_command": "seqtk subseq At.pep.fa At.id > out.fa",
|
|
"command_template": "seqtk subseq {input_fasta} {input_file} > {output_dir}/hits.fa --min {param_min_length}",
|
|
"input_roles": {
|
|
"input_fasta": {"role": "target_pep_fasta", "resolve_by": "config_species"},
|
|
"input_file": {"role": "target_id_list", "resolve_by": "species_final_gfmembers_id"},
|
|
},
|
|
"parameters": {
|
|
"min-length": {"default": 80, "editable": True},
|
|
"internal flag": {"default": "legacy", "editable": False},
|
|
},
|
|
})
|
|
|
|
assert spec["input_roles"]["input_fasta"]["role"] == "target_pep_fasta"
|
|
assert spec["input_roles"]["input_file"]["required"] is True
|
|
assert "min_length" in spec["parameters"]
|
|
assert spec["parameters"]["internal_flag"]["editable"] is False
|
|
assert spec["requires_user_confirmation"] is False
|
|
|
|
|
|
def test_validate_spec_rejects_editable_parameter_not_used_by_template():
|
|
with pytest.raises(ApiConversionError, match="editable parameter"):
|
|
_validate_spec({
|
|
"original_command": "blastp -evalue 1e-5",
|
|
"command_template": "blastp -query {input_fasta} -out {output_dir}/blast.tsv -evalue 1e-5",
|
|
"parameters": {"evalue": {"default": "1e-5", "editable": True}},
|
|
})
|
|
|
|
|
|
def test_repair_prompt_does_not_forbid_parameter_placeholders(tmp_path: Path):
|
|
raw_code = tmp_path / "code"
|
|
raw_data = tmp_path / "data"
|
|
raw_code.mkdir()
|
|
raw_data.mkdir()
|
|
(raw_code / "run.sh").write_text("echo test\n", encoding="utf-8")
|
|
(raw_data / "At.id").write_text("AT1G01010\n", encoding="utf-8")
|
|
|
|
prompt = _build_repair_prompt(
|
|
extension={
|
|
"template_type": "shell_pipeline",
|
|
"entrypoint": {"command_template": "echo {input_file} > {output_dir}/out.txt"},
|
|
"parameters": {},
|
|
"inputs": {},
|
|
"outputs": {},
|
|
"requirements": {},
|
|
"runtime": {},
|
|
},
|
|
raw_code=raw_code,
|
|
raw_data=raw_data,
|
|
validation_context={"status": "failed", "error": "missing value"},
|
|
attempt=1,
|
|
max_attempts=3,
|
|
lessons=[],
|
|
)
|
|
|
|
assert "For v1, do not put parameter placeholders" not in prompt
|
|
assert "Every parameter with editable=true must be used" in prompt
|
|
assert "input_roles" in prompt
|
|
|
|
|
|
def test_repair_prompt_includes_phase_specific_rules(tmp_path: Path):
|
|
raw_code = tmp_path / "code"
|
|
raw_data = tmp_path / "data"
|
|
raw_code.mkdir()
|
|
raw_data.mkdir()
|
|
(raw_code / "run.sh").write_text("echo test\n", encoding="utf-8")
|
|
(raw_data / "input.fa").write_text(">g1\nMA\n", encoding="utf-8")
|
|
|
|
prompt = _build_repair_prompt(
|
|
extension={
|
|
"template_type": "shell_pipeline",
|
|
"entrypoint": {"command_template": "bash {code_dir}/run.sh > {output_dir}/out.txt"},
|
|
"parameters": {},
|
|
"inputs": {},
|
|
"outputs": {},
|
|
"requirements": {},
|
|
"runtime": {},
|
|
},
|
|
raw_code=raw_code,
|
|
raw_data=raw_data,
|
|
validation_context={
|
|
"repair_phase": "publish_gate",
|
|
"status": "failed",
|
|
"error": "Publish gate failed",
|
|
"publish_checks": {"errors": ["golden original-vs-converted comparison did not pass"]},
|
|
},
|
|
attempt=1,
|
|
max_attempts=20,
|
|
lessons=[],
|
|
)
|
|
|
|
assert '"repair_phase": "publish_gate"' in prompt
|
|
assert "If repair_phase is publish_gate" in prompt
|
|
assert "writes outputs under work/current directory instead of {output_dir}" in prompt
|