Skip to content

Commit 7f2fb6c

Browse files
authored
Fix: missed project-id in command lean cloud live deploy ... (#531)
* 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
1 parent 26cbf34 commit 7f2fb6c

File tree

4 files changed

+77
-29
lines changed

4 files changed

+77
-29
lines changed

lean/commands/cloud/live/deploy.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ def deploy(project: str,
240240

241241
live_data_provider_settings = {}
242242
lean_config = container.lean_config_manager.get_lean_config()
243+
lean_config["project-id"] = cloud_project.projectId
243244

244245
if brokerage is not None:
245246
ensure_options(["brokerage", "node", "auto_restart", "notify_order_events", "notify_insights"])

tests/commands/cloud/live/test_cloud_live_commands.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
# limitations under the License.
1313

1414
from unittest import mock
15+
import responses
1516
from click.testing import CliRunner
1617
import pytest
1718
import sys
1819
from lean.commands import lean
1920
from lean.container import container
20-
from lean.models.api import QCEmailNotificationMethod, QCWebhookNotificationMethod, QCSMSNotificationMethod, QCTelegramNotificationMethod
21+
from lean.models.api import QCEmailNotificationMethod, QCWebhookNotificationMethod, QCSMSNotificationMethod, \
22+
QCTelegramNotificationMethod, QCAuth0Authorization
2123
from tests.test_helpers import create_fake_lean_cli_directory, create_qc_nodes
2224
from tests.commands.test_live import brokerage_required_options
2325

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

305307

308+
@responses.activate
306309
@pytest.mark.parametrize("brokerage,holdings", [("Paper Trading", ""),
307310
("Paper Trading", "A:A 2T:1:145.1"),
308311
("Paper Trading", "A:A 2T:1:145.1,AA:AA 2T:2:20.35"),
@@ -328,18 +331,23 @@ def test_cloud_live_deploy_with_live_cash_balance(brokerage: str, cash: str) ->
328331
("Tradier", "A:A 2T:1:145.1"),
329332
("Zerodha", ""),
330333
("Zerodha", "A:A 2T:1:145.1"),
331-
("TDAmeritrade", ""),
332-
("TDAmeritrade", "A:A 2T:1:145.1")])
334+
("CharlesSchwab", ""),
335+
("CharlesSchwab", "A:A 2T:1:145.1")])
333336
def test_cloud_live_deploy_with_live_holdings(brokerage: str, holdings: str) -> None:
334337
if (brokerage == "Interactive Brokers" and sys.platform == "darwin"):
335338
pytest.skip("MacOS does not support IB tests")
336339

337340
create_fake_lean_cli_directory()
338-
339-
cloud_project_manager = mock.Mock()
340-
container.cloud_project_manager = cloud_project_manager
341+
container.cloud_project_manager = mock.Mock(get_cloud_project=mock.Mock(return_value=mock.Mock(projectId=123)))
341342

342343
api_client = mock.Mock()
344+
api_client.auth0.read.return_value = QCAuth0Authorization(
345+
authorization={
346+
"accounts": [
347+
{"id": "123", "name": "123 | Margin | USD"}
348+
]
349+
}
350+
)
343351
api_client.nodes.get_all.return_value = create_qc_nodes()
344352
api_client.get.return_value = {
345353
"status": "stopped",

tests/commands/test_live.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@
1919
from unittest import mock
2020

2121
import pytest
22+
import responses
2223
from click.testing import CliRunner
2324

2425
from lean.commands import lean
2526
from lean.constants import DEFAULT_ENGINE_IMAGE
2627
from lean.container import container
2728
from lean.models.docker import DockerImage
2829
from lean.models.json_module import JsonModule
29-
from tests.test_helpers import create_fake_lean_cli_directory, reset_state_installed_modules
30+
from tests.test_helpers import create_fake_lean_cli_directory, reset_state_installed_modules, \
31+
setup_mock_api_client_and_responses
3032
from tests.conftest import initialize_container
3133
from click.testing import Result
3234

@@ -428,10 +430,8 @@ def test_live_sets_dependent_configurations_from_modules_json_based_on_environme
428430
"tt-order-routing-port": "abc",
429431
"tt-log-fix-messages": "no"
430432
},
431-
"TDAmeritrade": {
432-
"tdameritrade-account-number": "123",
433-
"tdameritrade-api-key": "abc",
434-
"tdameritrade-access-token": "abc",
433+
"CharlesSchwab": {
434+
"charles-schwab-account-number": "123"
435435
},
436436
"Bybit": {
437437
"bybit-api-key": "abc",
@@ -441,10 +441,6 @@ def test_live_sets_dependent_configurations_from_modules_json_based_on_environme
441441
}
442442
}
443443

444-
brokerage_required_options_not_persistently_save_in_lean_config = {
445-
"TDAmeritrade": ["tdameritrade-access-token"]
446-
}
447-
448444
data_feed_required_options = {
449445
"Interactive Brokers": brokerage_required_options["Interactive Brokers"],
450446
"Tradier": brokerage_required_options["Tradier"],
@@ -456,7 +452,7 @@ def test_live_sets_dependent_configurations_from_modules_json_based_on_environme
456452
"Samco": brokerage_required_options["Samco"],
457453
"Terminal Link": terminal_link_required_options,
458454
"Kraken": brokerage_required_options["Kraken"],
459-
"TDAmeritrade": brokerage_required_options["TDAmeritrade"],
455+
"CharlesSchwab": brokerage_required_options["CharlesSchwab"],
460456
"Bybit": brokerage_required_options["Bybit"],
461457
}
462458

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

591587

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

598595
create_fake_lean_cli_directory()
596+
container.api_client = setup_mock_api_client_and_responses()
597+
599598
lean_runner = container.lean_runner
600599

601600
options = []
@@ -630,18 +629,17 @@ def test_live_non_interactive_do_not_store_non_persistent_properties_in_lean_con
630629
{})
631630

632631
config = container.lean_config_manager.get_lean_config()
633-
if brokerage in brokerage_required_options_not_persistently_save_in_lean_config:
634-
for key in brokerage_required_options_not_persistently_save_in_lean_config[brokerage]:
635-
assert key not in config
636632

637633

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

644641
create_fake_lean_cli_directory()
642+
container.api_client = setup_mock_api_client_and_responses()
645643
lean_runner = container.lean_runner
646644

647645
options = []
@@ -675,13 +673,15 @@ def test_live_non_interactive_calls_run_lean_when_all_options_given(brokerage: s
675673
{},
676674
{})
677675

676+
@responses.activate
678677
@pytest.mark.parametrize("brokerage,data_feed1,data_feed2",[(brokerage, *data_feeds) for brokerage, data_feeds in
679678
itertools.product(brokerage_required_options.keys(), itertools.combinations(data_feed_required_options.keys(), 2))])
680679
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:
681680
if ((brokerage == "Interactive Brokers" or data_feed1 == "Interactive Brokers" or data_feed2 == "Interactive Brokers") and sys.platform == "darwin"):
682681
pytest.skip("MacOS does not support IB tests")
683682

684683
create_fake_lean_cli_directory()
684+
container.api_client = setup_mock_api_client_and_responses()
685685
lean_runner = container.lean_runner
686686

687687
options = []
@@ -833,12 +833,14 @@ def test_live_non_interactive_falls_back_to_lean_config_for_data_feed_settings(d
833833
{})
834834

835835

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

841842
create_fake_lean_cli_directory()
843+
mock_api_client = setup_mock_api_client_and_responses()
842844

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

853855
options = []
854856

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

985987

988+
@responses.activate
986989
@pytest.mark.parametrize("brokerage,cash", [("Paper Trading", ""),
987990
("Paper Trading", "USD:100"),
988991
("Paper Trading", "USD:100,EUR:200"),
@@ -1009,14 +1012,15 @@ def test_live_passes_custom_python_venv_to_lean_runner_when_given_as_option(pyth
10091012
("Tradier", "USD:100"),
10101013
("Zerodha", ""),
10111014
("Zerodha", "USD:100"),
1012-
("TDAmeritrade", ""),
1013-
("TDAmeritrade", "USD:100")])
1015+
("CharlesSchwab", ""),
1016+
("CharlesSchwab", "USD:100")])
10141017
def test_live_passes_live_cash_balance_to_lean_runner_when_given_as_option(brokerage: str, cash: str) -> None:
10151018
if (brokerage == "Interactive Brokers" and sys.platform == "darwin"):
10161019
pytest.skip("MacOS does not support IB tests")
10171020

10181021
create_fake_lean_cli_directory()
1019-
lean_runner= container.lean_runner
1022+
container.api_client = setup_mock_api_client_and_responses()
1023+
lean_runner = container.lean_runner
10201024

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

10531057

1058+
@responses.activate
10541059
@pytest.mark.parametrize("brokerage,holdings", [("Paper Trading", ""),
10551060
("Paper Trading", "A:A 2T:1:145.1"),
10561061
("Paper Trading", "A:A 2T:1:145.1,AA:AA 2T:2:20.35"),
@@ -1076,14 +1081,15 @@ def test_live_passes_live_cash_balance_to_lean_runner_when_given_as_option(broke
10761081
("Tradier", "A:A 2T:1:145.1"),
10771082
("Zerodha", ""),
10781083
("Zerodha", "A:A 2T:1:145.1"),
1079-
("TDAmeritrade", ""),
1080-
("TDAmeritrade", "A:A 2T:1:145.1")])
1084+
("CharlesSchwab", ""),
1085+
("CharlesSchwab", "A:A 2T:1:145.1")])
10811086
def test_live_passes_live_holdings_to_lean_runner_when_given_as_option(brokerage: str, holdings: str) -> None:
10821087
if (brokerage == "Interactive Brokers" and sys.platform == "darwin"):
10831088
pytest.skip("MacOS does not support IB tests")
10841089

10851090
create_fake_lean_cli_directory()
1086-
lean_runner= container.lean_runner
1091+
container.api_client = setup_mock_api_client_and_responses()
1092+
lean_runner = container.lean_runner
10871093

10881094
options = []
10891095
required_options = brokerage_required_options[brokerage].items()

tests/test_helpers.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@
1212
# limitations under the License.
1313

1414
import json
15+
import responses
1516
from datetime import datetime
1617
from pathlib import Path
1718
from typing import List
19+
from unittest import mock
1820

19-
from lean.constants import DEFAULT_LEAN_DOTNET_FRAMEWORK
21+
from lean.components.util.http_client import HTTPClient
22+
from lean.components.api.api_client import APIClient
23+
from lean.constants import DEFAULT_LEAN_DOTNET_FRAMEWORK, API_BASE_URL
2024
from lean.models.cli import (cli_brokerages, cli_data_downloaders, cli_data_queue_handlers,
2125
cli_addon_modules, cli_history_provider)
2226

@@ -85,7 +89,9 @@ def _get_lean_config_file_content() -> str:
8589
"data-folder": "data",
8690
8791
// organization-id documentation
88-
"organization-id": "abc"
92+
"organization-id": "abc",
93+
94+
"project-id": 123
8995
}
9096
"""
9197

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

107113

114+
def setup_mock_api_client_and_responses() -> APIClient:
115+
"""
116+
Sets up a mock API client and configures a mock response for API calls.
117+
118+
- Creates a mock `APIClient` with test credentials.
119+
- Adds a mock POST response to the `live/auth0/read` endpoint with sample authorization data.
120+
121+
Returns:
122+
APIClient: A mock API client for testing.
123+
"""
124+
api_client = APIClient(mock.Mock(), HTTPClient(mock.Mock()), user_id="123", api_token="abc")
125+
responses.add(
126+
responses.POST,
127+
f"{API_BASE_URL}live/auth0/read",
128+
json={
129+
"authorization": {
130+
"accounts": [
131+
{"id": "123", "name": "123 | Margin | USD"}
132+
]
133+
},
134+
"success": "true"
135+
},
136+
status=200
137+
)
138+
return api_client
139+
140+
108141
def create_fake_lean_cli_project(name: str, language: str) -> None:
109142
"""Creates a directory structure similar to the one created by `lean init` with a given project info"""
110143
(Path.cwd() / "data").mkdir()

0 commit comments

Comments
 (0)