diff --git a/.github/workflows/platform-api-integration-test.yml b/.github/workflows/platform-api-integration-test.yml index 88792efd6a8..02545c85764 100644 --- a/.github/workflows/platform-api-integration-test.yml +++ b/.github/workflows/platform-api-integration-test.yml @@ -1,9 +1,6 @@ name: API Integration Tests on: - push: - branches: - - feature/openbb-sdk-v4 workflow_dispatch: concurrency: @@ -15,6 +12,9 @@ jobs: runs-on: ubuntu-latest env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + OPENBB_API_AUTH: true + OPENBB_API_USERNAME: "USER" + OPENBB_API_PASSWORD: "PASS" steps: - name: Slack Notification - Starting @@ -43,7 +43,7 @@ jobs: uses: actions/cache@v3 with: path: runner_env - key: venv-${{ runner.os }}-v1-${{ hashFiles('**/poetry.lock', '**/dev_install.py') }} + key: runner_env-${{ runner.os }}-v1-${{ hashFiles('**/poetry.lock', '**/dev_install.py') }} - name: Install Dependencies if: steps.cached-python-environment.outputs.cache-hit != 'true' @@ -60,7 +60,8 @@ jobs: ./openbb_platform/providers/intrinio \ ./openbb_platform/extensions/futures \ ./openbb_platform/extensions/qa \ - ./openbb_platform/extensions/ta + ./openbb_platform/extensions/ta \ + ./openbb_platform/extensions/econometrics pip uninstall -y openbb_core pip uninstall -y openbb_provider cd openbb_platform/platform/core @@ -72,13 +73,14 @@ jobs: - name: Populate System Setting Files run: | - mkdir -p ${{ github.workspace }}/.openbb_platform - touch ${{ github.workspace }}/.openbb_platform/system_settings.json - echo '{"log_collect":false}' > ${{ github.workspace }}/.openbb_platform/system_settings.json + mkdir -p ~/.openbb_platform + touch ~/.openbb_platform/system_settings.json + echo '{"log_collect":false}' > ~/.openbb_platform/system_settings.json + cat ~/.openbb_platform/system_settings.json - name: Create and Populate user_settings.json from GitHub Secrets run: | - touch ${{ github.workspace }}/.openbb_platform/user_settings.json + touch ~/.openbb_platform/user_settings.json echo '{ "credentials": { "benzinga_api_key": ${{ secrets.BENZINGA_API_KEY }}, @@ -88,25 +90,31 @@ jobs: "fred_api_key": "${{ secrets.FRED_API_KEY }}", "intrinio_api_key": "${{ secrets.INTRINIO_API_KEY }}" } - }' > ${{ github.workspace }}/.openbb_platform/user_settings.json + }' > ~/.openbb_platform/user_settings.json - name: Launch the Uvicorn Process run: | source runner_env/bin/activate pip list - pwd uvicorn openbb_core.api.rest_api:app --host 0.0.0.0 --port 8000 --reload & - - name: Wait for 1 minute - run: sleep 60 + - name: Wait for 10 seconds + run: sleep 10 - name: Run API Integration Tests run: | source runner_env/bin/activate pip list - pwd cd openbb_platform - pytest . -m integration + pytest -m integration + + - name: Cache pip packages + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: runner_env-${{ runner.os }}-v1-${{ hashFiles('**/poetry.lock', '**/dev_install.py') }} + restore-keys: | + ${{ runner.os }}-pip- - name: Upload summary to Slack uses: adrey/slack-file-upload-action@master diff --git a/openbb_platform/platform/core/tests/__init__.py b/openbb_platform/extensions/__init__.py similarity index 100% rename from openbb_platform/platform/core/tests/__init__.py rename to openbb_platform/extensions/__init__.py diff --git a/openbb_platform/extensions/crypto/integration/test_crypto_api.py b/openbb_platform/extensions/crypto/integration/test_crypto_api.py index 54d67068e31..9dc6f14aca2 100644 --- a/openbb_platform/extensions/crypto/integration/test_crypto_api.py +++ b/openbb_platform/extensions/crypto/integration/test_crypto_api.py @@ -1,20 +1,18 @@ +import base64 + import pytest import requests +from openbb_core.env import Env from openbb_provider.utils.helpers import get_querystring -def get_token(): - return requests.post( - "http://0.0.0.0:8000/api/v1/account/token", - data={"username": "openbb", "password": "openbb"}, - timeout=5, - ) - - @pytest.fixture(scope="session") def headers(): - access_token = get_token().json()["access_token"] - return {"Authorization": f"Bearer {access_token}"} + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"} @pytest.mark.parametrize( @@ -28,6 +26,7 @@ def headers(): "symbol": "BTCUSD", "start_date": "2023-01-01", "end_date": "2023-01-02", + "timeseries": 1, } ), ( @@ -37,6 +36,7 @@ def headers(): "symbol": "BTCUSD", "start_date": "2023-01-01", "end_date": "2023-06-06", + "timeseries": 1, } ), ( @@ -93,6 +93,6 @@ def test_crypto_load(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/crypto/load?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 diff --git a/openbb_platform/extensions/crypto/integration/test_crypto_python.py b/openbb_platform/extensions/crypto/integration/test_crypto_python.py index 7df073d0ac0..f621443a166 100644 --- a/openbb_platform/extensions/crypto/integration/test_crypto_python.py +++ b/openbb_platform/extensions/crypto/integration/test_crypto_python.py @@ -31,6 +31,7 @@ def obb(pytestconfig): "symbol": "BTCUSD", "start_date": "2023-01-01", "end_date": "2023-01-02", + "timeseries": 1, } ), ( @@ -40,6 +41,7 @@ def obb(pytestconfig): "symbol": "BTCUSD", "start_date": "2023-01-01", "end_date": "2023-06-06", + "timeseries": 1, } ), ( diff --git a/openbb_platform/extensions/econometrics/integration/test_econometrics_api.py b/openbb_platform/extensions/econometrics/integration/test_econometrics_api.py index 631a33b6f0f..d2927a8e376 100644 --- a/openbb_platform/extensions/econometrics/integration/test_econometrics_api.py +++ b/openbb_platform/extensions/econometrics/integration/test_econometrics_api.py @@ -1,246 +1,273 @@ +import base64 import json import random -from typing import List +from typing import Literal import pytest import requests +from openbb_core.env import Env from openbb_provider.utils.helpers import get_querystring - -def get_token(): - return requests.post( - "http://0.0.0.0:8000/api/v1/account/token", - data={"username": "openbb", "password": "openbb"}, - timeout=5, - ) +data = {} -def auth_header(): - access_token = get_token().json()["access_token"] - return {"Authorization": f"Bearer {access_token}"} +def get_headers(): + if "headers" in data: + return data["headers"] + + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + data["headers"] = {"Authorization": f"Basic {base64_bytes.decode('ascii')}"} + return data["headers"] -@pytest.fixture(scope="session") -def headers(): - h = {} - auth = auth_header() - h.update(auth) - return h - - -def get_random_data(menu: str, symbols: List[str], providers: List[str]): +def request_data(menu: str, symbol: str, provider: str): """Randomly pick a symbol and a provider and get data from the selected menu.""" - - symbol = random.choice(symbols) # noqa: S311 - provider = random.choice(providers) # noqa: S311 - url = f"http://0.0.0.0:8000/api/v1/{menu}/load?symbol={symbol}&provider={provider}" - result = requests.get(url, headers=auth_header(), timeout=5) + result = requests.get(url, headers=get_headers(), timeout=10) return result.json()["results"] -symbols = ["AAPL", "NVDA", "MSFT", "TSLA", "AMZN", "GOOG", "FB", "BABA", "TSM", "V"] -providers = ["fmp", "intrinio", "polygon", "yfinance"] -stocks_data = get_random_data("stocks", symbols=symbols, providers=providers) +def get_stocks_data(): + if "stocks_data" in data: + return data["stocks_data"] -# TODO : add more crypto providers and symbols -symbols_crypto = ["BTC"] -providers_crypto = ["fmp"] -crypto_data = get_random_data( - menu="crypto", symbols=symbols_crypto, providers=providers_crypto -) + symbol = random.choice(["AAPL", "NVDA", "MSFT", "TSLA", "AMZN", "V"]) # noqa: S311 + provider = random.choice(["fmp", "intrinio", "polygon", "yfinance"]) # noqa: S311 + + data["stocks_data"] = request_data("stocks", symbol=symbol, provider=provider) + return data["stocks_data"] + + +def get_crypto_data(): + if "crypto_data" in data: + return data["crypto_data"] + + # TODO : add more crypto providers and symbols + symbol = random.choice(["BTC"]) # noqa: S311 + provider = random.choice(["fmp"]) # noqa: S311 + + data["crypto_data"] = request_data( + menu="crypto", + symbol=symbol, + provider=provider, + ) + return data["crypto_data"] + + +def get_data(menu: Literal["stocks", "crypto"]): + funcs = {"stocks": get_stocks_data, "crypto": get_crypto_data} + return funcs[menu]() @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data}), - ({"data": crypto_data}), + ({"data": ""}, "stocks"), + ({"data": ""}, "crypto"), ], ) @pytest.mark.integration -def test_econometrics_corr(params, headers): +def test_econometrics_corr(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + + body = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/econometrics/corr?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=body) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "y_column": "close", "x_columns": ["date"]}), - ({"data": crypto_data, "y_column": "close", "x_columns": ["date"]}), + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "stocks", + ), + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "crypto", + ), ], ) @pytest.mark.integration -def test_econometrics_ols_summary(params, headers): +def test_econometrics_ols_summary(params, data_type): params = {p: v for p, v in params.items() if v} body = json.dumps( { - "data": params.pop("data"), + "data": get_data(data_type), "x_columns": params.pop("x_columns"), } ) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/econometrics/ols_summary?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=body) + result = requests.post(url, headers=get_headers(), timeout=10, data=body) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "y_column": "volume", "x_columns": ["close"]}), - ({"data": crypto_data, "y_column": "volume", "x_columns": ["close"]}), + ( + {"data": "", "y_column": "volume", "x_columns": ["close"]}, + "stocks", + ), + ( + {"data": "", "y_column": "volume", "x_columns": ["close"]}, + "crypto", + ), ], ) @pytest.mark.integration -def test_econometrics_dwat(params, headers): +def test_econometrics_dwat(params, data_type): params = {p: v for p, v in params.items() if v} body = json.dumps( { - "data": params.pop("data"), + "data": get_data(data_type), "x_columns": params.pop("x_columns"), } ) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/econometrics/dwat?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=body) + result = requests.post(url, headers=get_headers(), timeout=10, data=body) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "y_column": "volume", "x_columns": ["close"], "lags": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "y_column": "volume", "x_columns": ["close"], "lags": "2", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_econometrics_bgot(params, headers): +def test_econometrics_bgot(params, data_type): params = {p: v for p, v in params.items() if v} body = json.dumps( { - "data": params.pop("data"), + "data": get_data(data_type), "x_columns": params.pop("x_columns"), } ) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/econometrics/bgot?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=body) + result = requests.post(url, headers=get_headers(), timeout=10, data=body) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "columns": ["close", "volume"], - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "columns": ["close", "volume"], - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_econometrics_coint(params, headers): +def test_econometrics_coint(params, data_type): params = {p: v for p, v in params.items() if v} body = json.dumps( { - "data": params.pop("data"), + "data": get_data(data_type), "columns": params.pop("columns"), } ) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/econometrics/coint?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=body) + result = requests.post(url, headers=get_headers(), timeout=10, data=body) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "y_column": "volume", - "x_columns": "close", + "x_column": "close", "lag": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "y_column": "volume", - "x_columns": "close", + "x_column": "close", "lag": "2", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_econometrics_granger(params, headers): +def test_econometrics_granger(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + body = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/econometrics/granger?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=body) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", - [ - ({"data": stocks_data, "column": "close", "regression": "c"}), - ({"data": crypto_data, "column": "volume", "regression": "ctt"}), - ], + "params, data_type", + [({"data": "", "column": "", "regression": ""}, "stocks")], ) @pytest.mark.integration -def test_econometrics_unitroot(params, headers): +def test_econometrics_unitroot(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + body = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/econometrics/unitroot?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=body) assert isinstance(result, requests.Response) assert result.status_code == 200 diff --git a/openbb_platform/extensions/econometrics/integration/test_econometrics_python.py b/openbb_platform/extensions/econometrics/integration/test_econometrics_python.py new file mode 100644 index 00000000000..81d0e71201e --- /dev/null +++ b/openbb_platform/extensions/econometrics/integration/test_econometrics_python.py @@ -0,0 +1,410 @@ +"""Test econometrics extension.""" +import random +from typing import Literal + +import pytest +from openbb_core.app.model.obbject import OBBject + + +@pytest.fixture(scope="session") +def obb(pytestconfig): + """Fixture to setup obb.""" + if pytestconfig.getoption("markexpr") != "not integration": + import openbb + + return openbb.obb + + +data = {} + + +def get_stocks_data(): + import openbb # pylint:disable=import-outside-toplevel + + if "stocks_data" in data: + return data["stocks_data"] + + symbol = random.choice(["AAPL", "NVDA", "MSFT", "TSLA", "AMZN", "V"]) # noqa: S311 + provider = random.choice(["fmp", "intrinio", "polygon", "yfinance"]) # noqa: S311 + + data["stocks_data"] = openbb.obb.stocks.load( + symbol=symbol, provider=provider + ).results + return data["stocks_data"] + + +def get_crypto_data(): + import openbb # pylint:disable=import-outside-toplevel + + if "crypto_data" in data: + return data["crypto_data"] + + # TODO : add more crypto providers and symbols + symbol = random.choice(["BTC"]) # noqa: S311 + provider = random.choice(["fmp"]) # noqa: S311 + + data["crypto_data"] = openbb.obb.crypto.load( + symbol=symbol, provider=provider + ).results + return data["crypto_data"] + + +def get_data(menu: Literal["stocks", "crypto"]): + funcs = {"stocks": get_stocks_data, "crypto": get_crypto_data} + return funcs[menu]() + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": ""}, "stocks"), + ({"data": ""}, "crypto"), + ], +) +@pytest.mark.integration +def test_econometrics_corr(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.corr(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "stocks", + ), + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_ols(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.ols(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params", + [ + ({"data": "", "y_column": "close", "x_columns": ["date"]}), + ({"data": "", "y_column": "close", "x_columns": ["date"]}), + ], +) +@pytest.mark.integration +def test_econometrics_ols_summary(params, obb): + params = {p: v for p, v in params.items() if v} + + result = obb.econometrics.ols_summary(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + {"data": "", "y_column": "volume", "x_columns": ["close"]}, + "stocks", + ), + ( + {"data": "", "y_column": "volume", "x_columns": ["close"]}, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_dwat(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.dwat(**params) + assert result + assert isinstance(result, OBBject) + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "y_column": "volume", + "x_columns": ["close"], + "lags": "", + }, + "stocks", + ), + ( + { + "data": "", + "y_column": "volume", + "x_columns": ["close"], + "lags": "2", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_bgot(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.bgot(**params) + assert result + assert isinstance(result, OBBject) + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "columns": ["close", "volume"], + }, + "stocks", + ), + ( + { + "data": "", + "columns": ["close", "volume"], + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_coint(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.coint(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "y_column": "volume", + "x_column": "close", + "lag": "", + }, + "stocks", + ), + ( + { + "data": "", + "y_column": "volume", + "x_column": "close", + "lag": "2", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_granger(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.granger(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "column": "close", "regression": "c"}, "stocks"), + ( + {"data": "", "column": "volume", "regression": "ctt"}, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_unitroot(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.unitroot(**params) + assert result + assert isinstance(result, OBBject) + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "stocks", + ), + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_panelre(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.panelre(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "stocks", + ), + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_panelbols(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.panelbols(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "stocks", + ), + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_panelpols(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.panelpols(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "stocks", + ), + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_panelols(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.panelols(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "stocks", + ), + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_panelfd(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.panelfd(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "stocks", + ), + ( + {"data": "", "y_column": "close", "x_columns": ["date"]}, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_econometrics_panelfmac(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + + params["data"] = get_data(data_type) + + result = obb.econometrics.panelfmac(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 diff --git a/openbb_platform/extensions/economy/integration/test_economy_api.py b/openbb_platform/extensions/economy/integration/test_economy_api.py index 2c22d197ea5..2b0c38fecb0 100644 --- a/openbb_platform/extensions/economy/integration/test_economy_api.py +++ b/openbb_platform/extensions/economy/integration/test_economy_api.py @@ -1,20 +1,18 @@ +import base64 + import pytest import requests +from openbb_core.env import Env from openbb_provider.utils.helpers import get_querystring -def get_token(): - return requests.post( - "http://0.0.0.0:8000/api/v1/account/token", - data={"username": "openbb", "password": "openbb"}, - timeout=5, - ) - - @pytest.fixture(scope="session") def headers(): - access_token = get_token().json()["access_token"] - return {"Authorization": f"Bearer {access_token}"} + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"} @pytest.mark.parametrize( @@ -27,7 +25,7 @@ def test_economy_const(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/const?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -53,7 +51,7 @@ def test_economy_cpi(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/cpi?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -87,6 +85,7 @@ def test_economy_cpi(params, headers): "symbol": "^DJI", "start_date": "2023-01-01", "end_date": "2023-06-06", + "timeseries": 1, } ), ( @@ -96,6 +95,7 @@ def test_economy_cpi(params, headers): "symbol": "^DJI", "start_date": "2023-01-01", "end_date": "2023-06-06", + "timeseries": 1, } ), ( @@ -156,7 +156,7 @@ def test_economy_index(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/index?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -191,7 +191,7 @@ def test_economy_european_index(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/european_index?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -206,14 +206,19 @@ def test_economy_european_index_constituents(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/european_index_constituents?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( "params", - [({}), ({"europe": True, "provider": "cboe"})], + [ + ({}), + ({"europe": True, "provider": "cboe"}), + ({"provider": "fmp"}), + ({"provider": "yfinance"}), + ], ) @pytest.mark.integration def test_economy_available_indices(params, headers): @@ -221,7 +226,7 @@ def test_economy_available_indices(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/available_indices?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -236,7 +241,7 @@ def test_economy_risk(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/risk?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -261,7 +266,7 @@ def test_economy_index_search(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/index_search?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -276,7 +281,7 @@ def test_economy_index_snapshots(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/index_snapshots?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -291,7 +296,7 @@ def test_economy_cot_search(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/cot_search?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -321,7 +326,7 @@ def test_economy_cot(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/cot?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -346,7 +351,7 @@ def test_economy_sp500_multiples(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/sp500_multiples?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -370,6 +375,6 @@ def test_economy_fred_index(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/economy/fred_index?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 diff --git a/openbb_platform/extensions/economy/integration/test_economy_python.py b/openbb_platform/extensions/economy/integration/test_economy_python.py index 78b967dfffe..a5d8ab147fb 100644 --- a/openbb_platform/extensions/economy/integration/test_economy_python.py +++ b/openbb_platform/extensions/economy/integration/test_economy_python.py @@ -86,6 +86,7 @@ def test_economy_cpi(params): "symbol": "^DJI", "start_date": "2023-01-01", "end_date": "2023-06-06", + "timeseries": 1, } ), ( @@ -95,6 +96,7 @@ def test_economy_cpi(params): "symbol": "^DJI", "start_date": "2023-01-01", "end_date": "2023-06-06", + "timeseries": 1, } ), ( @@ -214,6 +216,8 @@ def test_economy_european_index_constituents(params): [ ({}), ({"europe": True, "provider": "cboe"}), + ({"provider": "fmp"}), + ({"provider": "yfinance"}), ], ) @pytest.mark.integration diff --git a/openbb_platform/extensions/fixedincome/integration/test_fixedincome_api.py b/openbb_platform/extensions/fixedincome/integration/test_fixedincome_api.py index 3a96790948c..146555c025d 100644 --- a/openbb_platform/extensions/fixedincome/integration/test_fixedincome_api.py +++ b/openbb_platform/extensions/fixedincome/integration/test_fixedincome_api.py @@ -1,20 +1,18 @@ +import base64 + import pytest import requests +from openbb_core.env import Env from openbb_provider.utils.helpers import get_querystring -def get_token(): - return requests.post( - "http://0.0.0.0:8000/api/v1/account/token", - data={"username": "openbb", "password": "openbb"}, - timeout=5, - ) - - @pytest.fixture(scope="session") def headers(): - access_token = get_token().json()["access_token"] - return {"Authorization": f"Bearer {access_token}"} + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"} @pytest.mark.parametrize( @@ -27,7 +25,7 @@ def test_fixedincome_treasury(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/fixedincome/treasury?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -42,7 +40,7 @@ def test_fixedincome_ycrv(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/fixedincome/ycrv?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -67,7 +65,7 @@ def test_fixedincome_sofr(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/fixedincome/sofr?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -92,7 +90,7 @@ def test_fixedincome_estr(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/fixedincome/estr?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -117,7 +115,7 @@ def test_fixedincome_sonia(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/fixedincome/sonia?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -142,7 +140,7 @@ def test_fixedincome_ameribor(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/fixedincome/ameribor?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -167,7 +165,7 @@ def test_fixedincome_fed(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/fixedincome/fed?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -182,7 +180,7 @@ def test_fixedincome_projections(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/fixedincome/projections?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -197,6 +195,6 @@ def test_fixedincome_iorb(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/fixedincome/iorb?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 diff --git a/openbb_platform/extensions/forex/integration/test_forex_api.py b/openbb_platform/extensions/forex/integration/test_forex_api.py index fa2880bbdb4..d54c76f7fb0 100644 --- a/openbb_platform/extensions/forex/integration/test_forex_api.py +++ b/openbb_platform/extensions/forex/integration/test_forex_api.py @@ -1,20 +1,18 @@ +import base64 + import pytest import requests +from openbb_core.env import Env from openbb_provider.utils.helpers import get_querystring -def get_token(): - return requests.post( - "http://0.0.0.0:8000/api/v1/account/token", - data={"username": "openbb", "password": "openbb"}, - timeout=5, - ) - - @pytest.fixture(scope="session") def headers(): - access_token = get_token().json()["access_token"] - return {"Authorization": f"Bearer {access_token}"} + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"} @pytest.mark.parametrize( @@ -33,6 +31,16 @@ def headers(): "provider": "polygon", } ), + ( + { + "provider": "fmp", + } + ), + ( + { + "provider": "intrinio", + } + ), ], ) @pytest.mark.integration @@ -41,7 +49,7 @@ def test_forex_pairs(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/forex/pairs?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -62,7 +70,7 @@ def test_forex_pairs(params, headers): ( { "interval": "1day", - "provider": "fmp", + "provider": "intrinio", "symbol": "EURUSD", "start_date": "2023-01-01", "end_date": "2023-06-06", @@ -122,6 +130,6 @@ def test_forex_load(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/forex/load?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 diff --git a/openbb_platform/extensions/forex/integration/test_forex_python.py b/openbb_platform/extensions/forex/integration/test_forex_python.py index 710858406ff..5d2beb30d5e 100644 --- a/openbb_platform/extensions/forex/integration/test_forex_python.py +++ b/openbb_platform/extensions/forex/integration/test_forex_python.py @@ -31,6 +31,16 @@ def obb(pytestconfig): "provider": "polygon", } ), + ( + { + "provider": "fmp", + } + ), + ( + { + "provider": "intrinio", + } + ), ], ) @pytest.mark.integration @@ -44,13 +54,7 @@ def test_forex_pairs(params, obb): @pytest.mark.parametrize( "params", [ - ( - { - "symbol": "EURUSD", - "start_date": "2023-01-01", - "end_date": "2023-06-06", - } - ), + ({"symbol": "EURUSD", "start_date": "2023-01-01", "end_date": "2023-06-06"}), ( { "interval": "1min", @@ -63,7 +67,7 @@ def test_forex_pairs(params, obb): ( { "interval": "1day", - "provider": "fmp", + "provider": "intrinio", "symbol": "EURUSD", "start_date": "2023-01-01", "end_date": "2023-06-06", diff --git a/openbb_platform/extensions/futures/integration/test_futures_api.py b/openbb_platform/extensions/futures/integration/test_futures_api.py index 2cd5d3638c6..2ad25d8f258 100644 --- a/openbb_platform/extensions/futures/integration/test_futures_api.py +++ b/openbb_platform/extensions/futures/integration/test_futures_api.py @@ -1,20 +1,18 @@ +import base64 + import pytest import requests +from openbb_core.env import Env from openbb_provider.utils.helpers import get_querystring -def get_token(): - return requests.post( - "http://0.0.0.0:8000/api/v1/account/token", - data={"username": "openbb", "password": "openbb"}, - timeout=5, - ) - - @pytest.fixture(scope="session") def headers(): - access_token = get_token().json()["access_token"] - return {"Authorization": f"Bearer {access_token}"} + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"} @pytest.mark.parametrize( @@ -50,14 +48,18 @@ def test_futures_load(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/futures/load?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( "params", - [({"symbol": "AAPL", "date": "2023-01-01"})], + [ + ({"symbol": "AAPL", "date": "2023-01-01"}), + ({"provider": "cboe", "symbol": "AAPL", "date": "2023-01-01"}), + ({"provider": "yfinance", "symbol": "AAPL", "date": "2023-01-01"}), + ], ) @pytest.mark.integration def test_futures_curve(params, headers): @@ -65,6 +67,6 @@ def test_futures_curve(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/futures/curve?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 diff --git a/openbb_platform/extensions/futures/integration/test_futures_python.py b/openbb_platform/extensions/futures/integration/test_futures_python.py index fef638d86f1..b04a4b6ef53 100644 --- a/openbb_platform/extensions/futures/integration/test_futures_python.py +++ b/openbb_platform/extensions/futures/integration/test_futures_python.py @@ -52,6 +52,8 @@ def test_futures_load(params, obb): "params", [ ({"symbol": "AAPL", "date": "2023-01-01"}), + ({"provider": "cboe", "symbol": "AAPL", "date": "2023-01-01"}), + ({"provider": "yfinance", "symbol": "AAPL", "date": "2023-01-01"}), ], ) @pytest.mark.integration diff --git a/openbb_platform/extensions/news/integration/test_news_api.py b/openbb_platform/extensions/news/integration/test_news_api.py index 6e6cecbbf11..d7bca14967d 100644 --- a/openbb_platform/extensions/news/integration/test_news_api.py +++ b/openbb_platform/extensions/news/integration/test_news_api.py @@ -1,20 +1,18 @@ +import base64 + import pytest import requests +from openbb_core.env import Env from openbb_provider.utils.helpers import get_querystring -def get_token(): - return requests.post( - "http://0.0.0.0:8000/api/v1/account/token", - data={"username": "openbb", "password": "openbb"}, - timeout=5, - ) - - @pytest.fixture(scope="session") def headers(): - access_token = get_token().json()["access_token"] - return {"Authorization": f"Bearer {access_token}"} + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"} @pytest.mark.parametrize( @@ -41,6 +39,18 @@ def headers(): "limit": 20, } ), + ( + { + "provider": "fmp", + "limit": 20, + } + ), + ( + { + "provider": "intrinio", + "limit": 20, + } + ), ], ) @pytest.mark.integration @@ -49,6 +59,6 @@ def test_news_globalnews(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/news/globalnews?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 diff --git a/openbb_platform/extensions/news/integration/test_news_python.py b/openbb_platform/extensions/news/integration/test_news_python.py index c94fc54a68f..6c7d15232a1 100644 --- a/openbb_platform/extensions/news/integration/test_news_python.py +++ b/openbb_platform/extensions/news/integration/test_news_python.py @@ -38,6 +38,18 @@ def obb(pytestconfig): "limit": 20, } ), + ( + { + "provider": "fmp", + "limit": 20, + } + ), + ( + { + "provider": "intrinio", + "limit": 20, + } + ), ], ) @pytest.mark.integration diff --git a/openbb_platform/extensions/qa/integration/test_qa_api.py b/openbb_platform/extensions/qa/integration/test_qa_api.py index e967a965c76..eda64698b2b 100644 --- a/openbb_platform/extensions/qa/integration/test_qa_api.py +++ b/openbb_platform/extensions/qa/integration/test_qa_api.py @@ -1,276 +1,317 @@ +import base64 import json import random -from typing import List +from typing import Literal import pytest import requests +from openbb_core.env import Env from openbb_provider.utils.helpers import get_querystring - -def get_token(): - return requests.post( - "http://0.0.0.0:8000/api/v1/account/token", - data={"username": "openbb", "password": "openbb"}, - timeout=5, - ) +data = {} -def auth_header(): - access_token = get_token().json()["access_token"] - return {"Authorization": f"Bearer {access_token}"} +def get_headers(): + if "headers" in data: + return data["headers"] + + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + data["headers"] = {"Authorization": f"Basic {base64_bytes.decode('ascii')}"} + return data["headers"] -@pytest.fixture(scope="session") -def headers(): - h = {} - auth = auth_header() - h.update(auth) - return h +def get_data(menu: Literal["stocks", "crypto"]): + funcs = {"stocks": get_stocks_data, "crypto": get_crypto_data} + return funcs[menu]() -def get_random_data(menu: str, symbols: List[str], providers: List[str]): +def request_data(menu: str, symbol: str, provider: str): """Randomly pick a symbol and a provider and get data from the selected menu.""" - symbol = random.choice(symbols) # noqa: S311 - provider = random.choice(providers) # noqa: S311 - url = f"http://0.0.0.0:8000/api/v1/{menu}/load?symbol={symbol}&provider={provider}" - result = requests.get(url, headers=auth_header(), timeout=5) + result = requests.get(url, headers=get_headers(), timeout=10) return result.json()["results"] -symbols = ["AAPL", "NVDA", "MSFT", "TSLA", "AMZN", "GOOG", "FB", "BABA", "TSM", "V"] -providers = ["fmp", "intrinio", "polygon", "yfinance"] -stocks_data = get_random_data("stocks", symbols=symbols, providers=providers) +def get_stocks_data(): + if "stocks_data" in data: + return data["stocks_data"] -# TODO : add more crypto providers and symbols -symbols_crypto = ["BTC"] -providers_crypto = ["fmp"] -crypto_data = get_random_data( - menu="crypto", symbols=symbols_crypto, providers=providers_crypto -) + symbol = random.choice(["AAPL", "NVDA", "MSFT", "TSLA", "AMZN", "V"]) # noqa: S311 + provider = random.choice(["fmp", "intrinio", "polygon", "yfinance"]) # noqa: S311 + + data["stocks_data"] = request_data("stocks", symbol=symbol, provider=provider) + return data["stocks_data"] + + +def get_crypto_data(): + if "crypto_data" in data: + return data["crypto_data"] + + # TODO : add more crypto providers and symbols + symbol = random.choice(["BTC"]) # noqa: S311 + provider = random.choice(["fmp"]) # noqa: S311 + + data["crypto_data"] = request_data( + menu="crypto", + symbol=symbol, + provider=provider, + ) + return data["crypto_data"] @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "target": "close"}), - ({"data": crypto_data, "target": "high"}), + ({"data": "", "target": "close"}, "stocks"), + ({"data": "", "target": "high"}, "crypto"), ], ) @pytest.mark.integration -def test_qa_normality(params, headers): +def test_qa_normality(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/qa/normality?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "target": "close"}), - ({"data": crypto_data, "target": "high"}), + ({"data": "", "target": "close"}, "stocks"), + ({"data": "", "target": "high"}, "crypto"), ], ) @pytest.mark.integration -def test_qa_capm(params, headers): +def test_qa_capm(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/qa/capm?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "target": "close", "threshold_start": "", "threshold_end": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "target": "high", "threshold_start": "0.1", "threshold_end": "1.6", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_qa_om(params, headers): +def test_qa_om(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/qa/om?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "target": "close", "window": "5"}), - ({"data": crypto_data, "target": "high", "window": "10"}), + ({"data": "", "target": "close", "window": "5"}, "stocks"), + ({"data": "", "target": "high", "window": "10"}, "crypto"), ], ) @pytest.mark.integration -def test_qa_kurtosis(params, headers): +def test_qa_kurtosis(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/qa/kurtosis?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", - [ - ({"data": stocks_data, "target": "close", "fuller_reg": "c", "kpss_reg": "ct"}), - ({"data": stocks_data, "target": "high", "fuller_reg": "ct", "kpss_reg": "c"}), - ], -) -@pytest.mark.integration -def test_qa_unitroot(params, headers): - params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) - - query_str = get_querystring(params, []) - url = f"http://0.0.0.0:8000/api/v1/qa/unitroot?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) - assert isinstance(result, requests.Response) - assert result.status_code == 200 - - -@pytest.mark.parametrize( - "params", - [ - ({"data": stocks_data, "target": "close", "rfr": "", "window": ""}), - ({"data": crypto_data, "target": "high", "rfr": "0.5", "window": "250"}), - ], -) -@pytest.mark.integration -def test_qa_sh(params, headers): - params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) - - query_str = get_querystring(params, []) - url = f"http://0.0.0.0:8000/api/v1/qa/sh?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) - assert isinstance(result, requests.Response) - assert result.status_code == 200 - - -@pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", + "target": "close", + "fuller_reg": "c", + "kpss_reg": "ct", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "fuller_reg": "ct", + "kpss_reg": "c", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_qa_unitroot(params, data_type): + params = {p: v for p, v in params.items() if v} + data = json.dumps(get_data(data_type)) + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/qa/unitroot?{query_str}" + result = requests.post(url, headers=get_headers(), timeout=10, data=data) + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "rfr": "", "window": ""}, "stocks"), + ({"data": "", "target": "high", "rfr": "0.5", "window": "250"}, "crypto"), + ], +) +@pytest.mark.integration +def test_qa_sh(params, data_type): + params = {p: v for p, v in params.items() if v} + data = json.dumps(get_data(data_type)) + + query_str = get_querystring(params, []) + url = f"http://0.0.0.0:8000/api/v1/qa/sh?{query_str}" + result = requests.post(url, headers=get_headers(), timeout=10, data=data) + assert isinstance(result, requests.Response) + assert result.status_code == 200 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", "target": "close", "target_return": "", "window": "", "adjusted": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "target": "close", "target_return": "0.5", "window": "275", "adjusted": "true", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_qa_so(params, headers): +def test_qa_so(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/qa/so?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "target": "close", "window": "220"}), + ({"data": "", "target": "close", "window": "220"}, "stocks"), ], ) @pytest.mark.integration -def test_qa_skew(params, headers): +def test_qa_skew(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/qa/skew?{query_str}" - result = requests.post(url, headers=headers, timeout=60, data=data) + result = requests.post(url, headers=get_headers(), timeout=60, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "target": "close", "window": "10", "quantile_pct": ""}), ( { - "data": crypto_data, + "data": "", + "target": "close", + "window": "10", + "quantile_pct": "", + }, + "stocks", + ), + ( + { + "data": "", "target": "high", "window": "50", "quantile_pct": "0.6", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_qa_quantile(params, headers): +def test_qa_quantile(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/qa/quantile?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "target": "close"}), - ({"data": crypto_data, "target": "high"}), + ({"data": "", "target": "close"}, "stocks"), + ({"data": "", "target": "high"}, "crypto"), ], ) @pytest.mark.integration -def test_qa_summary(params, headers): +def test_qa_summary(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/qa/summary?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 diff --git a/openbb_platform/extensions/qa/integration/test_qa_python.py b/openbb_platform/extensions/qa/integration/test_qa_python.py new file mode 100644 index 00000000000..756173e03e9 --- /dev/null +++ b/openbb_platform/extensions/qa/integration/test_qa_python.py @@ -0,0 +1,292 @@ +"""Test qa extension.""" +import random +from typing import Literal + +import pytest +from openbb_core.app.model.obbject import OBBject + + +@pytest.fixture(scope="session") +def obb(pytestconfig): + """Fixture to setup obb.""" + if pytestconfig.getoption("markexpr") != "not integration": + import openbb + + return openbb.obb + + +data = {} + + +def get_stocks_data(): + import openbb # pylint:disable=import-outside-toplevel + + if "stocks_data" in data: + return data["stocks_data"] + + symbol = random.choice(["AAPL", "NVDA", "MSFT", "TSLA", "AMZN", "V"]) # noqa: S311 + provider = random.choice(["fmp", "intrinio", "polygon", "yfinance"]) # noqa: S311 + + data["stocks_data"] = openbb.obb.stocks.load( + symbol=symbol, provider=provider + ).results + return data["stocks_data"] + + +def get_crypto_data(): + import openbb # pylint:disable=import-outside-toplevel + + if "crypto_data" in data: + return data["crypto_data"] + + # TODO : add more crypto providers and symbols + symbol = random.choice(["BTC"]) # noqa: S311 + provider = random.choice(["fmp"]) # noqa: S311 + + data["crypto_data"] = openbb.obb.crypto.load( + symbol=symbol, provider=provider + ).results + return data["crypto_data"] + + +def get_data(menu: Literal["stocks", "crypto"]): + funcs = {"stocks": get_stocks_data, "crypto": get_crypto_data} + return funcs[menu]() + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "target": "close"}, "stocks"), + ({"data": "", "target": "high"}, "crypto"), + ], +) +@pytest.mark.integration +def test_qa_normality(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.qa.normality(**params) + assert result + assert isinstance(result, OBBject) + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "target": "close"}, "stocks"), + ({"data": "", "target": "high"}, "crypto"), + ], +) +@pytest.mark.integration +def test_qa_capm(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.qa.capm(**params) + assert result + assert isinstance(result, OBBject) + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "close", + "threshold_start": "", + "threshold_end": "", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "threshold_start": "0.1", + "threshold_end": "1.6", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_qa_om(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.qa.om(**params) + assert result + assert isinstance(result, OBBject) + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "window": "5"}, "stocks"), + ({"data": "", "target": "high", "window": "10"}, "crypto"), + ], +) +@pytest.mark.integration +def test_qa_kurtosis(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.qa.kurtosis(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "close", + "fuller_reg": "c", + "kpss_reg": "ct", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "fuller_reg": "ct", + "kpss_reg": "c", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_qa_unitroot(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.qa.unitroot(**params) + assert result + assert isinstance(result, OBBject) + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "rfr": "", "window": ""}, "stocks"), + ({"data": "", "target": "high", "rfr": "0.5", "window": "250"}, "crypto"), + ], +) +@pytest.mark.integration +def test_qa_sh(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.qa.sh(**params) + assert result + assert isinstance(result, OBBject) + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "close", + "target_return": "", + "window": "", + "adjusted": "", + }, + "stocks", + ), + ( + { + "data": "", + "target": "close", + "target_return": "0.5", + "window": "275", + "adjusted": "true", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_qa_so(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.qa.so(**params) + assert result + assert isinstance(result, OBBject) + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "target": "close", "window": "220"}, "stocks"), + ], +) +@pytest.mark.integration +def test_qa_skew(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.qa.skew(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "close", + "window": "10", + "quantile_pct": "", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "window": "50", + "quantile_pct": "0.6", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_qa_quantile(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.qa.quantile(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "target": "close"}, "stocks"), + ({"data": "", "target": "high"}, "crypto"), + ], +) +@pytest.mark.integration +def test_qa_summary(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.qa.summary(**params) + assert result + assert isinstance(result, OBBject) diff --git a/openbb_platform/extensions/stocks/integration/test_stocks_api.py b/openbb_platform/extensions/stocks/integration/test_stocks_api.py index e497887e5cc..678b6c2f2df 100644 --- a/openbb_platform/extensions/stocks/integration/test_stocks_api.py +++ b/openbb_platform/extensions/stocks/integration/test_stocks_api.py @@ -1,20 +1,18 @@ +import base64 + import pytest import requests +from openbb_core.env import Env from openbb_provider.utils.helpers import get_querystring -def get_token(): - return requests.post( - "http://0.0.0.0:8000/api/v1/account/token", - data={"username": "openbb", "password": "openbb"}, - timeout=5, - ) - - @pytest.fixture(scope="session") def headers(): - access_token = get_token().json()["access_token"] - return {"Authorization": f"Bearer {access_token}"} + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"} @pytest.mark.parametrize( @@ -33,9 +31,9 @@ def headers(): ), ( { - "company_name": "Apple Inc", - "company_name_search": "AAPL", - "sic": "35719904", + "company_name": "Apple Inc.", + "company_name_search": "Apple Inc.", + "sic": "3571", "include_sources": True, "order": "asc", "sort": "filing_date", @@ -43,6 +41,33 @@ def headers(): "symbol": "AAPL", "period": "annual", "limit": 12, + "filing_date": "2022-10-27", + "filing_date_lt": "2022-11-01", + "filing_date_lte": "2022-11-01", + "filing_date_gt": "2022-10-10", + "filing_date_gte": "2022-10-10", + "period_of_report_date": "2022-09-24", + "period_of_report_date_lt": "2022-11-01", + "period_of_report_date_lte": "2022-11-01", + "period_of_report_date_gt": "2022-10-10", + "period_of_report_date_gte": "2022-10-10", + } + ), + ( + { + "symbol": "AAPL", + "period": "annual", + "limit": 12, + "provider": "fmp", + "cik": "0000320193", + } + ), + ( + { + "symbol": "AAPL", + "period": "annual", + "limit": 12, + "provider": "yfinance", } ), ], @@ -53,7 +78,7 @@ def test_stocks_fa_balance(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/balance?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -68,7 +93,7 @@ def test_stocks_fa_balance_growth(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/balance_growth?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -83,7 +108,7 @@ def test_stocks_fa_cal(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/cal?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -114,6 +139,33 @@ def test_stocks_fa_cal(params, headers): "symbol": "AAPL", "period": "annual", "limit": 12, + "filing_date": "2023-09-15", + "filing_date_lt": "2023-09-20", + "filing_date_lte": "2023-09-20", + "filing_date_gt": "2023-09-10", + "filing_date_gte": "2023-09-10", + "period_of_report_date": "2023-10-15", + "period_of_report_date_lt": "2023-10-20", + "period_of_report_date_lte": "2023-10-20", + "period_of_report_date_gt": "2023-10-10", + "period_of_report_date_gte": "2023-10-10", + } + ), + ( + { + "symbol": "AAPL", + "period": "annual", + "limit": 12, + "provider": "fmp", + "cik": "0000320193", + } + ), + ( + { + "symbol": "AAPL", + "period": "annual", + "limit": 12, + "provider": "yfinance", } ), ], @@ -124,7 +176,7 @@ def test_stocks_fa_cash(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/cash?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -139,7 +191,7 @@ def test_stocks_fa_cash_growth(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/cash_growth?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -154,7 +206,7 @@ def test_stocks_fa_comp(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/comp?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -169,7 +221,7 @@ def test_stocks_fa_comsplit(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/comsplit?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -184,7 +236,7 @@ def test_stocks_fa_divs(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/divs?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -199,7 +251,7 @@ def test_stocks_fa_earning(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/earning?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -214,7 +266,7 @@ def test_stocks_fa_emp(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/emp?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -229,7 +281,7 @@ def test_stocks_fa_est(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/est?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -250,9 +302,9 @@ def test_stocks_fa_est(params, headers): ), ( { - "company_name": "Apple Inc", - "company_name_search": "AAPL", - "sic": "35719904", + "company_name": "Apple Inc.", + "company_name_search": "Apple Inc.", + "sic": "3571", "include_sources": True, "order": "asc", "sort": "filing_date", @@ -260,6 +312,33 @@ def test_stocks_fa_est(params, headers): "symbol": "AAPL", "period": "annual", "limit": 12, + "filing_date": "2022-10-27", + "filing_date_lt": "2022-11-01", + "filing_date_lte": "2022-11-01", + "filing_date_gt": "2022-10-10", + "filing_date_gte": "2022-10-10", + "period_of_report_date": "2022-09-24", + "period_of_report_date_lt": "2022-11-01", + "period_of_report_date_lte": "2022-11-01", + "period_of_report_date_gt": "2022-10-10", + "period_of_report_date_gte": "2022-10-10", + } + ), + ( + { + "symbol": "AAPL", + "period": "annual", + "limit": 12, + "provider": "fmp", + "cik": "0000320193", + } + ), + ( + { + "symbol": "AAPL", + "period": "annual", + "limit": 12, + "provider": "yfinance", } ), ], @@ -270,7 +349,7 @@ def test_stocks_fa_income(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/income?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -285,7 +364,7 @@ def test_stocks_fa_income_growth(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/income_growth?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -310,7 +389,7 @@ def test_stocks_fa_ins(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/ins?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -325,7 +404,7 @@ def test_stocks_fa_ins_own(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/ins_own?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -340,7 +419,7 @@ def test_stocks_fa_metrics(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/metrics?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -355,7 +434,7 @@ def test_stocks_fa_mgmt(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/mgmt?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -370,7 +449,7 @@ def test_stocks_fa_overview(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/overview?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -385,7 +464,7 @@ def test_stocks_fa_own(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/own?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -400,7 +479,7 @@ def test_stocks_fa_pt(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/pt?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -415,7 +494,7 @@ def test_stocks_fa_pta(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/pta?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -430,7 +509,7 @@ def test_stocks_fa_ratios(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/ratios?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -445,7 +524,7 @@ def test_stocks_fa_revgeo(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/revgeo?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -460,7 +539,7 @@ def test_stocks_fa_revseg(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/revseg?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -475,7 +554,7 @@ def test_stocks_fa_filings(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/filings?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -490,7 +569,7 @@ def test_stocks_fa_shrs(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/shrs?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -505,7 +584,7 @@ def test_stocks_fa_split(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/split?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -520,7 +599,7 @@ def test_stocks_fa_transcript(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/fa/transcript?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -535,7 +614,7 @@ def test_stocks_ca_peers(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/ca/peers?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -545,6 +624,7 @@ def test_stocks_ca_peers(params, headers): [ ({"symbol": "AAPL"}), ({"date": "2023-01-01", "provider": "intrinio", "symbol": "AAPL"}), + ({"provider": "cboe", "symbol": "AAPL"}), ], ) @pytest.mark.integration @@ -553,7 +633,7 @@ def test_stocks_options_chains(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/options/chains?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -574,6 +654,7 @@ def test_stocks_options_chains(params, headers): "symbol": "AAPL", "start_date": "2023-01-01", "end_date": "2023-06-06", + "function_": "TIME_SERIES_DAILY", } ), ( @@ -601,6 +682,7 @@ def test_stocks_options_chains(params, headers): "symbol": "AAPL", "start_date": "2023-01-01", "end_date": "2023-01-02", + "timeseries": 1, } ), ( @@ -610,16 +692,20 @@ def test_stocks_options_chains(params, headers): "symbol": "AAPL", "start_date": "2023-01-01", "end_date": "2023-06-06", + "timeseries": 1, } ), ( { "timezone": "UTC", + "source": "realtime", "interval_size": "30m", "provider": "intrinio", "symbol": "AAPL", "start_date": "2023-01-01", "end_date": "2023-01-02", + "start_time": "00:00:00", + "end_time": "23:59:59", } ), ( @@ -631,6 +717,8 @@ def test_stocks_options_chains(params, headers): "symbol": "AAPL", "start_date": "2023-01-01", "end_date": "2023-01-02", + "start_time": "00:00:00", + "end_time": "23:59:59", } ), ( @@ -707,7 +795,7 @@ def test_stocks_load(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/load?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -746,6 +834,27 @@ def test_stocks_load(params, headers): "limit": 20, } ), + ( + { + "provider": "fmp", + "symbols": "AAPL", + "limit": 20, + } + ), + ( + { + "provider": "yfinance", + "symbols": "AAPL", + "limit": 20, + } + ), + ( + { + "provider": "intrinio", + "symbols": "AAPL", + "limit": 20, + } + ), ], ) @pytest.mark.integration @@ -754,7 +863,7 @@ def test_stocks_news(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/news?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -769,7 +878,7 @@ def test_stocks_multiples(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/multiples?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -784,7 +893,7 @@ def test_stocks_search(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/search?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -794,6 +903,7 @@ def test_stocks_search(params, headers): [ ({"symbol": "AAPL"}), ({"source": "iex", "provider": "intrinio", "symbol": "AAPL"}), + ({"symbol": "AAPL", "provider": "fmp"}), ], ) @pytest.mark.integration @@ -802,7 +912,7 @@ def test_stocks_quote(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/quote?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 @@ -817,6 +927,6 @@ def test_stocks_info(params, headers): query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/stocks/info?{query_str}" - result = requests.get(url, headers=headers, timeout=5) + result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 diff --git a/openbb_platform/extensions/stocks/integration/test_stocks_python.py b/openbb_platform/extensions/stocks/integration/test_stocks_python.py index 83157bdfac7..31008e4ed9b 100644 --- a/openbb_platform/extensions/stocks/integration/test_stocks_python.py +++ b/openbb_platform/extensions/stocks/integration/test_stocks_python.py @@ -29,9 +29,9 @@ def obb(pytestconfig): ), ( { - "company_name": "Apple Inc", - "company_name_search": "AAPL", - "sic": "35719904", + "company_name": "Apple Inc.", + "company_name_search": "Apple Inc.", + "sic": "3571", "include_sources": True, "order": "asc", "sort": "filing_date", @@ -39,6 +39,33 @@ def obb(pytestconfig): "symbol": "AAPL", "period": "annual", "limit": 12, + "filing_date": "2022-10-27", + "filing_date_lt": "2022-11-01", + "filing_date_lte": "2022-11-01", + "filing_date_gt": "2022-10-10", + "filing_date_gte": "2022-10-10", + "period_of_report_date": "2022-09-24", + "period_of_report_date_lt": "2022-11-01", + "period_of_report_date_lte": "2022-11-01", + "period_of_report_date_gt": "2022-10-10", + "period_of_report_date_gte": "2022-10-10", + } + ), + ( + { + "symbol": "AAPL", + "period": "annual", + "limit": 12, + "provider": "fmp", + "cik": "0000320193", + } + ), + ( + { + "symbol": "AAPL", + "period": "annual", + "limit": 12, + "provider": "yfinance", } ), ], @@ -100,9 +127,9 @@ def test_stocks_fa_cal(params, obb): ), ( { - "company_name": "Apple Inc", - "company_name_search": "AAPL", - "sic": "35719904", + "company_name": "Apple Inc.", + "company_name_search": "Apple Inc.", + "sic": "3571", "include_sources": True, "order": "asc", "sort": "filing_date", @@ -110,6 +137,33 @@ def test_stocks_fa_cal(params, obb): "symbol": "AAPL", "period": "annual", "limit": 12, + "filing_date": "2022-10-27", + "filing_date_lt": "2022-11-01", + "filing_date_lte": "2022-11-01", + "filing_date_gt": "2022-10-10", + "filing_date_gte": "2022-10-10", + "period_of_report_date": "2022-09-24", + "period_of_report_date_lt": "2022-11-01", + "period_of_report_date_lte": "2022-11-01", + "period_of_report_date_gt": "2022-10-10", + "period_of_report_date_gte": "2022-10-10", + } + ), + ( + { + "symbol": "AAPL", + "period": "annual", + "limit": 12, + "provider": "fmp", + "cik": "0000320193", + } + ), + ( + { + "symbol": "AAPL", + "period": "annual", + "limit": 12, + "provider": "yfinance", } ), ], @@ -241,9 +295,9 @@ def test_stocks_fa_est(params, obb): ), ( { - "company_name": "Apple Inc", - "company_name_search": "AAPL", - "sic": "35719904", + "company_name": "Apple Inc.", + "company_name_search": "Apple Inc.", + "sic": "3571", "include_sources": True, "order": "asc", "sort": "filing_date", @@ -251,6 +305,33 @@ def test_stocks_fa_est(params, obb): "symbol": "AAPL", "period": "annual", "limit": 12, + "filing_date": "2022-10-27", + "filing_date_lt": "2022-11-01", + "filing_date_lte": "2022-11-01", + "filing_date_gt": "2022-10-10", + "filing_date_gte": "2022-10-10", + "period_of_report_date": "2022-09-24", + "period_of_report_date_lt": "2022-11-01", + "period_of_report_date_lte": "2022-11-01", + "period_of_report_date_gt": "2022-10-10", + "period_of_report_date_gte": "2022-10-10", + } + ), + ( + { + "provider": "fmp", + "symbol": "AAPL", + "limit": 12, + "period": "annual", + "cik": "0000320193", + } + ), + ( + { + "provider": "yfinance", + "symbol": "AAPL", + "limit": 12, + "period": "annual", } ), ], @@ -265,9 +346,7 @@ def test_stocks_fa_income(params, obb): @pytest.mark.parametrize( "params", - [ - ({"symbol": "AAPL", "limit": 10, "period": "annual"}), - ], + [({"symbol": "AAPL", "limit": 10, "period": "annual"})], ) @pytest.mark.integration def test_stocks_fa_income_growth(params, obb): @@ -521,6 +600,7 @@ def test_stocks_ca_peers(params, obb): [ ({"symbol": "AAPL"}), ({"date": "2023-01-01", "provider": "intrinio", "symbol": "AAPL"}), + ({"provider": "cboe", "symbol": "AAPL"}), ], ) @pytest.mark.integration @@ -553,6 +633,7 @@ def test_stocks_options_chains(params, obb): "symbol": "AAPL", "start_date": "2023-01-01", "end_date": "2023-06-06", + "function_": "TIME_SERIES_DAILY", } ), ( @@ -580,6 +661,7 @@ def test_stocks_options_chains(params, obb): "symbol": "AAPL", "start_date": "2023-01-01", "end_date": "2023-01-02", + "timeseries": 1, } ), ( @@ -589,16 +671,20 @@ def test_stocks_options_chains(params, obb): "symbol": "AAPL", "start_date": "2023-01-01", "end_date": "2023-06-06", + "timeseries": 1, } ), ( { "timezone": "UTC", + "source": "realtime", "interval_size": "30m", "provider": "intrinio", "symbol": "AAPL", "start_date": "2023-01-01", "end_date": "2023-01-02", + "start_time": "00:00:00", + "end_time": "23:59:59", } ), ( @@ -610,6 +696,8 @@ def test_stocks_options_chains(params, obb): "symbol": "AAPL", "start_date": "2023-01-01", "end_date": "2023-01-02", + "start_time": "00:00:00", + "end_time": "23:59:59", } ), ( @@ -722,6 +810,27 @@ def test_stocks_load(params, obb): "limit": 20, } ), + ( + { + "provider": "fmp", + "symbols": "AAPL", + "limit": 20, + } + ), + ( + { + "provider": "yfinance", + "symbols": "AAPL", + "limit": 20, + } + ), + ( + { + "provider": "intrinio", + "symbols": "AAPL", + "limit": 20, + } + ), ], ) @pytest.mark.integration @@ -764,6 +873,7 @@ def test_stocks_search(params, obb): [ ({"symbol": "AAPL"}), ({"source": "iex", "provider": "intrinio", "symbol": "AAPL"}), + ({"symbol": "AAPL", "provider": "fmp"}), ], ) @pytest.mark.integration diff --git a/openbb_platform/extensions/ta/integration/test_ta_api.py b/openbb_platform/extensions/ta/integration/test_ta_api.py index 471dcb1d70f..68fe9f143af 100644 --- a/openbb_platform/extensions/ta/integration/test_ta_api.py +++ b/openbb_platform/extensions/ta/integration/test_ta_api.py @@ -1,813 +1,926 @@ +import base64 import json import random -from typing import List +from typing import Literal import pytest import requests +from openbb_core.env import Env from openbb_provider.utils.helpers import get_querystring - -def get_token(): - return requests.post( - "http://0.0.0.0:8000/api/v1/account/token", - data={"username": "openbb", "password": "openbb"}, - timeout=5, - ) +data = {} -def auth_header(): - access_token = get_token().json()["access_token"] - return {"Authorization": f"Bearer {access_token}"} +def get_headers(): + if "headers" in data: + return data["headers"] + + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + data["headers"] = {"Authorization": f"Basic {base64_bytes.decode('ascii')}"} + return data["headers"] -@pytest.fixture(scope="session") -def headers(): - h = {} - auth = auth_header() - h.update(auth) - return h +def get_data(menu: Literal["stocks", "crypto"]): + funcs = {"stocks": get_stocks_data, "crypto": get_crypto_data} + return funcs[menu]() -def get_random_data(menu: str, symbols: List[str], providers: List[str]): +def request_data(menu: str, symbol: str, provider: str): """Randomly pick a symbol and a provider and get data from the selected menu.""" - symbol = random.choice(symbols) # noqa: S311 - provider = random.choice(providers) # noqa: S311 - url = f"http://0.0.0.0:8000/api/v1/{menu}/load?symbol={symbol}&provider={provider}" - result = requests.get(url, headers=auth_header(), timeout=5) + result = requests.get(url, headers=get_headers(), timeout=10) return result.json()["results"] -symbols = ["AAPL", "NVDA", "MSFT", "TSLA", "AMZN", "GOOG", "FB", "BABA", "TSM", "V"] -providers = ["fmp", "intrinio", "polygon", "yfinance"] -stocks_data = get_random_data("stocks", symbols=symbols, providers=providers) +def get_stocks_data(): + if "stocks_data" in data: + return data["stocks_data"] -# TODO : add more crypto providers and symbols -symbols_crypto = ["BTC"] -providers_crypto = ["fmp"] -crypto_data = get_random_data( - menu="crypto", symbols=symbols_crypto, providers=providers_crypto -) + symbol = random.choice(["AAPL", "NVDA", "MSFT", "TSLA", "AMZN", "V"]) # noqa: S311 + provider = random.choice(["fmp", "intrinio", "polygon", "yfinance"]) # noqa: S311 + + data["stocks_data"] = request_data("stocks", symbol=symbol, provider=provider) + return data["stocks_data"] + + +def get_crypto_data(): + if "crypto_data" in data: + return data["crypto_data"] + + # TODO : add more crypto providers and symbols + symbol = random.choice(["BTC"]) # noqa: S311 + provider = random.choice(["fmp"]) # noqa: S311 + + data["crypto_data"] = request_data( + menu="crypto", + symbol=symbol, + provider=provider, + ) + return data["crypto_data"] @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "index": "", "length": "", "mamode": "", "drift": "", "offset": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "index": "date", "length": "15", "mamode": "rma", "drift": "2", "offset": "1", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_atr(params, headers): +def test_ta_atr(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/atr?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "index": "", "close_column": "", "period": "", "start_date": "", "end_date": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "index": "date", "close_column": "adj_close", "period": "125", "start_date": "", "end_date": "", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_fib(params, headers): +def test_ta_fib(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/fib?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "index": "", "offset": ""}), - ({"data": crypto_data, "index": "date", "offset": "1"}), + ({"data": "", "index": "", "offset": ""}, "stocks"), + ({"data": "", "index": "date", "offset": "1"}, "crypto"), ], ) @pytest.mark.integration -def test_ta_obv(params, headers): +def test_ta_obv(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/obv?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "index": "", "length": "", "signal": ""}), - ({"data": crypto_data, "index": "date", "length": "15", "signal": "2"}), + ({"data": "", "index": "", "length": "", "signal": ""}, "stocks"), + ({"data": "", "index": "date", "length": "15", "signal": "2"}, "crypto"), ], ) @pytest.mark.integration -def test_ta_fisher(params, headers): +def test_ta_fisher(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/fisher?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "index": "", "fast": "", "slow": "", "offset": ""}), ( { - "data": crypto_data, + "data": "", + "index": "", + "fast": "", + "slow": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", "index": "date", "fast": "5", "slow": "15", "offset": "2", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_adosc(params, headers): +def test_ta_adosc(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/adosc?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "target": "", "index": "", "length": "", "std": "", "mamode": "", "offset": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "target": "high", "index": "date", "length": "55", "std": "3", "mamode": "wma", "offset": "1", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_bbands(params, headers): +def test_ta_bbands(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/bbands?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "target": "", "index": "", "length": "", "offset": ""}), ( { - "data": crypto_data, + "data": "", + "target": "", + "index": "", + "length": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", "target": "high", "index": "date", "length": "55", "offset": "5", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_zlma(params, headers): +def test_ta_zlma(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/zlma?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "index": "", "length": "", "scalar": ""}), + ({"data": "", "index": "", "length": "", "scalar": ""}, "stocks"), ( { - "data": crypto_data, + "data": "", "index": "date", "length": "30", "scalar": "110", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_aroon(params, headers): +def test_ta_aroon(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/aroon?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "target": "", "index": "", "length": "", "offset": ""}), ( { - "data": crypto_data, + "data": "", + "target": "", + "index": "", + "length": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", "target": "high", "index": "date", "length": "55", "offset": "2", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_sma(params, headers): +def test_ta_sma(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/sma?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "index": "", "target": "", "show_all": "", "asint": "", "offset": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "index": "date", "target": "high", "show_all": "true", "asint": "true", "offset": "5", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_demark(params, headers): +def test_ta_demark(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/demark?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "index": "", "anchor": "", "offset": ""}), - ({"data": crypto_data, "index": "date", "anchor": "W", "offset": "5"}), + ({"data": "", "index": "", "anchor": "", "offset": ""}, "stocks"), + ({"data": "", "index": "date", "anchor": "W", "offset": "5"}, "crypto"), ], ) @pytest.mark.integration -def test_ta_vwap(params, headers): +def test_ta_vwap(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/vwap?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "target": "", "index": "", "fast": "", "slow": "", "signal": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "target": "high", "index": "date", "fast": "10", "slow": "30", "signal": "10", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_macd(params, headers): +def test_ta_macd(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/macd?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "target": "", "index": "", "length": "", "offset": ""}), ( { - "data": crypto_data, + "data": "", + "target": "", + "index": "", + "length": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", "target": "high", "index": "date", "length": "55", "offset": "2", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_hma(params, headers): +def test_ta_hma(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/hma?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "index": "", "lower_length": "", "upper_length": "", "offset": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "index": "date", "lower_length": "30", "upper_length": "40", "offset": "5", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_donchian(params, headers): +def test_ta_donchian(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/donchian?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "index": "", "conversion": "", "base": "", "lagging": "", "offset": "", "lookahead": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "index": "date", "conversion": "10", "base": "30", "lagging": "50", "offset": "30", "lookahead": "true", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_ichimoku(params, headers): +def test_ta_ichimoku(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/ichimoku?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "index": "", "target": "", "period": ""}), - ({"data": crypto_data, "index": "date", "target": "close", "period": "95"}), + ({"data": "", "index": "", "target": "", "period": ""}, "stocks"), + ( + { + "data": "", + "index": "date", + "target": "close", + "period": "95", + }, + "crypto", + ), ], ) @pytest.mark.integration -def test_ta_clenow(params, headers): +def test_ta_clenow(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/clenow?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "index": "", "offset": ""}), - ({"data": crypto_data, "index": "date", "offset": "5"}), + ({"data": "", "index": "", "offset": ""}, "stocks"), + ({"data": "", "index": "date", "offset": "5"}, "crypto"), ], ) @pytest.mark.integration -def test_ta_ad(params, headers): +def test_ta_ad(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/ad?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "index": "", "length": "", "scalar": "", "drift": ""}), ( { - "data": crypto_data, + "data": "", + "index": "", + "length": "", + "scalar": "", + "drift": "", + }, + "stocks", + ), + ( + { + "data": "", "index": "date", "length": "60", "scalar": "90.0", "drift": "2", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_adx(params, headers): +def test_ta_adx(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/adx?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "target": "", "index": "", "length": "", "offset": ""}), ( { - "data": crypto_data, + "data": "", + "target": "", + "index": "", + "length": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", "target": "high", "index": "date", "length": "60", "offset": "10", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_wma(params, headers): +def test_ta_wma(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/wma?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "index": "", "length": "", "scalar": ""}), - ({"data": crypto_data, "index": "date", "length": "16", "scalar": "0.02"}), + ({"data": "", "index": "", "length": "", "scalar": ""}, "stocks"), + ( + { + "data": "", + "index": "date", + "length": "16", + "scalar": "0.02", + }, + "crypto", + ), ], ) @pytest.mark.integration -def test_ta_cci(params, headers): +def test_ta_cci(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/cci?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "target": "", "index": "", "length": "", "scalar": "", "drift": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "target": "high", "index": "date", "length": "16", "scalar": "90.0", "drift": "2", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_rsi(params, headers): +def test_ta_rsi(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/rsi?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "index": "", "fast_k_period": "", "slow_d_period": "", "slow_k_period": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "index": "date", "fast_k_period": "12", "slow_d_period": "2", "slow_k_period": "2", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_stoch(params, headers): +def test_ta_stoch(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/stoch?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "index": "", "length": "", "scalar": "", "mamode": "", "offset": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "index": "date", "length": "22", "scalar": "24", "mamode": "sma", "offset": "5", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_kc(params, headers): +def test_ta_kc(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/kc?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ - ({"data": stocks_data, "index": "", "length": ""}), - ({"data": crypto_data, "index": "date", "length": "20"}), + ({"data": "", "index": "", "length": ""}, "stocks"), + ({"data": "", "index": "date", "length": "20"}, "crypto"), ], ) @pytest.mark.integration -def test_ta_cg(params, headers): +def test_ta_cg(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/cg?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "index": "", "lower_q": "", "upper_q": "", "model": "", "is_crypto": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "index": "date", "lower_q": "0.3", "upper_q": "0.7", "model": "Parkinson", "is_crypto": "true", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_cones(params, headers): +def test_ta_cones(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/cones?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 @pytest.mark.parametrize( - "params", + "params, data_type", [ ( { - "data": stocks_data, + "data": "", "target": "close", "index": "date", "length": "", "offset": "", - } + }, + "stocks", ), ( { - "data": crypto_data, + "data": "", "target": "high", "index": "", "length": "60", "offset": "10", - } + }, + "crypto", ), ], ) @pytest.mark.integration -def test_ta_ema(params, headers): +def test_ta_ema(params, data_type): params = {p: v for p, v in params.items() if v} - data = json.dumps(params.pop("data")) + data = json.dumps(get_data(data_type)) query_str = get_querystring(params, []) url = f"http://0.0.0.0:8000/api/v1/ta/ema?{query_str}" - result = requests.post(url, headers=headers, timeout=5, data=data) + result = requests.post(url, headers=get_headers(), timeout=10, data=data) assert isinstance(result, requests.Response) assert result.status_code == 200 diff --git a/openbb_platform/extensions/ta/integration/test_ta_python.py b/openbb_platform/extensions/ta/integration/test_ta_python.py new file mode 100644 index 00000000000..5d4da9f9a6b --- /dev/null +++ b/openbb_platform/extensions/ta/integration/test_ta_python.py @@ -0,0 +1,874 @@ +"""Test ta extension.""" +import random +from typing import Literal + +import pytest +from openbb_core.app.model.obbject import OBBject + + +@pytest.fixture(scope="session") +def obb(pytestconfig): + """Fixture to setup obb.""" + if pytestconfig.getoption("markexpr") != "not integration": + import openbb + + return openbb.obb + + +data = {} + + +def get_stocks_data(): + import openbb # pylint:disable=import-outside-toplevel + + if "stocks_data" in data: + return data["stocks_data"] + + symbol = random.choice(["AAPL", "NVDA", "MSFT", "TSLA", "AMZN", "V"]) # noqa: S311 + provider = random.choice(["fmp", "intrinio", "polygon", "yfinance"]) # noqa: S311 + + data["stocks_data"] = openbb.obb.stocks.load( + symbol=symbol, provider=provider + ).results + return data["stocks_data"] + + +def get_crypto_data(): + import openbb # pylint:disable=import-outside-toplevel + + if "crypto_data" in data: + return data["crypto_data"] + + # TODO : add more crypto providers and symbols + symbol = random.choice(["BTC"]) # noqa: S311 + provider = random.choice(["fmp"]) # noqa: S311 + + data["crypto_data"] = openbb.obb.crypto.load( + symbol=symbol, provider=provider + ).results + return data["crypto_data"] + + +def get_data(menu: Literal["stocks", "crypto"]): + funcs = {"stocks": get_stocks_data, "crypto": get_crypto_data} + return funcs[menu]() + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "index": "", + "length": "", + "mamode": "", + "drift": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", + "index": "date", + "length": "15", + "mamode": "rma", + "drift": "2", + "offset": "1", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_atr(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.atr(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "index": "", + "close_column": "", + "period": "", + "start_date": "", + "end_date": "", + }, + "stocks", + ), + ( + { + "data": "", + "index": "date", + "close_column": "adj_close", + "period": "125", + "start_date": "", + "end_date": "", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_fib(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.fib(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "index": "", "offset": ""}, "stocks"), + ({"data": "", "index": "date", "offset": "1"}, "crypto"), + ], +) +@pytest.mark.integration +def test_ta_obv(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.obv(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "index": "", "length": "", "signal": ""}, "stocks"), + ({"data": "", "index": "date", "length": "15", "signal": "2"}, "crypto"), + ], +) +@pytest.mark.integration +def test_ta_fisher(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.fisher(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "index": "", + "fast": "", + "slow": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", + "index": "date", + "fast": "5", + "slow": "15", + "offset": "2", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_adosc(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.adosc(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "", + "index": "", + "length": "", + "std": "", + "mamode": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "index": "date", + "length": "55", + "std": "3", + "mamode": "wma", + "offset": "1", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_bbands(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.bbands(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "", + "index": "", + "length": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "index": "date", + "length": "55", + "offset": "5", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_zlma(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.zlma(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "index": "", "length": "", "scalar": ""}, "stocks"), + ( + { + "data": "", + "index": "date", + "length": "30", + "scalar": "110", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_aroon(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.aroon(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "", + "index": "", + "length": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "index": "date", + "length": "55", + "offset": "2", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_sma(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.sma(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "index": "", + "target": "", + "show_all": "", + "asint": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", + "index": "date", + "target": "high", + "show_all": "true", + "asint": "true", + "offset": "5", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_demark(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.demark(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "index": "", "anchor": "", "offset": ""}, "stocks"), + ({"data": "", "index": "date", "anchor": "W", "offset": "5"}, "crypto"), + ], +) +@pytest.mark.integration +def test_ta_vwap(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.vwap(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "", + "index": "", + "fast": "", + "slow": "", + "signal": "", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "index": "date", + "fast": "10", + "slow": "30", + "signal": "10", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_macd(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.macd(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "", + "index": "", + "length": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "index": "date", + "length": "55", + "offset": "2", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_hma(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.hma(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "index": "", + "lower_length": "", + "upper_length": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", + "index": "date", + "lower_length": "30", + "upper_length": "40", + "offset": "5", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_donchian(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.donchian(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "index": "", + "conversion": "", + "base": "", + "lagging": "", + "offset": "", + "lookahead": "", + }, + "stocks", + ), + ( + { + "data": "", + "index": "date", + "conversion": "10", + "base": "30", + "lagging": "50", + "offset": "30", + "lookahead": "true", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_ichimoku(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.ichimoku(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "index": "", "target": "", "period": ""}, "stocks"), + ( + { + "data": "", + "index": "date", + "target": "close", + "period": "95", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_clenow(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.clenow(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "index": "", + "length": "", + "scalar": "", + "drift": "", + }, + "stocks", + ), + ( + { + "data": "", + "index": "date", + "length": "60", + "scalar": "90.0", + "drift": "2", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_adx(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.adx(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "", + "index": "", + "length": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "index": "date", + "length": "60", + "offset": "10", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_wma(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.wma(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "index": "", "length": "", "scalar": ""}, "stocks"), + ( + { + "data": "", + "index": "date", + "length": "16", + "scalar": "0.02", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_cci(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.cci(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "", + "index": "", + "length": "", + "scalar": "", + "drift": "", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "index": "date", + "length": "16", + "scalar": "90.0", + "drift": "2", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_rsi(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.rsi(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "index": "", + "fast_k_period": "", + "slow_d_period": "", + "slow_k_period": "", + }, + "stocks", + ), + ( + { + "data": "", + "index": "date", + "fast_k_period": "12", + "slow_d_period": "2", + "slow_k_period": "2", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_stoch(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.stoch(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "index": "", + "length": "", + "scalar": "", + "mamode": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", + "index": "date", + "length": "22", + "scalar": "24", + "mamode": "sma", + "offset": "5", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_kc(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.kc(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ({"data": "", "index": "", "length": ""}, "stocks"), + ({"data": "", "index": "date", "length": "20"}, "crypto"), + ], +) +@pytest.mark.integration +def test_ta_cg(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.cg(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "index": "", + "lower_q": "", + "upper_q": "", + "model": "", + "is_crypto": "", + }, + "stocks", + ), + ( + { + "data": "", + "index": "date", + "lower_q": "0.3", + "upper_q": "0.7", + "model": "Parkinson", + "is_crypto": "true", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_cones(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.cones(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 + + +@pytest.mark.parametrize( + "params, data_type", + [ + ( + { + "data": "", + "target": "close", + "index": "date", + "length": "", + "offset": "", + }, + "stocks", + ), + ( + { + "data": "", + "target": "high", + "index": "", + "length": "60", + "offset": "10", + }, + "crypto", + ), + ], +) +@pytest.mark.integration +def test_ta_ema(params, data_type, obb): + params = {p: v for p, v in params.items() if v} + params["data"] = get_data(data_type) + + result = obb.ta.ema(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 diff --git a/openbb_platform/extensions/tests/__init__.py b/openbb_platform/extensions/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_platform/extensions/tests/test_integration_tests_api.py b/openbb_platform/extensions/tests/test_integration_tests_api.py new file mode 100644 index 00000000000..6aadd20ae77 --- /dev/null +++ b/openbb_platform/extensions/tests/test_integration_tests_api.py @@ -0,0 +1,26 @@ +"""Test the integration tests.""" +from extensions.tests.utils.integration_tests_testers import ( + check_missing_integration_test_params, + check_missing_integration_test_providers, + get_integration_tests, + get_module_functions, +) + + +def run_test(test_type: str, check_function) -> None: + """Run tests helper function.""" + integration_tests = get_integration_tests(test_type=test_type) + functions = get_module_functions(integration_tests) + missing_items = check_function(functions) + + assert not missing_items, "\n".join(missing_items) + + +def test_api_interface_integration_test_providers() -> None: + """Test if there are any missing providers for integration tests.""" + run_test("api", check_missing_integration_test_providers) + + +def test_api_interface_integration_test_params() -> None: + """Test if there are any missing params for integration tests.""" + run_test("api", check_missing_integration_test_params) diff --git a/openbb_platform/extensions/tests/test_integration_tests_python.py b/openbb_platform/extensions/tests/test_integration_tests_python.py new file mode 100644 index 00000000000..0575ebdc64e --- /dev/null +++ b/openbb_platform/extensions/tests/test_integration_tests_python.py @@ -0,0 +1,26 @@ +"""Test the integration tests.""" +from extensions.tests.utils.integration_tests_testers import ( + check_missing_integration_test_params, + check_missing_integration_test_providers, + get_integration_tests, + get_module_functions, +) + + +def run_test(test_type: str, check_function) -> None: + """Run tests helper function.""" + integration_tests = get_integration_tests(test_type=test_type) + functions = get_module_functions(integration_tests) + missing_items = check_function(functions) + + assert not missing_items, "\n".join(missing_items) + + +def test_python_interface_integration_test_providers() -> None: + """Test if there are any missing providers for integration tests.""" + run_test("python", check_missing_integration_test_providers) + + +def test_python_interface_integration_test_params() -> None: + """Test if there are any missing params for integration tests.""" + run_test("python", check_missing_integration_test_params) diff --git a/openbb_platform/extensions/tests/utils/__init__.py b/openbb_platform/extensions/tests/utils/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_platform/extensions/tests/utils/helpers.py b/openbb_platform/extensions/tests/utils/helpers.py new file mode 100644 index 00000000000..b4c307b542a --- /dev/null +++ b/openbb_platform/extensions/tests/utils/helpers.py @@ -0,0 +1,13 @@ +import base64 + +from openbb_core.env import Env + + +def get_auth() -> str: + """Returns the basic auth header.""" + + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + return f"Basic {base64_bytes.decode('ascii')}" diff --git a/openbb_platform/extensions/tests/utils/integration_tests_api_generator.py b/openbb_platform/extensions/tests/utils/integration_tests_api_generator.py index a69cd915f81..d77df54d2b5 100644 --- a/openbb_platform/extensions/tests/utils/integration_tests_api_generator.py +++ b/openbb_platform/extensions/tests/utils/integration_tests_api_generator.py @@ -1,9 +1,10 @@ import os -from platform.core.openbb_core.app.provider_interface import ProviderInterface -from platform.core.openbb_core.app.router import CommandMap from typing import Dict, List, Type, get_type_hints import requests +from openbb_core.app.provider_interface import ProviderInterface +from openbb_core.app.router import CommandMap + from extensions.tests.utils.integration_tests_generator import get_test_params @@ -25,20 +26,17 @@ def write_init_test_template(http_method: str, path: str): import pytest import requests from openbb_provider.utils.helpers import get_querystring - - -def get_token(): - return requests.post( - "http://0.0.0.0:8000/api/v1/account/token", - data={"username": "openbb", "password": "openbb"}, - timeout=5, - ) +import base64 +from openbb_core.env import Env @pytest.fixture(scope="session") def headers(): - access_token = get_token().json()["access_token"] - return {"Authorization": f"Bearer {access_token}"} + userpass = f"{Env().API_USERNAME}:{Env().API_PASSWORD}" + userpass_bytes = userpass.encode("ascii") + base64_bytes = base64.b64encode(userpass_bytes) + + return {"Authorization": f"Basic {base64_bytes.decode('ascii')}"} """ with open(path, "w") as f: @@ -51,11 +49,11 @@ def write_test_w_template( params_str = ",\n".join([f"({params})" for params in params_list]) http_template_request = { - "get": "requests.get(url, headers=headers, timeout=5)", - "post": "requests.post(url, headers=headers, timeout=5, data=data)", + "get": "requests.get(url, headers=headers, timeout=10)", + "post": "requests.post(url, headers=headers, timeout=10, data=body)", } - http_template_params = {"get": "", "post": "data = json.dumps(params.pop('data'))"} + http_template_params = {"get": "", "post": "body = json.dumps(params.pop('data'))"} template = f""" @pytest.mark.parametrize( @@ -132,7 +130,7 @@ def write_integration_tests( if __name__ == "__main__": - r = requests.get("http://0.0.0.0:8000/openapi.json", timeout=5).json() + r = requests.get("http://0.0.0.0:8000/openapi.json", timeout=10).json() if not r: raise Exception("Could not get openapi.json") diff --git a/openbb_platform/extensions/tests/utils/integration_tests_generator.py b/openbb_platform/extensions/tests/utils/integration_tests_generator.py index ca924dc7ef9..2d4ecc6200e 100644 --- a/openbb_platform/extensions/tests/utils/integration_tests_generator.py +++ b/openbb_platform/extensions/tests/utils/integration_tests_generator.py @@ -1,18 +1,32 @@ """Integration test generator.""" from pathlib import Path, PosixPath -from platform.core.openbb_core.app.provider_interface import ProviderInterface -from platform.core.openbb_core.app.router import CommandMap -from typing import Any, Dict, List, Literal, get_origin +from typing import Any, Dict, List, Literal, Tuple, Type, get_origin, get_type_hints +from openbb_core.app.provider_interface import ProviderInterface +from openbb_core.app.router import CommandMap from pydantic.fields import FieldInfo from pydantic_core import PydanticUndefined -cm = CommandMap(coverage_sep=".") +TEST_TEMPLATE = """\n\n@pytest.mark.parametrize( + "params", + [ + {params} + ], +) +@pytest.mark.integration +def test_{test_name}(params, obb): + params = {{p: v for p, v in params.items() if v}} + + result = obb.{command_name}(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 +""" def find_extensions(): """Find extensions.""" - filter_ext = ["tests", "ta", "qa", "econometrics", "charting"] + filter_ext = ["tests", "charting", "__pycache__"] extensions = [x for x in Path("openbb_platform/extensions").iterdir() if x.is_dir()] extensions = [x for x in extensions if x.name not in filter_ext] return extensions @@ -70,7 +84,7 @@ def get_model_params(param_fields: Dict[str, FieldInfo]) -> Dict[str, Any]: elif field.annotation == bool: test_params[field_name] = True elif get_origin(field.annotation) is Literal: - test_params[field_name] = field.annotation.__args__[0] + test_params[field_name] = field.annotation.__args__[0] # type: ignore return test_params @@ -103,83 +117,132 @@ def get_test_params( return test_params_list +def get_test_params_data_processing(hints: Dict[str, Type]): + """Get the test params for the data processing commands.""" + return list(hints.keys()) + + +def get_full_command_name_and_test_name(route: str) -> Tuple[str, str]: + """Get the full command name and test name.""" + cmd_parts = route.split("/") + del cmd_parts[0] + + menu = cmd_parts[0] + command = cmd_parts[-1] + sub_menus = cmd_parts[1:-1] + + sub_menus_str_test_name = f"_{'_'.join(sub_menus)}" if sub_menus else "" + sub_menu_str_cmd = f".{'.'.join(sub_menus)}" if sub_menus else "" + + full_command = f"{menu}{sub_menu_str_cmd}.{command}" + test_name = f"{menu}{sub_menus_str_test_name}_{command}" + + return test_name, full_command + + +def test_exists(command_name: str, path: str): + """Check if test exists.""" + with open(path) as f: + return command_name in f.read() + + +def write_to_file_w_template(test_file, params_list, full_command, test_name): + """Write to file with template.""" + params = "" + for test_params in params_list: + params += f"({test_params}),\n" + + if not test_exists(command_name=full_command, path=test_file): + with open(test_file, "a", encoding="utf-8", newline="\n") as f: + f.write( + TEST_TEMPLATE.format( + test_name=test_name, + command_name=full_command, + params=params, + ) + ) + + +def write_test( + test_file: PosixPath, + commands_model: Dict[str, str], + extension_name: str, + provider_interface_map: Dict[str, Any], +): + """Write test.""" + for route, model in commands_model.items(): + if extension_name in route and route.startswith(f"/{extension_name}/"): + test_name, full_command = get_full_command_name_and_test_name(route=route) + + test_params_list = get_test_params( + model_name=model, + provider_interface_map=provider_interface_map, + ) + + write_to_file_w_template( + test_file=test_file, + params_list=test_params_list, + full_command=full_command, + test_name=test_name, + ) + + +def write_test_data_processing( + test_file: PosixPath, commands_map: Dict[str, str], extension_name: str +): + """Write test for data processing commands.""" + for route, _ in commands_map.items(): + if extension_name in route and route.startswith(f"/{extension_name}/"): + test_name, full_command = get_full_command_name_and_test_name(route=route) + + hints = get_type_hints(commands_map[route]) + hints.pop("cc", None) + hints.pop("return", None) + test_params_list = [{k: "" for k in get_test_params_data_processing(hints)}] + + write_to_file_w_template( + test_file=test_file, + params_list=test_params_list, + full_command=full_command, + test_name=test_name, + ) + + def add_test_commands_to_file( # pylint: disable=W0102 extensions: List[PosixPath], - commands_model: Dict[str, str] = cm.commands_model, ) -> None: """Add test commands to file.""" provider_interface = ProviderInterface() provider_interface_map = provider_interface.map - template = """\n\n@pytest.mark.parametrize( - "params", - [ - {params} - ], -) -@pytest.mark.integration -def test_{test_name}(**params, obb): - result = obb.{command_name}(**params) - assert result - assert isinstance(result, OBBject) - assert len(result.results) > 0 -""" + cm = CommandMap() + commands_model = cm.commands_model + commands_map = cm.map + + extensions_names = [path.name for path in extensions] + extensions_w_models = list({route.split("/")[1] for route in commands_model}) + extensions_data_processing = [ + ext for ext in extensions_names if ext not in extensions_w_models + ] + for extension in extensions: extension_name = extension.name test_file_name = f"test_{extension_name}_python.py" test_file = extension / "integration" / test_file_name - with open(test_file, "a", encoding="utf-8", newline="\n") as f: - for command in commands_model: - if extension_name in command and command.startswith( - f".{extension_name}." - ): - extension_name = command.split(".")[1] - if len(command.split(".")) > 3: - test_name = ( - extension_name - + "_" - + command.split(".")[2] - + "_" - + command.split(".")[3] - ) - elif len(command.split(".")) == 3: - test_name = extension_name + "_" + command.split(".")[2] - - command_name = command.split(".")[-1] - if "_" in command_name: - full_command_name = test_name.replace("_", ".", 2) - # The code below double checks if the full command name is correct. - # This eliminates edge cases. - fix_full_command_name = full_command_name.split(".") - for i, part in enumerate(fix_full_command_name): - if part == command_name.split("_")[0]: - fix_full_command_name[i] = command_name - fix_full_command_name.pop() - - full_command_name = ".".join(fix_full_command_name) - - else: - full_command_name = test_name.replace("_", ".") - - test_params_list = get_test_params( - model_name=commands_model[command], - provider_interface_map=provider_interface_map, - ) - - params = "" - for test_params in test_params_list: - params += f"({test_params}),\n" - - read_file = open(test_file) # noqa - if full_command_name not in read_file.read(): - f.write( - template.format( - test_name=test_name, - command_name=full_command_name, - params=params, - ) - ) + if extension_name in extensions_data_processing: + write_test_data_processing( + test_file=test_file, + commands_map=commands_map, + extension_name=extension_name, + ) + else: + write_test( + test_file=test_file, + commands_model=commands_model, + extension_name=extension_name, + provider_interface_map=provider_interface_map, + ) def write_integration_test() -> None: diff --git a/openbb_platform/extensions/tests/utils/integration_tests_testers.py b/openbb_platform/extensions/tests/utils/integration_tests_testers.py new file mode 100644 index 00000000000..ced6ad7c725 --- /dev/null +++ b/openbb_platform/extensions/tests/utils/integration_tests_testers.py @@ -0,0 +1,196 @@ +"""Test if there are any missing providers for python interface integration tests.""" +import importlib.util +import inspect +import os +from typing import Any, Callable, Dict, List, Literal, Tuple, Union, get_type_hints + +from openbb_core.app.provider_interface import ProviderInterface +from openbb_core.app.router import CommandMap + +from extensions.tests.utils.integration_tests_generator import ( + find_extensions, + get_test_params_data_processing, +) + + +def get_integration_tests(test_type: Literal["api", "python"]) -> List[Any]: + """Get integration tests for the OpenBB Platform.""" + integration_tests: List[Any] = [] + + if test_type == "python": + file_end = "_python.py" + elif test_type == "api": + file_end = "_api.py" + + for extension in find_extensions(): + integration_folder = os.path.join(extension, "integration") + for file in os.listdir(integration_folder): + if file.endswith(file_end): + file_path = os.path.join(integration_folder, file) + module_name = file[:-3] # Remove .py from file name + + spec = importlib.util.spec_from_file_location(module_name, file_path) + if spec: + module = importlib.util.module_from_spec(spec) + if spec.loader: + spec.loader.exec_module(module) + integration_tests.append(module) + + return integration_tests + + +def get_module_functions(module_list: List[Any]) -> Dict[str, Any]: + """Get all functions from a list of modules.""" + functions = {} + for module in module_list: + for name, obj in inspect.getmembers(module): + if inspect.isfunction(obj): + functions[name] = obj + return functions + + +def check_missing_providers( + command_params: Union[Dict[str, Dict[str, dict]], List[Tuple[Dict[str, str], str]]], + function_params: List[dict], + function, + processing: bool = False, +) -> List[str]: + """Check if there are any missing providers for a command.""" + if processing or not isinstance(command_params, dict): + return [] + + missing_providers: List[str] = [] + providers = list(command_params.keys()) + providers.remove("openbb") + + for test_params in function_params: + provider = test_params.get("provider", None) + if provider: + try: # noqa + providers.remove(provider) + except ValueError: + pass + + if providers: + # if there is only one provider left and the length of the + # test_params is 1, we can ignore because it is picked up by default + if len(providers) == 1 and len(function_params) == 1: + pass + else: + missing_providers.append(f"Missing providers for {function}: {providers}") + + return missing_providers + + +def check_missing_params( + command_params: Union[Dict[str, Dict[str, dict]], List[Tuple[Dict[str, str], str]]], + function_params: List[dict], + function, + processing: bool = False, +) -> List[str]: + """Check if there are any missing params for a command.""" + missing_params = [] + if not processing: + for test_params in function_params: + if "provider" in test_params: + provider = test_params["provider"] + if provider in command_params: + for expected_param in command_params[provider]["QueryParams"][ + "fields" + ]: + if expected_param not in test_params: + missing_params.append( + f"Missing param {expected_param} for provider {provider} in function {function}" + ) + elif isinstance(command_params, dict): + for expected_param in command_params["openbb"]["QueryParams"]["fields"]: + if expected_param not in test_params: + missing_params.append( + f"Missing standard param {expected_param} in function {function}" + ) + else: + for test_params in function_params: + if isinstance(command_params, list): + try: + iter_commands_params = command_params[0][0] + except KeyError: + iter_commands_params = command_params[0] # type: ignore + + for expected_param in iter_commands_params: + try: + used_params = test_params[0].keys() + except KeyError: + used_params = test_params.keys() + if expected_param not in used_params and expected_param != "return": + missing_params.append( + f"Missing param {expected_param} in function {function}" + ) + return missing_params + + +def check_integration_tests( + functions: Dict[str, Any], + check_function: Callable[ + [ + Union[Dict[str, Dict[str, dict]], List[Tuple[Dict[str, str], str]]], + List[dict], + str, + bool, + ], + List[str], + ], +) -> List[str]: + """Check if there are any missing items for integration tests.""" + pi = ProviderInterface() + provider_interface_map = pi.map + cm = CommandMap(coverage_sep=".") + + function_params: List[dict] = [] + all_missing_items: List[str] = [] + used_functions: List[str] = [] + + for command, model in cm.commands_model.items(): + for function in functions: + if command[1:].replace(".", "_") == function.replace("test_", ""): + command_params: Dict[str, Dict[str, dict]] = provider_interface_map[ + model + ] + function_params = functions[function].pytestmark[1].args[1] + + missing_items = check_function( + command_params, function_params, function, False + ) + all_missing_items.extend(missing_items) + used_functions.append(function) + + # the processing commands are the ones that are left + processing_functions = [ + function for function in functions if function not in used_functions + ] + + for route, _ in cm.map.items(): + for function in processing_functions: + if route.replace("/", "_")[1:] == function.replace("test_", ""): + hints = get_type_hints(cm.map[route]) + processing_command_params = [ + {k: "" for k in get_test_params_data_processing(hints)} + ] + function_params = functions[function].pytestmark[1].args[1] + + missing_items = check_function( + processing_command_params, function_params, function, True # type: ignore + ) + + all_missing_items.extend(missing_items) + + return all_missing_items + + +def check_missing_integration_test_providers(functions: Dict[str, Any]) -> List[str]: + """Check if there are any missing providers for integration tests.""" + return check_integration_tests(functions, check_missing_providers) + + +def check_missing_integration_test_params(functions: Dict[str, Any]) -> List[str]: + """Check if there are any missing params for integration tests.""" + return check_integration_tests(functions, check_missing_params) diff --git a/openbb_platform/platform/core/tests/api/test_auth/test_user_auth.py b/openbb_platform/platform/core/tests/api/test_auth/test_user_auth.py index 3c99e64cefb..27909f01403 100644 --- a/openbb_platform/platform/core/tests/api/test_auth/test_user_auth.py +++ b/openbb_platform/platform/core/tests/api/test_auth/test_user_auth.py @@ -12,9 +12,6 @@ from openbb_core.api.auth.user import ( ) -# ruff: noqa: S105 S106 - - @pytest.mark.parametrize( "error, correct, received", [ diff --git a/openbb_platform/platform/core/tests/app/test_router.py b/openbb_platform/platform/core/tests/app/test_platform_router.py similarity index 100% rename from openbb_platform/platform/core/tests/app/test_router.py rename to openbb_platform/platform/core/tests/app/test_platform_router.py diff --git a/openbb_platform/platform/core/tests/test_query.py b/openbb_platform/platform/core/tests/test_query.py deleted file mode 100644 index d4df7740ce2..00000000000 --- a/openbb_platform/platform/core/tests/test_query.py +++ /dev/null @@ -1,125 +0,0 @@ -"""Test the Query class.""" -# pylint: disable=redefined-outer-name - -from dataclasses import dataclass -from unittest.mock import MagicMock, patch - -import pytest -from openbb_core.app.model.command_context import CommandContext -from openbb_core.app.provider_interface import ( - ExtraParams, - ProviderChoices, - StandardParams, -) -from openbb_core.app.query import Query -from pydantic import BaseModel, ConfigDict - - -class MockBaseModel(BaseModel): - """Mock BaseModel class.""" - - model_config = ConfigDict(extra="allow", populate_by_name=True) - - -def create_mock_query(): - """Mock query.""" - - class StockHistorical: - """Mock StockHistorical class.""" - - start_date = "2020-01-01" - end_date = "2020-01-05" - symbol = "AAPL" - - return StockHistorical() - - -def create_mock_extra_params(): - """Mock ExtraParams dataclass.""" - - @dataclass - class StockHistorical: - """Mock ExtraParams dataclass.""" - - sort: str = "desc" - - return StockHistorical() - - -@pytest.fixture(scope="module") -def query(): - """Set up query.""" - return Query( - cc=CommandContext(), - provider_choices=ProviderChoices(provider="fmp"), - standard_params=StandardParams(), - extra_params=ExtraParams(), - ) - - -def test_init(query): - """Test init.""" - assert query - - -def test_filter_extra_params(query): - """Test filter_extra_params.""" - extra_params = create_mock_extra_params() - extra_params = query.filter_extra_params(extra_params, "fmp") - - assert isinstance(extra_params, dict) - assert len(extra_params) == 0 - - -def test_filter_extra_params_wrong_param(query): - """Test filter_extra_params.""" - - @dataclass - class StockHistorical: - """Mock ExtraParams dataclass.""" - - sort: str = "desc" - limit: int = 4 - - extra_params = StockHistorical() - - assert not query.filter_extra_params(extra_params, "fmp") - - -@pytest.fixture -def mock_registry(): - """Mock registry.""" - with patch( - "openbb_core.app.provider_interface.ProviderInterface" - ) as mock_get_provider_interface: - mock_registry = MagicMock() - mock_get_provider_interface.return_value.build_registry.return_value = ( - mock_registry - ) - yield mock_registry - - -@pytest.fixture -def query_instance(): - """Set up query.""" - standard_params = create_mock_query() - extra_params = create_mock_extra_params() - - cc = CommandContext() - setattr(cc.user_settings.credentials, "fmp_api_key", "1234") - - return Query( - cc=cc, - provider_choices=ProviderChoices(provider="fmp"), - standard_params=standard_params, - extra_params=extra_params, - ) - - -def test_execute_method_fake_credentials(query_instance, mock_registry): - """Test execute method without setting credentials.""" - mock_fetch_result = MockBaseModel() - mock_registry.fetch.return_value = mock_fetch_result - - with pytest.raises(Exception): - query_instance.execute() diff --git a/openbb_platform/tests/conftest.py b/openbb_platform/tests/conftest.py index 2c3d415748d..c35312c6cda 100644 --- a/openbb_platform/tests/conftest.py +++ b/openbb_platform/tests/conftest.py @@ -1,2 +1,3 @@ # TODO: See if we can dynamically import the test modules under the openbb_platform -# file structure. + +# TODO: Add global fixture for headers here diff --git a/pytest.ini b/pytest.ini index ef177d5cbb0..3edf92fc11b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,6 +2,7 @@ addopts = -p no:warnings markers = linux: tests that are not stable on Windows + integration: OpenBB Platform integration test marker testpaths = tests openbb_platform/**/tests