def test_get_pets_param_coordinates(self, spec): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets' coordinates = { 'lat': 1.12, 'lon': 32.12, } query_params = { 'limit': None, 'coordinates': json.dumps(coordinates), } request = MockRequest( host_url, 'GET', '/pets', path_pattern=path_pattern, args=query_params, ) parameters = validate_parameters(spec, request) assert parameters == RequestParameters(query={ 'limit': None, 'page': 1, 'search': '', 'coordinates': coordinates, }) body = validate_body(spec, request) assert body is None
def test_override_invalid(self, request_validator): request = MockRequest(self.host_url, 'post', '/resource/one') result = request_validator.validate(request) assert type(result.errors[0]) == InvalidSecurity assert result.security is None
def test_default_invalid(self, request_validator): request = MockRequest(self.host_url, "get", "/resource/one") result = request_validator.validate(request) assert type(result.errors[0]) == InvalidSecurity assert result.security is None
def test_get_pets_response_no_schema(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets' query_params = { 'limit': '20', } request = MockRequest( host_url, 'GET', '/pets', path_pattern=path_pattern, args=query_params, ) parameters = validate_parameters(spec, request) body = validate_body(spec, request) assert parameters == RequestParameters(query={ 'limit': 20, 'page': 1, 'search': '', }) assert body is None data = '<html></html>' response = MockResponse(data, status_code=404, mimetype='text/html') response_result = response_validator.validate(request, response) assert response_result.errors == [] assert response_result.data == data
def test_missing_body(self, validator): headers = { 'api_key': self.api_key_encoded, } cookies = { 'user': '******', } request = MockRequest( 'https://development.gigantic-server.com', 'post', '/v1/pets', path_pattern='/v1/pets', headers=headers, cookies=cookies, ) result = validator.validate(request) assert len(result.errors) == 1 assert type(result.errors[0]) == MissingRequiredRequestBody assert result.body is None assert result.parameters == RequestParameters( header={ 'api_key': self.api_key, }, cookie={ 'user': 123, }, )
def test_post_pets_plain_no_schema(self, validator, spec_dict): data = 'plain text' headers = { 'api_key': self.api_key_encoded, } cookies = { 'user': '******', } request = MockRequest( 'https://development.gigantic-server.com', 'post', '/v1/pets', path_pattern='/v1/pets', data=data, headers=headers, cookies=cookies, mimetype='text/plain', ) result = validator.validate(request) assert result.errors == [] assert result.parameters == RequestParameters( header={ 'api_key': self.api_key, }, cookie={ 'user': 123, }, ) assert result.security == {} assert result.body == data
def test_get_pets_param_order(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets" query_params = { "limit": None, "order": "desc", } request = MockRequest( host_url, "GET", "/pets", path_pattern=path_pattern, args=query_params, ) with pytest.warns(DeprecationWarning): parameters = spec_validate_parameters(spec, request) assert parameters == Parameters( query={ "limit": None, "order": "desc", "page": 1, "search": "", } ) body = spec_validate_body(spec, request) assert body is None
def test_get_pets_param_coordinates(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets" coordinates = { "lat": 1.12, "lon": 32.12, } query_params = { "limit": None, "coordinates": json.dumps(coordinates), } request = MockRequest( host_url, "GET", "/pets", path_pattern=path_pattern, args=query_params, ) with pytest.warns(DeprecationWarning): parameters = spec_validate_parameters(spec, request) assert parameters == Parameters( query={ "limit": None, "page": 1, "search": "", "coordinates": coordinates, } ) body = spec_validate_body(spec, request) assert body is None
def test_post_tags_extra_body_properties(self, spec, spec_dict): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/tags" pet_name = "Dog" alias = "kitty" data_json = { "name": pet_name, "alias": alias, } data = json.dumps(data_json) request = MockRequest( host_url, "POST", "/tags", path_pattern=path_pattern, data=data, ) parameters = spec_validate_parameters(spec, request) assert parameters == Parameters() with pytest.raises(InvalidSchemaValue): spec_validate_body(spec, request)
def test_delete_tags_raises_missing_required_response_header( self, spec, response_validator ): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/tags" request = MockRequest( host_url, "DELETE", "/tags", path_pattern=path_pattern, ) parameters = spec_validate_parameters(spec, request) body = spec_validate_body(spec, request) assert parameters == Parameters() assert body is None data = None response = MockResponse(data, status_code=200) with pytest.warns(DeprecationWarning): response_result = response_validator.validate(request, response) assert response_result.errors == [ MissingRequiredHeader(name="x-delete-confirm"), ] assert response_result.data is None
def test_get_tags(self, spec, response_validator): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/tags" request = MockRequest( host_url, "GET", "/tags", path_pattern=path_pattern, ) parameters = spec_validate_parameters(spec, request) body = spec_validate_body(spec, request) assert parameters == Parameters() assert body is None data_json = ["cats", "birds"] data = json.dumps(data_json) response = MockResponse(data) response_result = response_validator.validate(request, response) assert response_result.errors == [] assert response_result.data == data_json
def test_get_pet_wildcard(self, spec, response_validator): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets/{petId}" view_args = { "petId": "1", } request = MockRequest( host_url, "GET", "/pets/1", path_pattern=path_pattern, view_args=view_args, ) parameters = spec_validate_parameters(spec, request) assert parameters == Parameters( path={ "petId": 1, } ) body = spec_validate_body(spec, request) assert body is None data = b"imagedata" response = MockResponse(data, mimetype="image/png") with pytest.warns(UserWarning): response_result = response_validator.validate(request, response) assert response_result.errors == [] assert response_result.data == data
def test_post_pets_raises_invalid_server_error(self, spec): host_url = 'http://flowerstore.swagger.io/v1' path_pattern = '/v1/pets' data_json = { 'name': 'Cat', 'tag': 'cats', } data = json.dumps(data_json) headers = { 'api_key': '12345', } cookies = { 'user': '******', } request = MockRequest( host_url, 'POST', '/pets', path_pattern=path_pattern, data=data, mimetype='text/html', headers=headers, cookies=cookies, ) with pytest.raises(InvalidServer): validate_parameters(spec, request) with pytest.raises(InvalidServer): validate_body(spec, request)
def test_get_pets_allow_empty_value(self, spec): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets' query_params = { 'limit': 20, 'search': '', } request = MockRequest( host_url, 'GET', '/pets', path_pattern=path_pattern, args=query_params, ) with pytest.warns(DeprecationWarning): parameters = spec_validate_parameters(spec, request) assert parameters == RequestParameters(query={ 'page': 1, 'limit': 20, 'search': '', }) body = spec_validate_body(spec, request) assert body is None
def test_remove(self, request_validator): request = MockRequest(self.host_url, 'put', '/resource/one') result = request_validator.validate(request) assert not result.errors assert result.security == {}
def test_get_pet_wildcard(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets/{petId}' view_args = { 'petId': '1', } request = MockRequest( host_url, 'GET', '/pets/1', path_pattern=path_pattern, view_args=view_args, ) parameters = validate_parameters(spec, request) assert parameters == RequestParameters( path={ 'petId': 1, } ) body = validate_body(spec, request) assert body is None data = b'imagedata' response = MockResponse(data, mimetype='image/png') response_result = response_validator.validate(request, response) assert response_result.errors == [] assert response_result.data == data
def test_delete_tags_raises_missing_required_response_header( self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/tags' request = MockRequest( host_url, 'DELETE', '/tags', path_pattern=path_pattern, ) parameters = spec_validate_parameters(spec, request) body = spec_validate_body(spec, request) assert parameters == RequestParameters() assert body is None data = None response = MockResponse(data, status_code=200) response_result = response_validator.validate(request, response) assert response_result.errors == [ MissingRequiredHeader(name='x-delete-confirm'), ] assert response_result.data is None
def test_post_pets_raises_invalid_mimetype(self, spec): host_url = 'https://staging.gigantic-server.com/v1' path_pattern = '/v1/pets' data_json = { 'name': 'Cat', 'tag': 'cats', } data = json.dumps(data_json) headers = { 'api_key': self.api_key_encoded, } cookies = { 'user': '******', } request = MockRequest( host_url, 'POST', '/pets', path_pattern=path_pattern, data=data, mimetype='text/html', headers=headers, cookies=cookies, ) parameters = validate_parameters(spec, request) assert parameters == RequestParameters( header={ 'api_key': self.api_key, }, cookie={ 'user': 123, }, ) with pytest.raises(MediaTypeNotFound): validate_body(spec, request)
def test_post_pets_missing_header(self, spec, spec_dict): host_url = 'https://staging.gigantic-server.com/v1' path_pattern = '/v1/pets' pet_name = 'Cat' pet_healthy = True data_json = { 'name': pet_name, 'ears': { 'healthy': pet_healthy, } } data = json.dumps(data_json) cookies = { 'user': '******', } request = MockRequest( host_url, 'POST', '/pets', path_pattern=path_pattern, data=data, cookies=cookies, ) with pytest.raises(MissingRequiredParameter): validate_parameters(spec, request) body = validate_body(spec, request) schemas = spec_dict['components']['schemas'] pet_model = schemas['PetCreate']['x-model'] assert body.__class__.__name__ == pet_model assert body.name == pet_name assert not hasattr(body, 'tag') assert not hasattr(body, 'address')
def test_post_tags_extra_body_properties(self, spec, spec_dict): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/tags' pet_name = 'Dog' alias = 'kitty' data_json = { 'name': pet_name, 'alias': alias, } data = json.dumps(data_json) request = MockRequest( host_url, 'POST', '/tags', path_pattern=path_pattern, data=data, ) parameters = validate_parameters(spec, request) assert parameters == RequestParameters() with pytest.raises(InvalidSchemaValue): validate_body(spec, request)
def test_get_pets(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets' query_params = { 'limit': '20', } request = MockRequest( host_url, 'GET', '/pets', path_pattern=path_pattern, args=query_params, ) parameters = validate_parameters(spec, request) body = validate_body(spec, request) assert parameters == RequestParameters( query={ 'limit': 20, 'page': 1, 'search': '', } ) assert body is None data_json = { 'data': [], } data = json.dumps(data_json) response = MockResponse(data) response_result = response_validator.validate(request, response) assert response_result.errors == [] assert isinstance(response_result.data, BaseModel) assert response_result.data.data == []
def test_get_pets_none_value(self, spec): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/pets' query_params = { 'limit': None, } request = MockRequest( host_url, 'GET', '/pets', path_pattern=path_pattern, args=query_params, ) parameters = validate_parameters(spec, request) assert parameters == RequestParameters( query={ 'limit': None, 'page': 1, 'search': '', } ) body = validate_body(spec, request) assert body is None
def test_invalid_content_type(self, validator): headers = { 'api_key': self.api_key_encoded, } cookies = { 'user': '******', } request = MockRequest( 'https://development.gigantic-server.com', 'post', '/v1/pets', path_pattern='/v1/pets', mimetype='text/csv', headers=headers, cookies=cookies, ) result = validator.validate(request) assert len(result.errors) == 1 assert type(result.errors[0]) == InvalidContentType assert result.body is None assert result.parameters == RequestParameters( header={ 'api_key': self.api_key, }, cookie={ 'user': 123, }, )
def test_get_tags(self, spec, response_validator): host_url = 'http://petstore.swagger.io/v1' path_pattern = '/v1/tags' request = MockRequest( host_url, 'GET', '/tags', path_pattern=path_pattern, ) parameters = validate_parameters(spec, request) body = validate_body(spec, request) assert parameters == RequestParameters() assert body is None data_json = ['cats', 'birds'] data = json.dumps(data_json) response = MockResponse(data) response_result = response_validator.validate(request, response) assert response_result.errors == [] assert response_result.data == data_json
def test_post_pets_plain_no_schema(self, validator, spec_dict): data = "plain text" headers = { "api-key": self.api_key_encoded, } cookies = { "user": "******", } request = MockRequest( "https://development.gigantic-server.com", "post", "/v1/pets", path_pattern="/v1/pets", data=data, headers=headers, cookies=cookies, mimetype="text/plain", ) with pytest.warns(UserWarning): result = validator.validate(request) assert result.errors == [] assert result.parameters == Parameters( header={ "api-key": self.api_key, }, cookie={ "user": 123, }, ) assert result.security == {} assert result.body == data
def test_invalid_content_type(self, validator): data = "csv,data" headers = { "api-key": self.api_key_encoded, } cookies = { "user": "******", } request = MockRequest( "https://development.gigantic-server.com", "post", "/v1/pets", path_pattern="/v1/pets", mimetype="text/csv", data=data, headers=headers, cookies=cookies, ) result = validator.validate(request) assert len(result.errors) == 1 assert type(result.errors[0]) == MediaTypeNotFound assert result.body is None assert result.parameters == Parameters( header={ "api-key": self.api_key, }, cookie={ "user": 123, }, )
def test_missing_body(self, validator): headers = { "api-key": self.api_key_encoded, } cookies = { "user": "******", } request = MockRequest( "https://development.gigantic-server.com", "post", "/v1/pets", path_pattern="/v1/pets", headers=headers, cookies=cookies, ) result = validator.validate(request) assert len(result.errors) == 1 assert type(result.errors[0]) == MissingRequiredRequestBody assert result.body is None assert result.parameters == Parameters( header={ "api-key": self.api_key, }, cookie={ "user": 123, }, )
def test_post_cats_boolean_string(self, spec, spec_dict): host_url = 'https://staging.gigantic-server.com/v1' path_pattern = '/v1/pets' pet_name = 'Cat' pet_tag = 'cats' pet_street = 'Piekna' pet_city = 'Warsaw' pet_healthy = False data_json = { 'name': pet_name, 'tag': pet_tag, 'position': 2, 'address': { 'street': pet_street, 'city': pet_city, }, 'healthy': pet_healthy, 'ears': { 'healthy': pet_healthy, } } data = json.dumps(data_json) headers = { 'api_key': self.api_key_encoded, } cookies = { 'user': '******', } request = MockRequest( host_url, 'POST', '/pets', path_pattern=path_pattern, data=data, headers=headers, cookies=cookies, ) parameters = validate_parameters(spec, request) assert parameters == RequestParameters( header={ 'api_key': self.api_key, }, cookie={ 'user': 123, }, ) body = validate_body(spec, request) schemas = spec_dict['components']['schemas'] pet_model = schemas['PetCreate']['x-model'] address_model = schemas['Address']['x-model'] assert body.__class__.__name__ == pet_model assert body.name == pet_name assert body.tag == pet_tag assert body.position == 2 assert body.address.__class__.__name__ == address_model assert body.address.street == pet_street assert body.address.city == pet_city assert body.healthy is False
def validate_request( url: str, method: str, data: Union[str, bytes, Dict[str, Any]], http_headers: Dict[str, Any], json_url: bool, status_code: str, intentionally_undocumented: bool = False, ) -> None: # Some JSON endpoints have different parameters compared to # their `/api/v1` counterparts. if json_url and (url, method) in SKIP_JSON: return # TODO: Add support for file upload endpoints that lack the /json/ # or /api/v1/ prefix. if url == "/user_uploads" or url.startswith("/realm/emoji/"): return # Now using the openapi_core APIs, validate the request schema # against the OpenAPI documentation. mock_request = MockRequest("http://localhost:9991/", method, "/api/v1" + url, headers=http_headers, args=data) result = openapi_spec.request_validator().validate(mock_request) if len(result.errors) != 0: # Requests that do not validate against the OpenAPI spec must either: # * Have returned a 400 (bad request) error # * Have returned a 200 (success) with this request marked as intentionally # undocumented behavior. if status_code.startswith("4"): return if status_code.startswith("2") and intentionally_undocumented: return # If no errors are raised, then validation is successful if len(result.errors) == 0: return # Show a block error message explaining the options for fixing it. msg = f""" Error! The OpenAPI schema for {method} {url} is not consistent with the parameters passed in this HTTP request. Consider: * Updating the OpenAPI schema defined in zerver/openapi/zulip.yaml * Adjusting the test to pass valid parameters. If the test fails due to intentionally_undocumented features, you need to pass `intentionally_undocumented=True` to self.client_{method.lower()} or self.api_{method.lower()} to document your intent. See https://zulip.readthedocs.io/en/latest/documentation/api.html for help. The errors logged by the OpenAPI validator are below:\n""" for error in result.errors: msg += f"* {str(error)}\n" raise SchemaError(msg)
def test_request_missing_param(self, validator): request = MockRequest('http://example.com', 'get', '/resource') result = validator.validate(request) assert len(result.errors) == 1 assert type(result.errors[0]) == MissingRequiredParameter assert result.body is None assert result.parameters == RequestParameters()