#137: Add additional logging for lookup resource tests (#140)

* #137: Added enhanced logging for Lookup tests
* #137: Don't fast fail when the Lookup Resource tests fail
This commit is contained in:
Joshua Darnell 2022-10-11 10:19:51 -07:00 committed by GitHub
parent abc27aa9df
commit 1c6e831dba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 118 additions and 96 deletions

View File

@ -40,8 +40,7 @@ import java.util.stream.Collectors;
import static org.junit.Assert.*;
import static org.reso.commander.Commander.*;
import static org.reso.commander.common.ErrorMsg.getDefaultErrorMessage;
import static org.reso.commander.common.TestUtils.HEADER_ODATA_VERSION;
import static org.reso.commander.common.TestUtils.JSON_VALUE_PATH;
import static org.reso.commander.common.TestUtils.*;
import static org.reso.models.Request.loadFromRESOScript;
/**
@ -390,7 +389,8 @@ public final class WebAPITestContainer implements TestContainer {
URI pathToMetadata = getCommander().getPathToMetadata(request.getRequestUrl());
if (pathToMetadata != null) {
LOG.info("Requesting XML Metadata from: " + pathToMetadata.toString());
LOG.info("Requesting XML Metadata from: " + pathToMetadata);
ODataTransportWrapper wrapper = getCommander().executeODataGetRequest(pathToMetadata.toString());
setODataRawResponse(wrapper.getODataRawResponse());
responseCode.set(wrapper.getHttpResponseCode());
@ -404,8 +404,8 @@ public final class WebAPITestContainer implements TestContainer {
xmlResponseData.set(wrapper.getResponseData());
xmlMetadata.set(Commander.deserializeXMLMetadata(xmlResponseData.get(), getCommander().getClient()));
} else {
LOG.error(getDefaultErrorMessage("could not create metadata URI from given requestUri:", request.getRequestUrl()));
System.exit(NOT_OK);
failAndExitWithErrorMessage(getDefaultErrorMessage("Could not create metadata URI from given requestUri:",
request.getRequestUrl()), LOG);
}
} finally {
haveMetadataBeenRequested.set(true);
@ -706,15 +706,23 @@ public final class WebAPITestContainer implements TestContainer {
}
private void processODataRequestException(ODataClientErrorException exception) {
LOG.debug("ODataClientErrorException caught. Check tests for asserted conditions...");
LOG.debug(exception);
/*
TODO: determine whether these additional lines are needed or whether the bubbled error is sufficient
LOG.error("ODataClientErrorException caught. Check tests for asserted conditions...");
LOG.error(exception);
*/
setODataClientErrorException(exception);
setServerODataHeaderVersion(TestUtils.getHeaderData(HEADER_ODATA_VERSION, Arrays.asList(exception.getHeaderInfo())));
setResponseCode(exception.getStatusLine().getStatusCode());
}
private void processODataRequestException(ODataServerErrorException exception) {
LOG.debug("ODataServerErrorException thrown in executeGetRequest. Check tests for asserted conditions...");
/*
TODO: determine whether these additional lines are needed or whether the bubbled error is sufficient
LOG.error("ODataServerErrorException thrown in executeGetRequest. Check tests for asserted conditions...");
*/
//TODO: look for better ways to do this in Olingo or open PR
if (exception.getMessage().contains(Integer.toString(HttpStatus.SC_NOT_IMPLEMENTED))) {
setResponseCode(HttpStatus.SC_NOT_IMPLEMENTED);
@ -722,7 +730,6 @@ public final class WebAPITestContainer implements TestContainer {
setODataServerErrorException(exception);
}
public boolean getIsValidXMLMetadata() {
return isValidXMLMetadata.get();
}
@ -769,7 +776,7 @@ public final class WebAPITestContainer implements TestContainer {
&& edm.get() != null && getIsValidEdm();
}
public final WebAPITestContainer validateMetadata() {
public WebAPITestContainer validateMetadata() {
try {
if (!haveMetadataBeenRequested.get()) fetchXMLMetadata();
assertNotNull(getDefaultErrorMessage("no XML response data found!"), getXMLResponseData());

View File

@ -24,8 +24,10 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.reso.certification.stepdefs.DataAvailability.CERTIFICATION_RESULTS_PATH;
import static org.reso.commander.common.ErrorMsg.getDefaultErrorMessage;
import static org.reso.commander.common.ODataUtils.*;
import static org.reso.commander.common.TestUtils.failAndExitWithErrorMessage;
import static org.reso.commander.common.Utils.wrapColumns;
@ -37,7 +39,9 @@ public class LookupResource {
private static final String PATH_TO_RESOSCRIPT_ARG = "pathToRESOScript";
private static final AtomicReference<Map<String, List<ClientEntity>>> lookupResourceCache = new AtomicReference<>(new LinkedHashMap<>());
private static final String LOOKUP_RESOURCE_LOOKUP_METADATA_FILE_NAME = "lookup-resource-lookup-metadata.json";
private static final String LOOKUP_RESOURCE_FIELD_METADATA_FILE_NAME = "lookup-resource-field-metadata.json";
//TODO: only output file in DEBUG mode
//private static final String LOOKUP_RESOURCE_FIELD_METADATA_FILE_NAME = "lookup-resource-field-metadata.json";
private static final String LOOKUP_NAME_FIELD = "LookupName";
@ -78,9 +82,8 @@ public class LookupResource {
lookupResourceCache.get().get(resourceName).addAll(results);
final JsonObject metadata = new JsonObject();
metadata.add("lookups", ODataUtils.serializeLookupMetadata(container.get().getCommander().getClient(), results));
Utils.createFile(CERTIFICATION_RESULTS_PATH, LOOKUP_RESOURCE_LOOKUP_METADATA_FILE_NAME, metadata.toString());
Utils.createFile(CERTIFICATION_RESULTS_PATH, LOOKUP_RESOURCE_LOOKUP_METADATA_FILE_NAME,
ODataUtils.serializeLookupMetadata(container.get().getCommander().getClient(), results).toString());
} catch (Exception exception) {
failAndExitWithErrorMessage("Unable to retrieve data from the Lookup Resource! " + exception.getMessage(), scenario);
@ -145,6 +148,7 @@ public class LookupResource {
standardLookupFieldCache.get(resourceName).values().stream()
.filter(referenceStandardField -> referenceStandardField.getLookupName() != null)).collect(Collectors.toSet());
final ArrayList<String> fieldsWithMissingAnnotations = new ArrayList<>();
lookupFields.forEach(referenceStandardField -> {
LOG.debug("Standard Field: { "
+ "resourceName: \"" + referenceStandardField.getParentResourceName() + "\""
@ -155,15 +159,19 @@ public class LookupResource {
final boolean isStringDataType = foundElement != null &&
foundElement.getType().getFullQualifiedName().toString().contentEquals(EdmPrimitiveTypeKind.String.getFullQualifiedName().toString());
if (foundElement != null && isStringDataType) {
if (!hasAnnotationTerm(foundElement, annotationTerm)) {
final String message = "Could not find required annotation with term \"" + annotationTerm + "\" for field: "
+ referenceStandardField.getStandardName();
LOG.info("WARN: " + message);
failAndExitWithErrorMessage(message, scenario);
}
if (foundElement != null && isStringDataType && !hasAnnotationTerm(foundElement, annotationTerm)) {
fieldsWithMissingAnnotations.add(referenceStandardField.getStandardName());
}
});
if (fieldsWithMissingAnnotations.size() > 0) {
final String msg = "The following fields are missing the required '" + annotationTerm + "' annotation: "
+ wrapColumns(String.join(", ", fieldsWithMissingAnnotations)) + "\n";
LOG.error(getDefaultErrorMessage(msg));
fail(msg);
}
}
@And("fields with the annotation term {string} MUST have a LookupName in the Lookup Resource")
@ -185,18 +193,14 @@ public class LookupResource {
final Set<String> missingLookupNames = Utils.getDifference(annotatedLookupNames, lookupNamesFromLookupData);
if (missingLookupNames.size() > 0) {
failAndExitWithErrorMessage("LookupName elements missing from LookupMetadata: "
+ wrapColumns(String.join(", ", missingLookupNames)), scenario);
} else {
if (filteredResourceFieldMap.size() > 0) {
scenario.log("Found all annotated LookupName elements in the Lookup data. Unique count: " + annotatedLookupNames.size());
scenario.log("LookupNames: " + wrapColumns(String.join(", ", annotatedLookupNames)));
final String msg = "The following fields have LookupName annotations but are missing from the Lookup Resource: "
+ wrapColumns(String.join(", ", missingLookupNames)) + "\n";
Utils.createFile(CERTIFICATION_RESULTS_PATH, LOOKUP_RESOURCE_FIELD_METADATA_FILE_NAME,
ODataUtils.serializeFieldMetadataForLookupFields(filteredResourceFieldMap).toString());
} else {
scenario.log("No annotated lookup names found in the OData XML Metadata.");
}
LOG.error(getDefaultErrorMessage(msg));
fail(msg);
} else {
scenario.log("Found all annotated LookupName elements in the Lookup data. Unique count: " + annotatedLookupNames.size());
scenario.log("LookupNames: " + wrapColumns(String.join(", ", annotatedLookupNames)));
}
}
}

View File

@ -36,7 +36,6 @@ import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import static io.restassured.path.json.JsonPath.from;
import static org.junit.Assert.*;
@ -49,7 +48,6 @@ import static org.reso.commander.common.ErrorMsg.getDefaultErrorMessage;
import static org.reso.commander.common.TestUtils.DateParts.FRACTIONAL;
import static org.reso.commander.common.TestUtils.*;
import static org.reso.commander.common.TestUtils.Operators.*;
import static org.reso.models.Request.loadFromRESOScript;
/**
* Contains the glue code for Web API Core 2.0.0 Certification as well as previous Platinum tests,
@ -89,13 +87,6 @@ public class WebAPIServerCore implements En {
if (!container.get().getIsInitialized()) {
container.get().setSettings(Settings.loadFromRESOScript(new File(System.getProperty(PATH_TO_RESOSCRIPT_KEY))));
//moved to container initialization
// //overwrite any requests loaded with the reference queries
// container.get().getSettings().setRequests(loadFromRESOScript(new File(Objects.requireNonNull(
// getClass().getClassLoader().getResource(WEB_API_CORE_REFERENCE_REQUESTS)).getPath()))
// .stream().map(request -> Settings.resolveParameters(request, container.get().getSettings())).collect(Collectors.toList()));
container.get().initialize();
}
}
@ -261,11 +252,10 @@ public class WebAPIServerCore implements En {
* Rather than returning an integer response, this implementation expects the @odata.count property to be
* available when requested, and a $top=0 may be used to restrict the number of items returned as results.
*/
And("^the \"([^\"]*)\" value is greater than or equal to the number of results$", (String field) -> {
assertTrue(getDefaultErrorMessage("the @odata.count value MUST be present",
"and contain a non-zero value greater than or equal to the number of results!"),
TestUtils.validateODataCount(container.get().getResponseData()));
});
And("^the \"([^\"]*)\" value is greater than or equal to the number of results$", (String field) ->
assertTrue(getDefaultErrorMessage("the @odata.count value MUST be present",
"and contain a non-zero value greater than or equal to the number of results!"),
TestUtils.validateODataCount(container.get().getResponseData())));
And("^data in the \"([^\"]*)\" fields are different in the second request than in the first$", (String parameterUniqueId) -> {
try {
@ -469,7 +459,7 @@ public class WebAPIServerCore implements En {
String op = andOrOp.toLowerCase();
boolean isAndOp = op.contains(AND);
//these should default to true when And and false when Or for the purpose of boolean comparisons
//these should default to true when And, and false when Or for the purpose of boolean comparisons
AtomicBoolean lhsResult = new AtomicBoolean(isAndOp);
AtomicBoolean rhsResult = new AtomicBoolean(isAndOp);
AtomicBoolean itemResult = new AtomicBoolean(isAndOp);
@ -620,7 +610,7 @@ public class WebAPIServerCore implements En {
from(container.get().getResponseData()).getList(JSON_VALUE_PATH, ObjectNode.class).forEach(item -> {
fieldValue.set(item.get(fieldName).toString());
String assertMessage = EMPTY_STRING;
String assertMessage;
if (useCollections) {
if (item.get(fieldName).isArray()) {
result.set(result.get() && TestUtils.testAnyOperator(item, fieldName, assertedValue.get()));
@ -795,16 +785,16 @@ public class WebAPIServerCore implements En {
/*
* Ensures that the server metadata for the given resource in parameterResourceName contains
* all of the fields in the given parameterSelectList.
* each of the fields in the given parameterSelectList.
*/
And("^resource metadata for \"([^\"]*)\" contains the fields in the given select list$", (String parameterResourceName) -> {
assertTrue(getDefaultErrorMessage("no $select list found for requestId:", container.get().getRequest().getRequestId()),
container.get().getSelectList().size() > 0);
try {
LOG.info("Searching metadata for fields in given select list: " + container.get().getSelectList().toString());
LOG.info("Searching metadata for fields in given select list: " + container.get().getSelectList());
container.get().getSelectList().forEach(fieldName -> {
//need to skip the expand field when looking through the metadata
//need to skip the expanded field when looking through the metadata
if (container.get().getExpandField() != null && !fieldName.contentEquals(container.get().getExpandField())) {
try {
assertNotNull(getDefaultErrorMessage("Field name '" + fieldName + "' is not present in server metadata!"),
@ -850,16 +840,13 @@ public class WebAPIServerCore implements En {
try {
if (container.get().fetchXMLMetadata() == null) {
//force exit rather than allowing the tests to finish
LOG.error(getDefaultErrorMessage("could not retrieve valid XML Metadata for given service root:", serviceRoot));
System.exit(NOT_OK);
failAndExitWithErrorMessage("Could not retrieve valid XML Metadata for given service root: " + serviceRoot, LOG);
}
} catch (ODataClientErrorException cex) {
container.get().setResponseCode(cex.getStatusLine().getStatusCode());
LOG.error(getDefaultErrorMessage(cex));
System.exit(NOT_OK);
failAndExitWithErrorMessage(getDefaultErrorMessage(cex), LOG);
} catch (Exception ex) {
LOG.error(getDefaultErrorMessage(ex));
System.exit(NOT_OK);
failAndExitWithErrorMessage(getDefaultErrorMessage(ex), LOG);
}
});
@ -1107,7 +1094,7 @@ public class WebAPIServerCore implements En {
*/
void prepareAndExecuteRawGetRequest(String requestId) {
try {
//reset local state each time a get request request is run
//reset local state each time a get request is run
container.get().resetState();
assertNotNull(getDefaultErrorMessage("request Id cannot be null!"), requestId);
@ -1130,10 +1117,9 @@ public class WebAPIServerCore implements En {
//execute request
container.get().executePreparedRawGetRequest();
} catch (MalformedURLException urlException) {
LOG.info("Malformed URL was thrown in " + this.getClass() + ": " + urlException.toString()
+ "\nSkipping Request!");
LOG.info("Malformed URL was thrown in " + this.getClass() + ": " + urlException + "\nSkipping Request!");
} catch (Exception ex) {
LOG.info("Exception was thrown in " + this.getClass() + "!\n" + ex.toString());
LOG.info("Exception was thrown in " + this.getClass() + "!\n" + ex);
}
}

View File

@ -549,27 +549,24 @@ public class Commander {
* @return a URI with the metadata path included
*/
public URI getPathToMetadata(String requestUri) {
if (requestUri == null) {
LOG.error(getDefaultErrorMessage("service root is null!"));
System.exit(NOT_OK);
}
try {
String uri = requestUri;
if (!requestUri.contains(METADATA_PATH)) {
uri += METADATA_PATH;
if (requestUri == null || requestUri.length() == 0) {
TestUtils.failAndExitWithErrorMessage("OData service root is missing!", LOG);
} else {
try {
String uri = requestUri;
if (!requestUri.contains(METADATA_PATH)) {
uri += METADATA_PATH;
}
return new URI(uri).normalize();
} catch (Exception ex) {
TestUtils.failAndExitWithErrorMessage("Could not create metadata URI.\n\t" + ex, LOG);
}
return new URI(uri);
} catch (Exception ex) {
LOG.error(getDefaultErrorMessage("could not create path to metadata.\n" + ex.toString()));
System.exit(NOT_OK);
}
return null;
}
/**
* Executes an OData GET Request w ith the current Commander instance
* Executes an OData GET Request with the current Commander instance
* @param wrapper the OData transport wrapper to use for the request
* @return and OData transport wrapper with the response, or exception if one was thrown
*/

View File

@ -80,22 +80,33 @@ public class ODataUtils {
/**
* Serializes a list of OData ClientEntity items in a JSON Array with those properties.
*
* @param results list of OData ClientEntity results
* @param lookups list of OData ClientEntity results
* @param client OData client to use as serializer
* @return a JsonArray of results
*/
public static JsonArray serializeLookupMetadata(ODataClient client, List<ClientEntity> results) {
final JsonArray lookups = new JsonArray();
public static JsonObject serializeLookupMetadata(ODataClient client, List<ClientEntity> lookups) {
final String
DESCRIPTION_KEY = "description", DESCRIPTION = "Data Dictionary Lookup Resource Metadata",
VERSION_KEY = "version", VERSION = "1.7",
GENERATED_ON_KEY = "generatedOn",
LOOKUPS_KEY = "lookups";
JsonObject metadataReport = new JsonObject();
metadataReport.addProperty(DESCRIPTION_KEY, DESCRIPTION);
metadataReport.addProperty(VERSION_KEY, VERSION);
metadataReport.addProperty(GENERATED_ON_KEY, Utils.getIsoTimestamp());
final JsonArray lookupsArray = new JsonArray();
try {
final Gson gson = new Gson();
final JsonSerializer jsonSerializer = new JsonSerializer(false, ContentType.APPLICATION_JSON);
results.forEach(clientEntity -> {
lookups.forEach(clientEntity -> {
try {
StringWriter writer = new StringWriter();
jsonSerializer.write(writer, client.getBinder().getEntity(clientEntity));
Optional<JsonElement> element = Optional.ofNullable(gson.fromJson(writer.toString(), JsonElement.class));
element.ifPresent(lookups::add);
element.ifPresent(lookupsArray::add);
} catch (ODataSerializerException e) {
LOG.error("ERROR: could not deserialize. Exception: " + e);
}
@ -104,15 +115,17 @@ public class ODataUtils {
LOG.error(exception);
}
return lookups;
metadataReport.add(LOOKUPS_KEY, lookupsArray);
return metadataReport;
}
//TODO: Only output the field metadata in DEBUG mode
public static JsonObject serializeFieldMetadataForLookupFields(Map<String, Set<EdmElement>> resourceFieldMap) {
//TODO: migrate to test file
final String LOOKUP_ANNOTATION_TERM = "RESO.OData.Metadata.LookupName";
final String
DESCRIPTION_KEY = "description", DESCRIPTION = "Lookup Resource Annotated Fields Metadata",
DESCRIPTION_KEY = "description", DESCRIPTION = "Data Dictionary Lookup Resource Annotated Fields Metadata",
VERSION_KEY = "version", VERSION = "1.7",
GENERATED_ON_KEY = "generatedOn",
FIELDS_KEY = "fields";

View File

@ -65,7 +65,7 @@ public final class TestUtils {
return URI.create(
queryString.replace(" ", "%20")
/* add other handlers here */
);
).normalize();
}
/**
@ -560,7 +560,7 @@ public final class TestUtils {
}
/**
* Helper method to find headers with a given key in an an array of headers
* Helper method to find headers with a given key in an array of headers
*
* @param key the header to get
* @param headers an array containing Header objects
@ -701,7 +701,7 @@ public final class TestUtils {
*/
public static String convertInputStreamToString(InputStream inputStream) {
try {
InputStreamReader isReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8.name());
InputStreamReader isReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isReader);
StringBuilder sb = new StringBuilder();
String str;
@ -796,27 +796,29 @@ public final class TestUtils {
*/
public static void assertXMLMetadataAreRequestedFromTheServer(WebAPITestContainer container, Scenario scenario) {
if (container == null || container.getCommander() == null) {
failAndExitWithErrorMessage("Cannot create Commander instance!", scenario);
failAndExitWithErrorMessage("Cannot create Commander instance!", LOG);
return;
}
if (!container.getHaveMetadataBeenRequested()) {
final String serviceRoot = Settings.resolveParametersString(container.getServiceRoot(), container.getSettings());
if (!serviceRoot.contentEquals(container.getCommander().getServiceRoot())) {
failAndExitWithErrorMessage("given service root doesn't match the one configured in the Commander", scenario);
failAndExitWithErrorMessage("Given service root doesn't match the one configured in the Commander", scenario);
return;
}
try {
if (container.fetchXMLMetadata() == null) {
failAndExitWithErrorMessage("could not retrieve valid XML Metadata for given service root: " + serviceRoot, scenario);
failAndExitWithErrorMessage("Could not retrieve valid XML Metadata for given service root: "
+ serviceRoot, LOG);
}
} catch (ODataClientErrorException cex) {
container.setResponseCode(cex.getStatusLine().getStatusCode());
failAndExitWithErrorMessage(cex.getMessage(), scenario);
failAndExitWithErrorMessage("Could not retrieve valid XML Metadata for given service root: "
+ serviceRoot + "\n\tException: " + cex.getMessage(), LOG);
} catch (Exception ex) {
failAndExitWithErrorMessage(ex.toString(), scenario);
failAndExitWithErrorMessage("Could not retrieve valid XML Metadata for given service root: "
+ serviceRoot + "\n\tException: " + ex, LOG);
}
}
}
@ -829,11 +831,17 @@ public final class TestUtils {
public static void assertXMLMetadataHasValidServiceDocument(WebAPITestContainer container, Scenario scenario) {
try {
if (container == null || container.getEdm() == null || container.getEdm().getEntityContainer() == null) {
failAndExitWithErrorMessage("Could not find default entity container for given service root: " + container.getServiceRoot(), scenario);
final String serviceRoot = container != null && container.getServiceRoot() != null
? container.getServiceRoot() : "<null>";
failAndExitWithErrorMessage("Could not find default entity container for given service root: " + serviceRoot, scenario);
} else {
LOG.info("Found Default Entity Container: '" + container.getEdm().getEntityContainer().getNamespace() + "'");
}
LOG.info("Found Default Entity Container: '" + container.getEdm().getEntityContainer().getNamespace() + "'");
} catch (ODataClientErrorException cex) {
container.setResponseCode(cex.getStatusLine().getStatusCode());
if (container != null) {
container.setResponseCode(cex.getStatusLine().getStatusCode());
}
failAndExitWithErrorMessage(cex.toString(), scenario);
} catch (Exception ex) {
failAndExitWithErrorMessage(getDefaultErrorMessage(ex), scenario);
@ -863,7 +871,7 @@ public final class TestUtils {
* Validates that the given response data have a valid OData count
*
* @param responseData the data to check for a count against
* @return true if the there is a count present and it's greater than or equal to the number of results
* @return true if the there is a count present, and it's greater than or equal to the number of results
*/
public static boolean validateODataCount(String responseData) {
List<String> items = from(responseData).getList(JSON_VALUE_PATH);
@ -948,6 +956,13 @@ public final class TestUtils {
System.exit(NOT_OK);
}
public static void failAndExitWithErrorMessage(String msg, Logger logger) {
if (logger != null) {
logger.error(getDefaultErrorMessage(msg));
}
System.exit(NOT_OK);
}
/**
* Builds a Data Dictionary Cache
*