Add target list CLI option (#711)

* Add target list CLI option

* Handle target list comments and encoding errors
This commit is contained in:
alex s
2026-07-06 23:33:08 -04:00
committed by GitHub
parent 375fc9c3d0
commit 9f278b9a5c
7 changed files with 222 additions and 10 deletions
+90
View File
@@ -0,0 +1,90 @@
"""Tests for CLI target-list argument parsing."""
from __future__ import annotations
import importlib
import sys
from types import SimpleNamespace
from typing import TYPE_CHECKING, Any
import pytest
if TYPE_CHECKING:
from pathlib import Path
cli_main: Any = importlib.import_module("strix.interface.main")
def _stub_settings(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
cli_main,
"load_settings",
lambda: SimpleNamespace(runtime=SimpleNamespace(max_local_copy_mb=1024)),
)
def test_parse_arguments_accepts_target_list_file(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
target_list = tmp_path / "targets.txt"
target_list.write_text(
"https://test1.com/\n"
"\n"
"http://test2.com:5789/\n",
encoding="utf-8",
)
_stub_settings(monkeypatch)
monkeypatch.setattr(sys, "argv", ["strix", "--target-list", str(target_list), "-n"])
args = cli_main.parse_arguments()
assert [target["original"] for target in args.targets_info] == [
"https://test1.com/",
"http://test2.com:5789/",
]
assert [target["type"] for target in args.targets_info] == [
"web_application",
"web_application",
]
def test_parse_arguments_combines_target_and_target_list(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
target_list = tmp_path / "targets.txt"
target_list.write_text("http://test2.com:5789/\n", encoding="utf-8")
_stub_settings(monkeypatch)
monkeypatch.setattr(
sys,
"argv",
["strix", "-t", "https://test1.com/", "--target-list", str(target_list)],
)
args = cli_main.parse_arguments()
assert [target["original"] for target in args.targets_info] == [
"https://test1.com/",
"http://test2.com:5789/",
]
def test_parse_arguments_rejects_resume_with_target_list(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
target_list = tmp_path / "targets.txt"
target_list.write_text("https://test1.com/\n", encoding="utf-8")
monkeypatch.setattr(
sys,
"argv",
["strix", "--resume", "old-run", "--target-list", str(target_list)],
)
with pytest.raises(SystemExit):
cli_main.parse_arguments()
assert (
"Cannot combine --resume with --target/--target-list/--mount"
in capsys.readouterr().err
)
+61
View File
@@ -19,6 +19,7 @@ from strix.interface.utils import (
dedupe_local_targets,
directory_size_bytes,
find_oversized_local_targets,
read_target_list_file,
)
@@ -157,6 +158,66 @@ def test_build_mount_targets_info_rejects_empty_path(empty: str) -> None:
build_mount_targets_info([empty])
def test_read_target_list_file_strips_blank_lines(tmp_path: Path) -> None:
target_list = tmp_path / "targets.txt"
target_list.write_text(
"\n"
" https://test1.com/ \n"
"\n"
"http://test2.com:5789/\n"
" \n",
encoding="utf-8",
)
assert read_target_list_file(str(target_list)) == [
"https://test1.com/",
"http://test2.com:5789/",
]
def test_read_target_list_file_ignores_comment_lines(tmp_path: Path) -> None:
target_list = tmp_path / "targets.txt"
target_list.write_text(
"# production targets\n"
"https://test1.com/\n"
" # staging targets\n"
"http://test2.com:5789/\n",
encoding="utf-8",
)
assert read_target_list_file(str(target_list)) == [
"https://test1.com/",
"http://test2.com:5789/",
]
def test_read_target_list_file_rejects_empty_file(tmp_path: Path) -> None:
target_list = tmp_path / "targets.txt"
target_list.write_text(" \n# no targets yet\n\n", encoding="utf-8")
with pytest.raises(ValueError, match="is empty"):
read_target_list_file(str(target_list))
def test_read_target_list_file_rejects_missing_path(tmp_path: Path) -> None:
with pytest.raises(ValueError, match="not an existing file"):
read_target_list_file(str(tmp_path / "missing.txt"))
def test_read_target_list_file_rejects_non_utf8_file(tmp_path: Path) -> None:
target_list = tmp_path / "targets.txt"
target_list.write_bytes(b"https://test1.com/\xff\n")
with pytest.raises(ValueError, match="must be valid UTF-8 text"):
read_target_list_file(str(target_list))
@pytest.mark.parametrize("empty", ["", " "])
def test_read_target_list_file_rejects_empty_path(empty: str) -> None:
with pytest.raises(ValueError, match="must not be empty"):
read_target_list_file(empty)
def test_dedupe_keeps_distinct_targets_in_order() -> None:
targets = [
_local_target("/a"),