diff --git a/test/api/libs/responseVerifyer.py b/test/api/libs/responseVerifyer.py index cb3b9f07..aefde49a 100644 --- a/test/api/libs/responseVerifyer.py +++ b/test/api/libs/responseVerifyer.py @@ -38,8 +38,10 @@ class ResponseVerifyer(): self.errors.append("Method " + method + " not found in OpenAPI specs (" + endpoint + ")") return self.errors - # Get YAML response schema - YAMLresponseSchema = self.openapi.paths[endpoint][method]['responses'][str(rcode)]['content']['application/json']['schema'] + # Get YAML response schema and examples (if applicable) + jsonData = self.openapi.paths[endpoint][method]['responses'][str(rcode)]['content']['application/json'] + YAMLresponseSchema = jsonData['schema'] + YAMLresponseExamples = jsonData['examples'] if 'examples' in jsonData else None # Prepare required parameters (if any) FTLparameters = [] if 'parameters' in self.openapi.paths[endpoint][method]: @@ -53,26 +55,32 @@ class ResponseVerifyer(): continue FTLparameters.append(param['name'] + "=" + urllib.parse.quote_plus(str(param['example']))) - self.openapi.paths[endpoint][method]['responses'][str(rcode)]['content']['application/json']['schema'] FTLresponse = self.ftl.getFTLresponse("/api" + endpoint, FTLparameters) if FTLresponse is None: return self.ftl.errors if 'type' in YAMLresponseSchema and YAMLresponseSchema['type'] == 'object': for prop in YAMLresponseSchema['properties']: - self.verify_property(YAMLresponseSchema['properties'], FTLresponse, [prop]) + self.verify_property(YAMLresponseSchema['properties'], YAMLresponseExamples, FTLresponse, [prop]) elif 'allOf' in YAMLresponseSchema and len(YAMLresponseSchema['allOf']) > 0: for i in range(len(YAMLresponseSchema['allOf'])): for prop in YAMLresponseSchema['allOf'][i]['properties']: - self.verify_property(YAMLresponseSchema['allOf'][i]['properties'], FTLresponse, [prop]) + self.verify_property(YAMLresponseSchema['allOf'][i]['properties'], YAMLresponseExamples, FTLresponse, [prop]) else: self.errors.append("Top-level response should be either an object or a non-empty allOf/anyOf/oneOf") return self.errors - def verify_property(self, YAMLprops: dict, FTLprops: dict, props: list): + def verify_type(self, prop_type: any, yaml_type: str, yaml_nullable: bool): + # None is an acceptable reply when this is specified in the API specs + if prop_type == NoneType and yaml_nullable: + return True + return prop_type in self.YAML_TYPES[yaml_type] + + + def verify_property(self, YAMLprops: dict, YAMLexamples: dict, FTLprops: dict, props: list): all_okay = True if props[-1] not in YAMLprops: @@ -80,34 +88,40 @@ class ResponseVerifyer(): return False YAMLprop = YAMLprops[props[-1]] if props[-1] not in FTLprops: - self.errors.append("Property " + props[-1] + " missing in FTL' response") + self.errors.append("Property " + props[-1] + " missing in FTL's response") return False FTLprop = FTLprops[props[-1]] # If this is another object, we have to dive deeper if YAMLprop['type'] == 'object': for prop in YAMLprop['properties']: - if not self.verify_property(YAMLprop['properties'], FTLprop, props + [prop]): + if not self.verify_property(YAMLprop['properties'], YAMLexamples, FTLprop, props + [prop]): all_okay = False else: # Check this property full_path = " => ".join(props) yaml_type = YAMLprop['type'] + yaml_nullable = 'nullable' in YAMLprop and YAMLprop['nullable'] == True # Check type of YAML example (if defined) if 'example' in YAMLprop: example_type = type(YAMLprop['example']) - if example_type not in self.YAML_TYPES[yaml_type]: - self.errors.append(f"API example ({str(yaml_type)}) does not match defined type ({yaml_type}) in {full_path}") + if not self.verify_type(example_type, yaml_type, yaml_nullable): + self.errors.append(f"API example ({str(example_type)}) does not match defined type ({yaml_type}) in {full_path}") return False + elif YAMLexamples is not None: + for t in YAMLexamples: + example = YAMLexamples[t]['value'] + for p in props: + example = example[p] + example_type = type(example) + if not self.verify_type(example_type, yaml_type, yaml_nullable): + self.errors.append(f"API example ({str(example_type)}) does not match defined type ({yaml_type}) in {full_path}") + return False - ftl_type = type(FTLprop) - - # None is an acceptable reply when this is specified in the API specs - if ftl_type == NoneType and 'nullable' in YAMLprop and YAMLprop['nullable'] == True: - pass # Compare type of FTL's reply against what we defined in the API specs - elif ftl_type not in self.YAML_TYPES[yaml_type]: + ftl_type = type(FTLprop) + if not self.verify_type(ftl_type, yaml_type, yaml_nullable): self.errors.append(f"FTL's reply ({str(ftl_type)}) does not match defined type ({yaml_type}) in {full_path}") return False return all_okay