def test_attributes(self) -> None: """ Checks: * All endpoints have `operationId` and `tag` attributes. * All example responses match their schema. * That no opaque object exists. """ EXCLUDE = ["/real-time", "/register", "/events"] VALID_TAGS = [ "users", "server_and_organizations", "authentication", "real_time_events", "streams", "messages", "users", "webhooks" ] paths = OpenAPISpec(OPENAPI_SPEC_PATH).openapi()["paths"] for path, path_item in paths.items(): if path in EXCLUDE: continue for method, operation in path_item.items(): # Check if every file has an operationId assert ("operationId" in operation) assert ("tags" in operation) tag = operation["tags"][0] assert (tag in VALID_TAGS) for status_code, response in operation['responses'].items(): schema = response['content']['application/json']['schema'] if 'oneOf' in schema: for subschema_index, subschema in enumerate( schema['oneOf']): validate_schema(subschema) assert (validate_against_openapi_schema( subschema['example'], path, method, status_code + '_' + str(subschema_index))) continue validate_schema(schema) assert (validate_against_openapi_schema( schema['example'], path, method, status_code))
def test_attributes(self) -> None: """ Checks: * All endpoints have `operationId` and `tag` attributes. * All example responses match their schema. * That no opaque object exists. """ EXCLUDE = ["/real-time"] VALID_TAGS = [ "users", "server_and_organizations", "authentication", "real_time_events", "streams", "messages", "drafts", "webhooks", ] paths = OpenAPISpec(OPENAPI_SPEC_PATH).openapi()["paths"] for path, path_item in paths.items(): if path in EXCLUDE: continue for method, operation in path_item.items(): # Check if every file has an operationId assert "operationId" in operation assert "tags" in operation tag = operation["tags"][0] assert tag in VALID_TAGS for status_code, response in operation["responses"].items(): schema = response["content"]["application/json"]["schema"] if "oneOf" in schema: for subschema_index, subschema in enumerate( schema["oneOf"]): validate_schema(subschema) assert validate_against_openapi_schema( subschema["example"], path, method, status_code + "_" + str(subschema_index), ) continue validate_schema(schema) assert validate_against_openapi_schema( schema["example"], path, method, status_code)
def test_attributes(self) -> None: """ Checks: * All endpoints have `operationId` and `tag` attributes. * All example responses match their schema. * That no opaque object exists. """ EXCLUDE = ["/real-time", "/register", "/events"] VALID_TAGS = [ "users", "server_and_organizations", "authentication", "real_time_events", "streams", "messages", "users", "webhooks" ] openapi_spec = OpenAPISpec(OPENAPI_SPEC_PATH).spec()["paths"] for path in openapi_spec: if path in EXCLUDE: continue for method in openapi_spec[path]: # Check if every file has an operationId assert ("operationId" in openapi_spec[path][method]) assert ("tags" in openapi_spec[path][method]) tag = openapi_spec[path][method]["tags"][0] assert (tag in VALID_TAGS) for response in openapi_spec[path][method]['responses']: response_schema = ( openapi_spec[path][method]['responses'][response] ['content']['application/json']['schema']) if 'oneOf' in response_schema: cnt = 0 for entry in response_schema['oneOf']: validate_schema(entry) assert (validate_against_openapi_schema( entry['example'], path, method, response + '_' + str(cnt))) cnt += 1 continue validate_schema(response_schema) assert (validate_against_openapi_schema( response_schema['example'], path, method, response))
def test_validate_against_openapi_schema(self) -> None: with self.assertRaises( ValidationError, msg= "Additional properties are not allowed ('foo' was unexpected)" ): bad_content: Dict[str, object] = { "msg": "", "result": "success", "foo": "bar", } validate_against_openapi_schema(bad_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) with self.assertRaises(ValidationError, msg=("42 is not of type string")): bad_content = { "msg": 42, "result": "success", } validate_against_openapi_schema(bad_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) with self.assertRaises(ValidationError, msg='Expected to find the "msg" required key'): bad_content = { "result": "success", } validate_against_openapi_schema(bad_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) # No exceptions should be raised here. good_content = { "msg": "", "result": "success", } validate_against_openapi_schema(good_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) # Overwrite the exception list with a mocked one test_dict: Dict[str, Any] = {} # Check that validate_against_openapi_schema correctly # descends into 'deep' objects and arrays. Test 1 should # pass, Test 2 has a 'deep' extraneous key and Test 3 has a # 'deep' opaque object. Also the parameters are a heterogeneous # mix of arrays and objects to verify that our descent logic # correctly gets to the the deeply nested objects. with open( os.path.join(os.path.dirname(OPENAPI_SPEC_PATH), "testing.yaml")) as test_file: test_dict = yaml.safe_load(test_file) openapi_spec.openapi()["paths"]["testing"] = test_dict try: validate_against_openapi_schema( (test_dict["test1"]["responses"]["200"]["content"] ["application/json"]["example"]), "testing", "test1", "200", ) with self.assertRaises( ValidationError, msg='Extraneous key "str4" in response\'s content'): validate_against_openapi_schema( (test_dict["test2"]["responses"]["200"]["content"] ["application/json"]["example"]), "testing", "test2", "200", ) with self.assertRaises(SchemaError, msg='Opaque object "obj"'): # Checks for opaque objects validate_schema(test_dict["test3"]["responses"]["200"] ["content"]["application/json"]["schema"]) finally: openapi_spec.openapi()["paths"].pop("testing", None)
def test_validate_against_openapi_schema(self) -> None: with self.assertRaisesRegex( SchemaError, r"Additional properties are not allowed \('foo' was unexpected\)" ): bad_content: Dict[str, object] = { "msg": "", "result": "success", "foo": "bar", } validate_against_openapi_schema(bad_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) with self.assertRaisesRegex(SchemaError, r"42 is not of type string"): bad_content = { "msg": 42, "result": "success", } validate_against_openapi_schema(bad_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) with self.assertRaisesRegex(SchemaError, r"'msg' is a required property"): bad_content = { "result": "success", } validate_against_openapi_schema(bad_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) # No exceptions should be raised here. good_content = { "msg": "", "result": "success", } validate_against_openapi_schema(good_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) # Overwrite the exception list with a mocked one test_dict: Dict[str, Any] = {} # Check that validate_against_openapi_schema correctly # descends into 'deep' objects and arrays. Test 1 should # pass, Test 2 has a 'deep' extraneous key and Test 3 has a # 'deep' opaque object. Also the parameters are a heterogeneous # mix of arrays and objects to verify that our descent logic # correctly gets to the the deeply nested objects. test_filename = os.path.join(os.path.dirname(OPENAPI_SPEC_PATH), "testing.yaml") with open(test_filename) as test_file: test_dict = yaml.safe_load(test_file) with patch("zerver.openapi.openapi.openapi_spec", OpenAPISpec(test_filename)): validate_against_openapi_schema( { "top_array": [ { "obj": { "str3": "test" } }, [{ "str1": "success", "str2": "success" }], ], }, "/test1", "get", "200", ) with self.assertRaisesRegex( SchemaError, r"\{'obj': \{'str3': 'test', 'str4': 'extraneous'\}\} is not valid under any of the given schemas", ): validate_against_openapi_schema( { "top_array": [ { "obj": { "str3": "test", "str4": "extraneous" } }, [{ "str1": "success", "str2": "success" }], ], }, "/test2", "get", "200", ) with self.assertRaisesRegex( SchemaError, r"additionalProperties needs to be defined for objects to makesure they have no additional properties left to be documented\.", ): # Checks for opaque objects validate_schema( test_dict["paths"]["/test3"]["get"]["responses"]["200"] ["content"]["application/json"]["schema"])
def test_validate_against_openapi_schema(self) -> None: with self.assertRaises(ValidationError, msg=("Additional properties are not" + " allowed ('foo' was unexpected)")): bad_content: Dict[str, object] = { 'msg': '', 'result': 'success', 'foo': 'bar', } validate_against_openapi_schema(bad_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) with self.assertRaises(ValidationError, msg=("42 is not of type string")): bad_content = { 'msg': 42, 'result': 'success', } validate_against_openapi_schema(bad_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) with self.assertRaises(ValidationError, msg='Expected to find the "msg" required key'): bad_content = { 'result': 'success', } validate_against_openapi_schema(bad_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) # No exceptions should be raised here. good_content = { 'msg': '', 'result': 'success', } validate_against_openapi_schema(good_content, TEST_ENDPOINT, TEST_METHOD, TEST_RESPONSE_SUCCESS) # Overwrite the exception list with a mocked one test_dict: Dict[str, Any] = {} # Check that validate_against_openapi_schema correctly # descends into 'deep' objects and arrays. Test 1 should # pass, Test 2 has a 'deep' extraneous key and Test 3 has a # 'deep' opaque object. Also the parameters are a heterogenous # mix of arrays and objects to verify that our descent logic # correctly gets to the the deeply nested objects. with open( os.path.join(os.path.dirname(OPENAPI_SPEC_PATH), "testing.yaml")) as test_file: test_dict = yaml.safe_load(test_file) openapi_spec.spec()['paths']['testing'] = test_dict try: validate_against_openapi_schema( (test_dict['test1']['responses']['200']['content'] ['application/json']['example']), 'testing', 'test1', '200') with self.assertRaises( ValidationError, msg='Extraneous key "str4" in response\'s content'): validate_against_openapi_schema( (test_dict['test2']['responses']['200']['content'] ['application/json']['example']), 'testing', 'test2', '200') with self.assertRaises(SchemaError, msg='Opaque object "obj"'): # Checks for opaque objects validate_schema((test_dict['test3']['responses']['200'] ['content']['application/json']['schema'])) finally: openapi_spec.spec()['paths'].pop('testing', None)