package com.lordofthejars.nosqlunit.couchdb;
import com.lordofthejars.nosqlunit.core.FailureHandler;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.ektorp.CouchDbConnector;
import static com.lordofthejars.nosqlunit.util.DeepEquals.deepEquals;
public class CouchDbAssertion {
private static final String _REV = "_rev";
private static final String _ID = "_id";
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private CouchDbAssertion() {
super();
}
public static final void strictAssertEquals(InputStream expectedData, CouchDbConnector couchDb) {
try {
List<Map<String, Object>> expectedDocuments = DataLoader.getDocuments(expectedData);
List<String> allDocIds = couchDb.getAllDocIds();
checkNumberOfDocuments(expectedDocuments, allDocIds);
List<Map<String, Object>> remainingExpectedElementsWithoutIds = checkDocumentsWithId(expectedDocuments,
couchDb, allDocIds);
checkDocumentsWithoutId(couchDb, allDocIds, remainingExpectedElementsWithoutIds);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
private static void checkNumberOfDocuments(List<Map<String, Object>> expectedDocuments, List<String> allDocIds) {
int expectedSize = expectedDocuments.size();
int currentSize = allDocIds.size();
if (expectedSize != currentSize) {
throw FailureHandler.createFailure("Expected number of elements are %s but insert are %s.",
expectedSize, currentSize);
}
}
private static void checkDocumentsWithoutId(CouchDbConnector couchDb, List<String> allDocIds,
List<Map<String, Object>> remainingExpectedElementsWithoutIds) {
List<Map<String, Object>> storedElementsWihtoutExpectedId = loadDocuments(allDocIds, couchDb);
for (Map<String, Object> expectedElement : remainingExpectedElementsWithoutIds) {
if (!isExpectedElementPresent(expectedElement, storedElementsWihtoutExpectedId)) {
throw FailureHandler.createFailure("Expected element # %s # is not found.",
jsonStringify(expectedElement));
}
}
}
private static boolean isExpectedElementPresent(Map<String, Object> expectedElement,
List<Map<String, Object>> storedElementsWihtoutExpectedId) {
for (Map<String, Object> element : storedElementsWihtoutExpectedId) {
if (deepEquals(expectedElement, element)) {
return true;
}
}
return false;
}
private static List<Map<String, Object>> loadDocuments(List<String> allDocIds, CouchDbConnector couchDb) {
List<Map<String, Object>> loadedDocuments = new ArrayList<Map<String, Object>>();
for (String id : allDocIds) {
Map<String, Object> element = couchDb.get(Map.class, id);
removeIdTag(element);
removeRevisionTag(element);
loadedDocuments.add(element);
}
return loadedDocuments;
}
private static List<Map<String, Object>> checkDocumentsWithId(List<Map<String, Object>> expectedDocuments,
CouchDbConnector couchDb, List<String> allDocIds) throws IOException, JsonProcessingException {
Iterator<Map<String, Object>> expectedDocumentsIterator = expectedDocuments.iterator();
List<Map<String, Object>> remainingElements = new ArrayList<Map<String, Object>>();
while (expectedDocumentsIterator.hasNext()) {
Map<String, Object> expectedDocument = expectedDocumentsIterator.next();
Object idNode = expectedDocument.get(_ID);
if (isIdDefined(idNode)) {
String expectedId = toStringValue(idNode);
if (allDocIds.remove(expectedId)) {
checkDocument(couchDb, expectedDocument, expectedId);
} else {
throw FailureHandler.createFailure("Document with id %s is not found.", expectedId);
}
expectedDocumentsIterator.remove();
} else {
remainingElements.add(expectedDocument);
}
}
return remainingElements;
}
private static void checkDocument(CouchDbConnector couchDb, Map<String, Object> expectedDocument, String expectedId)
throws Error {
// Element must be in database
Map<String, Object> element = couchDb.get(Map.class, expectedId);
// We remove rev because it is impossible to know by the end
// user
removeRevisionTag(element);
if (!deepEquals(element, expectedDocument)) {
throw FailureHandler.createFailure(
"Expected element # %s # is not found but # %s # was found.",
jsonStringify(expectedDocument), jsonStringify(element));
}
}
private static boolean isIdDefined(Object idNode) {
return idNode != null;
}
private static void removeRevisionTag(Map<String, Object> element) {
element.remove(_REV);
}
private static void removeIdTag(Map<String, Object> element) {
element.remove(_ID);
}
private static String toStringValue(Object value) {
if (value instanceof String) {
return (String) value;
} else {
throw new IllegalArgumentException("An String value was expected.");
}
}
private static String jsonStringify(Map<String, Object> document) {
try {
return OBJECT_MAPPER.writeValueAsString(document);
} catch (JsonGenerationException e) {
throw new IllegalStateException(e);
} catch (JsonMappingException e) {
throw new IllegalStateException(e);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}