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", "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: EXCLUDE = ["/real-time"] 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)
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.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"])