def test_sateless_faker_1(mock_data_store): faker = StatefulFaker(mock_data_store) request = RequestBuilder.from_dict( dict(method="get", protocol="http", path="/", host="api.com") ) schema = { "type": "array", "items": { "type": "object", "required": ["foo", "baz"], "properties": { "foo": {"type": "number"}, "bar": {"type": "string"}, "baz": {"type": "string"}, }, }, } res = faker.process( OpenAPISpecification(source="default", api=spec(response_schema=schema)), request, ) assert valid_schema(res.bodyAsJson, schema)
def test_request_logging_none(tmp_dir): request = RequestBuilder.from_dict( dict( method="get", host="api.com", pathname="/echo", query={"message": "Hello"}, body="", protocol="http", headers={}, )) response = ResponseBuilder.from_dict( dict( statusCode=200, body='{"message": "hello"}', bodyAsJson={"message": "hello"}, headers={}, )) log_dir = os.path.join(tmp_dir, "logs") specs_dir = os.path.join(tmp_dir, "specs") with RequestLoggingCallback(log_dir=log_dir, specs_dir=specs_dir, update_mode=None) as data_callback: data_callback.log(request, response) expected_recordings_path = os.path.join(log_dir, "api.com-recordings.jsonl") assert os.path.exists(expected_recordings_path) assert 0 == len(os.listdir(specs_dir))
def test_sateless_faker_2(mock_data_store): faker = StatefulFaker(mock_data_store) request = RequestBuilder.from_dict( dict(method="get", protocol="http", path="/", host="api.com") ) schema = { "$id": "https://example.com/person.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "title": "Person", "type": "object", "properties": { "firstName": {"type": "string", "description": "The person's first name."}, "lastName": {"type": "string", "description": "The person's last name."}, "age": { "description": "Age in years which must be equal to or greater than zero.", "type": "integer", "minimum": 0.0, }, }, } res = faker.process( OpenAPISpecification(source="default", api=spec(response_schema=schema)), request, ) assert valid_schema(res.bodyAsJson, schema)
def test_sateless_faker_3(mock_data_store): faker = StatefulFaker(mock_data_store) request = RequestBuilder.from_dict( dict(method="get", protocol="http", path="/", host="api.com") ) schema = { "$id": "https://example.com/geographical-location.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "title": "Longitude and Latitude Values", "description": "A geographical coordinate.", "required": ["latitude", "longitude"], "type": "object", "properties": { "latitude": {"type": "number", "minimum": -90.0, "maximum": 90.0}, "longitude": {"type": "number", "minimum": -180.0, "maximum": 180.0}, }, } res = faker.process( OpenAPISpecification(source="default", api=spec(response_schema=schema)), request, ) assert valid_schema(res.bodyAsJson, schema)
def test_sateless_faker_4(mock_data_store): faker = StatefulFaker(mock_data_store) request = RequestBuilder.from_dict( dict(method="get", protocol="http", path="/", host="api.com")) schema = { "$id": "https://example.com/arrays.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A representation of a person, company, organization, or place", "type": "object", "properties": { "fruits": { "type": "array", "items": { "type": "string" } }, "vegetables": { "type": "array", "items": { "$ref": "#/components/schemas/veggie" }, }, }, } components = { "schemas": { "veggie": { "type": "object", "required": ["veggieName", "veggieLike"], "properties": { "veggieName": { "type": "string", "description": "The name of the vegetable.", }, "veggieLike": { "type": "boolean", "description": "Do I like this vegetable?", }, }, } } } oai = spec(response_schema=schema, components=components) res = faker.process( "/", OpenAPISpecification(source="default", api=oai, definitions=make_definitions_from_spec(oai)), request, ) schema["components"] = components assert valid_schema(res.bodyAsJson, schema)
def test_fake_array(mock_data_store): faker = StatefulFaker(mock_data_store) request = RequestBuilder.from_dict( dict(method="get", protocol="http", path="/items", host="api.com") ) schema = {"type": "array", "items": {"$ref": "#/components/schemas/item"}} components = { "schemas": { "item": { "type": "object", "required": ["foo", "bar"], "x-hmt-id-path": "itemId", "properties": { "foo": {"type": "number"}, "bar": {"type": "string"}, "itemId": {"type": "string"}, }, } } } spec = spec_dict( path="/items", response_schema=schema, components=components, method="get" ) spec["paths"]["/items"]["x-hmt-entity"] = "item" spec["paths"]["/items"]["get"]["x-hmt-operation"] = "read" spec = convert_to_OpenAPIObject(spec) mock_data_store.add_mock(OpenAPISpecification(spec, "default")) schema["components"] = components spec = OpenAPISpecification(source="default", api=spec) res = faker.process(spec, request) assert valid_schema(res.bodyAsJson, schema) assert 0 == len(res.bodyAsJson) mock_data_store["default"].item.insert({"foo": 10, "bar": "val", "itemId": "id123"}) res = faker.process(spec, request) assert valid_schema(res.bodyAsJson, schema) assert 1 == len(res.bodyAsJson) mock_data_store["default"].item.insert( {"foo": 10, "bar": "val", "itemId": "id1234"} ) res = faker.process(spec, request) assert valid_schema(res.bodyAsJson, schema) assert 2 == len(res.bodyAsJson)
def test_matcher_4(): assert match_request_to_openapi( RequestBuilder.from_dict({ "headers": {}, "host": "api.foo.com", "path": "/user/55", # correctly parses number "pathname": "/user/55", # correcly parses number "protocol": "https", "method": "get", "query": {}, }), store, ) == ("/user/{id}", store[0])
def test_matcher_3(): assert match_request_to_openapi( RequestBuilder.from_dict({ "headers": {}, "host": "api.foo.com", "path": "/users", # incorrect, should be user "pathname": "/users", # incorrect, should be user "protocol": "https", "method": "get", "query": {}, }), store, ) == (None, None)
def test_matcher_2(): assert match_request_to_openapi( RequestBuilder.from_dict({ "headers": {}, "host": "api.bar.com", "path": "/v1/guest/{id}", "pathname": "/v1/guest/{id}", "protocol": "https", "method": "post", "query": {}, }), store, ) == ("/guest/{id}", store[1])
def test_matcher_5(): assert match_request_to_openapi( RequestBuilder.from_dict({ "headers": {}, "host": "api.foo.com", "path": "/user/fdsfsfwef", # no validation on path params "pathname": "/user/fdsfsfwef", # no validation on path params "protocol": "https", "method": "get", "query": {}, }), store, ) == ("/user/{id}", store[0])
def test_matcher_7(): assert match_request_to_openapi( RequestBuilder.from_dict({ "headers": {}, "host": "api.foo.commmm", # does not exist "path": "/user", "pathname": "/user", "protocol": "https", "method": "get", "query": {}, }), store, ) == (None, None)
def test_matcher_10(): assert (match_request_to_openapi( RequestBuilder.from_dict({ "headers": {}, "host": "api.baz.com", "path": "/guest", "pathname": "/guest", "protocol": "https", "method": "get", "query": {}, # query is not validated here }), store, )[0] == "/guest")
def test_sateless_faker_5(mock_data_store): faker = StatefulFaker(mock_data_store) request = RequestBuilder.from_dict( dict(method="get", protocol="http", path="/", host="api.com") ) schema = {"type": "array"} res = faker.process( OpenAPISpecification(source="default", api=spec(response_schema=schema)), request, ) assert valid_schema(res.bodyAsJson, schema)
def _serve(self): headers = {k: v for k, v in self.request.headers.get_all()} route_info = self._router.route(self.request.path, headers) headers["Host"] = route_info.host query = parse.parse_qs(self.request.query) fullpath = ("{}?{}".format(route_info.path, self.request.query) if query else route_info.path) # ignoring type due to this error """ 46:34 - error: Argument of type 'str' cannot be assigned to parameter 'method' of type 'Literal['connect', 'head', 'trace', 'options', 'delete', 'patch', 'post', 'put', 'get']' 'str' cannot be assigned to 'Literal['connect']' 'str' cannot be assigned to 'Literal['head']' 'str' cannot be assigned to 'Literal['trace']' 'str' cannot be assigned to 'Literal['options']' 'str' cannot be assigned to 'Literal['delete']' """ request = RequestBuilder.from_dict({ "method": self.request.method.lower(), "host": route_info.host, "path": fullpath, "pathname": route_info.path, "protocol": route_info.scheme, "query": query, "body": self.request.body.decode("utf-8"), "headers": headers, }) logger.debug("Processing request: %s", asdict(request)) response = self._request_processor.process(request) logger.debug("Resolved response: %s", asdict(response)) for header, value in response.headers.items(): self.set_header(header, value) self._http_log.put(request, response) self.set_status(response.statusCode) self.write(response.body) logger.debug("Handled writing response")
def test_matcher_14(): assert (match_request_to_openapi( RequestBuilder.from_dict({ "headers": {}, # no header will lead to undefined "host": "api.baz.com", "path": "/guest/4", "pathname": "/guest/4", "protocol": "https", "method": "post", "query": { "zzz": "aaa", "a": "foo", "b": "baz" }, }), store, )[0] == "/guest/{id}")
def test_matcher_12(): assert (match_request_to_openapi( RequestBuilder.from_dict({ "headers": {}, "host": "api.baz.com", "path": "/guest/3/name", "pathname": "/guest/3/name", "protocol": "https", "method": "post", "query": {}, "body": json.dumps({"age": "42"}), "bodyAsJson": { "age": "42" }, # wrong type, as 42 is string }), store, )[0] == "/guest/{id}/name")
def test_request_logging_mixed_append(tmp_dir): specs_dir = os.path.join(tmp_dir, "specs") spec_path = os.path.join(specs_dir, "another.api.com_mixed.json") os.makedirs(specs_dir) with open(spec_path, "w") as f: json.dump(convert_from_openapi(BASE_SCHEMA), f) with open(spec_path) as f: spec = convert_to_openapi(json.load(f)) assert spec.servers is None request = RequestBuilder.from_dict( dict( method="get", host="another.api.com", pathname="/echo", query={"message": "Hello"}, body="", bodyAsJson={}, path="/echo", protocol="http", headers={}, )) response = ResponseBuilder.from_dict( dict( statusCode=200, body='{"message": "hello"}', bodyAsJson={"message": "hello"}, headers={}, )) log_dir = os.path.join(tmp_dir, "logs") with RequestLoggingCallback(log_dir=log_dir, specs_dir=specs_dir, update_mode=UpdateMode.MIXED) as data_callback: data_callback.log(request, response) assert os.path.exists( os.path.join(log_dir, "another.api.com-recordings.jsonl")) assert os.path.exists(spec_path) with open(spec_path) as f: spec = convert_to_openapi(json.load(f)) assert "http://another.api.com" == spec.servers[0].url
def test_faker_5(): faker = StatelessFaker() request = RequestBuilder.from_dict( dict(method="get", protocol="http", path="/", host="api.com") ) schema = {"type": "array"} res = faker.process( "/", OpenAPISpecification( source="default", api=spec(response_schema=schema), definitions={"definitions": {}}, ), request, ) assert valid_schema(res.bodyAsJson, schema)
def test_request_logging_gen(tmp_dir): request = RequestBuilder.from_dict( dict( method="get", host="api.com", pathname="/echo", query={"message": "Hello"}, body="", protocol="http", headers={}, )) response = ResponseBuilder.from_dict( dict( statusCode=200, body='{"message": "hello"}', bodyAsJson={"message": "hello"}, headers={}, )) log_dir = os.path.join(tmp_dir, "logs") specs_dir = os.path.join(tmp_dir, "specs") with RequestLoggingCallback(log_dir=log_dir, specs_dir=specs_dir, update_mode=UpdateMode.GEN) as data_callback: data_callback.log(request, response) expected_recordings_path = os.path.join(log_dir, "api.com-recordings.jsonl") assert os.path.exists(expected_recordings_path) expected_specs_path = os.path.join(specs_dir, "api.com_gen.json") assert os.path.exists(expected_specs_path) with open(expected_recordings_path, "r") as f: data = [x for x in f.read().split("\n") if x != ""] assert 1 == len(data) http_exchange = HttpExchangeReader.from_json(data[0]) assert request == http_exchange.request assert response == http_exchange.response
def test_insert(mock_data_store): faker = StatefulFaker(mock_data_store) request_schema = { "type": "object", "properties": {"item": {"$ref": "#/components/schemas/item"}}, } response_schema = {"$ref": "#/components/schemas/item"} components = { "schemas": { "item": { "type": "object", "required": ["foo"], "x-hmt-id-path": "itemId", "properties": { "foo": {"type": "number"}, "bar": {"type": "string"}, "baz": {"type": "string"}, "itemId": {"type": "string"}, }, } } } spec = spec_dict( path="/items", request_schema=request_schema, response_schema=response_schema, components=components, method="post", ) spec["paths"]["/items"]["x-hmt-entity"] = "item" spec["paths"]["/items"]["post"]["x-hmt-operation"] = "insert" spec = convert_to_OpenAPIObject(spec) mock_data_store.add_mock(OpenAPISpecification(spec, "default")) schema = response_schema schema["components"] = components spec = OpenAPISpecification(source="default", api=spec) request = RequestBuilder.from_dict( dict( method="post", protocol="http", path="/items", host="api.com", bodyAsJson={"item": {"foo": 10, "bar": "val"}}, ) ) res = faker.process(spec, request) assert valid_schema(res.bodyAsJson, schema) assert res.bodyAsJson["itemId"] is not None assert 10 == res.bodyAsJson["foo"] assert 1 == len(mock_data_store["default"].item) assert "val" == mock_data_store["default"].item[res.bodyAsJson["itemId"]]["bar"] request = RequestBuilder.from_dict( dict( method="post", protocol="http", path="/items", host="api.com", bodyAsJson={"item": {"foo": 20, "bar": "val1", "itemId": "id123"}}, ) ) res = faker.process(spec, request) assert valid_schema(res.bodyAsJson, schema) assert "id123" == res.bodyAsJson["itemId"] assert 20 == res.bodyAsJson["foo"] assert 2 == len(mock_data_store["default"].item) assert "val1" == mock_data_store["default"].item[res.bodyAsJson["itemId"]]["bar"] request = RequestBuilder.from_dict( dict( method="post", protocol="http", path="/items", host="api.com", bodyAsJson={"item": {"foo": 30, "itemId": "id123"}}, ) ) res = faker.process(spec, request) assert 2 == len(mock_data_store["default"].item) assert "bar" not in mock_data_store["default"].item[res.bodyAsJson["itemId"]] assert 30 == mock_data_store["default"].item[res.bodyAsJson["itemId"]]["foo"]
def test_query(): schema_single = {"$ref": "#/components/schemas/item"} schema_array = { "accounts": { "items": { "$ref": "#/components/schemas/items" }, "type": "array" } } components = { "schemas": { "item": { "type": "object", "required": ["foo", "baz"], "x-hmt-id-path": "itemId", "properties": { "foo": { "type": "number" }, "bar": { "type": "string" }, "itemId": { "type": "string" }, }, } } } spec = spec_dict( path="/items/{id}", response_schema=schema_single, components=components, method="get", ) spec["paths"]["/items/{id}"]["x-hmt-entity"] = "item" spec["paths"]["/items/{id}"]["get"]["x-hmt-operation"] = "read" add_item( spec, path="/items", response_schema=schema_array, components=components, method="get", ) spec["paths"]["/items"]["x-hmt-entity"] = "item" spec["paths"]["/items"]["get"]["x-hmt-operation"] = "read" spec = convert_to_OpenAPIObject(spec) entity = Entity("item", spec) entity.insert({"foo": 10, "bar": "val", "itemId": "id123"}) entity.insert({"foo": 20, "bar": "val1", "itemId": "id1234"}) entity.insert({"foo": 30, "bar": "val2", "itemId": "id12345"}) res = entity.query_one( "/items/{id}", RequestBuilder.from_dict( dict(method="get", protocol="http", path="/items/id1234", host="api.com")), ) assert 20 == res["foo"] res = entity.query( "/items", RequestBuilder.from_dict( dict(method="get", protocol="http", path="/items", host="api.com")), ) assert 3 == len(res)
def test_insert_from_request(): schema = {"$ref": "#/components/schemas/item"} components = { "schemas": { "item": { "type": "object", "required": ["foo", "baz"], "x-hmt-id-path": "itemId", "properties": { "foo": { "type": "number" }, "bar": { "type": "string" }, "itemId": { "type": "string" }, }, } } } spec = spec_dict( path="/items/create", response_schema=schema, request_schema=schema, components=components, method="post", ) spec["paths"]["/items/create"]["x-hmt-entity"] = "item" spec["paths"]["/items/create"]["post"]["x-hmt-operation"] = "insert" spec = convert_to_OpenAPIObject(spec) entity = Entity("item", spec) request = RequestBuilder.from_dict( dict( method="post", protocol="http", path="/items", host="api.com", bodyAsJson={ "foo": 15, "bar": "val2" }, )) entity.insert_from_request("/items/create", request) res = next(iter(entity.values())) assert 15 == res["foo"] assert res["itemId"] is not None request = RequestBuilder.from_dict( dict( method="post", protocol="http", path="/items", host="api.com", bodyAsJson={ "foo": 10, "bar": "val", "itemId": "id123" }, )) entity.insert_from_request("/items/create", request) assert len(entity) == 2 res = entity["id123"] assert 10 == res["foo"]