Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions src/telemetry_window_demo/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,11 @@ def plot_command(args: argparse.Namespace) -> None:
print(f" - {plot_path.name}")


def run_ai_demo_command(args: argparse.Namespace) -> None:
from .ai_assisted_detection_demo import default_demo_root, run_demo
demo_root = Path(args.demo_root).resolve() if args.demo_root else default_demo_root()
result = run_demo(demo_root=demo_root)
def run_ai_demo_command(args: argparse.Namespace) -> None:
from .ai_assisted_detection_demo import default_demo_root, run_demo

demo_root = _demo_root_path(args.demo_root, default_demo_root())
result = run_demo(demo_root=demo_root)

print(f"[OK] Loaded {result['raw_event_count']} raw events")
print(f"[OK] Normalized {result['normalized_event_count']} events")
Expand All @@ -235,8 +235,8 @@ def run_ai_demo_command(args: argparse.Namespace) -> None:
def run_rule_dedup_demo_command(args: argparse.Namespace) -> None:
from .rule_evaluation_and_dedup_demo import default_demo_root, run_demo

demo_root = Path(args.demo_root).resolve() if args.demo_root else default_demo_root()
result = run_demo(demo_root=demo_root)
demo_root = _demo_root_path(args.demo_root, default_demo_root())
result = run_demo(demo_root=demo_root)

print(f"[OK] Loaded {result['raw_hit_count']} raw rule hits")
print(f"[OK] Retained {result['retained_alert_count']} alerts")
Expand All @@ -250,7 +250,7 @@ def run_rule_dedup_demo_command(args: argparse.Namespace) -> None:
def run_config_change_demo_command(args: argparse.Namespace) -> None:
from .config_change_investigation_demo import default_demo_root, run_demo

demo_root = Path(args.demo_root).resolve() if args.demo_root else default_demo_root()
demo_root = _demo_root_path(args.demo_root, default_demo_root())
result = run_demo(demo_root=demo_root)

print(f"[OK] Loaded {result['change_event_count']} config changes")
Expand All @@ -270,6 +270,15 @@ def _display_path(path: Path) -> str:
return resolved.as_posix()


def _demo_root_path(value: str | None, default_root: Path) -> Path:
demo_root = Path(value).resolve() if value else default_root.resolve()
if not demo_root.exists():
raise FileNotFoundError(f"Demo root not found: {demo_root}")
if not demo_root.is_dir():
raise ValueError(f"Demo root path is not a directory: {demo_root}")
return demo_root


def _validate_run_config(config: Mapping[str, Any]) -> dict[str, Any]:
time_config = _optional_mapping(config.get("time", {}), "time")
feature_config = _optional_mapping(config.get("features", {}), "features")
Expand Down
31 changes: 31 additions & 0 deletions tests/test_cli_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,37 @@ def test_main_reports_file_plot_output_dir_without_traceback(tmp_path, capsys) -
assert "Traceback" not in stderr


@pytest.mark.parametrize(
"command",
["run-ai-demo", "run-rule-dedup-demo", "run-config-change-demo"],
)
def test_main_reports_file_demo_root_without_traceback(command, tmp_path, capsys) -> None:
demo_root = tmp_path / "demo-root"
demo_root.write_text("not a directory\n", encoding="utf-8")

with pytest.raises(SystemExit) as excinfo:
main([command, "--demo-root", str(demo_root)])

assert excinfo.value.code == 1
stderr = capsys.readouterr().err
assert stderr.startswith("error: ")
assert "Demo root path is not a directory" in stderr
assert "Traceback" not in stderr


def test_main_reports_missing_demo_root_without_traceback(tmp_path, capsys) -> None:
demo_root = tmp_path / "missing-demo-root"

with pytest.raises(SystemExit) as excinfo:
main(["run-ai-demo", "--demo-root", str(demo_root)])

assert excinfo.value.code == 1
stderr = capsys.readouterr().err
assert stderr.startswith("error: ")
assert "Demo root not found" in stderr
assert "Traceback" not in stderr


def test_main_reports_directory_plot_artifact_without_traceback(tmp_path, capsys) -> None:
features_path = tmp_path / "features.csv"
features_path.write_text(
Expand Down
Loading