Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: making import of ninja optional #42

Merged
merged 1 commit into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions openapi_tester/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
from __future__ import annotations

import http
import logging
from typing import TYPE_CHECKING

# pylint: disable=import-error
from ninja import NinjaAPI, Router
from ninja.testing import TestClient
try:
from ninja import NinjaAPI, Router
from ninja.testing import TestClient
except ImportError:
NinjaAPI = Router = TestClient = object
logging.info("Django-Ninja is not installed.")


from rest_framework.test import APIClient

from .exceptions import APIFrameworkNotInstalledError
from .response_handler_factory import ResponseHandlerFactory
from .schema_tester import SchemaTester
from .utils import serialize_json
Expand Down Expand Up @@ -129,8 +136,11 @@ def __init__(
schema_tester: SchemaTester | None = None,
**kwargs,
) -> None:
"""Initialize ``OpenAPIClient`` instance."""
super().__init__(*args, router_or_app=router_or_app, **kwargs)
"""Initialize ``OpenAPINinjaClient`` instance."""
if not isinstance(object, TestClient):
super().__init__(*args, router_or_app=router_or_app, **kwargs)
else:
raise APIFrameworkNotInstalledError("Django-Ninja is not installed.")
self.schema_tester = schema_tester or self._schema_tester_factory()
self._ninja_path_prefix = path_prefix

Expand Down
8 changes: 8 additions & 0 deletions openapi_tester/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@ class UndocumentedSchemaSectionError(OpenAPISchemaError):
"""

pass


class APIFrameworkNotInstalledError(Exception):
"""
Raised when a required API framework is not installed.
"""

pass
13 changes: 4 additions & 9 deletions openapi_tester/schema_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,11 +623,8 @@ def validate_request(
"""
Verifies that an OpenAPI schema definition matches an API request body.

:param request: The HTTP request
:param case_tester: Optional Callable that checks a string's casing
:param ignore_case: Optional list of keys to ignore in case testing
:param validators: Optional list of validator functions
:param **kwargs: Request keyword arguments
:param response_handler: The HTTP response handler (can be a DRF or Ninja response)
:param test_config: Optional object with test configuration
:raises: ``openapi_tester.exceptions.DocumentationError`` for inconsistencies in the API response and schema.
``openapi_tester.exceptions.CaseError`` for case errors.
"""
Expand Down Expand Up @@ -660,10 +657,8 @@ def validate_response(
"""
Verifies that an OpenAPI schema definition matches an API response.

:param response: The HTTP response
:param case_tester: Optional Callable that checks a string's casing
:param ignore_case: Optional list of keys to ignore in case testing
:param validators: Optional list of validator functions
:param response_handler: The HTTP response handler (can be a DRF or Ninja response)
:param test_config: Optional object with test configuration
:raises: ``openapi_tester.exceptions.DocumentationError`` for inconsistencies in the API response and schema.
``openapi_tester.exceptions.CaseError`` for case errors.
"""
Expand Down
14 changes: 14 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pytest
from rest_framework.response import Response

import openapi_tester
from openapi_tester.response_handler import GenericRequest
from tests.schema_converter import SchemaToPythonConverter
from tests.utils import TEST_ROOT
Expand Down Expand Up @@ -98,3 +99,16 @@ def response(
return response

return response


@pytest.fixture
def users_ninja_api_schema() -> Path:
return TEST_ROOT / "schemas" / "users_django_api_schema.yaml"


@pytest.fixture
def ninja_not_installed():
former_client = openapi_tester.clients.TestClient
openapi_tester.clients.TestClient = object
yield
openapi_tester.clients.TestClient = former_client
24 changes: 22 additions & 2 deletions tests/test_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
from django.test.testcases import SimpleTestCase
from rest_framework import status

from openapi_tester.clients import OpenAPIClient
from openapi_tester.exceptions import DocumentationError, UndocumentedSchemaSectionError
from openapi_tester.clients import OpenAPIClient, OpenAPINinjaClient
from openapi_tester.exceptions import (
APIFrameworkNotInstalledError,
DocumentationError,
UndocumentedSchemaSectionError,
)
from openapi_tester.schema_tester import SchemaTester

if TYPE_CHECKING:
Expand All @@ -33,6 +37,15 @@ def test_init_schema_tester_passed():
assert client.schema_tester is schema_tester


def test_init_schema_tester_passed_ninja():
"""Ensure passed ``SchemaTester`` instance is used."""
schema_tester = SchemaTester()

client = OpenAPINinjaClient(router_or_app=None, schema_tester=schema_tester)

assert client.schema_tester is schema_tester


def test_get_request(cars_api_schema: "Path"):
schema_tester = SchemaTester(schema_file_path=str(cars_api_schema))
openapi_client = OpenAPIClient(schema_tester=schema_tester)
Expand Down Expand Up @@ -162,3 +175,10 @@ class DummyTestCase(SimpleTestCase):
test_case._pre_setup()

assert isinstance(test_case.client, OpenAPIClient)


def test_ninja_not_installed(ninja_not_installed):
OpenAPIClient()

with pytest.raises(APIFrameworkNotInstalledError):
OpenAPINinjaClient(router_or_app=None)
22 changes: 8 additions & 14 deletions tests/test_django_ninja.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,35 @@

import pytest

from openapi_tester import OpenAPIClient, SchemaTester
from openapi_tester import SchemaTester
from openapi_tester.clients import OpenAPINinjaClient
from openapi_tester.exceptions import UndocumentedSchemaSectionError
from test_project.api.ninja.api import router
from tests.utils import TEST_ROOT

if TYPE_CHECKING:
from pathlib import Path


@pytest.fixture
def users_ninja_api_schema() -> "Path":
return TEST_ROOT / "schemas" / "users_django_api_schema.yaml"


@pytest.fixture
def client(users_ninja_api_schema: "Path") -> OpenAPIClient:
def client(users_ninja_api_schema: "Path") -> OpenAPINinjaClient:
return OpenAPINinjaClient(
router_or_app=router,
path_prefix="/ninja_api/users",
schema_tester=SchemaTester(schema_file_path=str(users_ninja_api_schema)),
)


def test_get_users(client: OpenAPIClient):
def test_get_users(client: OpenAPINinjaClient):
response = client.get("/")
assert response.status_code == 200


def test_get_user(client: OpenAPIClient):
def test_get_user(client: OpenAPINinjaClient):
response = client.get("/1")
assert response.status_code == 200


def test_create_user(client: OpenAPIClient):
def test_create_user(client: OpenAPINinjaClient):
payload = {
"name": "John Doe",
"email": "john.doe@example.com",
Expand All @@ -52,7 +46,7 @@ def test_create_user(client: OpenAPIClient):
assert response.status_code == 201


def test_update_user(client: OpenAPIClient):
def test_update_user(client: OpenAPINinjaClient):
payload = {
"name": "John Doe",
"email": "john.doe@example.com",
Expand All @@ -67,14 +61,14 @@ def test_update_user(client: OpenAPIClient):
assert response.status_code == 200


def test_delete_user(client: OpenAPIClient):
def test_delete_user(client: OpenAPINinjaClient):
response = client.delete(
path="/1",
)
assert response.status_code == 204


def test_patch_user_undocumented_path(client: OpenAPIClient):
def test_patch_user_undocumented_path(client: OpenAPINinjaClient):
payload = {
"name": "John Doe",
}
Expand Down
Loading