|
| 1 | +"""CLI argparse + validate-results end-to-end (no network).""" |
| 2 | +from __future__ import annotations |
| 3 | + |
| 4 | +import json |
| 5 | +import pathlib |
| 6 | +import subprocess |
| 7 | +import sys |
| 8 | + |
| 9 | +import pytest |
| 10 | + |
| 11 | +from hermes_sci.cli import build_parser |
| 12 | + |
| 13 | +PKG_ROOT = pathlib.Path(__file__).resolve().parent.parent |
| 14 | + |
| 15 | + |
| 16 | +def test_parser_lists_all_subcommands(): |
| 17 | + p = build_parser() |
| 18 | + ns = p.parse_args(["ideate", "--topic", "x", "-o", "i.json"]) |
| 19 | + assert ns.cmd == "ideate" |
| 20 | + ns = p.parse_args(["writeup", "--ideas-json", "i.json", "-o", "out"]) |
| 21 | + assert ns.cmd == "writeup" |
| 22 | + ns = p.parse_args(["review", "--paper", "p.pdf"]) |
| 23 | + assert ns.cmd == "review" |
| 24 | + ns = p.parse_args(["pipeline", "--topic", "x", "-o", "out"]) |
| 25 | + assert ns.cmd == "pipeline" |
| 26 | + ns = p.parse_args(["validate-results", "r.json"]) |
| 27 | + assert ns.cmd == "validate-results" |
| 28 | + |
| 29 | + |
| 30 | +@pytest.mark.parametrize("sink", ["human", "jsonl", "off"]) |
| 31 | +def test_progress_flag_accepted_everywhere(sink): |
| 32 | + p = build_parser() |
| 33 | + ns = p.parse_args(["ideate", "--topic", "x", "-o", "i.json", |
| 34 | + "--progress", sink]) |
| 35 | + assert ns.progress == sink |
| 36 | + |
| 37 | + |
| 38 | +def test_progress_rejects_unknown_value(): |
| 39 | + p = build_parser() |
| 40 | + with pytest.raises(SystemExit): |
| 41 | + p.parse_args(["ideate", "--topic", "x", "-o", "i.json", |
| 42 | + "--progress", "spinner"]) |
| 43 | + |
| 44 | + |
| 45 | +def test_coherence_default_false(): |
| 46 | + """Coherence is opt-in on every subcommand; pipeline previously had a |
| 47 | + bug where it used args.no_coherence (never defined).""" |
| 48 | + p = build_parser() |
| 49 | + ns = p.parse_args(["pipeline", "--topic", "x", "-o", "out"]) |
| 50 | + assert ns.coherence is False |
| 51 | + ns = p.parse_args(["pipeline", "--topic", "x", "-o", "out", "--coherence"]) |
| 52 | + assert ns.coherence is True |
| 53 | + |
| 54 | + |
| 55 | +def test_validate_results_good_exits_0(tmp_path): |
| 56 | + doc = {"metrics": [{"name": "BLEU", "value": 28.3}]} |
| 57 | + p = tmp_path / "r.json" |
| 58 | + p.write_text(json.dumps(doc)) |
| 59 | + r = subprocess.run( |
| 60 | + [sys.executable, "-m", "hermes_sci.cli", "validate-results", str(p)], |
| 61 | + capture_output=True, text=True, cwd=str(PKG_ROOT), |
| 62 | + ) |
| 63 | + assert r.returncode == 0, r.stderr |
| 64 | + assert "matches results.json schema" in r.stdout |
| 65 | + |
| 66 | + |
| 67 | +def test_validate_results_bad_exits_1(tmp_path): |
| 68 | + doc = {"metrics": [], "tables": [ |
| 69 | + {"id": "bad id", "headers": ["x"], "rows": []}]} |
| 70 | + p = tmp_path / "bad.json" |
| 71 | + p.write_text(json.dumps(doc)) |
| 72 | + r = subprocess.run( |
| 73 | + [sys.executable, "-m", "hermes_sci.cli", "validate-results", str(p)], |
| 74 | + capture_output=True, text=True, cwd=str(PKG_ROOT), |
| 75 | + ) |
| 76 | + assert r.returncode == 1 |
| 77 | + assert "schema violation" in r.stderr |
| 78 | + |
| 79 | + |
| 80 | +def test_validate_results_missing_file_exits_2(tmp_path): |
| 81 | + r = subprocess.run( |
| 82 | + [sys.executable, "-m", "hermes_sci.cli", |
| 83 | + "validate-results", str(tmp_path / "nope.json")], |
| 84 | + capture_output=True, text=True, cwd=str(PKG_ROOT), |
| 85 | + ) |
| 86 | + assert r.returncode == 2 |
0 commit comments