Skip to content

Commit

Permalink
Make tests framework agnostic
Browse files Browse the repository at this point in the history
  • Loading branch information
RobbeSneyders committed Feb 13, 2023
1 parent 642a5f2 commit 30375f5
Show file tree
Hide file tree
Showing 16 changed files with 475 additions and 459 deletions.
3 changes: 2 additions & 1 deletion connexion/apps/asynchronous.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from starlette.responses import Response as StarletteResponse
from starlette.routing import Router
from starlette.testclient import TestClient
from starlette.types import Receive, Scope, Send

from connexion.apps.abstract import AbstractApp
Expand Down Expand Up @@ -187,4 +188,4 @@ def add_error_handler(
"""TODO: implement"""

def test_client(self, **kwargs):
"""TODO: implement"""
return TestClient(self)
17 changes: 17 additions & 0 deletions connexion/apps/flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import werkzeug.exceptions
from flask import Response as FlaskResponse
from flask import signals
from flask.testing import FlaskClient
from starlette.types import Receive, Scope, Send

from connexion.apps.abstract import AbstractApp
Expand Down Expand Up @@ -253,4 +254,20 @@ def add_error_handler(

def test_client(self, **kwargs):
self.app.wsgi_app = a2wsgi.ASGIMiddleware(self.middleware)
self.app.test_client_class = ConnexionTestClient
return self.app.test_client(**kwargs)


class ConnexionTestClient(FlaskClient):

# def __init__(self, *args, **kwargs):
# cookies = kwargs.pop("cookies")
# super().__init__(*args, **kwargs)
# if cookies:
# for k, v in cookies.items():
# self.set_cookie('localhost', k, v)

def open(self, *args, **kwargs):
# Align with async test client
kwargs["query_string"] = kwargs.pop("params", None)
return super().open(*args, **kwargs)
80 changes: 56 additions & 24 deletions tests/api/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@


@pytest.fixture(scope="session")
def simple_app(spec):
return build_app_from_fixture("simple", validate_responses=True)
def simple_app(spec, app_class):
return build_app_from_fixture(
"simple", app_class=app_class, spec_file=spec, validate_responses=True
)


@pytest.fixture(scope="session")
def simple_openapi_app():
return build_app_from_fixture("simple", OPENAPI3_SPEC, validate_responses=True)
def simple_openapi_app(app_class):
return build_app_from_fixture(
"simple", app_class=app_class, spec_file=OPENAPI3_SPEC, validate_responses=True
)


@pytest.fixture(scope="session")
def reverse_proxied_app(spec):
def reverse_proxied_app(spec, app_class):
class ReverseProxied:
def __init__(self, app, root_path=None, scheme=None, server=None):
self.app = app
Expand Down Expand Up @@ -47,62 +51,90 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send):

return await self.app(scope, receive, send)

app = build_app_from_fixture("simple", spec, validate_responses=True)
app = build_app_from_fixture(
"simple", app_class=app_class, spec_file=spec, validate_responses=True
)
app.middleware = ReverseProxied(app.middleware, root_path="/reverse_proxied/")
return app


@pytest.fixture(scope="session")
def snake_case_app(spec):
def snake_case_app(spec, app_class):
return build_app_from_fixture(
"snake_case", spec, validate_responses=True, pythonic_params=True
"snake_case",
app_class=app_class,
spec_file=spec,
validate_responses=True,
pythonic_params=True,
)


@pytest.fixture(scope="session")
def invalid_resp_allowed_app(spec):
return build_app_from_fixture("simple", spec, validate_responses=False)
def invalid_resp_allowed_app(spec, app_class):
return build_app_from_fixture(
"simple", app_class=app_class, spec_file=spec, validate_responses=False
)


@pytest.fixture(scope="session")
def strict_app(spec):
def strict_app(spec, app_class):
return build_app_from_fixture(
"simple", spec, validate_responses=True, strict_validation=True
"simple",
app_class=app_class,
spec_file=spec,
validate_responses=True,
strict_validation=True,
)


@pytest.fixture(scope="session")
def problem_app(spec):
return build_app_from_fixture("problem", spec, validate_responses=True)
def problem_app(spec, app_class):
return build_app_from_fixture(
"problem", app_class=app_class, spec_file=spec, validate_responses=True
)


@pytest.fixture(scope="session")
def schema_app(spec):
return build_app_from_fixture("different_schemas", spec, validate_responses=True)
def schema_app(spec, app_class):
return build_app_from_fixture(
"different_schemas",
app_class=app_class,
spec_file=spec,
validate_responses=True,
)


@pytest.fixture(scope="session")
def secure_endpoint_app(spec):
def secure_endpoint_app(spec, app_class):
return build_app_from_fixture(
"secure_endpoint",
spec,
app_class=app_class,
spec_file=spec,
validate_responses=True,
)


@pytest.fixture(scope="session")
def secure_api_app(spec):
def secure_api_app(spec, app_class):
options = {"swagger_ui": False}
return build_app_from_fixture(
"secure_api", spec, options=options, auth_all_paths=True
"secure_api",
app_class=app_class,
spec_file=spec,
options=options,
auth_all_paths=True,
)


@pytest.fixture(scope="session")
def unordered_definition_app(spec):
return build_app_from_fixture("unordered_definition", spec)
def unordered_definition_app(spec, app_class):
return build_app_from_fixture(
"unordered_definition", app_class=app_class, spec_file=spec
)


@pytest.fixture(scope="session")
def bad_operations_app(spec):
return build_app_from_fixture("bad_operations", spec, resolver_error=501)
def bad_operations_app(spec, app_class):
return build_app_from_fixture(
"bad_operations", app_class=app_class, spec_file=spec, resolver_error=501
)
61 changes: 33 additions & 28 deletions tests/api/test_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_app_with_relative_path(simple_api_spec_dir, spec):
app.add_api(spec)

app_client = app.test_client()
get_bye = app_client.get("/v1.0/bye/jsantos") # type: flask.Response
get_bye = app_client.get("/v1.0/bye/jsantos")
assert get_bye.status_code == 200
assert get_bye.data == b"Goodbye jsantos"

Expand Down Expand Up @@ -51,9 +51,7 @@ def test_app_with_different_uri_parser(simple_api_spec_dir):
app.add_api("swagger.yaml")

app_client = app.test_client()
resp = app_client.get(
"/v1.0/test_array_csv_query_param?items=a,b,c&items=d,e,f"
) # type: flask.Response
resp = app_client.get("/v1.0/test_array_csv_query_param?items=a,b,c&items=d,e,f")
assert resp.status_code == 200
j = json.loads(resp.get_data(as_text=True))
assert j == ["a", "b", "c"]
Expand All @@ -63,7 +61,7 @@ def test_swagger_ui(simple_api_spec_dir, spec):
app = App(__name__, specification_dir=simple_api_spec_dir)
app.add_api(spec)
app_client = app.test_client()
swagger_ui = app_client.get("/v1.0/ui/") # type: flask.Response
swagger_ui = app_client.get("/v1.0/ui/")
assert swagger_ui.status_code == 200
spec_json_filename = "/v1.0/{spec}".format(spec=spec.replace("yaml", "json"))
assert spec_json_filename.encode() in swagger_ui.data
Expand All @@ -81,7 +79,7 @@ def test_swagger_ui_with_config(simple_api_spec_dir, spec):
)
app.add_api(spec)
app_client = app.test_client()
swagger_ui = app_client.get("/v1.0/ui/") # type: flask.Response
swagger_ui = app_client.get("/v1.0/ui/")
assert swagger_ui.status_code == 200
if "openapi" in spec:
assert b'configUrl: "swagger-ui-config.json"' in swagger_ui.data
Expand All @@ -97,13 +95,13 @@ def test_no_swagger_ui(simple_api_spec_dir, spec):
app.add_api(spec)

app_client = app.test_client()
swagger_ui = app_client.get("/v1.0/ui/") # type: flask.Response
swagger_ui = app_client.get("/v1.0/ui/")
assert swagger_ui.status_code == 404

app2 = App(__name__, specification_dir=simple_api_spec_dir)
app2.add_api(spec, swagger_ui_options={"swagger_ui": False})
app2_client = app2.test_client()
swagger_ui2 = app2_client.get("/v1.0/ui/") # type: flask.Response
swagger_ui2 = app2_client.get("/v1.0/ui/")
assert swagger_ui2.status_code == 404


Expand All @@ -119,7 +117,7 @@ def test_swagger_ui_config_json(simple_api_spec_dir, spec):
app.add_api(spec)
app_client = app.test_client()
url = "/v1.0/ui/swagger-ui-config.json"
swagger_ui_config_json = app_client.get(url) # type: flask.Response
swagger_ui_config_json = app_client.get(url)
assert swagger_ui_config_json.status_code == 200
assert swagger_ui_config == json.loads(
swagger_ui_config_json.get_data(as_text=True)
Expand All @@ -132,7 +130,7 @@ def test_no_swagger_ui_config_json(simple_api_spec_dir, spec):
app.add_api(spec)
app_client = app.test_client()
url = "/v1.0/ui/swagger-ui-config.json"
swagger_ui_config_json = app_client.get(url) # type: flask.Response
swagger_ui_config_json = app_client.get(url)
assert swagger_ui_config_json.status_code == 404


Expand All @@ -143,7 +141,7 @@ def test_swagger_json_app(simple_api_spec_dir, spec):
app_client = app.test_client()
url = "/v1.0/{spec}"
url = url.format(spec=spec.replace("yaml", "json"))
spec_json = app_client.get(url) # type: flask.Response
spec_json = app_client.get(url)
assert spec_json.status_code == 200


Expand All @@ -154,7 +152,7 @@ def test_swagger_yaml_app(simple_api_spec_dir, spec):
app_client = app.test_client()
url = "/v1.0/{spec}"
url = url.format(spec=spec)
spec_response = app_client.get(url) # type: flask.Response
spec_response = app_client.get(url)
assert spec_response.status_code == 200


Expand All @@ -171,7 +169,7 @@ def test_no_swagger_json_app(simple_api_spec_dir, spec):
app_client = app.test_client()
url = "/v1.0/{spec}"
url = url.format(spec=spec.replace("yaml", "json"))
spec_json = app_client.get(url) # type: flask.Response
spec_json = app_client.get(url)
assert spec_json.status_code == 404


Expand All @@ -193,7 +191,7 @@ def test_dict_as_yaml_path(simple_api_spec_dir, spec):

app_client = app.test_client()
url = "/v1.0/{spec}".format(spec=spec.replace("yaml", "json"))
swagger_json = app_client.get(url) # type: flask.Response
swagger_json = app_client.get(url)
assert swagger_json.status_code == 200


Expand All @@ -204,7 +202,7 @@ def test_swagger_json_api(simple_api_spec_dir, spec):

app_client = app.test_client()
url = "/v1.0/{spec}".format(spec=spec.replace("yaml", "json"))
swagger_json = app_client.get(url) # type: flask.Response
swagger_json = app_client.get(url)
assert swagger_json.status_code == 200


Expand All @@ -215,17 +213,17 @@ def test_no_swagger_json_api(simple_api_spec_dir, spec):

app_client = app.test_client()
url = "/v1.0/{spec}".format(spec=spec.replace("yaml", "json"))
swagger_json = app_client.get(url) # type: flask.Response
swagger_json = app_client.get(url)
assert swagger_json.status_code == 404


def test_swagger_json_content_type(simple_app):
app_client = simple_app.test_client()
spec = simple_app._spec_file
url = "/v1.0/{spec}".format(spec=spec.replace("yaml", "json"))
response = app_client.get(url) # type: flask.Response
response = app_client.get(url)
assert response.status_code == 200
assert response.content_type == "application/json"
assert response.headers.get("content-type") == "application/json"


def test_single_route():
Expand All @@ -242,29 +240,29 @@ def route2():

app.add_url_rule("/single1", "single1", route1, methods=["GET"])

get_single1 = app_client.get("/single1") # type: flask.Response
get_single1 = app_client.get("/single1")
assert get_single1.data == b"single 1"

post_single1 = app_client.post("/single1") # type: flask.Response
post_single1 = app_client.post("/single1")
assert post_single1.status_code == 405

post_single2 = app_client.post("/single2") # type: flask.Response
post_single2 = app_client.post("/single2")
assert post_single2.data == b"single 2"

get_single2 = app_client.get("/single2") # type: flask.Response
get_single2 = app_client.get("/single2")
assert get_single2.status_code == 405


def test_resolve_method(simple_app):
app_client = simple_app.test_client()
resp = app_client.get("/v1.0/resolver-test/method") # type: flask.Response
assert resp.data == b'"DummyClass"\n'
resp = app_client.get("/v1.0/resolver-test/method")
assert resp.text == '"DummyClass"\n'


def test_resolve_classmethod(simple_app):
app_client = simple_app.test_client()
resp = app_client.get("/v1.0/resolver-test/classmethod") # type: flask.Response
assert resp.data.decode("utf-8", "replace") == '"DummyClass"\n'
resp = app_client.get("/v1.0/resolver-test/classmethod")
assert resp.text == '"DummyClass"\n'


def test_add_api_with_function_resolver_function_is_wrapped(simple_api_spec_dir, spec):
Expand All @@ -273,9 +271,16 @@ def test_add_api_with_function_resolver_function_is_wrapped(simple_api_spec_dir,
assert api.resolver.resolve_function_from_operation_id("faux")("bah") == "bar"


def test_default_query_param_does_not_match_defined_type(default_param_error_spec_dir):
def test_default_query_param_does_not_match_defined_type(
default_param_error_spec_dir, app_class, spec
):
with pytest.raises(InvalidSpecification):
build_app_from_fixture(default_param_error_spec_dir, validate_responses=True)
build_app_from_fixture(
default_param_error_spec_dir,
app_class=app_class,
spec_file=spec,
validate_responses=True,
)


def test_handle_add_operation_error(simple_api_spec_dir, monkeypatch):
Expand Down
Loading

0 comments on commit 30375f5

Please sign in to comment.