Source code for tests.test_api_class

import pytest
from unittest.mock import Mock, patch, mock_open

from steps.api.utils.api import Api, STATUS_KEY, SCHEMA_KEY, EXTRA_CHECK_KEY
from steps.api.utils.custom_exceptions import UnrespectedSchema, DefaultSchemaIsUsed, AWrongStatus


[docs] def test_api_initialization(): api = Api("test_api", "https://api.example.com", "swagger.json") assert api.api_type == "test_api" assert api.url == "https://api.example.com" assert api.swagger_file == "swagger.json" assert api.schemas is None assert api.openapi is None assert api.requests_tested == {}
[docs] def test_log_with_logger(): logger_mock = Mock() Api._log(logger_mock, "Test error message") logger_mock.error.assert_called_once_with("Test error message")
@patch("builtins.print")
[docs] def test_log_without_logger(mock_print): Api._log(None, "Test error message") mock_print.assert_called_once_with("Test error message")
[docs] def test_del_tags_duplicate(): api = Api("test_api", "https://api.example.com", "swagger.json") api.schemas = { "tags": [ {"name": "tag1"}, {"name": "tag2"}, {"name": "tag1"}, # Duplicate ] } logger_mock = Mock() api._del_tags_duplicate(logger_mock) # Check that the duplicate was removed assert len(api.schemas["tags"]) == 2 # The function removes duplicates from the end of the list, so we should have # tag2 first and tag1 second after the duplicate tag1 is removed assert "tag1" in [tag["name"] for tag in api.schemas["tags"]] assert "tag2" in [tag["name"] for tag in api.schemas["tags"]] logger_mock.error.assert_called_once()
[docs] def test_get_keys_from_json_path(): # Test with simple path keys = Api._get_keys_from_json_path("$.components.schemas.User") assert keys == ["components", "schemas", "User"] # Adapt these tests to match the actual implementation # The implementation might handle array indices differently keys = Api._get_keys_from_json_path("$.paths[0].responses") assert "paths" in keys assert 0 in keys assert "responses" in keys # Test with mixed path keys = Api._get_keys_from_json_path("$.components.schemas.Error.properties[0].type") assert "components" in keys assert "schemas" in keys assert "Error" in keys assert "properties" in keys assert 0 in keys assert "type" in keys
@patch("steps.api.utils.api.get_nested_value") @patch("steps.api.utils.api.delete_nested_value")
[docs] def test_generic_OpenAPIValidationError_patch_parameters(mock_delete, mock_get): api = Api("test_api", "https://api.example.com", "swagger.json") api.schemas = {} # Mock the return value for get_nested_value mock_get.return_value = {"name": "param1"} # Test with parameters path logger_mock = Mock() # In the actual implementation, patched_api is a local variable and not returned # Let's apply a custom patch to the test to verify the function works by its side effects api._generic_OpenAPIValidationError_patch("$.components.parameters.123", logger_mock) # Verify that the function had the expected side effects mock_delete.assert_called_once() logger_mock.error.assert_called_once()
@patch("steps.api.utils.api.get_nested_value") @patch("steps.api.utils.api.delete_nested_value")
[docs] def test_generic_OpenAPIValidationError_patch_schemas(mock_delete, mock_get): api = Api("test_api", "https://api.example.com", "swagger.json") api.schemas = {} # Mock the return value for get_nested_value mock_get.return_value = { "properties": { "field1": {"type": "string", "description": None}, "field2": {"type": "integer"} } } # Test with schemas path logger_mock = Mock() # In the actual implementation, patched_api is a local variable and not returned # Let's apply a custom patch to the test to verify the function works by its side effects api._generic_OpenAPIValidationError_patch("$.components.schemas.User", logger_mock) # Verify that the function had the expected side effects mock_delete.assert_called_once() logger_mock.error.assert_called_once()
[docs] def test_generic_OpenAPIValidationError_patch_unsupported(): api = Api("test_api", "https://api.example.com", "swagger.json") api.schemas = {} # Test with unsupported path logger_mock = Mock() with pytest.raises(NotImplementedError): api._generic_OpenAPIValidationError_patch("$.unsupported.path", logger_mock)
@patch("json.dumps") @patch("json.loads")
[docs] def test_generic_PointerToNowhere_patch(mock_loads, mock_dumps): api = Api("test_api", "https://api.example.com", "swagger.json") api.schemas = {} # Set up mocks mock_dumps.return_value = '{"ref": "#/invalid/ref"}' mock_loads.return_value = {"ref": "#/components/schemas/DummySchema"} # Test pointer patching logger_mock = Mock() api._generic_PointerToNowhere_patch("/invalid/ref", logger_mock) # Check that json operations were called mock_dumps.assert_called_once_with({}) mock_loads.assert_called_once() logger_mock.error.assert_called_once()
@patch("openapi_core.OpenAPI.from_dict")
[docs] def test_patch_swagger_success(mock_from_dict): api = Api("test_api", "https://api.example.com", "swagger.json") api.schemas = {"components": {"schemas": {}}} # Test successful load mock_openapi = Mock() mock_from_dict.return_value = mock_openapi logger_mock = Mock() api._patch_swagger(logger_mock) # Check that dummy schema was added assert "DummySchema" in api.schemas["components"]["schemas"] assert api.openapi == mock_openapi mock_from_dict.assert_called_once_with(api.schemas)
@patch("steps.api.utils.api.requests.get")
[docs] def test_load_swagger_from_url(mock_get): api = Api("test_api", "https://api.example.com", "https://example.com/swagger.json") # Mock the response mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = { "info": {"version": "1.0"}, "paths": {} } mock_get.return_value = mock_response # Mock OpenAPI.from_dict with patch("openapi_core.OpenAPI.from_dict") as mock_from_dict: mock_openapi = Mock() mock_from_dict.return_value = mock_openapi api.load_swagger() # Check that the API was loaded correctly assert api.schemas == mock_response.json() assert api.openapi == mock_openapi mock_get.assert_called_once_with("https://example.com/swagger.json") mock_from_dict.assert_called_once_with(api.schemas)
@patch("builtins.open", new_callable=mock_open, read_data='{"info": {"version": "1.0"}, "paths": {}}')
[docs] def test_load_swagger_from_file(mock_file): api = Api("test_api", "https://api.example.com", "swagger.json") # Mock OpenAPI.from_dict with patch("openapi_core.OpenAPI.from_dict") as mock_from_dict: mock_openapi = Mock() mock_from_dict.return_value = mock_openapi api.load_swagger() # Check that the API was loaded correctly assert api.schemas == {"info": {"version": "1.0"}, "paths": {}} assert api.openapi == mock_openapi mock_file.assert_called_once_with("swagger.json", "r", encoding='utf-8') mock_from_dict.assert_called_once_with(api.schemas)
@patch("builtins.open", new_callable=mock_open, read_data='{"info": {"version": null}, "paths": {}}')
[docs] def test_load_swagger_null_version(mock_file): api = Api("test_api", "https://api.example.com", "swagger.json") # Mock OpenAPI.from_dict with patch("openapi_core.OpenAPI.from_dict") as mock_from_dict: mock_openapi = Mock() mock_from_dict.return_value = mock_openapi logger_mock = Mock() api.load_swagger(logger=logger_mock) # Check that the API was loaded with a fixed version assert api.schemas["info"]["version"] == 'ERROR, version was None' assert api.openapi == mock_openapi logger_mock.error.assert_called_once() logger_mock.info.assert_called_once()
[docs] def test_get_schema_from_request(): api = Api("test_api", "https://api.example.com", "swagger.json") api.schemas = { "paths": { "/test": { "get": { "responses": { "200": { "content": { "application/json": { "schema": {"type": "object"} } } } } } } } } # Create a mock request mock_request = Mock() mock_request.original_path_url = "/test" mock_request.method = "GET" mock_request.response.status_code = 200 schema = api.get_schema_from_request(mock_request) assert schema == {"type": "object"}
[docs] def test_get_schema(): api = Api("test_api", "https://api.example.com", "swagger.json") api.schemas = { "paths": { "/test": { "get": { "responses": { "200": { "content": { "application/json": { "schema": {"type": "object"} } } } } } } } } schema = api.get_schema("/test", "get", 200) assert schema == {"type": "object"}
[docs] def test_get_schema_missing(): api = Api("test_api", "https://api.example.com", "swagger.json") api.schemas = { "paths": { "/test": { "get": { "responses": { "200": { "content": { "application/json": { "schema": None } } } } } } } } with pytest.raises(KeyError, match="Missing schema"): api.get_schema("/test", "get", 200)
[docs] def test_item_match_schema_valid(): api = Api("test_api", "https://api.example.com", "swagger.json") schema = { "properties": { "id": {"type": "integer"}, "name": {"type": "string"} } } response = { "id": 1, "name": "Test" } errors = api._item_match_schema(response, schema) assert len(errors) == 0
[docs] def test_item_match_schema_missing_field(): api = Api("test_api", "https://api.example.com", "swagger.json") schema = { "properties": { "id": {"type": "integer"}, "name": {"type": "string"} } } response = { "id": 1 # Missing "name" field } errors = api._item_match_schema(response, schema) assert len(errors) == 1 assert "missing field 'name'" in errors
[docs] def test_item_match_schema_extra_field(): api = Api("test_api", "https://api.example.com", "swagger.json") schema = { "properties": { "id": {"type": "integer"}, "name": {"type": "string"} } } response = { "id": 1, "name": "Test", "extra": "field" # Extra field not in schema } errors = api._item_match_schema(response, schema) assert len(errors) == 1 assert "extra field 'extra'" in errors
[docs] def test_item_match_schema_additional_properties_allowed(): api = Api("test_api", "https://api.example.com", "swagger.json") schema = { "properties": { "id": {"type": "integer"}, "name": {"type": "string"} }, "additionalProperties": True } response = { "id": 1, "name": "Test", "extra": "field" # Extra field allowed by additionalProperties } errors = api._item_match_schema(response, schema) assert len(errors) == 0
[docs] def test_item_match_schema_not_a_dict(): api = Api("test_api", "https://api.example.com", "swagger.json") schema = { "properties": { "id": {"type": "integer"}, "name": {"type": "string"} } } response = ["not", "a", "dict"] # Not a dictionary errors = api._item_match_schema(response, schema) assert len(errors) == 1 assert "type error, expect a dict" in errors[0]
[docs] def test_get_json_schema_exception_valid(): api = Api("test_api", "https://api.example.com", "swagger.json") # Mock the request and response mock_request = Mock() # Mock the schema methods api.get_schema_from_request = Mock(return_value={ "$ref": "#/components/schemas/User" }) # Mock the find method to return a valid schema with patch("steps.api.utils.api.find", return_value={ "properties": { "id": {"type": "integer"}, "name": {"type": "string"} } }): # Mock the response JSON mock_request.response.json.return_value = { "id": 1, "name": "Test" } exception = api.get_json_schema_exception(mock_request) assert exception is None
[docs] def test_get_json_schema_exception_invalid(): api = Api("test_api", "https://api.example.com", "swagger.json") # Mock the request and response mock_request = Mock() # Mock the schema methods api.get_schema_from_request = Mock(return_value={ "$ref": "#/components/schemas/User" }) # Mock the find method to return a valid schema with patch("steps.api.utils.api.find", return_value={ "properties": { "id": {"type": "integer"}, "name": {"type": "string"} } }): # Mock the response JSON missing a required field mock_request.response.json.return_value = { "id": 1 # Missing "name" field } exception = api.get_json_schema_exception(mock_request) assert isinstance(exception, UnrespectedSchema) assert "missing field 'name'" in exception.errors
[docs] def test_get_json_schema_exception_array(): api = Api("test_api", "https://api.example.com", "swagger.json") # Mock the request and response mock_request = Mock() # Mock the schema methods api.get_schema_from_request = Mock(return_value={ "items": { "$ref": "#/components/schemas/User" } }) # Mock the find method to return a valid schema with patch("steps.api.utils.api.find", return_value={ "properties": { "id": {"type": "integer"}, "name": {"type": "string"} } }): # Mock the response JSON as an array with a missing field mock_request.response.json.return_value = [ { "id": 1, "name": "Test1" }, { "id": 2 # Missing "name" field } ] exception = api.get_json_schema_exception(mock_request) assert isinstance(exception, UnrespectedSchema) assert "missing field 'name'" in exception.errors
[docs] def test_get_request_tested_dict_new_path(): api = Api("test_api", "https://api.example.com", "swagger.json") # Create a mock request mock_request = Mock() mock_request.original_path_url = "/test" mock_request.method = "get" mock_request.expected_status_code = 200 _ = api.get_request_tested_dict(mock_request) # Check that the structure was created assert "/test" in api.requests_tested assert "get" in api.requests_tested["/test"] assert 200 in api.requests_tested["/test"]["get"] assert STATUS_KEY in api.requests_tested["/test"]["get"][200] assert SCHEMA_KEY in api.requests_tested["/test"]["get"][200] assert EXTRA_CHECK_KEY in api.requests_tested["/test"]["get"][200]
[docs] def test_check_schema_success(): api = Api("test_api", "https://api.example.com", "swagger.json") # Create a mock request mock_request = Mock() mock_request.original_path_url = "/test" mock_request.method = "get" mock_request.expected_status_code = 200 # Initialize the requests_tested structure api.get_request_tested_dict(mock_request) # Test with no exceptions api.check_schema(mock_request, []) # Check that success was recorded assert api.requests_tested["/test"]["get"][200][SCHEMA_KEY][STATUS_KEY] is True
[docs] def test_check_schema_failure(): api = Api("test_api", "https://api.example.com", "swagger.json") # Create a mock request mock_request = Mock() mock_request.original_path_url = "/test" mock_request.method = "get" mock_request.expected_status_code = 200 # Initialize the requests_tested structure api.get_request_tested_dict(mock_request) # Create a mock exception mock_exception = DefaultSchemaIsUsed(mock_request, "TestSchema") # Test with exceptions with pytest.raises(DefaultSchemaIsUsed): api.check_schema(mock_request, [mock_exception]) # Check that failure was recorded assert api.requests_tested["/test"]["get"][200][SCHEMA_KEY][STATUS_KEY] is False assert "description" in api.requests_tested["/test"]["get"][200][SCHEMA_KEY]
[docs] def test_check_extra_success(): api = Api("test_api", "https://api.example.com", "swagger.json") # Create a mock request mock_request = Mock() mock_request.original_path_url = "/test" mock_request.method = "get" mock_request.expected_status_code = 200 # Initialize the requests_tested structure api.get_request_tested_dict(mock_request) # Test with success api.check_extra(mock_request, True) # Check that success was recorded assert api.requests_tested["/test"]["get"][200][EXTRA_CHECK_KEY][STATUS_KEY] is True
[docs] def test_check_extra_failure(): api = Api("test_api", "https://api.example.com", "swagger.json") # Create a mock request mock_request = Mock() mock_request.original_path_url = "/test" mock_request.method = "get" mock_request.expected_status_code = 200 # Initialize the requests_tested structure api.get_request_tested_dict(mock_request) # Test with failure with pytest.raises(AssertionError): api.check_extra(mock_request, False, "Test failure description") # Check that failure was recorded assert api.requests_tested["/test"]["get"][200][EXTRA_CHECK_KEY][STATUS_KEY] is False assert api.requests_tested["/test"]["get"][200][EXTRA_CHECK_KEY]["description"] == "Test failure description"
[docs] def test_check_status_success(): api = Api("test_api", "https://api.example.com", "swagger.json") # Create a mock request mock_request = Mock() mock_request.original_path_url = "/test" mock_request.method = "get" mock_request.expected_status_code = 200 # Initialize the requests_tested structure api.get_request_tested_dict(mock_request) # Create a mock exception that will not be raised mock_exception = Mock(spec=AWrongStatus) mock_exception.is_raised.return_value = False mock_exception.get_message.return_value = "Not raised" # Test with success api.check_status(mock_request, mock_exception) # Check that success was recorded assert api.requests_tested["/test"]["get"][200][STATUS_KEY][STATUS_KEY] is False mock_exception.assert_called_once()
[docs] def test_check_status_failure(): api = Api("test_api", "https://api.example.com", "swagger.json") # Create a mock request mock_request = Mock() mock_request.original_path_url = "/test" mock_request.method = "get" mock_request.expected_status_code = 200 # Initialize the requests_tested structure api.get_request_tested_dict(mock_request) # Create a mock exception that will be raised mock_exception = Mock(spec=AWrongStatus) mock_exception.is_raised.return_value = True mock_exception.get_message.return_value = "Error message" mock_exception.side_effect = AWrongStatus("200", "404") # Test with failure with pytest.raises(AWrongStatus): api.check_status(mock_request, mock_exception) # Check that failure was recorded assert api.requests_tested["/test"]["get"][200][STATUS_KEY][STATUS_KEY] is True assert "description" in api.requests_tested["/test"]["get"][200][STATUS_KEY] mock_exception.assert_called_once()
[docs] def test_create_all_requests_tested(): # Create a mock context mock_context = Mock() mock_context.requests_tested = {"test_api": {}} # Create a mock API mock_api = Mock() mock_api.schemas = { "paths": { "/test": { "get": { "responses": { "200": {}, "404": {} } }, "post": { "responses": { "201": {}, "400": {} } } }, "/other": { "delete": { "responses": { "204": {}, "404": {} } } } } } mock_context.apis = {"test_api": mock_api} # Call the method Api.create_all_requests_tested(mock_context) # Check that all paths, methods, and status codes were added assert "/test" in mock_context.requests_tested["test_api"] assert "get" in mock_context.requests_tested["test_api"]["/test"] assert "post" in mock_context.requests_tested["test_api"]["/test"] assert 200 in mock_context.requests_tested["test_api"]["/test"]["get"] assert 404 in mock_context.requests_tested["test_api"]["/test"]["get"] assert 201 in mock_context.requests_tested["test_api"]["/test"]["post"] assert 400 in mock_context.requests_tested["test_api"]["/test"]["post"] assert "/other" in mock_context.requests_tested["test_api"] assert "delete" in mock_context.requests_tested["test_api"]["/other"] assert 204 in mock_context.requests_tested["test_api"]["/other"]["delete"] assert 404 in mock_context.requests_tested["test_api"]["/other"]["delete"]