"""P18/C1 — ``--docs-dir`` is optional once ``--bundle-dir`` is given. P16 FUNN 2: the documented stress command names the same directory twice (``--docs-dir --bundle-dir ``), because single-project mode demanded ``--docs-dir`` even on the bundle path — where it is never read. Retrieval, the chunk tool and the "no citable content" check all live in the ROAD branch (``run.py``); the bundle branch builds its citations from the navigated base. So the flag was required for a path that ignores it, and the published command had to satisfy the requirement by repeating itself. **This is not the "--docs-dir omvei"** (feeding project documents through retrieval INSTEAD of ingesting them into a knowledge base), which STATE forbids and this order forbids again. No such path is opened: the road branch still refuses without a real ``--docs-dir``, and the value is bound ONCE from ``--bundle-dir`` — byte-identically what the README already tells an operator to type by hand, so every existing invocation, the two-flag form included, is unchanged. """ from __future__ import annotations import json import shutil from pathlib import Path from typing import Any import pytest from portfolio_optimiser import run _FIXTURES = Path(__file__).parent / "fixtures" _PRICED = _FIXTURES / "k2-prisskjema-SYNTETISK" _IR_PROJECTION = { "project_id": "K2", "measure": "energy_efficiency", "claimed_saving_nok": 1000.0, "affected_codes": ["01.1"], } def _runnable(tmp_path: Path) -> str: root = tmp_path / "base" shutil.copytree(_PRICED, root) (root / "validator-input.json").write_text(json.dumps(_IR_PROJECTION), encoding="utf-8") return str(root) def test_a_bundle_run_needs_no_docs_dir(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: """(a) The fix. A free dry run on ``--bundle-dir`` alone is ACCEPTED and reaches the bundle path — asserted on the run-config the dry run prints, not on rc alone, since rc 0 is also what a run that silently took the road path would return.""" rc = run.main(["K2", "--bundle-dir", _runnable(tmp_path), "--live-dry-run"]) assert rc == 0 assert "LIVE-DRY-RUN OK" in capsys.readouterr().out def test_the_documented_two_flag_form_still_works( tmp_path: Path, capsys: pytest.CaptureFixture[str] ) -> None: """(b) The CONTROL that this is a widening, not a change. The README's form — the same directory twice — must be byte-for-byte as accepted as it was before.""" base = _runnable(tmp_path) rc = run.main(["K2", "--docs-dir", base, "--bundle-dir", base, "--live-dry-run"]) assert rc == 0 assert "LIVE-DRY-RUN OK" in capsys.readouterr().out def test_neither_flag_is_still_refused_by_name(capsys: pytest.CaptureFixture[str]) -> None: """(c) The half that must NOT be relaxed: the road path has no base to fall back to, so an argv naming neither is refused, and the refusal names BOTH doors rather than only the one it used to name.""" rc = run.main(["K2", "--live-dry-run"]) assert rc == 1 err = capsys.readouterr().err assert "run refused" in err and "--docs-dir" in err and "--bundle-dir" in err def test_the_road_path_still_requires_a_real_docs_dir( tmp_path: Path, capsys: pytest.CaptureFixture[str] ) -> None: """(d) The anti-omvei arm. With no bundle, ``--docs-dir`` is still the only door AND it is still read: a directory holding nothing citable is refused by the road branch's own check, so nothing here turns retrieval into a substitute for ingestion.""" empty = tmp_path / "tomt" empty.mkdir() rc = run.main(["P1", "--docs-dir", str(empty), "--live-dry-run"]) assert rc == 1 assert capsys.readouterr().err.strip(), "the road path must say why, not fail silently" def test_the_bundle_value_is_bound_once_and_reaches_run_project( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """(e) The SEAM. rc 0 above would also be satisfied by a CLI that never forwarded the value, so the argument ``run_project`` actually receives is recorded — one binding, both dispatch sites.""" base = _runnable(tmp_path) seen: dict[str, Any] = {} async def _record(project_id: str, profile: Any, **kw: Any) -> Any: seen.update(kw) raise SystemExit(0) monkeypatch.setattr("portfolio_optimiser.run.run_project", _record) with pytest.raises(SystemExit): run.main(["K2", "--bundle-dir", base, "--live-dry-run"]) assert seen["docs_dir"] == base == seen["bundle_dir"]