Skip to content
10 changes: 8 additions & 2 deletions lean/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,16 @@ def _download_modules_json() -> None:
from requests import get
res = get(url, timeout=5)
if res.ok:
from json import dump
from json import dumps
# Decoded and checked before the file is opened, since opening it for writing empties it: a response that
# isn't a modules file would replace a usable one with one the CLI can't start from
data = res.json()
if not isinstance(data, dict) or "modules" not in data:
raise ValueError(f"{url} returned no modules")
content = dumps(data, ensure_ascii=False, indent=4)
file_path.parent.mkdir(parents=True, exist_ok=True)
with open(file_path, 'w', encoding='utf-8') as f:
dump(res.json(), f, ensure_ascii=False, indent=4)
f.write(content)
else:
res.raise_for_status()

Expand Down
54 changes: 54 additions & 0 deletions tests/models/test_download_modules_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean CLI v1.0. Copyright 2021 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: add the license header the other test files start with (see tests/models/test_modules.py).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 0468910.

from pathlib import Path

import pytest
from responses import RequestsMock

import lean.models as models


def _write_cache(path: Path) -> str:
content = json.dumps({"modules": {"SavedModule": {"version": "1.0"}}})
path.write_text(content, encoding="utf-8")
return content


@pytest.mark.parametrize("body", ["not json", "", "{}", "[]", "null"])
def test_download_modules_json_keeps_cache_when_response_has_no_modules(requests_mock: RequestsMock,
monkeypatch: pytest.MonkeyPatch,
body: str) -> None:
cache = Path.cwd() / "modules.json"
content = _write_cache(cache)
monkeypatch.setattr(models, "file_path", cache)
requests_mock.add(requests_mock.GET, models.url, body=body, status=200)

with pytest.raises(ValueError):
models._download_modules_json()

assert cache.read_text(encoding="utf-8") == content


def test_download_modules_json_replaces_cache_with_valid_response(requests_mock: RequestsMock,
monkeypatch: pytest.MonkeyPatch) -> None:
cache = Path.cwd() / "modules.json"
_write_cache(cache)
monkeypatch.setattr(models, "file_path", cache)
new_modules = {"modules": {"NewModule": {"version": "2.0"}}}
requests_mock.add(requests_mock.GET, models.url, json=new_modules, status=200)

models._download_modules_json()

assert json.loads(cache.read_text(encoding="utf-8")) == new_modules
Loading