import json import time from contextlib import contextmanager from multiprocessing import Process import pytest import requests import responses from flask import Flask, request from requests import HTTPError from urlobject import URLObject from ..test_utils import silence from ...base import get_host_ip, success_response, get_values_from_response, split_http_address, HttpEngine, \ get_http_engine_class app = Flask('_test_get_host_ip') @app.route('/ping', methods=['GET']) def ping_method(): return success_response(message='PONG!') @app.route('/shutdown', methods=['DELETE']) def shutdown_method(): _shutdown_func = request.environ.get('werkzeug.server.shutdown') if _shutdown_func is None: raise RuntimeError('Not running with the Werkzeug Server') _shutdown_func() return success_response(message='Shutdown request received, this server will be down later.') _APP_PORT = 17503 def run_test_app(): with silence(): app.run(host='0.0.0.0', port=_APP_PORT) @pytest.mark.unittest class TestInteractionBaseNetwork: @pytest.mark.execution_timeout(5.0, method='thread') def test_get_host_ip(self): app_process = Process(target=run_test_app) app_process.start() _local_ip = get_host_ip() _local_server_host = URLObject().with_scheme('http').with_hostname(_local_ip).with_port(_APP_PORT) try: _start_time = time.time() _start_complete = False while not _start_complete and time.time() - _start_time < 5.0: try: response = requests.get(_local_server_host.add_path('/ping')) if response.ok: _start_complete = True break time.sleep(0.2) except (requests.exceptions.BaseHTTPError, requests.exceptions.RequestException): time.sleep(0.2) if not _start_complete: pytest.fail('Test server start failed.') assert get_values_from_response(response) == ( 200, True, 0, 'PONG!', None, ) finally: try: requests.delete(_local_server_host.add_path('/shutdown')) finally: app_process.join() def test_split_http_address(self): assert split_http_address('http://1.2.3.4') == ('1.2.3.4', 80, False, '') assert split_http_address('https://1.2.3.4') == ('1.2.3.4', 443, True, '') assert split_http_address('http://1.2.3.4:8888') == ('1.2.3.4', 8888, False, '') assert split_http_address('https://1.2.3.4:8787/this/is/path') == ('1.2.3.4', 8787, True, '/this/is/path') @pytest.mark.unittest class TestInteractionBaseHttpEngine: @contextmanager def __yield_http_engine(self): with responses.RequestsMock(assert_all_requests_are_fired=False) as rsp: rsp.add( **{ 'method': responses.GET, 'url': 'http://example.com:7777/this/is/404', 'body': json.dumps({"exception": "reason"}), 'status': 404, 'content_type': 'application/json', } ) rsp.add( **{ 'method': responses.GET, 'url': 'http://example.com:7777/this/is/200', 'body': json.dumps({"success": True}), 'status': 200, 'content_type': 'application/json', } ) yield @responses.activate def test_http_engine_basic(self): with self.__yield_http_engine(): engine = HttpEngine(host='example.com', port=7777) response = engine.request('GET', '/this/is/200') assert response.status_code == 200 assert json.loads(response.content.decode()) == {"success": True} with pytest.raises(HTTPError) as ei: engine.request('GET', '/this/is/404') err = ei.value assert err.response.status_code == 404 assert json.loads(err.response.content.decode()) == {'exception': 'reason'} @responses.activate def test_http_engine_with_path(self): with self.__yield_http_engine(): engine = HttpEngine(host='example.com', port=7777, path='/this/is') response = engine.request('GET', '200') assert response.status_code == 200 assert json.loads(response.content.decode()) == {"success": True} with pytest.raises(HTTPError) as ei: engine.request('GET', '404') err = ei.value assert err.response.status_code == 404 assert json.loads(err.response.content.decode()) == {'exception': 'reason'} @responses.activate def test_get_http_engine_class(self): with self.__yield_http_engine(): _token = '233' _http_engine_class = get_http_engine_class( headers={'Token': lambda: _token}, data_processor=(lambda d: { 'data': json.dumps(d) }), http_error_gene=lambda e: RuntimeError('This is {status}'.format(status=e.response.status_code)) )() engine = _http_engine_class(host='example.com', port=7777, path='/this/is') response = engine.request('GET', '200', {'a': 'skdjgflksdj'}) assert response.status_code == 200 assert json.loads(response.content.decode()) == {"success": True} assert response.request.headers['Token'] == '233' assert json.loads(response.request.body) == {'data': json.dumps({'a': 'skdjgflksdj'})} with pytest.raises(RuntimeError) as ei: engine.request('GET', '404', {'a': 'skdjgflksdj'}) err = ei.value assert 'This is 404' in str(err)