Skip to content

Commit

Permalink
Fix: missed project-id in command lean cloud live deploy ... (#531)
Browse files Browse the repository at this point in the history
* fix: add project in lean config when run cloud project

* test:fix: use CharlesSchwab

* test:fix: mock response for CharlesSchwab in test_cloud_live_deploy_with_live_holdings
  • Loading branch information
Romazes authored Dec 30, 2024
1 parent 26cbf34 commit 7f2fb6c
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 29 deletions.
1 change: 1 addition & 0 deletions lean/commands/cloud/live/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ def deploy(project: str,

live_data_provider_settings = {}
lean_config = container.lean_config_manager.get_lean_config()
lean_config["project-id"] = cloud_project.projectId

if brokerage is not None:
ensure_options(["brokerage", "node", "auto_restart", "notify_order_events", "notify_insights"])
Expand Down
22 changes: 15 additions & 7 deletions tests/commands/cloud/live/test_cloud_live_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
# limitations under the License.

from unittest import mock
import responses
from click.testing import CliRunner
import pytest
import sys
from lean.commands import lean
from lean.container import container
from lean.models.api import QCEmailNotificationMethod, QCWebhookNotificationMethod, QCSMSNotificationMethod, QCTelegramNotificationMethod
from lean.models.api import QCEmailNotificationMethod, QCWebhookNotificationMethod, QCSMSNotificationMethod, \
QCTelegramNotificationMethod, QCAuth0Authorization
from tests.test_helpers import create_fake_lean_cli_directory, create_qc_nodes
from tests.commands.test_live import brokerage_required_options

Expand Down Expand Up @@ -245,7 +247,7 @@ def test_cloud_live_deploy_with_notifications(notice_method: str, configs: str)
("Terminal Link", "USD:100"),
("Tradier", "USD:100"),
("Zerodha", "USD:100"),
("TDAmeritrade", "USD:100")])
("CharlesSchwab", "USD:100")])
def test_cloud_live_deploy_with_live_cash_balance(brokerage: str, cash: str) -> None:
if (brokerage == "Interactive Brokers" and sys.platform == "darwin"):
pytest.skip("MacOS does not support IB tests")
Expand Down Expand Up @@ -303,6 +305,7 @@ def test_cloud_live_deploy_with_live_cash_balance(brokerage: str, cash: str) ->
mock.ANY)


@responses.activate
@pytest.mark.parametrize("brokerage,holdings", [("Paper Trading", ""),
("Paper Trading", "A:A 2T:1:145.1"),
("Paper Trading", "A:A 2T:1:145.1,AA:AA 2T:2:20.35"),
Expand All @@ -328,18 +331,23 @@ def test_cloud_live_deploy_with_live_cash_balance(brokerage: str, cash: str) ->
("Tradier", "A:A 2T:1:145.1"),
("Zerodha", ""),
("Zerodha", "A:A 2T:1:145.1"),
("TDAmeritrade", ""),
("TDAmeritrade", "A:A 2T:1:145.1")])
("CharlesSchwab", ""),
("CharlesSchwab", "A:A 2T:1:145.1")])
def test_cloud_live_deploy_with_live_holdings(brokerage: str, holdings: str) -> None:
if (brokerage == "Interactive Brokers" and sys.platform == "darwin"):
pytest.skip("MacOS does not support IB tests")

create_fake_lean_cli_directory()

cloud_project_manager = mock.Mock()
container.cloud_project_manager = cloud_project_manager
container.cloud_project_manager = mock.Mock(get_cloud_project=mock.Mock(return_value=mock.Mock(projectId=123)))

api_client = mock.Mock()
api_client.auth0.read.return_value = QCAuth0Authorization(
authorization={
"accounts": [
{"id": "123", "name": "123 | Margin | USD"}
]
}
)
api_client.nodes.get_all.return_value = create_qc_nodes()
api_client.get.return_value = {
"status": "stopped",
Expand Down
46 changes: 26 additions & 20 deletions tests/commands/test_live.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@
from unittest import mock

import pytest
import responses
from click.testing import CliRunner

from lean.commands import lean
from lean.constants import DEFAULT_ENGINE_IMAGE
from lean.container import container
from lean.models.docker import DockerImage
from lean.models.json_module import JsonModule
from tests.test_helpers import create_fake_lean_cli_directory, reset_state_installed_modules
from tests.test_helpers import create_fake_lean_cli_directory, reset_state_installed_modules, \
setup_mock_api_client_and_responses
from tests.conftest import initialize_container
from click.testing import Result

Expand Down Expand Up @@ -428,10 +430,8 @@ def test_live_sets_dependent_configurations_from_modules_json_based_on_environme
"tt-order-routing-port": "abc",
"tt-log-fix-messages": "no"
},
"TDAmeritrade": {
"tdameritrade-account-number": "123",
"tdameritrade-api-key": "abc",
"tdameritrade-access-token": "abc",
"CharlesSchwab": {
"charles-schwab-account-number": "123"
},
"Bybit": {
"bybit-api-key": "abc",
Expand All @@ -441,10 +441,6 @@ def test_live_sets_dependent_configurations_from_modules_json_based_on_environme
}
}

brokerage_required_options_not_persistently_save_in_lean_config = {
"TDAmeritrade": ["tdameritrade-access-token"]
}

data_feed_required_options = {
"Interactive Brokers": brokerage_required_options["Interactive Brokers"],
"Tradier": brokerage_required_options["Tradier"],
Expand All @@ -456,7 +452,7 @@ def test_live_sets_dependent_configurations_from_modules_json_based_on_environme
"Samco": brokerage_required_options["Samco"],
"Terminal Link": terminal_link_required_options,
"Kraken": brokerage_required_options["Kraken"],
"TDAmeritrade": brokerage_required_options["TDAmeritrade"],
"CharlesSchwab": brokerage_required_options["CharlesSchwab"],
"Bybit": brokerage_required_options["Bybit"],
}

Expand Down Expand Up @@ -589,13 +585,16 @@ def test_live_non_interactive_aborts_when_missing_data_feed_options(data_feed: s
container.lean_runner.run_lean.assert_not_called()


@responses.activate
@pytest.mark.parametrize("brokerage,data_feed",
itertools.product(brokerage_required_options.keys(), data_feed_required_options.keys()))
def test_live_non_interactive_do_not_store_non_persistent_properties_in_lean_config(brokerage: str, data_feed: str) -> None:
if ((brokerage == "Interactive Brokers" or data_feed == "Interactive Brokers") and sys.platform == "darwin"):
pytest.skip("MacOS does not support IB tests")

create_fake_lean_cli_directory()
container.api_client = setup_mock_api_client_and_responses()

lean_runner = container.lean_runner

options = []
Expand Down Expand Up @@ -630,18 +629,17 @@ def test_live_non_interactive_do_not_store_non_persistent_properties_in_lean_con
{})

config = container.lean_config_manager.get_lean_config()
if brokerage in brokerage_required_options_not_persistently_save_in_lean_config:
for key in brokerage_required_options_not_persistently_save_in_lean_config[brokerage]:
assert key not in config


@responses.activate
@pytest.mark.parametrize("brokerage,data_feed",
itertools.product(brokerage_required_options.keys(), data_feed_required_options.keys()))
def test_live_non_interactive_calls_run_lean_when_all_options_given(brokerage: str, data_feed: str) -> None:
if ((brokerage == "Interactive Brokers" or data_feed == "Interactive Brokers") and sys.platform == "darwin"):
pytest.skip("MacOS does not support IB tests")

create_fake_lean_cli_directory()
container.api_client = setup_mock_api_client_and_responses()
lean_runner = container.lean_runner

options = []
Expand Down Expand Up @@ -675,13 +673,15 @@ def test_live_non_interactive_calls_run_lean_when_all_options_given(brokerage: s
{},
{})

@responses.activate
@pytest.mark.parametrize("brokerage,data_feed1,data_feed2",[(brokerage, *data_feeds) for brokerage, data_feeds in
itertools.product(brokerage_required_options.keys(), itertools.combinations(data_feed_required_options.keys(), 2))])
def test_live_non_interactive_calls_run_lean_when_all_options_given_with_multiple_data_feeds(brokerage: str, data_feed1: str, data_feed2: str) -> None:
if ((brokerage == "Interactive Brokers" or data_feed1 == "Interactive Brokers" or data_feed2 == "Interactive Brokers") and sys.platform == "darwin"):
pytest.skip("MacOS does not support IB tests")

create_fake_lean_cli_directory()
container.api_client = setup_mock_api_client_and_responses()
lean_runner = container.lean_runner

options = []
Expand Down Expand Up @@ -833,12 +833,14 @@ def test_live_non_interactive_falls_back_to_lean_config_for_data_feed_settings(d
{})


@responses.activate
@pytest.mark.parametrize("data_feed1,data_feed2", itertools.combinations(data_feed_required_options.keys(), 2))
def test_live_non_interactive_falls_back_to_lean_config_for_multiple_data_feed_settings(data_feed1: str, data_feed2: str) -> None:
if ((data_feed1 == "Interactive Brokers" or data_feed2 == "Interactive Brokers") and sys.platform == "darwin"):
pytest.skip("MacOS does not support IB tests")

create_fake_lean_cli_directory()
mock_api_client = setup_mock_api_client_and_responses()

required_options = list(data_feed_required_options[data_feed1].items()) + list(data_feed_required_options[data_feed2].items())
if len(required_options) > 8:
Expand All @@ -848,7 +850,7 @@ def test_live_non_interactive_falls_back_to_lean_config_for_multiple_data_feed_s
for current_options in itertools.combinations(required_options, length):
lean_runner = mock.Mock()
# refresh so we assert we are called once
initialize_container(None, lean_runner)
initialize_container(None, lean_runner,api_client_to_use=mock_api_client)

options = []

Expand Down Expand Up @@ -983,6 +985,7 @@ def test_live_passes_custom_python_venv_to_lean_runner_when_given_as_option(pyth
assert "python-venv" not in args[0]


@responses.activate
@pytest.mark.parametrize("brokerage,cash", [("Paper Trading", ""),
("Paper Trading", "USD:100"),
("Paper Trading", "USD:100,EUR:200"),
Expand All @@ -1009,14 +1012,15 @@ def test_live_passes_custom_python_venv_to_lean_runner_when_given_as_option(pyth
("Tradier", "USD:100"),
("Zerodha", ""),
("Zerodha", "USD:100"),
("TDAmeritrade", ""),
("TDAmeritrade", "USD:100")])
("CharlesSchwab", ""),
("CharlesSchwab", "USD:100")])
def test_live_passes_live_cash_balance_to_lean_runner_when_given_as_option(brokerage: str, cash: str) -> None:
if (brokerage == "Interactive Brokers" and sys.platform == "darwin"):
pytest.skip("MacOS does not support IB tests")

create_fake_lean_cli_directory()
lean_runner= container.lean_runner
container.api_client = setup_mock_api_client_and_responses()
lean_runner = container.lean_runner

options = []
required_options = brokerage_required_options[brokerage].items()
Expand Down Expand Up @@ -1051,6 +1055,7 @@ def test_live_passes_live_cash_balance_to_lean_runner_when_given_as_option(broke
assert args[0]["live-cash-balance"] == cash_list


@responses.activate
@pytest.mark.parametrize("brokerage,holdings", [("Paper Trading", ""),
("Paper Trading", "A:A 2T:1:145.1"),
("Paper Trading", "A:A 2T:1:145.1,AA:AA 2T:2:20.35"),
Expand All @@ -1076,14 +1081,15 @@ def test_live_passes_live_cash_balance_to_lean_runner_when_given_as_option(broke
("Tradier", "A:A 2T:1:145.1"),
("Zerodha", ""),
("Zerodha", "A:A 2T:1:145.1"),
("TDAmeritrade", ""),
("TDAmeritrade", "A:A 2T:1:145.1")])
("CharlesSchwab", ""),
("CharlesSchwab", "A:A 2T:1:145.1")])
def test_live_passes_live_holdings_to_lean_runner_when_given_as_option(brokerage: str, holdings: str) -> None:
if (brokerage == "Interactive Brokers" and sys.platform == "darwin"):
pytest.skip("MacOS does not support IB tests")

create_fake_lean_cli_directory()
lean_runner= container.lean_runner
container.api_client = setup_mock_api_client_and_responses()
lean_runner = container.lean_runner

options = []
required_options = brokerage_required_options[brokerage].items()
Expand Down
37 changes: 35 additions & 2 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@
# limitations under the License.

import json
import responses
from datetime import datetime
from pathlib import Path
from typing import List
from unittest import mock

from lean.constants import DEFAULT_LEAN_DOTNET_FRAMEWORK
from lean.components.util.http_client import HTTPClient
from lean.components.api.api_client import APIClient
from lean.constants import DEFAULT_LEAN_DOTNET_FRAMEWORK, API_BASE_URL
from lean.models.cli import (cli_brokerages, cli_data_downloaders, cli_data_queue_handlers,
cli_addon_modules, cli_history_provider)

Expand Down Expand Up @@ -85,7 +89,9 @@ def _get_lean_config_file_content() -> str:
"data-folder": "data",
// organization-id documentation
"organization-id": "abc"
"organization-id": "abc",
"project-id": 123
}
"""

Expand All @@ -105,6 +111,33 @@ def create_fake_lean_cli_directory() -> None:
_write_fake_directory(files)


def setup_mock_api_client_and_responses() -> APIClient:
"""
Sets up a mock API client and configures a mock response for API calls.
- Creates a mock `APIClient` with test credentials.
- Adds a mock POST response to the `live/auth0/read` endpoint with sample authorization data.
Returns:
APIClient: A mock API client for testing.
"""
api_client = APIClient(mock.Mock(), HTTPClient(mock.Mock()), user_id="123", api_token="abc")
responses.add(
responses.POST,
f"{API_BASE_URL}live/auth0/read",
json={
"authorization": {
"accounts": [
{"id": "123", "name": "123 | Margin | USD"}
]
},
"success": "true"
},
status=200
)
return api_client


def create_fake_lean_cli_project(name: str, language: str) -> None:
"""Creates a directory structure similar to the one created by `lean init` with a given project info"""
(Path.cwd() / "data").mkdir()
Expand Down

0 comments on commit 7f2fb6c

Please sign in to comment.