Пример #1
0
def test_numeric_status_codes(empty_open_api_3_schema):
    # When the API schema contains a numeric status code, which is not allowed by the spec
    empty_open_api_3_schema["paths"] = {
        "/foo": {
            "parameters": [],
            "get": {
                "responses": {
                    200: {
                        "description": "OK"
                    }
                },
            },
            "post": {
                "responses": {
                    201: {
                        "description": "OK"
                    }
                },
            },
        },
    }
    # And schema validation is enabled
    # Then Schemathesis reports an error about numeric status codes
    with pytest.raises(SchemaLoadingError,
                       match=NUMERIC_STATUS_CODES_MESSAGE) as exc:
        schemathesis.from_dict(empty_open_api_3_schema)
    # And shows all locations of these keys
    assert " - 200 at schema['paths']['/foo']['get']['responses']" in exc.value.args[
        0]
    assert " - 201 at schema['paths']['/foo']['post']['responses']" in exc.value.args[
        0]
Пример #2
0
def test_invalid_code_sample_style(empty_open_api_3_schema):
    with pytest.raises(
            ValueError,
            match=
            "Invalid value for code sample style: ruby. Available styles: python, curl"
    ):
        schemathesis.from_dict(empty_open_api_3_schema,
                               code_sample_style="ruby")
Пример #3
0
def api_schema(request, openapi_version):
    if openapi_version.is_openapi_2:
        schema = request.getfixturevalue("empty_open_api_2_schema")
        schema["paths"] = {
            "/payload": {
                "post": {
                    "parameters": [{
                        "in": "body",
                        "required": True,
                        "name": "payload",
                        "schema": {
                            "type": "boolean",
                            "x-nullable": True
                        },
                    }],
                    "responses": {
                        "200": {
                            "description": "OK"
                        }
                    },
                }
            }
        }
        jsonschema.validate(schema, SWAGGER_20)
    else:
        schema = request.getfixturevalue("empty_open_api_3_schema")
        schema["paths"] = {
            "/payload": {
                "post": {
                    "requestBody": {
                        "required": True,
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "boolean",
                                    "nullable": True
                                }
                            }
                        },
                    },
                    "responses": {
                        "200": {
                            "description": "OK"
                        }
                    },
                }
            }
        }
        jsonschema.validate(schema, OPENAPI_30)
    if request.param == "aiohttp":
        base_url = request.getfixturevalue("base_url")
        return schemathesis.from_dict(schema, base_url=base_url)
    app = request.getfixturevalue("flask_app")
    return schemathesis.from_dict(schema, app=app, base_url="/api")
Пример #4
0
def test_date_format(data):
    raw_schema = {
        "openapi": "3.0.2",
        "info": {"title": "Test", "description": "Test", "version": "0.1.0"},
        "paths": {
            "/data": {
                "post": {
                    "requestBody": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "format": "date",
                                    "type": "string",
                                },
                            }
                        },
                        "required": True,
                    },
                    "responses": {"200": {"description": "OK"}},
                }
            }
        },
    }
    schema = schemathesis.from_dict(raw_schema)
    strategy = schema["/data"]["POST"].as_strategy()
    case = data.draw(strategy)
    datetime.datetime.strptime(case.body, "%Y-%m-%d")
Пример #5
0
def test_parameters_jsonified(empty_open_api_3_schema, expected):
    # See GH-1166
    # When `None` or `True` / `False` are generated in path or query
    empty_open_api_3_schema["paths"] = {
        "/foo/{param_path}": {
            "get": {
                "parameters": [
                    {
                        "name": f"param_{location}",
                        "in": location,
                        "required": True,
                        "schema": {"type": "boolean", "nullable": True},
                    }
                    for location in ("path", "query")
                ],
                "responses": {"200": {"description": "OK"}},
            }
        }
    }

    schema = schemathesis.from_dict(empty_open_api_3_schema)

    strategy = schema["/foo/{param_path}"]["GET"].as_strategy()

    @given(case=strategy)
    @settings(deadline=None, max_examples=1)
    def test(case):
        # Then they should be converted to their JSON equivalents
        assume(case.path_parameters["param_path"] == expected)
        assume(case.query["param_query"] == expected)

    test()
Пример #6
0
def test_iter_parameters(empty_open_api_3_schema):
    empty_open_api_3_schema["paths"] = {
        "/data": {
            "post": {
                "parameters": [
                    {
                        "name": "X-id",
                        "in": "header",
                        "required": True,
                        "schema": {
                            "type": "string"
                        },
                    },
                    {
                        "name": "q",
                        "in": "query",
                        "required": True,
                        "schema": {
                            "type": "string"
                        },
                    },
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                },
            },
        },
    }
    schema = schemathesis.from_dict(empty_open_api_3_schema)
    params = list(schema["/data"]["POST"].iter_parameters())
    assert len(params) == 2
    assert params[0].name == "X-id"
    assert params[1].name == "q"
Пример #7
0
def test_case_insensitive_headers(empty_open_api_3_schema):
    empty_open_api_3_schema["paths"] = {
        "/data": {
            "post": {
                "parameters": [{
                    "name": "X-id",
                    "in": "header",
                    "required": True,
                    "schema": {
                        "type": "string"
                    },
                }],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                },
            },
        },
    }
    # When headers are generated
    schema = schemathesis.from_dict(empty_open_api_3_schema)

    @given(case=schema["/data"]["POST"].as_strategy())
    @settings(max_examples=1)
    def test(case):
        assert "X-id" in case.headers
        # Then they are case-insensitive
        case.headers["x-ID"] = "foo"
        assert len(case.headers) == 1
        assert case.headers["X-id"] == "foo"

    test()
Пример #8
0
def test_partial_examples(empty_open_api_3_schema):
    # When the API schema contains multiple parameters in the same location
    # And some of them don't have explicit examples and others do
    empty_open_api_3_schema["paths"] = {
        "/test/{foo}/{bar}/": {
            "post": {
                "parameters": [
                    {"name": "foo", "in": "path", "required": True, "schema": {"type": "string", "enum": ["A"]}},
                    {
                        "name": "bar",
                        "in": "path",
                        "required": True,
                        "schema": {"type": "string", "example": "bar-example"},
                    },
                ],
                "responses": {"default": {"description": "OK"}},
            }
        }
    }
    schema = schemathesis.from_dict(empty_open_api_3_schema)
    operation = schema["/test/{foo}/{bar}/"]["POST"]
    strategy = operation.get_strategies_from_examples()[0]
    # Then all generated examples should have those missing parts generated according to the API schema
    example = get_single_example(strategy)
    parameters_schema = parameters_to_json_schema(operation.path_parameters)
    jsonschema.validate(example.path_parameters, parameters_schema)
Пример #9
0
def test_ref_field():
    # When the schema contains "$ref" field, that is not a reference (which is supported by the JSON Schema spec)
    raw_schema = {
        "openapi": "3.0.2",
        "info": {"title": "Test", "description": "Test", "version": "0.1.0"},
        "paths": {
            "/body": {
                "post": {
                    "requestBody": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "properties": {"$ref": {"type": "string"}},
                                    "required": ["$ref"],
                                    "type": "object",
                                }
                            }
                        },
                        "required": True,
                    },
                    "responses": {"200": {"description": "OK"}},
                }
            }
        },
    }
    schema = schemathesis.from_dict(raw_schema)

    @given(case=schema["/body"]["POST"].as_strategy())
    @settings(max_examples=5)
    def test(case):
        assert isinstance(case.body["$ref"], str)

    # Then "$ref" field should be generated
    test()
Пример #10
0
def test_link_override(empty_open_api_3_schema, schema_code, link_code):
    # See GH-1022
    # When the schema contains response codes as integers
    empty_open_api_3_schema["paths"] = {
        "/foo": {
            "get": {
                "parameters": [
                    {
                        "in": "query",
                        "name": "key",
                        "schema": {
                            "type": "integer"
                        },
                        "required": True
                    },
                ],
                "responses": {
                    schema_code: {
                        "description": "OK",
                        "schema": {
                            "type": "object"
                        }
                    }
                },
            }
        },
    }
    schema = schemathesis.from_dict(empty_open_api_3_schema,
                                    validate_schema=False)
    schema.add_link(source=schema["/foo"]["GET"],
                    target=schema["/foo"]["GET"],
                    status_code=link_code,
                    parameters={"key": "42"})
    assert "links" in schema.raw_schema["paths"]["/foo"]["get"]["responses"][
        schema_code]
Пример #11
0
def test_binary_body(mocker, flask_app):
    # When an endpoint accepts a binary input
    schema = schemathesis.from_dict(
        {
            "openapi": "3.0.2",
            "info": {"title": "Test", "description": "Test", "version": "0.1.0"},
            "paths": {
                "/api/upload_file": {
                    "post": {
                        "requestBody": {
                            "content": {"application/octet-stream": {"schema": {"format": "binary", "type": "string"}}}
                        },
                        "responses": {"200": {"description": "OK"}},
                    }
                }
            },
        },
        app=flask_app,
    )
    strategy = schema.endpoints["/api/upload_file"]["POST"].as_strategy()

    @given(case=strategy)
    @settings(max_examples=3, suppress_health_check=[HealthCheck.filter_too_much], deadline=None)
    def test(case):
        response = case.call_wsgi()
        assert response.status_code == 200
        assert response.json == {"size": mocker.ANY}

    # Then it should be sent correctly
    test()
Пример #12
0
def test_binary_data(empty_open_api_3_schema, media_type):
    empty_open_api_3_schema["paths"] = {
        "/test": {
            "post": {
                "requestBody": {
                    "required": True,
                    "content": {
                        media_type: {
                            "schema": {},
                            "examples": {"answer": {"externalValue": f"http://127.0.0.1:1/answer.json"}},
                        }
                    },
                },
                "responses": {"200": {"description": "OK"}},
            },
        },
    }
    schema = schemathesis.from_dict(empty_open_api_3_schema)
    operation = schema["/test"]["POST"]
    # When an explicit bytes value is passed as body (it happens with `externalValue`)
    body = b"\x92\x42"
    case = operation.make_case(body=body, media_type=media_type)
    # Then it should be used as is
    requests_kwargs = case.as_requests_kwargs()
    assert requests_kwargs["data"] == body
    werkzeug_kwargs = case.as_werkzeug_kwargs()
    assert werkzeug_kwargs["data"] == body
    if media_type != "multipart/form-data":
        # Don't know the proper header for raw multipart content
        assert requests_kwargs["headers"]["Content-Type"] == media_type
        assert werkzeug_kwargs["headers"]["Content-Type"] == media_type
    # And it is OK to send it over the network
    assert_requests_call(case)
Пример #13
0
def test_make_case_missing_media_type(empty_open_api_3_schema):
    # When there are multiple available media types
    empty_open_api_3_schema["paths"] = {
        "/data": {
            "post": {
                "requestBody": {
                    "required": True,
                    "content": {
                        "text/plain": {
                            "schema": {
                                "type": "string"
                            }
                        },
                        "application/json": {
                            "schema": {
                                "type": "array"
                            }
                        },
                    },
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                },
            },
        },
    }
    schema = schemathesis.from_dict(empty_open_api_3_schema)
    # And the `media_type` argument is not passed to `make_case`
    # Then there should be a usage error
    with pytest.raises(UsageError):
        schema["/data"]["POST"].make_case(body="foo")
Пример #14
0
def test_multipart_examples():
    # Regression after parameters refactoring
    # When the schema contains examples for multipart forms
    raw_schema = {
        "openapi": "3.0.0",
        "info": {"title": "Sample API", "description": "API description in Markdown.", "version": "1.0.0"},
        "paths": {
            "/test": {
                "post": {
                    "description": "Test",
                    "requestBody": {
                        "content": {
                            "multipart/form-data": {
                                "schema": {
                                    "properties": {
                                        "key": {
                                            "example": "test",
                                            "type": "string",
                                        },
                                    },
                                    "type": "object",
                                }
                            }
                        }
                    },
                    "responses": {"default": {"description": "OK"}},
                },
            },
        },
    }
    schema = schemathesis.from_dict(raw_schema)
    # Then examples should be correctly generated
    strategies = schema["/test"]["POST"].get_strategies_from_examples()
    assert len(strategies) == 1
    assert find(strategies[0], lambda case: case.body == {"key": "test"})
Пример #15
0
def test_path_serialization_styles_openapi3(schema, style, explode, expected):
    raw_schema = {
        "openapi": "3.0.2",
        "info": {"title": "Test", "description": "Test", "version": "0.1.0"},
        "paths": {
            "/teapot/{color}": {
                "get": {
                    "summary": "Test",
                    "parameters": [
                        {
                            "name": "color",
                            "in": "path",
                            "required": True,
                            "schema": schema,
                            "style": style,
                            "explode": explode,
                        }
                    ],
                    "responses": {"200": {"description": "OK"}},
                }
            }
        },
    }
    schema = schemathesis.from_dict(raw_schema)

    @given(case=schema["/teapot/{color}"]["GET"].as_strategy())
    def test(case):
        assert case.path_parameters == expected

    test()
Пример #16
0
def test_invalid_body_in_get_disable_validation(simple_schema):
    schema = schemathesis.from_dict(simple_schema, validate_schema=False)
    endpoint = Endpoint(
        path="/foo",
        method="GET",
        definition=EndpointDefinition({}, {}, "foo"),
        schema=schema,
        body={
            "required": ["foo"],
            "type": "object",
            "properties": {
                "foo": {
                    "type": "string"
                }
            }
        },
    )
    strategy = get_case_strategy(endpoint)

    @given(strategy)
    @settings(max_examples=1)
    def test(case):
        assert case.body is not None

    test()
Пример #17
0
def _assert_parameter(schema, schema_spec, location, expected=None):
    # When security definition is defined as "apiKey"
    schema = schemathesis.from_dict(schema)
    if schema_spec == "swagger":
        operation = schema["/users"]["get"]
        expected = (expected if expected is not None else [{
            "in": location,
            "name": "api_key",
            "type": "string",
            "required": True
        }])
    else:
        operation = schema["/query"]["get"]
        expected = (expected if expected is not None else [{
            "in": location,
            "name": "api_key",
            "schema": {
                "type": "string"
            },
            "required": True
        }])
    parameters = schema.security.get_security_definitions_as_parameters(
        schema.raw_schema, operation, schema.resolver, location)
    # Then it should be presented as a "string" parameter
    assert parameters == expected
Пример #18
0
def test_invalid_x_examples(empty_open_api_2_schema):
    # See GH-982
    # When an Open API 2.0 schema contains a non-object type in `x-examples`
    empty_open_api_2_schema["paths"] = {
        "/test": {
            "post": {
                "parameters": [{
                    "name": "body",
                    "in": "body",
                    "schema": {
                        "type": "integer"
                    },
                    "x-examples": {
                        "foo": "value"
                    }
                }],
                "responses": {
                    "default": {
                        "description": "OK"
                    }
                },
            }
        }
    }
    schema = schemathesis.from_dict(empty_open_api_2_schema)
    # Then such examples should be skipped as invalid (there should be an object)
    assert schema["/test"]["POST"].get_strategies_from_examples() == []
Пример #19
0
def test_operation_id(operation_id):
    parameters = {"responses": {"200": {"description": "OK"}}}
    raw = make_schema(
        "simple_openapi.yaml",
        paths={
            "/foo": {
                "get": {
                    **parameters, "operationId": "foo_get"
                }
            },
            "/bar": {
                "get": {
                    **parameters, "operationId": "bar_get"
                },
                "post": {
                    **parameters, "operationId": "bar_post"
                },
                "put": parameters,
            },
        },
    )
    schema = schemathesis.from_dict(raw, operation_id=operation_id)

    assert schema.operation_id == operation_id

    assert len(list(schema.get_all_operations())) == 1
    for operation in schema.get_all_operations():
        assert operation.ok().definition.raw["operationId"] == operation_id
Пример #20
0
def test_optional_form_data(openapi3_base_url, empty_open_api_3_schema):
    empty_open_api_3_schema["paths"]["/form"] = {
        "post": {
            "requestBody": {
                "content": {
                    "multipart/form-data": {
                        "schema": {
                            "type": "string",
                        },
                    }
                }
            },
            "responses": {"200": {"description": "OK"}},
        }
    }
    # When the multipart form is optional
    # Note, this test is similar to the one above, but has a simplified schema & conditions
    # It is done mostly due to performance reasons
    schema = schemathesis.from_dict(empty_open_api_3_schema, base_url=openapi3_base_url)

    @given(case=schema["/form"]["POST"].as_strategy())
    @settings(deadline=None, suppress_health_check=[HealthCheck.too_slow, HealthCheck.filter_too_much], max_examples=1)
    def inner(case):
        assume(case.body is NOT_SET)
        case.call()

    # Then payload can be absent
    inner()
Пример #21
0
def test_missing_content_and_schema(empty_open_api_3_schema, location):
    # When an Open API 3 parameter is missing `schema` & `content`
    empty_open_api_3_schema["paths"] = {
        "/foo": {
            "get": {
                "parameters": [{
                    "in": location,
                    "name": "X-Foo",
                    "required": True
                }]
            }
        }
    }
    schema = schemathesis.from_dict(empty_open_api_3_schema,
                                    validate_schema=False)

    @given(schema["/foo"]["GET"].as_strategy())
    @settings(max_examples=1)
    def test(case):
        pass

    # Then the proper error should be shown
    with pytest.raises(
            InvalidSchema,
            match=f'Can not generate data for {location} parameter "X-Foo"! '
            "It should have either `schema` or `content` keywords defined",
    ):
        test()
Пример #22
0
def test_data_generation_method_is_available(method, empty_open_api_3_schema):
    # When a new case is generated
    empty_open_api_3_schema["paths"] = {
        "/data": {
            "post": {
                "requestBody": {
                    "required": True,
                    "content": {
                        "text/plain": {
                            "schema": {
                                "type": "string"
                            }
                        }
                    },
                },
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                },
            },
        },
    }

    api_schema = schemathesis.from_dict(empty_open_api_3_schema)

    @given(case=api_schema["/data"]["POST"].as_strategy(
        data_generation_method=method))
    @settings(max_examples=1)
    def test(case):
        # Then its data generation method should be available
        assert case.data_generation_method == method

    test()
Пример #23
0
def test_loose_multipart_definition():
    # When the schema of "multipart/form-data" content does not define "object" type
    raw_schema = {
        "openapi": "3.0.2",
        "info": {"title": "Test", "description": "Test", "version": "0.1.0"},
        "paths": {
            "/body": {
                "post": {
                    "requestBody": {
                        "content": {"multipart/form-data": {"schema": {"properties": {"foo": {"type": "string"}}}}},
                        "required": True,
                    },
                    "responses": {"200": {"description": "OK"}},
                }
            }
        },
    }
    schema = schemathesis.from_dict(raw_schema)
    # Then non-object data should be excluded during generation

    @given(case=schema["/body"]["POST"].as_strategy())
    @settings(max_examples=5)
    def test(case):
        assert isinstance(case.body, dict)

    # And the resulting values should be valid
    test()
Пример #24
0
def test_invalid_body_in_get_disable_validation(simple_schema):
    schema = schemathesis.from_dict(simple_schema, validate_schema=False)
    endpoint = Endpoint(
        path="/foo",
        method="GET",
        definition=EndpointDefinition({}, {}, "foo", []),
        schema=schema,
        body=PayloadAlternatives(
            [
                OpenAPI20Body(
                    {
                        "name": "attributes",
                        "in": "body",
                        "required": True,
                        "schema": {"required": ["foo"], "type": "object", "properties": {"foo": {"type": "string"}}},
                    },
                    media_type="application/json",
                )
            ]
        ),
    )
    strategy = get_case_strategy(endpoint)

    @given(strategy)
    @settings(max_examples=1)
    def test(case):
        assert case.body is not None

    test()
Пример #25
0
def test_call_asgi_and_validate(fastapi_app):
    api_schema = schemathesis.from_dict(fastapi_app.openapi())

    @given(case=api_schema["/users"]["GET"].as_strategy())
    @settings(max_examples=1)
    def test(case):
        with pytest.raises(RuntimeError, match="The URL should be absolute"):
            case.call_and_validate()
Пример #26
0
def assert_generates(raw_schema, expected, parameter):
    schema = schemathesis.from_dict(raw_schema)

    @given(case=schema["/teapot"]["GET"].as_strategy())
    def test(case):
        assert getattr(case, "path_parameters" if parameter == "path" else parameter) == expected

    test()
Пример #27
0
def test_force_open_api_version(version, expected):
    schema = {
        # Invalid schema, but it happens in real applications
        "swagger": "2.0",
        "openapi": "3.0.0",
    }
    loaded = schemathesis.from_dict(schema, force_schema_version=version, validate_schema=False)
    assert isinstance(loaded, expected)
Пример #28
0
def test_not_recoverable_schema_error(simple_schema, validate_schema,
                                      expected_exception):
    # When there is an error in the API schema that leads to inability to generate any tests
    del simple_schema["paths"]
    # Then it is an explicit exception either during schema loading or processing API operations
    with pytest.raises(expected_exception):
        schema = schemathesis.from_dict(simple_schema,
                                        validate_schema=validate_schema)
        list(schema.get_all_operations())
Пример #29
0
def assert_content_type_conformance(raw_schema, content_type, is_error):
    schema = schemathesis.from_dict(raw_schema)
    endpoint = schema.endpoints["/users"]["get"]
    case = models.Case(endpoint)
    response = make_response(content_type=content_type)
    if not is_error:
        assert content_type_conformance(response, case) is None
    else:
        with pytest.raises(AssertionError):
            content_type_conformance(response, case)
Пример #30
0
def test_getitem(simple_schema, mocker):
    swagger = schemathesis.from_dict(simple_schema)
    mocked = mocker.patch("schemathesis.schemas.endpoints_to_dict", wraps=endpoints_to_dict)
    assert "_endpoints" not in swagger.__dict__
    assert isinstance(swagger["/users"], CaseInsensitiveDict)
    assert mocked.call_count == 1
    # Check cached access
    assert "_endpoints" in swagger.__dict__
    assert isinstance(swagger["/users"], CaseInsensitiveDict)
    assert mocked.call_count == 1