All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lordofthejars.nosqlunit.mongodb.MongoDbAssertion Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
package com.lordofthejars.nosqlunit.mongodb;

import com.lordofthejars.nosqlunit.core.FailureHandler;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoIterable;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class MongoDbAssertion {

    private static final String SYSTEM_COLLECTIONS_PATTERN = "system.";
    private static final String DATA = "data";

    private static final Logger logger = LoggerFactory.getLogger(MongoDbAssertion.class);

    private MongoDbAssertion() {
        super();
    }

    public static final void strictAssertEquals(Document expectedData, MongoDatabase mongoDb) {
        Set collectionaNames = expectedData.keySet();

        final MongoIterable collectionNames = mongoDb.listCollectionNames();

        checkCollectionsName(collectionaNames, collectionNames);

        for (String collectionName : collectionaNames) {

            checkCollectionObjects(expectedData, mongoDb, collectionaNames,
                    collectionName);

        }
    }

    private static void checkCollectionsName(
            Set expectedCollectionNames, MongoIterable mongodbCollectionNames) {

        Set mongoDbUserCollectionNames = getUserCollections(mongodbCollectionNames);

        Set allCollections = new HashSet(mongoDbUserCollectionNames);
        allCollections.addAll(expectedCollectionNames);

        if (allCollections.size() != expectedCollectionNames.size() || allCollections.size() != mongoDbUserCollectionNames.size()) {
            throw FailureHandler.createFailure("Expected collection names are %s but insert collection names are %s", expectedCollectionNames, mongoDbUserCollectionNames);
        }

    }

    private static Set getUserCollections(
            MongoIterable mongodbCollectionNames) {
        Set mongoDbUserCollectionNames = new HashSet();

        for (String mongodbCollectionName : mongodbCollectionNames) {

            if (isUserCollection(mongodbCollectionName)) {
                mongoDbUserCollectionNames.add(mongodbCollectionName);
            }

        }
        return mongoDbUserCollectionNames;
    }

    private static boolean isUserCollection(String mongodbCollectionName) {
        return !mongodbCollectionName.contains(SYSTEM_COLLECTIONS_PATTERN);
    }

    private static void checkCollectionObjects(Document expectedData,
                                               MongoDatabase mongoDb, Set collectionaNames, String collectionName)
            throws Error {
        Object object = expectedData.get(collectionName);
        List dataObjects = null;

        if (isDataDirectly(object)) {
            dataObjects = (List) object;
        } else {
            dataObjects = ((Document) object).get(DATA, List.class);
        }

        MongoCollection dbCollection = mongoDb.getCollection(collectionName);

        int expectedDataObjectsCount = dataObjects.size();
        long insertedDataObjectsCount = dbCollection.count();

        if (expectedDataObjectsCount != insertedDataObjectsCount) {
            throw FailureHandler.createFailure("Expected collection has %s elements but insert collection has %s", expectedDataObjectsCount, insertedDataObjectsCount);
        }

        for (Document expectedDataObject : dataObjects) {

            Document foundObject = dbCollection.find(expectedDataObject).first();


            if (!exists(foundObject)) {
                throw FailureHandler.createFailure("Object # %s # is not found into collection %s", expectedDataObject.toString(), collectionaNames);
            }

            checkSameKeys(expectedDataObject, foundObject);

        }
    }

    private static boolean isDataDirectly(Object object) {
        return List.class.isAssignableFrom(object.getClass());
    }

    private static void checkSameKeys(Document expectedDataObject, Document foundObject) {

        Set expectedKeys = expectedDataObject.keySet();
        Set expectedNoneSystemKeys = noneSystemKeys(expectedKeys);
        Set foundKeys = foundObject.keySet();
        Set foundNoneSystemKeys = noneSystemKeys(foundKeys);

        Set allKeys = new HashSet(expectedNoneSystemKeys);
        allKeys.addAll(foundNoneSystemKeys);

        if (allKeys.size() != expectedNoneSystemKeys.size() || allKeys.size() != foundNoneSystemKeys.size()) {
            throw FailureHandler.createFailure("Expected DbObject and insert DbObject have different keys: Expected: %s Inserted: %s", expectedNoneSystemKeys, foundNoneSystemKeys);
        }

    }

    private static Set noneSystemKeys(Set keys) {

        Set noneSystemKeys = new HashSet();

        for (String key : keys) {
            if (!key.startsWith("_")) {
                noneSystemKeys.add(key);
            }
        }

        return noneSystemKeys;
    }

    private static boolean exists(Document foundObject) {
        return foundObject != null;
    }

    //

    /**
     * Checks that all the expected data is present in MongoDB.
     *
     * @param expectedData Expected data.
     * @param mongoDb      Mongo Database.
     */
    public static void flexibleAssertEquals(Document expectedData, String[] ignorePropertyValues, MongoDatabase mongoDb) {
        // Get the expected collections
        Set collectionNames = expectedData.keySet();

        // Get the current collections in mongoDB
        final MongoIterable listCollectionNames = mongoDb.listCollectionNames();

        // Get the concrete property names that should be ignored
        // Map>
        Map> propertiesToIgnore = parseIgnorePropertyValues(collectionNames, ignorePropertyValues);

        // Check expected data
        flexibleCheckCollectionsName(collectionNames, listCollectionNames);
        for (String collectionName : collectionNames) {
            flexibleCheckCollectionObjects(expectedData, mongoDb, collectionName, propertiesToIgnore);
        }
    }

    /**
     * Resolve the properties that will be ignored for each expected collection.
     * 

* Parses the input value following the rules for valid collection and property names * defined in document. * * @param ignorePropertyValues Input values defined with @IgnorePropertyValue. * @return Map with the properties that will be ignored for each document. */ private static Map> parseIgnorePropertyValues(Set collectionNames, String[] ignorePropertyValues) { Map> propertiesToIgnore = new HashMap>(); Pattern collectionAndPropertyPattern = Pattern.compile("^(?!system\\.)([a-z,A-Z,_][^$\0]*)([.])([^$][^.\0]*)$"); Pattern propertyPattern = Pattern.compile("^([^$][^.0]*)$"); for (String ignorePropertyValue : ignorePropertyValues) { Matcher collectionAndPropertyMatcher = collectionAndPropertyPattern.matcher(ignorePropertyValue); Matcher propertyMatcher = propertyPattern.matcher(ignorePropertyValue); // If the property to ignore includes the collection, add it to only exclude // the property in the indicated collection if (collectionAndPropertyMatcher.matches()) { // Add the property to ignore to the proper collection String collectionName = collectionAndPropertyMatcher.group(1); String propertyName = collectionAndPropertyMatcher.group(3); if (collectionNames.contains(collectionName)) { Set properties = propertiesToIgnore.get(collectionName); if (properties == null) { properties = new HashSet(); } properties.add(propertyName); propertiesToIgnore.put(collectionName, properties); } else { logger.warn(String.format("Collection %s for %s is not defined as expected. It won't be used for ignoring properties", collectionName, ignorePropertyValue)); } // If the property to ignore doesn't include the collection, add it to // all the expected collections } else if (propertyMatcher.matches()) { String propertyName = propertyMatcher.group(0); // Add the property to ignore to all the expected collections for (String collectionName : collectionNames) { Set properties = propertiesToIgnore.get(collectionName); if (properties == null) { properties = new HashSet(); } properties.add(propertyName); propertiesToIgnore.put(collectionName, properties); } // If doesn't match any pattern } else { logger.warn(String.format("Property %s has an invalid collection.property value. It won't be used for ignoring properties", ignorePropertyValue)); } } return propertiesToIgnore; } /** * Checks that all the expected collection names are present in MongoDB. Does not check that all the collection * names present in Mongo are in the expected dataset collection names. *

* If any expected collection isn't found in the database collection, the returned error indicates only the * missing expected collections. * @param expectedCollectionNames Expected collection names. * @param mongodbCollectionNames Current MongoDB collection names. */ private static void flexibleCheckCollectionsName(Set expectedCollectionNames, MongoIterable mongodbCollectionNames) { Set mongoDbUserCollectionNames = getUserCollections(mongodbCollectionNames); boolean ok = true; HashSet notFoundCollectionNames = new HashSet(); for (String expectedCollectionName : expectedCollectionNames) { if (!mongoDbUserCollectionNames.contains(expectedCollectionName)) { ok = false; notFoundCollectionNames.add(expectedCollectionName); } } if (!ok) { throw FailureHandler.createFailure("The following collection names %s were not found in the inserted collection names", notFoundCollectionNames); } } /** * Checks that each expected object in the collection exists in the database. * * @param expectedData Expected data. * @param mongoDb Mongo database. * @param collectionName Collection name. */ private static void flexibleCheckCollectionObjects(Document expectedData, MongoDatabase mongoDb, String collectionName, Map> propertiesToIgnore) throws Error { Object object = expectedData.get(collectionName); List dataObjects = null; if (isDataDirectly(object)) { dataObjects = (List) object; } else { dataObjects = ((Document) object).get(DATA, List.class); } MongoCollection dbCollection = mongoDb.getCollection(collectionName); for (Document expectedDataObject : dataObjects) { Document filteredExpectedDataObject = filterProperties(expectedDataObject, propertiesToIgnore.get(collectionName)); final Document foundObject = dbCollection.find(filteredExpectedDataObject).first(); if (dbCollection.count(filteredExpectedDataObject) > 1) { logger.warn(String.format("There were found %d possible matches for this object # %s #. That could have been caused by ignoring too many properties.", dbCollection.count(filteredExpectedDataObject), expectedDataObject.toString())); } if (!exists(foundObject)) { throw FailureHandler.createFailure("Object # %s # is not found into collection %s", filteredExpectedDataObject.toString(), collectionName); } // Check same keys without filtering flexibleCheckSameKeys(expectedDataObject, foundObject); } } /** * Removes the properties defined with @IgnorePropertyValue annotation. * * @param dataObject Object to filter. * @param propertiesToIgnore Properties to filter * @return Data object without the properties to be ignored. */ private static Document filterProperties(Document dataObject, Set propertiesToIgnore) { Document filteredDataObject = new Document(); for (Map.Entry entry : dataObject.entrySet()) { if (propertiesToIgnore == null || !propertiesToIgnore.contains(entry.getKey())) { filteredDataObject.put(entry.getKey(), entry.getValue()); } } return filteredDataObject; } /** * Checks that all the properties are present in both expected and database objects, even the ignored properties. *

* When there are differences between both objects keys, the returned error indicates only the keys that * are missing in each object. * * @param expectedDataObject Expected object. * @param foundObject Database object. */ private static void flexibleCheckSameKeys(Document expectedDataObject, Document foundObject) { Set expectedKeys = expectedDataObject.keySet(); Set expectedNoneSystemKeys = noneSystemKeys(expectedKeys); Set foundKeys = foundObject.keySet(); Set foundNoneSystemKeys = noneSystemKeys(foundKeys); Set allKeys = new HashSet(expectedNoneSystemKeys); allKeys.addAll(foundNoneSystemKeys); HashSet expectedKeysNotInserted = new HashSet(); HashSet insertedKeysNotExpected = new HashSet(); for (String key : allKeys) { if (!expectedNoneSystemKeys.contains(key)) { insertedKeysNotExpected.add(key); } if (!foundNoneSystemKeys.contains(key)) { expectedKeysNotInserted.add(key); } } if (expectedKeysNotInserted.size() > 0 || insertedKeysNotExpected.size() > 0) { StringBuilder errorMessage = new StringBuilder("Expected Document and insert Document have different keys: "); if (expectedKeysNotInserted.size() > 0) { errorMessage.append("expected keys not inserted ").append(expectedKeysNotInserted).append(" "); } if (insertedKeysNotExpected.size() > 0) { errorMessage.append("inserted keys not expected ").append(insertedKeysNotExpected).append(" "); } throw FailureHandler.createFailure(errorMessage.toString()); } } // }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy