diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-01 17:56:16 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-01 17:56:16 +0900 |
| commit | fa7e1dc44787592da647bdda0a63310be0cfcc8b (patch) | |
| tree | 9533c747f8a40aaa1697252382bfbbafc8f3fe9d | |
| parent | a65575124b18f2ec5d418623e22c5bdef6c3424e (diff) | |
test: add CLI command tests and API router tests
| -rw-r--r-- | cli/tests/test_cli_backtest.py | 19 | ||||
| -rw-r--r-- | cli/tests/test_cli_portfolio.py | 16 | ||||
| -rw-r--r-- | cli/tests/test_cli_service.py | 22 | ||||
| -rw-r--r-- | cli/tests/test_cli_strategy.py | 47 | ||||
| -rw-r--r-- | services/api/tests/test_orders_router.py | 54 | ||||
| -rw-r--r-- | services/api/tests/test_portfolio_router.py | 73 |
6 files changed, 231 insertions, 0 deletions
diff --git a/cli/tests/test_cli_backtest.py b/cli/tests/test_cli_backtest.py new file mode 100644 index 0000000..84227a9 --- /dev/null +++ b/cli/tests/test_cli_backtest.py @@ -0,0 +1,19 @@ +"""Tests for backtest CLI commands.""" +from click.testing import CliRunner +from trading_cli.main import cli + + +def test_backtest_run_help(): + runner = CliRunner() + result = runner.invoke(cli, ["backtest", "run", "--help"]) + assert result.exit_code == 0 + assert "--strategy" in result.output + assert "--symbol" in result.output + assert "--balance" in result.output + + +def test_backtest_report_help(): + runner = CliRunner() + result = runner.invoke(cli, ["backtest", "report", "--help"]) + assert result.exit_code == 0 + assert "--id" in result.output diff --git a/cli/tests/test_cli_portfolio.py b/cli/tests/test_cli_portfolio.py new file mode 100644 index 0000000..351b49f --- /dev/null +++ b/cli/tests/test_cli_portfolio.py @@ -0,0 +1,16 @@ +"""Tests for portfolio CLI commands.""" +from click.testing import CliRunner +from trading_cli.main import cli + + +def test_portfolio_show_help(): + runner = CliRunner() + result = runner.invoke(cli, ["portfolio", "show", "--help"]) + assert result.exit_code == 0 + + +def test_portfolio_history_help(): + runner = CliRunner() + result = runner.invoke(cli, ["portfolio", "history", "--help"]) + assert result.exit_code == 0 + assert "--days" in result.output diff --git a/cli/tests/test_cli_service.py b/cli/tests/test_cli_service.py new file mode 100644 index 0000000..08cd396 --- /dev/null +++ b/cli/tests/test_cli_service.py @@ -0,0 +1,22 @@ +"""Tests for service CLI commands.""" +from click.testing import CliRunner +from trading_cli.main import cli + + +def test_service_up_help(): + runner = CliRunner() + result = runner.invoke(cli, ["service", "up", "--help"]) + assert result.exit_code == 0 + + +def test_service_down_help(): + runner = CliRunner() + result = runner.invoke(cli, ["service", "down", "--help"]) + assert result.exit_code == 0 + + +def test_service_logs_help(): + runner = CliRunner() + result = runner.invoke(cli, ["service", "logs", "--help"]) + assert result.exit_code == 0 + assert "--name" in result.output diff --git a/cli/tests/test_cli_strategy.py b/cli/tests/test_cli_strategy.py new file mode 100644 index 0000000..3c89477 --- /dev/null +++ b/cli/tests/test_cli_strategy.py @@ -0,0 +1,47 @@ +"""Tests for strategy CLI commands.""" +from unittest.mock import patch, MagicMock +from click.testing import CliRunner +from trading_cli.main import cli + + +def _make_mock_strategy(name, warmup_period=14): + s = MagicMock() + s.name = name + s.warmup_period = warmup_period + type(s).__name__ = "MockStrategy" + return s + + +def test_strategy_list(): + mock_strategies = [_make_mock_strategy("rsi"), _make_mock_strategy("macd", 26)] + with patch("trading_cli.commands.strategy._load_all_strategies", return_value=mock_strategies): + runner = CliRunner() + result = runner.invoke(cli, ["strategy", "list"]) + assert result.exit_code == 0 + assert "rsi" in result.output.lower() or "Strategy" in result.output + + +def test_strategy_list_empty(): + with patch("trading_cli.commands.strategy._load_all_strategies", return_value=[]): + runner = CliRunner() + result = runner.invoke(cli, ["strategy", "list"]) + assert result.exit_code == 0 + assert "No strategies found" in result.output + + +def test_strategy_info_rsi(): + mock_strategies = [_make_mock_strategy("rsi")] + with patch("trading_cli.commands.strategy._load_all_strategies", return_value=mock_strategies): + runner = CliRunner() + result = runner.invoke(cli, ["strategy", "info", "--name", "rsi"]) + assert result.exit_code == 0 + assert "rsi" in result.output.lower() + + +def test_strategy_info_unknown(): + mock_strategies = [_make_mock_strategy("rsi")] + with patch("trading_cli.commands.strategy._load_all_strategies", return_value=mock_strategies): + runner = CliRunner() + result = runner.invoke(cli, ["strategy", "info", "--name", "nonexistent"]) + # Should handle gracefully (exit 0 or 1 with message) + assert "not found" in result.output.lower() or result.exit_code != 0 or "nonexistent" in result.output diff --git a/services/api/tests/test_orders_router.py b/services/api/tests/test_orders_router.py new file mode 100644 index 0000000..899fb27 --- /dev/null +++ b/services/api/tests/test_orders_router.py @@ -0,0 +1,54 @@ +"""Tests for orders API router.""" +import pytest +from decimal import Decimal +from datetime import datetime, timezone +from unittest.mock import AsyncMock, MagicMock +from fastapi.testclient import TestClient +from fastapi import FastAPI + +from trading_api.routers.orders import router + + +@pytest.fixture +def app(): + app = FastAPI() + app.include_router(router, prefix="/orders") + return app + + +@pytest.fixture +def mock_db(): + db = AsyncMock() + mock_session = AsyncMock() + mock_session.__aenter__ = AsyncMock(return_value=mock_session) + mock_session.__aexit__ = AsyncMock(return_value=False) + db.get_session = MagicMock(return_value=mock_session) + return db, mock_session + + +def test_get_orders_empty(app, mock_db): + db, session = mock_db + app.state.db = db + + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [] + session.execute = AsyncMock(return_value=mock_result) + + client = TestClient(app) + response = client.get("/orders/") + assert response.status_code == 200 + assert response.json() == [] + + +def test_get_signals_empty(app, mock_db): + db, session = mock_db + app.state.db = db + + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [] + session.execute = AsyncMock(return_value=mock_result) + + client = TestClient(app) + response = client.get("/orders/signals") + assert response.status_code == 200 + assert response.json() == [] diff --git a/services/api/tests/test_portfolio_router.py b/services/api/tests/test_portfolio_router.py new file mode 100644 index 0000000..0993923 --- /dev/null +++ b/services/api/tests/test_portfolio_router.py @@ -0,0 +1,73 @@ +"""Tests for portfolio API router.""" +import pytest +from decimal import Decimal +from datetime import datetime, timezone +from unittest.mock import AsyncMock, MagicMock, patch +from fastapi.testclient import TestClient +from fastapi import FastAPI + +from trading_api.routers.portfolio import router + + +@pytest.fixture +def app(): + app = FastAPI() + app.include_router(router, prefix="/portfolio") + return app + + +@pytest.fixture +def mock_db(): + db = AsyncMock() + mock_session = AsyncMock() + mock_session.__aenter__ = AsyncMock(return_value=mock_session) + mock_session.__aexit__ = AsyncMock(return_value=False) + db.get_session = MagicMock(return_value=mock_session) + return db, mock_session + + +def test_get_positions_empty(app, mock_db): + db, session = mock_db + app.state.db = db + + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [] + session.execute = AsyncMock(return_value=mock_result) + + client = TestClient(app) + response = client.get("/portfolio/positions") + assert response.status_code == 200 + assert response.json() == [] + + +def test_get_positions_with_data(app, mock_db): + db, session = mock_db + app.state.db = db + + mock_row = MagicMock() + mock_row.symbol = "BTCUSDT" + mock_row.quantity = Decimal("0.1") + mock_row.avg_entry_price = Decimal("50000") + mock_row.current_price = Decimal("55000") + + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [mock_row] + session.execute = AsyncMock(return_value=mock_result) + + client = TestClient(app) + response = client.get("/portfolio/positions") + assert response.status_code == 200 + data = response.json() + assert len(data) == 1 + assert data[0]["symbol"] == "BTCUSDT" + + +def test_get_snapshots_empty(app, mock_db): + db, _ = mock_db + app.state.db = db + db.get_portfolio_snapshots = AsyncMock(return_value=[]) + + client = TestClient(app) + response = client.get("/portfolio/snapshots") + assert response.status_code == 200 + assert response.json() == [] |
