feat: add standardized testing scripts and documentation

Add comprehensive suite of testing scripts for different workflows:
- test.sh: Interactive menu for all testing operations
- quick_test.sh: Fast unit test feedback (~10-30s)
- run_tests.sh: Main test runner with full configuration options
- coverage_report.sh: Coverage analysis with HTML/JSON/terminal reports
- ci_test.sh: CI/CD optimized testing with JUnit/coverage XML output

Features:
- Colored terminal output with clear error messages
- Consistent option flags across all scripts
- Support for test markers (unit, integration, e2e, slow, etc.)
- Parallel execution support
- Coverage thresholds (default: 85%)
- Virtual environment and dependency checks

Documentation:
- Update CLAUDE.md with testing section and examples
- Expand docs/developer/testing.md with comprehensive guide
- Add scripts/README.md with quick reference

All scripts are tested and executable. This standardizes the testing
process for local development, CI/CD, and pull request workflows.
This commit is contained in:
2025-11-03 21:39:41 -05:00
parent 84320ab8a5
commit 923cdec5ca
12 changed files with 1467 additions and 48 deletions

View File

@@ -55,7 +55,7 @@ def test_complete_async_download_flow(test_client, monkeypatch):
monkeypatch.setattr("api.price_data_manager.PriceDataManager", MockPriceManager)
# Mock execution to avoid actual trading
def mock_execute_date(self, date, models, config_path):
def mock_execute_date(self, date, models, config_path, completion_skips=None):
# Update job details to simulate successful execution
from api.job_manager import JobManager
job_manager = JobManager(db_path=test_client.app.state.db_path)
@@ -155,7 +155,7 @@ def test_flow_with_partial_data(test_client, monkeypatch):
monkeypatch.setattr("api.price_data_manager.PriceDataManager", MockPriceManagerPartial)
def mock_execute_date(self, date, models, config_path):
def mock_execute_date(self, date, models, config_path, completion_skips=None):
# Update job details to simulate successful execution
from api.job_manager import JobManager
job_manager = JobManager(db_path=test_client.app.state.db_path)

View File

@@ -26,7 +26,7 @@ def test_worker_prepares_data_before_execution(tmp_path):
def mock_prepare(*args, **kwargs):
prepare_called.append(True)
return (["2025-10-01"], []) # Return available dates, no warnings
return (["2025-10-01"], [], {}) # Return available dates, no warnings, no completion skips
worker._prepare_data = mock_prepare
@@ -55,7 +55,7 @@ def test_worker_handles_no_available_dates(tmp_path):
worker = SimulationWorker(job_id=job_id, db_path=db_path)
# Mock _prepare_data to return empty dates
worker._prepare_data = Mock(return_value=([], []))
worker._prepare_data = Mock(return_value=([], [], {}))
# Run worker
result = worker.run()
@@ -84,7 +84,7 @@ def test_worker_stores_warnings(tmp_path):
# Mock _prepare_data to return warnings
warnings = ["Rate limited", "Skipped 1 date"]
worker._prepare_data = Mock(return_value=(["2025-10-01"], warnings))
worker._prepare_data = Mock(return_value=(["2025-10-01"], warnings, {}))
worker._execute_date = Mock()
# Run worker

View File

@@ -50,7 +50,7 @@ class TestSimulationWorkerExecution:
worker = SimulationWorker(job_id=job_id, db_path=clean_db)
# Mock _prepare_data to return both dates
worker._prepare_data = Mock(return_value=(["2025-01-16", "2025-01-17"], []))
worker._prepare_data = Mock(return_value=(["2025-01-16", "2025-01-17"], [], {}))
# Mock ModelDayExecutor
with patch("api.simulation_worker.ModelDayExecutor") as mock_executor_class:
@@ -82,7 +82,7 @@ class TestSimulationWorkerExecution:
worker = SimulationWorker(job_id=job_id, db_path=clean_db)
# Mock _prepare_data to return both dates
worker._prepare_data = Mock(return_value=(["2025-01-16", "2025-01-17"], []))
worker._prepare_data = Mock(return_value=(["2025-01-16", "2025-01-17"], [], {}))
execution_order = []
@@ -127,7 +127,7 @@ class TestSimulationWorkerExecution:
worker = SimulationWorker(job_id=job_id, db_path=clean_db)
# Mock _prepare_data to return the date
worker._prepare_data = Mock(return_value=(["2025-01-16"], []))
worker._prepare_data = Mock(return_value=(["2025-01-16"], [], {}))
def create_mock_executor(job_id, date, model_sig, config_path, db_path):
"""Create mock executor that simulates job detail status updates."""
@@ -168,7 +168,7 @@ class TestSimulationWorkerExecution:
worker = SimulationWorker(job_id=job_id, db_path=clean_db)
# Mock _prepare_data to return the date
worker._prepare_data = Mock(return_value=(["2025-01-16"], []))
worker._prepare_data = Mock(return_value=(["2025-01-16"], [], {}))
call_count = 0
@@ -223,7 +223,7 @@ class TestSimulationWorkerErrorHandling:
worker = SimulationWorker(job_id=job_id, db_path=clean_db)
# Mock _prepare_data to return the date
worker._prepare_data = Mock(return_value=(["2025-01-16"], []))
worker._prepare_data = Mock(return_value=(["2025-01-16"], [], {}))
execution_count = 0
@@ -298,7 +298,7 @@ class TestSimulationWorkerConcurrency:
worker = SimulationWorker(job_id=job_id, db_path=clean_db)
# Mock _prepare_data to return the date
worker._prepare_data = Mock(return_value=(["2025-01-16"], []))
worker._prepare_data = Mock(return_value=(["2025-01-16"], [], {}))
with patch("api.simulation_worker.ModelDayExecutor") as mock_executor_class:
mock_executor = Mock()
@@ -521,7 +521,7 @@ class TestSimulationWorkerHelperMethods:
worker.job_manager.get_completed_model_dates = Mock(return_value={})
# Execute
available_dates, warnings = worker._prepare_data(
available_dates, warnings, completion_skips = worker._prepare_data(
requested_dates=["2025-10-01"],
models=["gpt-5"],
config_path="config.json"
@@ -570,7 +570,7 @@ class TestSimulationWorkerHelperMethods:
worker.job_manager.get_completed_model_dates = Mock(return_value={})
# Execute
available_dates, warnings = worker._prepare_data(
available_dates, warnings, completion_skips = worker._prepare_data(
requested_dates=["2025-10-01"],
models=["gpt-5"],
config_path="config.json"