/** @see DATAMONGO-807 */
  @Test
  public void updateMapperShouldNotPersistTypeInformationForNullValues() {

    Update update = Update.update("model", null);
    UpdateMapper mapper = new UpdateMapper(converter);

    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ModelWrapper.class));

    DBObject set = getAsDBObject(mappedObject, "$set");
    assertThat(set.get("_class"), nullValue());
  }
  /** @see DATAMONGO-407 */
  @Test
  public void updateMapperShouldSupportNestedCollectionElementUpdates() {

    Update update = Update.update("list.$.value", "foo").set("list.$.otherValue", "bar");

    UpdateMapper mapper = new UpdateMapper(converter);
    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ParentClass.class));

    DBObject set = getAsDBObject(mappedObject, "$set");
    assertThat(set.get("aliased.$.value"), is((Object) "foo"));
    assertThat(set.get("aliased.$.otherValue"), is((Object) "bar"));
  }
  /** @see DATAMONGO-407 */
  @Test
  public void updateMapperShouldRetainTypeInformationForNestedCollectionElements() {

    Update update = Update.update("list.$", new ConcreteChildClass("42", "bubu"));

    UpdateMapper mapper = new UpdateMapper(converter);
    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ParentClass.class));

    DBObject set = getAsDBObject(mappedObject, "$set");
    DBObject modelDbObject = getAsDBObject(set, "aliased.$");
    assertThat(modelDbObject.get("_class"), is((Object) ConcreteChildClass.class.getName()));
  }
  /** @see DATAMONGO-807 */
  @Test
  public void updateMapperShouldRetainTypeInformationForNestedEntities() {

    Update update = Update.update("model", new ModelImpl(1));
    UpdateMapper mapper = new UpdateMapper(converter);

    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ModelWrapper.class));

    DBObject set = getAsDBObject(mappedObject, "$set");
    DBObject modelDbObject = (DBObject) set.get("model");
    assertThat(modelDbObject.get("_class"), not(nullValue()));
  }
  /** @see DATAMONGO-404 */
  @Test(expected = MappingException.class)
  public void rejectsInvalidFieldReferenceForDbRef() {

    Update update = new Update().pull("dbRefAnnotatedList.name", "NAME");
    mapper.getMappedObject(
        update.getUpdateObject(), context.getPersistentEntity(DocumentWithDBRefCollection.class));
  }
  /** @see DATAMONGO-404 */
  @Test
  public void rendersNestedDbRefCorrectly() {

    Update update = new Update().pull("nested.dbRefAnnotatedList.id", "2");
    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(Wrapper.class));

    DBObject pullClause = getAsDBObject(mappedObject, "$pull");
    assertThat(pullClause.containsField("mapped.dbRefAnnotatedList"), is(true));
  }
  /** @see DATAMONGO-862 */
  @Test
  public void rendersUpdateAndPreservesKeyForPathsNotPointingToProperty() {

    Update update = new Update().set("listOfInterface.$.value", "expected-value");
    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ParentClass.class));

    DBObject setClause = getAsDBObject(mappedObject, "$set");
    assertThat(setClause.containsField("listOfInterface.$.value"), is(true));
  }
  /** @see DATAMONGO-407 */
  @Test
  public void updateMapperShouldWriteTypeInformationForComplexNestedCollectionElementUpdates() {

    Update update =
        Update.update("list.$.value", "foo")
            .set("list.$.someObject", new ConcreteChildClass("42", "bubu"));

    UpdateMapper mapper = new UpdateMapper(converter);
    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ParentClass.class));

    DBObject dbo = getAsDBObject(mappedObject, "$set");
    assertThat(dbo.get("aliased.$.value"), is((Object) "foo"));

    DBObject someObject = getAsDBObject(dbo, "aliased.$.someObject");
    assertThat(someObject, is(notNullValue()));
    assertThat(someObject.get("_class"), is((Object) ConcreteChildClass.class.getName()));
    assertThat(someObject.get("value"), is((Object) "bubu"));
  }
  /** @see DATAMONGO-812 */
  @Test
  public void testUpdateShouldAllowMultiplePushEachForDifferentFields() {

    Update update =
        new Update().push("category").each("spring", "data").push("type").each("mongodb");
    DBObject mappedObject =
        mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));

    DBObject push = getAsDBObject(mappedObject, "$push");
    assertThat(getAsDBObject(push, "category").containsField("$each"), is(true));
    assertThat(getAsDBObject(push, "type").containsField("$each"), is(true));
  }
  /** @see DATAMONGO-404 */
  @Test
  public void createsDbRefForEntityIdOnPulls() {

    Update update = new Update().pull("dbRefAnnotatedList.id", "2");

    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(),
            context.getPersistentEntity(DocumentWithDBRefCollection.class));

    DBObject pullClause = getAsDBObject(mappedObject, "$pull");
    assertThat(pullClause.get("dbRefAnnotatedList"), is((Object) new DBRef(null, "entity", "2")));
  }
  /** @see DATAMONGO-812 */
  @Test
  public void updateMapperShouldConvertPushWhithoutAddingClassInformationWhenUsedWithEvery() {

    Update update = new Update().push("values").each("spring", "data", "mongodb");

    DBObject mappedObject =
        mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Model.class));
    DBObject push = getAsDBObject(mappedObject, "$push");
    DBObject values = getAsDBObject(push, "values");

    assertThat(push.get("_class"), nullValue());
    assertThat(values.get("_class"), nullValue());
  }
  /** @see DATAMONGO-410 */
  @Test
  public void testUpdateMapperShouldConsiderCustomWriteTarget() {

    List<NestedEntity> someValues =
        Arrays.asList(
            new NestedEntity("spring"), new NestedEntity("data"), new NestedEntity("mongodb"));
    NestedEntity[] array = new NestedEntity[someValues.size()];

    Update update = new Update().pushAll("collectionOfNestedEntities", someValues.toArray(array));
    mapper.getMappedObject(
        update.getUpdateObject(), context.getPersistentEntity(DomainEntity.class));

    verify(writingConverterSpy, times(3)).convert(Mockito.any(NestedEntity.class));
  }
  /** @see DATAMONGO-721 */
  @Test
  public void updateMapperRetainsTypeInformationForCollectionField() {

    Update update = new Update().push("list", new ConcreteChildClass("2", "BAR"));

    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ParentClass.class));

    DBObject push = getAsDBObject(mappedObject, "$push");
    DBObject list = getAsDBObject(push, "aliased");

    assertThat(list.get("_class"), is((Object) ConcreteChildClass.class.getName()));
  }
  /** @see DATAMONGO-1077 */
  @Test
  public void shouldNotRemovePositionalParameter() {

    Update update = new Update();
    update.unset("dbRefAnnotatedList.$");

    DBObject mappedUpdate =
        mapper.getMappedObject(
            update.getUpdateObject(),
            context.getPersistentEntity(DocumentWithDBRefCollection.class));

    DBObject $unset = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$unset");

    assertThat($unset, equalTo(new BasicDBObjectBuilder().add("dbRefAnnotatedList.$", 1).get()));
  }
  /** @see DATAMONGO-468 */
  @Test
  public void rendersUpdateOfDbRefPropertyWithDomainObjectCorrectly() {

    Entity entity = new Entity();
    entity.id = "5";

    Update update = new Update().set("dbRefProperty", entity);
    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(),
            context.getPersistentEntity(DocumentWithDBRefCollection.class));

    DBObject setClause = getAsDBObject(mappedObject, "$set");
    assertThat(setClause.get("dbRefProperty"), is((Object) new DBRef(null, "entity", entity.id)));
  }
  /** @see DATAMONGO-943 */
  @Test
  public void updatePushEachAtPositionWorksCorrectlyWhenGivenPositionNull() {

    Update update =
        new Update().push("key").atPosition(null).each(Arrays.asList("Arya", "Arry", "Weasel"));

    DBObject mappedObject =
        mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));

    DBObject push = getAsDBObject(mappedObject, "$push");
    DBObject key = getAsDBObject(push, "key");

    assertThat(key.containsField("$position"), is(false));
    assertThat(getAsDBObject(push, "key").containsField("$each"), is(true));
  }
  /** @see DATAMONGO-847 */
  @Test
  public void updateMapperConvertsPullWithNestedQuerfyOnDBRefCorrectly() {

    Update update =
        new Update().pull("dbRefAnnotatedList", Query.query(Criteria.where("id").is("1")));
    DBObject mappedUpdate =
        mapper.getMappedObject(
            update.getUpdateObject(),
            context.getPersistentEntity(DocumentWithDBRefCollection.class));

    DBObject $pull = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$pull");
    DBObject list = DBObjectTestUtils.getAsDBObject($pull, "dbRefAnnotatedList");

    assertThat(list, equalTo(new BasicDBObjectBuilder().add("_id", "1").get()));
  }
  /** @see DATAMONGO-847 */
  @Test
  public void updateMapperConvertsNestedQueryCorrectly() {

    Update update =
        new Update().pull("list", Query.query(Criteria.where("value").in("foo", "bar")));
    DBObject mappedUpdate =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ParentClass.class));

    DBObject $pull = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$pull");
    DBObject list = DBObjectTestUtils.getAsDBObject($pull, "aliased");
    DBObject value = DBObjectTestUtils.getAsDBObject(list, "value");
    BasicDBList $in = DBObjectTestUtils.getAsDBList(value, "$in");

    assertThat($in, IsIterableContainingInOrder.<Object>contains("foo", "bar"));
  }
  /** @see DATAMONG0-471 */
  @SuppressWarnings({"unchecked", "rawtypes"})
  @Test
  public void testUpdateShouldApply$addToSetCorrectlyWhenUsedWith$each() {

    Update update = new Update().addToSet("values").each("spring", "data", "mongodb");
    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ListModel.class));

    DBObject addToSet = getAsDBObject(mappedObject, "$addToSet");
    DBObject values = getAsDBObject(addToSet, "values");
    BasicDBList each = getAsDBList(values, "$each");

    assertThat(
        each.toMap(), (Matcher) allOf(hasValue("spring"), hasValue("data"), hasValue("mongodb")));
  }
  /** @see DATAMONGO-812 */
  @Test
  public void
      updateMapperShouldRetainClassInformationForPushCorrectlyWhenCalledWithEachUsingCustomTypes() {

    Update update = new Update().push("models").each(new ListModel("spring", "data", "mongodb"));
    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ModelWrapper.class));

    DBObject push = getAsDBObject(mappedObject, "$push");
    DBObject model = getAsDBObject(push, "models");
    BasicDBList each = getAsDBList(model, "$each");

    assertThat(
        ((DBObject) each.get(0)).get("_class").toString(), equalTo(ListModel.class.getName()));
  }
  /** @see DATAMONGO-897 */
  @Test
  public void
      updateOnDbrefPropertyOfInterfaceTypeWithoutExplicitGetterForIdShouldBeMappedCorrectly() {

    Update update =
        new Update().set("referencedDocument", new InterfaceDocumentDefinitionImpl("1", "Foo"));
    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(),
            context.getPersistentEntity(DocumentWithReferenceToInterfaceImpl.class));

    DBObject $set = DBObjectTestUtils.getAsDBObject(mappedObject, "$set");
    Object model = $set.get("referencedDocument");

    DBRef expectedDBRef = new DBRef(factory.getDb(), "interfaceDocumentDefinitionImpl", "1");
    assertThat(model, allOf(instanceOf(DBRef.class), IsEqual.<Object>equalTo(expectedDBRef)));
  }
  /** @see DATAMONGO-812 */
  @SuppressWarnings({"unchecked", "rawtypes"})
  @Test
  public void updateMapperShouldConvertPushCorrectlyWhenCalledWithEachUsingCustomTypes() {

    Update update = new Update().push("models").each(new ListModel("spring", "data", "mongodb"));
    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ModelWrapper.class));

    DBObject push = getAsDBObject(mappedObject, "$push");
    DBObject model = getAsDBObject(push, "models");
    BasicDBList each = getAsDBList(model, "$each");
    BasicDBList values = getAsDBList((DBObject) each.get(0), "values");

    assertThat(
        values.toMap(), (Matcher) allOf(hasValue("spring"), hasValue("data"), hasValue("mongodb")));
  }
  /** @see DATAMONGO-863 */
  @Test
  public void doesNotConvertRawDbObjects() {

    Update update = new Update();
    update.pull(
        "options",
        new BasicDBObject(
            "_id", new BasicDBObject("$in", converter.convertToMongoType(Arrays.asList(1L, 2L)))));

    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ParentClass.class));

    DBObject setClause = getAsDBObject(mappedObject, "$pull");
    DBObject options = getAsDBObject(setClause, "options");
    DBObject idClause = getAsDBObject(options, "_id");
    BasicDBList inClause = getAsDBList(idClause, "$in");

    assertThat(inClause, IsIterableContainingInOrder.<Object>contains(1L, 2L));
  }
  /** @see DATAMONG0-471 */
  @Test
  public void
      testUpdateShouldRetainClassTypeInformationWhenUsing$addToSetWith$eachForCustomTypes() {

    Update update =
        new Update()
            .addToSet("models")
            .each(new ModelImpl(2014), new ModelImpl(1), new ModelImpl(28));
    DBObject mappedObject =
        mapper.getMappedObject(
            update.getUpdateObject(), context.getPersistentEntity(ModelWrapper.class));

    DBObject addToSet = getAsDBObject(mappedObject, "$addToSet");

    DBObject values = getAsDBObject(addToSet, "models");
    BasicDBList each = getAsDBList(values, "$each");

    for (Object updateValue : each) {
      assertThat(
          ((DBObject) updateValue).get("_class").toString(),
          equalTo("org.springframework.data.mongodb.core.convert.UpdateMapperUnitTests$ModelImpl"));
    }
  }