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
12 changes: 11 additions & 1 deletion src/contextseek/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ def _resolve_scope(args: argparse.Namespace, default_scope: str) -> str:
return scope


def _positive_int(value: str) -> int:
"""Validate and parse a positive integer (> 0)."""
n = int(value)
if n <= 0:
raise argparse.ArgumentTypeError(
f"invalid positive int value: {value!r} (must be > 0)"
)
return n


def build_parser() -> argparse.ArgumentParser:
"""Build CLI parser for commands."""
parser = argparse.ArgumentParser(prog="contextseek")
Expand All @@ -73,7 +83,7 @@ def build_parser() -> argparse.ArgumentParser:
)
retrieve_parser.add_argument("--scope", default=None)
retrieve_parser.add_argument("--query", required=True)
retrieve_parser.add_argument("--k", type=int, default=10)
retrieve_parser.add_argument("--k", type=_positive_int, default=10)
retrieve_parser.add_argument(
"--full",
action="store_true",
Expand Down
52 changes: 52 additions & 0 deletions tests/unit_tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Tests for CLI argument parsing and validation."""

import argparse
from contextlib import redirect_stderr
from io import StringIO

import pytest

from contextseek.cli.main import build_parser, _positive_int


class TestPositiveInt:
def test_positive_value(self):
assert _positive_int("5") == 5
assert _positive_int("1") == 1

def test_zero_raises(self):
with pytest.raises(argparse.ArgumentTypeError, match="must be > 0"):
_positive_int("0")

def test_negative_raises(self):
with pytest.raises(argparse.ArgumentTypeError, match="must be > 0"):
_positive_int("-1")

def test_non_integer_raises(self):
with pytest.raises(ValueError):
_positive_int("abc")


class TestRetrieveKValidation:
def test_k_zero_rejected(self) -> None:
parser = build_parser()
err = StringIO()
with redirect_stderr(err), pytest.raises(SystemExit):
parser.parse_args(["retrieve", "--scope", "t", "--query", "q", "--k", "0"])
assert "must be > 0" in err.getvalue()

def test_k_negative_rejected(self) -> None:
parser = build_parser()
err = StringIO()
with redirect_stderr(err), pytest.raises(SystemExit):
parser.parse_args(
["retrieve", "--scope", "t", "--query", "q", "--k", "-1"]
)
assert "must be > 0" in err.getvalue()

def test_k_positive_accepted(self) -> None:
parser = build_parser()
args = parser.parse_args(
["retrieve", "--scope", "t", "--query", "q", "--k", "5"]
)
assert args.k == 5
Loading