""" Tests for utility modules: paths, execution, reporting, stats_helpers. """ from pathlib import Path from unittest.mock import patch import pandas as pd # ============================================================================= # Paths Tests # ============================================================================= class TestPaths: """Tests for path utilities.""" def test_ensure_dir_creates_directory(self): """Test ensure_dir creates directory.""" from core.utils.paths import ensure_dir with patch("os.makedirs") as mock_makedirs: result = ensure_dir("/tmp/test_dir") mock_makedirs.assert_called_once_with("/tmp/test_dir", exist_ok=True) assert isinstance(result, Path) def test_results_dir_constant(self): """Test RESULTS_DIR is defined.""" from core.utils.paths import RESULTS_DIR assert isinstance(RESULTS_DIR, Path) def test_data_dir_constant(self): """Test DATA_DIR is defined.""" from core.utils.paths import DATA_DIR assert isinstance(DATA_DIR, Path) def test_cache_dir_constant(self): """Test CACHE_DIR is defined.""" from core.utils.paths import CACHE_DIR assert isinstance(CACHE_DIR, Path) # ============================================================================= # Execution Tests # ============================================================================= class TestExecution: """Tests for ParallelExecutor.""" def test_parallel_executor_init(self): """Test ParallelExecutor initialization.""" from core.utils.execution import ParallelExecutor executor = ParallelExecutor(max_workers=4) assert executor.max_workers == 4 def test_parallel_executor_empty_input(self): """Test ParallelExecutor with empty input.""" from core.utils.execution import ParallelExecutor executor = ParallelExecutor(max_workers=2) results = list(executor.map(lambda x: x, [], desc="test")) assert results == [] # ============================================================================= # Reporting Tests # ============================================================================= class TestReporting: """Tests for reporting utilities.""" def test_csv_column_map_exists(self): """Test CSV_COLUMN_MAP is defined.""" from core.utils.reporting import CSV_COLUMN_MAP assert isinstance(CSV_COLUMN_MAP, dict) assert len(CSV_COLUMN_MAP) > 0 def test_generate_report_basic(self): """Test generate_report creates summary without error.""" from core.utils.reporting import generate_report mock_df = pd.DataFrame( { "symbol": ["AAPL", "GOOGL"], "date": pd.to_datetime(["2026-04-15", "2026-04-15"]).date, "entry": [150.0, 2800.0], "exit": [155.0, 2850.0], "profit": [500.0, 500.0], "ret": [3.3, 1.8], } ) # Should not raise generate_report(mock_df, initial_capital=10000) # ============================================================================= # Stats Helpers Tests # ============================================================================= class TestStatsHelpers: """Tests for statistical helper functions.""" def test_avg_vol10_calculation(self): """Test avg_vol10 calculation.""" from core.utils.stats_helpers import avg_vol10 df = pd.DataFrame( { "volume": [1000000, 1100000, 1200000, 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000], } ) result = avg_vol10(df, 9) expected = (1000000 + 1100000 + 1200000 + 1300000 + 1400000 + 1500000 + 1600000 + 1700000 + 1800000) / 9 assert result == expected def test_avg_vol10_first_day(self): """Test avg_vol10 for first day.""" from core.utils.stats_helpers import avg_vol10 df = pd.DataFrame( { "volume": [1000000], } ) result = avg_vol10(df, 0) assert result is None or result == 1000000 def test_gap_pct_calculation(self): """Test gap_pct calculation.""" from core.utils.stats_helpers import gap_pct result = gap_pct(100.0, 105.0) assert result == 5.0 def test_gap_pct_none_values(self): """Test gap_pct handles None values.""" from core.utils.stats_helpers import gap_pct result = gap_pct(None, 105.0) assert result is None def test_gap_pct_zero_prev_close(self): """Test gap_pct handles zero prev close.""" from core.utils.stats_helpers import gap_pct result = gap_pct(0, 105.0) assert result is None def test_bucket_gap_all_ranges(self): """Test bucket_gap for all ranges.""" from core.utils.stats_helpers import bucket_gap assert bucket_gap(-10.0) == "gap_n5_p" assert bucket_gap(-3.0) == "gap_n5_0" assert bucket_gap(3.0) == "gap_0_5" assert bucket_gap(7.0) == "gap_5_10" assert bucket_gap(15.0) == "gap_10_p" assert bucket_gap(None) is None def test_bucket_range_all_ranges(self): """Test bucket_range for all ranges.""" from core.utils.stats_helpers import bucket_range assert bucket_range(1.0) == "rng_0_2" assert bucket_range(3.0) == "rng_2_5" assert bucket_range(7.0) == "rng_5_10" assert bucket_range(15.0) == "rng_10_p" assert bucket_range(None) is None def test_bucket_price_all_ranges(self): """Test bucket_price for all ranges.""" from core.utils.stats_helpers import bucket_price assert bucket_price(3.0) == "prc_lt_5" assert bucket_price(15.0) == "prc_5_20" assert bucket_price(35.0) == "prc_20_50" assert bucket_price(75.0) == "prc_50_100" assert bucket_price(150.0) == "prc_gt_100" assert bucket_price(None) is None def test_bucket_rel_vol_all_ranges(self): """Test bucket_rel_vol for all ranges.""" from core.utils.stats_helpers import bucket_rel_vol assert bucket_rel_vol(1.0) == "rvol_lt_2" assert bucket_rel_vol(3.0) == "rvol_2_5" assert bucket_rel_vol(7.0) == "rvol_5_10" assert bucket_rel_vol(15.0) == "rvol_gt_10" assert bucket_rel_vol(None) is None def test_bucket_premium_pct_all_ranges(self): """Test bucket_premium_pct for all ranges.""" from core.utils.stats_helpers import bucket_premium_pct assert bucket_premium_pct(40.0, 100.0) == "pm_pct_0_50" assert bucket_premium_pct(60.0, 100.0) == "pm_pct_50_75" assert bucket_premium_pct(80.0, 100.0) == "pm_pct_75_90" assert bucket_premium_pct(95.0, 100.0) == "pm_pct_90_100" assert bucket_premium_pct(110.0, 100.0) == "pm_pct_100_p" assert bucket_premium_pct(None, 100.0) is None assert bucket_premium_pct(50.0, 0) is None