Source: MongoCollectionFake.js

/* eslint-disable class-methods-use-this */
import deepEqual from 'deep-equal';
import MongoCursorFake from './MongoCursorFake';
import ObjectID from './ObjectIdFake';

/**
 * Fake for the standard Mongo [Collection]{@link http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html} module.
 * Documents are stored in memory.
 */
class MongoCollectionFake {

  constructor() {

    this.docs = [];
    this.cursorFake = new MongoCursorFake();
  }

  /**
   * removes all stored documents
   *
   * [drop]{@link http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#drop}
   *
   * @returns {Promise.<T>}
   */
  drop() {

    this.docs = [];
    return Promise.resolve();
  }

  /**
   * find documents in the database
   *
   * [find]{@link http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#find}
   *
   * @param query currently has support for the following (anything else will be ignored and all documents will be returned)
   *  - no query, i.e. return all documents
   *  - '$or' type queries for strict equality, e.g. \{$or [ \{name: 'rimmer'\}, \{rank: 'third technician'\} ] \}
   * @returns {MongoCursorFake}
   */
  find(query) {

    let docsToIterateOver = this.docs;

    if (query && query.$or) {

      const queryDocs = query.$or;

      docsToIterateOver = this.filterDocsOr(queryDocs);
    }

    this.cursorFake.iterateOver(docsToIterateOver);

    return this.cursorFake;
  }

  /**
   * insert documents into the database
   *
   * [insertMany]{@link http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#insertMany}
   *
   * @param docs - Array of objects
   * @returns {Promise.<{insertedCount}>}
   */
  insertMany(docs) {

    const docsWithId = docs.map((doc) => {

      const updatedDocWithId = {
        _id: new ObjectID(),
        ...doc,
      };

      return updatedDocWithId;
    });

    this.docs = [...this.docs, ...docsWithId];

    return Promise.resolve({ insertedCount: docs.length });
  }

  /**
   * update many documents in the database
   *
   * [updateMany]{@link http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#updateMany}
   * @param filter currently has support for the following (anything else will be ignored and all documents will be updated)
   *  - no query, i.e. return all documents
   *  - '$or' type queries for strict equality, e.g. \{$or [ \{name: 'rimmer'\}, \{rank: 'third technician'\} ] \}
   * @param update - Supports $set
   * @param options - ignored
   * @returns {Promise.<T>}
   */
  // eslint-disable-next-line no-unused-vars
  updateMany(filter, update, options) {

    const updateObj = update.$set;
    let numDocsUpdated = 0;

    if (updateObj) {

      const propertiesToUpdate = Object.getOwnPropertyNames(updateObj);

      const keyToUpdate = propertiesToUpdate[0];

      this.docs.forEach((doc) => {

        const isMatching = MongoCollectionFake.isDocMatchingFilter(filter, doc);

        if (isMatching) {

          // eslint-disable-next-line no-param-reassign
          doc[keyToUpdate] = updateObj[keyToUpdate];

          numDocsUpdated += 1;
        }
      });
    }

    return Promise.resolve({ result: { n: numDocsUpdated } });
  }

  filterDocsOr(queryDocs) {

    let filteredDocs = [];

    queryDocs.forEach((queryDoc) => {

      const propertyToMatch = Object.keys(queryDoc)[0];

      const matchingDocs = this.docs.filter((doc) => {

        return MongoCollectionFake.isPropertyPresentAndMatching(doc, queryDoc, propertyToMatch);
      });

      filteredDocs = [...filteredDocs, ...matchingDocs];
    });

    return filteredDocs;
  }

  static isDocMatchingFilter(filter, doc) {

    let isMatching = false;

    if (filter && filter.$or) {

      filter.$or.forEach((queryDoc) => {

        const propertyToMatch = Object.keys(queryDoc)[0];

        isMatching = isMatching || MongoCollectionFake.isPropertyPresentAndMatching(doc, queryDoc, propertyToMatch);
      });
    } else {

      isMatching = true;
    }

    return isMatching;
  }

  static isPropertyPresentAndMatching(targetObj, testObj, property) {

    let propertyIsPresentAndMatching = false;
    const targetValue = targetObj[property];

    if (targetValue !== undefined) {

      propertyIsPresentAndMatching = deepEqual(targetValue, testObj[property], { strict: true });
    }

    return propertyIsPresentAndMatching;
  }
}


export default MongoCollectionFake;