Files
polymarket-insider-tracker/tests/test_main.py
T
Patrick Selamy 5cf6382e42 feat: add CLI entry point with argparse (#55)
Implement the main CLI entry point for the Polymarket Insider Tracker.

Features:
- argparse-based CLI with version, config-check, dry-run, log-level flags
- Structured logging configuration with multiple formatters
- Configuration validation with friendly error messages
- Startup banner display
- Config check mode for validation without running
- Integration with Pipeline orchestrator

Tests: 20 new tests covering parser, logging, banner, config validation,
and integration scenarios.

Closes #55

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 20:44:17 -05:00

215 lines
6.8 KiB
Python

"""Tests for the CLI entry point."""
from __future__ import annotations
from unittest.mock import patch
import pytest
from polymarket_insider_tracker.__main__ import (
EXIT_CONFIG_ERROR,
EXIT_SUCCESS,
configure_logging,
create_parser,
main,
print_banner,
run_config_check,
validate_config,
)
class TestCreateParser:
"""Tests for argument parser creation."""
def test_parser_has_version(self):
"""Parser should have version flag."""
parser = create_parser()
with pytest.raises(SystemExit) as exc_info:
parser.parse_args(["--version"])
assert exc_info.value.code == 0
def test_parser_config_check(self):
"""Parser should accept --config-check flag."""
parser = create_parser()
args = parser.parse_args(["--config-check"])
assert args.config_check is True
def test_parser_log_level(self):
"""Parser should accept --log-level option."""
parser = create_parser()
args = parser.parse_args(["--log-level", "DEBUG"])
assert args.log_level == "DEBUG"
def test_parser_dry_run(self):
"""Parser should accept --dry-run flag."""
parser = create_parser()
args = parser.parse_args(["--dry-run"])
assert args.dry_run is True
def test_parser_health_port(self):
"""Parser should accept --health-port option."""
parser = create_parser()
args = parser.parse_args(["--health-port", "9090"])
assert args.health_port == 9090
def test_parser_default_values(self):
"""Parser should have correct defaults."""
parser = create_parser()
args = parser.parse_args([])
assert args.config_check is False
assert args.log_level is None
assert args.dry_run is False
assert args.health_port is None
class TestConfigureLogging:
"""Tests for logging configuration."""
def test_configure_logging_info(self):
"""Should configure logging at INFO level."""
configure_logging("INFO")
import logging
assert logging.getLogger().level == logging.INFO
def test_configure_logging_debug(self):
"""Should configure logging at DEBUG level."""
configure_logging("DEBUG")
import logging
assert logging.getLogger().level == logging.DEBUG
class TestPrintBanner:
"""Tests for banner printing."""
def test_banner_contains_app_name(self, capsys):
"""Banner should contain application name."""
print_banner()
captured = capsys.readouterr()
assert "Polymarket Insider Tracker" in captured.out
def test_banner_contains_version(self, capsys):
"""Banner should contain version."""
print_banner()
captured = capsys.readouterr()
assert "v0.1.0" in captured.out
class TestValidateConfig:
"""Tests for configuration validation."""
def test_validate_config_success(self, monkeypatch):
"""Should return settings on valid config."""
monkeypatch.setenv("DATABASE_URL", "postgresql://localhost/test")
settings = validate_config()
assert settings is not None
def test_validate_config_failure(self, monkeypatch, capsys):
"""Should return None on invalid config."""
# Clear any existing DATABASE_URL
monkeypatch.delenv("DATABASE_URL", raising=False)
settings = validate_config()
assert settings is None
captured = capsys.readouterr()
assert "Configuration validation failed" in captured.err
class TestRunConfigCheck:
"""Tests for config check mode."""
def test_config_check_prints_summary(self, monkeypatch, capsys):
"""Config check should print configuration summary."""
monkeypatch.setenv("DATABASE_URL", "postgresql://localhost/test")
settings = validate_config()
assert settings is not None
result = run_config_check(settings)
assert result == EXIT_SUCCESS
captured = capsys.readouterr()
assert "Configuration is valid!" in captured.out
assert "Configuration:" in captured.out
class TestMain:
"""Tests for main entry point."""
def test_main_with_config_check(self, monkeypatch):
"""Main should exit successfully with --config-check."""
monkeypatch.setenv("DATABASE_URL", "postgresql://localhost/test")
with pytest.raises(SystemExit) as exc_info:
main(["--config-check"])
assert exc_info.value.code == EXIT_SUCCESS
def test_main_with_invalid_config(self, monkeypatch):
"""Main should exit with config error on invalid config."""
monkeypatch.delenv("DATABASE_URL", raising=False)
with pytest.raises(SystemExit) as exc_info:
main([])
assert exc_info.value.code == EXIT_CONFIG_ERROR
def test_main_with_dry_run_and_config_check(self, monkeypatch):
"""Main should handle dry-run with config-check."""
monkeypatch.setenv("DATABASE_URL", "postgresql://localhost/test")
with pytest.raises(SystemExit) as exc_info:
main(["--config-check", "--dry-run"])
assert exc_info.value.code == EXIT_SUCCESS
@patch("polymarket_insider_tracker.__main__.run_pipeline")
@patch("polymarket_insider_tracker.__main__.asyncio.run")
def test_main_runs_pipeline(self, mock_asyncio_run, _mock_run_pipeline, monkeypatch):
"""Main should run pipeline when not in config-check mode."""
monkeypatch.setenv("DATABASE_URL", "postgresql://localhost/test")
mock_asyncio_run.return_value = EXIT_SUCCESS
with pytest.raises(SystemExit) as exc_info:
main([])
assert exc_info.value.code == EXIT_SUCCESS
mock_asyncio_run.assert_called_once()
class TestIntegration:
"""Integration tests for CLI invocation."""
def test_cli_help_option(self, capsys):
"""CLI should display help with -h option."""
with pytest.raises(SystemExit) as exc_info:
main(["-h"])
assert exc_info.value.code == 0
captured = capsys.readouterr()
assert "polymarket-insider-tracker" in captured.out
assert "--config-check" in captured.out
assert "--dry-run" in captured.out
assert "--log-level" in captured.out
def test_cli_version_option(self, capsys):
"""CLI should display version with --version option."""
with pytest.raises(SystemExit) as exc_info:
main(["--version"])
assert exc_info.value.code == 0
captured = capsys.readouterr()
assert "0.1.0" in captured.out
def test_cli_invalid_log_level(self, capsys):
"""CLI should reject invalid log level."""
with pytest.raises(SystemExit) as exc_info:
main(["--log-level", "INVALID"])
assert exc_info.value.code != 0
captured = capsys.readouterr()
assert "invalid choice" in captured.err