diff --git a/freqtrade/tests/rpc/test_rpc_apiserver.py b/freqtrade/tests/rpc/test_rpc_apiserver.py index 5b9e538b5..620a7c34b 100644 --- a/freqtrade/tests/rpc/test_rpc_apiserver.py +++ b/freqtrade/tests/rpc/test_rpc_apiserver.py @@ -16,11 +16,19 @@ from freqtrade.tests.conftest import (get_patched_freqtradebot, log_has, patch_get_signal) +_TEST_USER = "FreqTrader" +_TEST_PASS = "SuperSecurePassword1!" + + @pytest.fixture def botclient(default_conf, mocker): default_conf.update({"api_server": {"enabled": True, "listen_ip_address": "127.0.0.1", - "listen_port": "8080"}}) + "listen_port": "8080", + "username": _TEST_USER, + "password": _TEST_PASS, + }}) + ftbot = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.rpc.api_server.ApiServer.run', MagicMock()) apiserver = ApiServer(ftbot) @@ -28,6 +36,21 @@ def botclient(default_conf, mocker): # Cleanup ... ? +def client_post(client, url, data={}): + headers = {"username": _TEST_USER, + "password": _TEST_PASS} + return client.post(url, + content_type="application/json", + data=data, + headers=headers) + + +def client_get(client, url): + headers = {"username": _TEST_USER, + "password": _TEST_PASS} + return client.get(url, headers=headers) + + def assert_response(response, expected_code=200): assert response.status_code == expected_code assert response.content_type == "application/json" @@ -36,7 +59,7 @@ def assert_response(response, expected_code=200): def test_api_not_found(botclient): ftbot, client = botclient - rc = client.post(f"{BASE_URI}/invalid_url") + rc = client_post(client, f"{BASE_URI}/invalid_url") assert_response(rc, 404) assert rc.json == {"status": "error", "reason": f"There's no API call for http://localhost{BASE_URI}/invalid_url.", @@ -44,27 +67,57 @@ def test_api_not_found(botclient): } +def test_api_unauthorized(botclient): + ftbot, client = botclient + # Don't send user/pass information + rc = client.get(f"{BASE_URI}/version") + assert_response(rc, 401) + assert rc.json == {'error': 'Unauthorized'} + + # Change only username + ftbot.config['api_server']['username'] = "Ftrader" + rc = client_get(client, f"{BASE_URI}/version") + assert_response(rc, 401) + assert rc.json == {'error': 'Unauthorized'} + + # Change only password + ftbot.config['api_server']['username'] = _TEST_USER + ftbot.config['api_server']['password'] = "WrongPassword" + rc = client_get(client, f"{BASE_URI}/version") + assert_response(rc, 401) + assert rc.json == {'error': 'Unauthorized'} + + ftbot.config['api_server']['username'] = "Ftrader" + ftbot.config['api_server']['password'] = "WrongPassword" + + rc = client_get(client, f"{BASE_URI}/version") + assert_response(rc, 401) + assert rc.json == {'error': 'Unauthorized'} + + + + def test_api_stop_workflow(botclient): ftbot, client = botclient assert ftbot.state == State.RUNNING - rc = client.post(f"{BASE_URI}/stop") + rc = client_post(client, f"{BASE_URI}/stop") assert_response(rc) assert rc.json == {'status': 'stopping trader ...'} assert ftbot.state == State.STOPPED # Stop bot again - rc = client.post(f"{BASE_URI}/stop") + rc = client_post(client, f"{BASE_URI}/stop") assert_response(rc) assert rc.json == {'status': 'already stopped'} # Start bot - rc = client.post(f"{BASE_URI}/start") + rc = client_post(client, f"{BASE_URI}/start") assert_response(rc) assert rc.json == {'status': 'starting trader ...'} assert ftbot.state == State.RUNNING # Call start again - rc = client.post(f"{BASE_URI}/start") + rc = client_post(client, f"{BASE_URI}/start") assert_response(rc) assert rc.json == {'status': 'already running'} @@ -153,7 +206,7 @@ def test_api_cleanup(default_conf, mocker, caplog): def test_api_reloadconf(botclient): ftbot, client = botclient - rc = client.post(f"{BASE_URI}/reload_conf") + rc = client_post(client, f"{BASE_URI}/reload_conf") assert_response(rc) assert rc.json == {'status': 'reloading config ...'} assert ftbot.state == State.RELOAD_CONF @@ -163,7 +216,7 @@ def test_api_stopbuy(botclient): ftbot, client = botclient assert ftbot.config['max_open_trades'] != 0 - rc = client.post(f"{BASE_URI}/stopbuy") + rc = client_post(client, f"{BASE_URI}/stopbuy") assert_response(rc) assert rc.json == {'status': 'No more buy will occur from now. Run /reload_conf to reset.'} assert ftbot.config['max_open_trades'] == 0 @@ -193,7 +246,7 @@ def test_api_balance(botclient, mocker, rpc_balance): mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance) mocker.patch('freqtrade.exchange.Exchange.get_ticker', side_effect=mock_ticker) - rc = client.get(f"{BASE_URI}/balance") + rc = client_get(client, f"{BASE_URI}/balance") assert_response(rc) assert "currencies" in rc.json assert len(rc.json["currencies"]) == 5 @@ -216,7 +269,7 @@ def test_api_count(botclient, mocker, ticker, fee, markets): get_fee=fee, markets=PropertyMock(return_value=markets) ) - rc = client.get(f"{BASE_URI}/count") + rc = client_get(client, f"{BASE_URI}/count") assert_response(rc) assert rc.json["current"] == 0 @@ -224,7 +277,7 @@ def test_api_count(botclient, mocker, ticker, fee, markets): # Create some test data ftbot.create_trade() - rc = client.get(f"{BASE_URI}/count") + rc = client_get(client, f"{BASE_URI}/count") assert_response(rc) assert rc.json["current"] == 1.0 assert rc.json["max"] == 1.0 @@ -240,7 +293,7 @@ def test_api_daily(botclient, mocker, ticker, fee, markets): get_fee=fee, markets=PropertyMock(return_value=markets) ) - rc = client.get(f"{BASE_URI}/daily") + rc = client_get(client, f"{BASE_URI}/daily") assert_response(rc) assert len(rc.json) == 7 assert rc.json[0][0] == str(datetime.utcnow().date()) @@ -256,7 +309,7 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): get_fee=fee, markets=PropertyMock(return_value=markets) ) - rc = client.get(f"{BASE_URI}/edge") + rc = client_get(client, f"{BASE_URI}/edge") assert_response(rc, 502) assert rc.json == {"error": "Error querying _edge: Edge is not enabled."} @@ -272,7 +325,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, limit_buy_order, li markets=PropertyMock(return_value=markets) ) - rc = client.get(f"{BASE_URI}/profit") + rc = client_get(client, f"{BASE_URI}/profit") assert_response(rc, 502) assert len(rc.json) == 1 assert rc.json == {"error": "Error querying _profit: no closed trade"} @@ -282,7 +335,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, limit_buy_order, li # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) - rc = client.get(f"{BASE_URI}/profit") + rc = client_get(client, f"{BASE_URI}/profit") assert_response(rc, 502) assert rc.json == {"error": "Error querying _profit: no closed trade"} @@ -291,7 +344,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, limit_buy_order, li trade.close_date = datetime.utcnow() trade.is_open = False - rc = client.get(f"{BASE_URI}/profit") + rc = client_get(client, f"{BASE_URI}/profit") assert_response(rc) assert rc.json == {'avg_duration': '0:00:00', 'best_pair': 'ETH/BTC', @@ -344,7 +397,7 @@ def test_api_performance(botclient, mocker, ticker, fee): Trade.session.add(trade) Trade.session.flush() - rc = client.get(f"{BASE_URI}/performance") + rc = client_get(client, f"{BASE_URI}/performance") assert_response(rc) assert len(rc.json) == 2 assert rc.json == [{'count': 1, 'pair': 'LTC/ETH', 'profit': 7.61}, @@ -362,12 +415,12 @@ def test_api_status(botclient, mocker, ticker, fee, markets): markets=PropertyMock(return_value=markets) ) - rc = client.get(f"{BASE_URI}/status") + rc = client_get(client, f"{BASE_URI}/status") assert_response(rc, 502) assert rc.json == {'error': 'Error querying _status: no active trade'} ftbot.create_trade() - rc = client.get(f"{BASE_URI}/status") + rc = client_get(client, f"{BASE_URI}/status") assert_response(rc) assert len(rc.json) == 1 assert rc.json == [{'amount': 90.99181074, @@ -394,7 +447,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): def test_api_version(botclient): ftbot, client = botclient - rc = client.get(f"{BASE_URI}/version") + rc = client_get(client, f"{BASE_URI}/version") assert_response(rc) assert rc.json == {"version": __version__} @@ -402,15 +455,15 @@ def test_api_version(botclient): def test_api_blacklist(botclient, mocker): ftbot, client = botclient - rc = client.get(f"{BASE_URI}/blacklist") + rc = client_get(client, f"{BASE_URI}/blacklist") assert_response(rc) assert rc.json == {"blacklist": ["DOGE/BTC", "HOT/BTC"], "length": 2, "method": "StaticPairList"} # Add ETH/BTC to blacklist - rc = client.post(f"{BASE_URI}/blacklist", data='{"blacklist": ["ETH/BTC"]}', - content_type='application/json') + rc = client_post(client, f"{BASE_URI}/blacklist", + data='{"blacklist": ["ETH/BTC"]}') assert_response(rc) assert rc.json == {"blacklist": ["DOGE/BTC", "HOT/BTC", "ETH/BTC"], "length": 3, @@ -420,7 +473,7 @@ def test_api_blacklist(botclient, mocker): def test_api_whitelist(botclient): ftbot, client = botclient - rc = client.get(f"{BASE_URI}/whitelist") + rc = client_get(client, f"{BASE_URI}/whitelist") assert_response(rc) assert rc.json == {"whitelist": ['ETH/BTC', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC'], "length": 4, @@ -430,7 +483,7 @@ def test_api_whitelist(botclient): def test_api_forcebuy(botclient, mocker, fee): ftbot, client = botclient - rc = client.post(f"{BASE_URI}/forcebuy", content_type='application/json', + rc = client_post(client, f"{BASE_URI}/forcebuy", data='{"pair": "ETH/BTC"}') assert_response(rc, 502) assert rc.json == {"error": "Error querying _forcebuy: Forcebuy not enabled."} @@ -440,7 +493,7 @@ def test_api_forcebuy(botclient, mocker, fee): fbuy_mock = MagicMock(return_value=None) mocker.patch("freqtrade.rpc.RPC._rpc_forcebuy", fbuy_mock) - rc = client.post(f"{BASE_URI}/forcebuy", content_type="application/json", + rc = client_post(client, f"{BASE_URI}/forcebuy", data='{"pair": "ETH/BTC"}') assert_response(rc) assert rc.json == {"status": "Error buying pair ETH/BTC."} @@ -461,7 +514,7 @@ def test_api_forcebuy(botclient, mocker, fee): )) mocker.patch("freqtrade.rpc.RPC._rpc_forcebuy", fbuy_mock) - rc = client.post(f"{BASE_URI}/forcebuy", content_type="application/json", + rc = client_post(client, f"{BASE_URI}/forcebuy", data='{"pair": "ETH/BTC"}') assert_response(rc) assert rc.json == {'amount': 1, @@ -491,14 +544,14 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): ) patch_get_signal(ftbot, (True, False)) - rc = client.post(f"{BASE_URI}/forcesell", content_type="application/json", + rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') assert_response(rc, 502) assert rc.json == {"error": "Error querying _forcesell: invalid argument"} ftbot.create_trade() - rc = client.post(f"{BASE_URI}/forcesell", content_type="application/json", + rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') assert_response(rc) assert rc.json == {'result': 'Created sell order for trade 1.'}