use std::time::Duration;
use bson::{bson, doc, oid::ObjectId, Bson};
use crate::{
bson_util,
cmap::{CommandResponse, StreamDescription},
coll::options::ReturnDocument,
operation::{FindAndModify, Operation},
options::{
FindOneAndDeleteOptions,
FindOneAndReplaceOptions,
FindOneAndUpdateOptions,
UpdateModifications,
},
Namespace,
};
fn empty_delete() -> FindAndModify {
let ns = Namespace {
db: "test_db".to_string(),
coll: "test_coll".to_string(),
};
let filter = doc! {};
FindAndModify::with_delete(ns, filter, None)
}
#[test]
fn build_with_delete_no_options() {
let ns = Namespace {
db: "test_db".to_string(),
coll: "test_coll".to_string(),
};
let filter = doc! { "x": { "$gt": 1 } };
let max_time = Duration::from_millis(2u64);
let op = FindAndModify::with_delete(ns, filter.clone(), None);
let description = StreamDescription::new_testing();
let mut cmd = op.build(&description).unwrap();
assert_eq!(cmd.name.as_str(), "findAndModify");
assert_eq!(cmd.target_db.as_str(), "test_db");
assert_eq!(cmd.read_pref.as_ref(), None);
let mut expected_body = doc! {
"findAndModify": "test_coll",
"query": filter,
"remove": true
};
bson_util::sort_document(&mut cmd.body);
bson_util::sort_document(&mut expected_body);
assert_eq!(cmd.body, expected_body);
}
#[test]
fn build_with_delete() {
let ns = Namespace {
db: "test_db".to_string(),
coll: "test_coll".to_string(),
};
let filter = doc! { "x": { "$gt": 1 } };
let max_time = Duration::from_millis(2u64);
let options = FindOneAndDeleteOptions {
max_time: Some(max_time),
..Default::default()
};
let op = FindAndModify::with_delete(ns, filter.clone(), Some(options));
let description = StreamDescription::new_testing();
let mut cmd = op.build(&description).unwrap();
assert_eq!(cmd.name.as_str(), "findAndModify");
assert_eq!(cmd.target_db.as_str(), "test_db");
assert_eq!(cmd.read_pref.as_ref(), None);
let mut expected_body = doc! {
"findAndModify": "test_coll",
"query": filter,
"maxTimeMS": max_time.as_millis() as i64,
"remove": true
};
bson_util::sort_document(&mut cmd.body);
bson_util::sort_document(&mut expected_body);
assert_eq!(cmd.body, expected_body);
}
#[test]
fn handle_success_delete() {
let op = empty_delete();
let value = doc! {
"_id" : Bson::ObjectId(ObjectId::new().unwrap()),
"name" : "Tom",
"state" : "active",
"rating" : 100,
"score" : 5
};
let ok_response = CommandResponse::with_document(doc! {
"lastErrorObject" : {
"connectionId" : 1,
"updatedExisting" : true,
"n" : 1,
"syncMillis" : 0,
"writtenTo" : null,
"err" : null,
"ok" : 1
},
"value" : value.clone(),
"ok" : 1
});
let result = op.handle_response(ok_response);
assert_eq!(
result.expect("handle failed").expect("result was None"),
value
);
}
#[test]
fn handle_null_value_delete() {
let op = empty_delete();
let null_value = CommandResponse::with_document(doc! { "ok": 1.0, "value": Bson::Null});
let result = op.handle_response(null_value);
assert!(result.is_ok());
assert_eq!(result.expect("handle failed"), None);
}
#[test]
fn handle_no_value_delete() {
let op = empty_delete();
let no_value = CommandResponse::with_document(doc! { "ok": 1.0 });
assert!(op.handle_response(no_value).is_err());
}
fn empty_replace() -> FindAndModify {
let ns = Namespace {
db: "test_db".to_string(),
coll: "test_coll".to_string(),
};
let filter = doc! {};
let replacement = doc! { "x": { "inc": 1 } };
FindAndModify::with_replace(ns, filter, replacement, None).unwrap()
}
#[test]
fn build_with_replace_no_options() {
let ns = Namespace {
db: "test_db".to_string(),
coll: "test_coll".to_string(),
};
let filter = doc! { "x": { "$gt": 1 } };
let replacement = doc! { "x": { "inc": 1 } };
let op = FindAndModify::with_replace(ns, filter.clone(), replacement.clone(), None).unwrap();
let description = StreamDescription::new_testing();
let mut cmd = op.build(&description).unwrap();
assert_eq!(cmd.name.as_str(), "findAndModify");
assert_eq!(cmd.target_db.as_str(), "test_db");
assert_eq!(cmd.read_pref.as_ref(), None);
let mut expected_body = doc! {
"findAndModify": "test_coll",
"query": filter,
"update": replacement,
};
bson_util::sort_document(&mut cmd.body);
bson_util::sort_document(&mut expected_body);
assert_eq!(cmd.body, expected_body);
}
#[test]
fn build_with_replace() {
let ns = Namespace {
db: "test_db".to_string(),
coll: "test_coll".to_string(),
};
let filter = doc! { "x": { "$gt": 1 } };
let replacement = doc! { "x": { "inc": 1 } };
let options = FindOneAndReplaceOptions {
upsert: Some(false),
bypass_document_validation: Some(true),
return_document: Some(ReturnDocument::After),
..Default::default()
};
let op = FindAndModify::with_replace(ns, filter.clone(), replacement.clone(), Some(options))
.unwrap();
let description = StreamDescription::new_testing();
let mut cmd = op.build(&description).unwrap();
assert_eq!(cmd.name.as_str(), "findAndModify");
assert_eq!(cmd.target_db.as_str(), "test_db");
assert_eq!(cmd.read_pref.as_ref(), None);
let mut expected_body = doc! {
"findAndModify": "test_coll",
"query": filter,
"update": replacement,
"upsert": false,
"bypassDocumentValidation": true,
"new": true
};
bson_util::sort_document(&mut cmd.body);
bson_util::sort_document(&mut expected_body);
assert_eq!(cmd.body, expected_body);
}
#[test]
fn handle_success_replace() {
let op = empty_replace();
let value = doc! {
"_id" : Bson::ObjectId(ObjectId::new().unwrap()),
"name" : "Tom",
"state" : "active",
"rating" : 100,
"score" : 5
};
let ok_response = CommandResponse::with_document(doc! {
"lastErrorObject" : {
"connectionId" : 1,
"updatedExisting" : true,
"n" : 1,
"syncMillis" : 0,
"writtenTo" : null,
"err" : null,
"ok" : 1
},
"value" : value.clone(),
"ok" : 1
});
let result = op.handle_response(ok_response);
assert_eq!(
result.expect("handle failed").expect("result was None"),
value
);
}
#[test]
fn handle_null_value_replace() {
let op = empty_replace();
let null_value = CommandResponse::with_document(doc! { "ok": 1.0, "value": Bson::Null});
let result = op.handle_response(null_value);
assert!(result.is_ok());
assert_eq!(result.expect("handle failed"), None);
}
#[test]
fn handle_no_value_replace() {
let op = empty_replace();
let no_value = CommandResponse::with_document(doc! { "ok": 1.0 });
assert!(op.handle_response(no_value).is_err());
}
fn empty_update() -> FindAndModify {
let ns = Namespace {
db: "test_db".to_string(),
coll: "test_coll".to_string(),
};
let filter = doc! {};
let update = UpdateModifications::Document(doc! { "$x": { "$inc": 1 } });
FindAndModify::with_update(ns, filter, update, None).unwrap()
}
#[test]
fn build_with_update_no_options() {
let ns = Namespace {
db: "test_db".to_string(),
coll: "test_coll".to_string(),
};
let filter = doc! { "x": { "$gt": 1 } };
let update = UpdateModifications::Document(doc! { "$x": { "$inc": 1 } });
let op = FindAndModify::with_update(ns, filter.clone(), update.clone(), None).unwrap();
let description = StreamDescription::new_testing();
let mut cmd = op.build(&description).unwrap();
assert_eq!(cmd.name.as_str(), "findAndModify");
assert_eq!(cmd.target_db.as_str(), "test_db");
assert_eq!(cmd.read_pref.as_ref(), None);
let mut expected_body = doc! {
"findAndModify": "test_coll",
"query": filter,
"update": update.to_bson(),
};
bson_util::sort_document(&mut cmd.body);
bson_util::sort_document(&mut expected_body);
assert_eq!(cmd.body, expected_body);
}
#[test]
fn build_with_update() {
let ns = Namespace {
db: "test_db".to_string(),
coll: "test_coll".to_string(),
};
let filter = doc! { "x": { "$gt": 1 } };
let update = UpdateModifications::Document(doc! { "$x": { "$inc": 1 } });
let options = FindOneAndUpdateOptions {
upsert: Some(false),
bypass_document_validation: Some(true),
..Default::default()
};
let op = FindAndModify::with_update(ns, filter.clone(), update.clone(), Some(options)).unwrap();
let description = StreamDescription::new_testing();
let mut cmd = op.build(&description).unwrap();
assert_eq!(cmd.name.as_str(), "findAndModify");
assert_eq!(cmd.target_db.as_str(), "test_db");
assert_eq!(cmd.read_pref.as_ref(), None);
let mut expected_body = doc! {
"findAndModify": "test_coll",
"query": filter,
"update": update.to_bson(),
"upsert": false,
"bypassDocumentValidation": true
};
bson_util::sort_document(&mut cmd.body);
bson_util::sort_document(&mut expected_body);
assert_eq!(cmd.body, expected_body);
}
#[test]
fn handle_success_update() {
let op = empty_update();
let value = doc! {
"_id" : Bson::ObjectId(ObjectId::new().unwrap()),
"name" : "Tom",
"state" : "active",
"rating" : 100,
"score" : 5
};
let ok_response = CommandResponse::with_document(doc! {
"lastErrorObject" : {
"connectionId" : 1,
"updatedExisting" : true,
"n" : 1,
"syncMillis" : 0,
"writtenTo" : null,
"err" : null,
"ok" : 1
},
"value" : value.clone(),
"ok" : 1
});
let result = op.handle_response(ok_response);
assert_eq!(
result.expect("handle failed").expect("result was None"),
value
);
}
#[test]
fn handle_null_value_update() {
let op = empty_update();
let null_value = CommandResponse::with_document(doc! { "ok": 1.0, "value": Bson::Null});
let result = op.handle_response(null_value);
assert!(result.is_ok());
assert_eq!(result.expect("handle failed"), None);
}
#[test]
fn handle_no_value_update() {
let op = empty_update();
let no_value = CommandResponse::with_document(doc! { "ok": 1.0 });
assert!(op.handle_response(no_value).is_err());
}