mongodb 1.2.0

The official MongoDB driver for Rust
Documentation
use std::time::Duration;

use crate::{
    bson::{doc, Document},
    bson_util,
    cmap::{CommandResponse, StreamDescription},
    cursor::CursorInformation,
    operation::{GetMore, Operation},
    options::StreamAddress,
    sdam::{ServerDescription, ServerInfo, ServerType},
    Namespace,
};

fn build_test(
    ns: Namespace,
    cursor_id: i64,
    address: StreamAddress,
    batch_size: Option<u32>,
    max_time: Option<Duration>,
    mut expected_body: Document,
) {
    let info = CursorInformation {
        ns: ns.clone(),
        id: cursor_id,
        address,
        batch_size,
        max_time,
    };
    let get_more = GetMore::new(info);

    let build_result = get_more.build(&StreamDescription::new_testing());
    assert!(build_result.is_ok());

    let mut cmd = build_result.unwrap();
    assert_eq!(cmd.name, "getMore".to_string());
    assert_eq!(cmd.read_pref, None);
    assert_eq!(cmd.target_db, ns.db);

    bson_util::sort_document(&mut expected_body);
    bson_util::sort_document(&mut cmd.body);
    assert_eq!(cmd.body, expected_body);
}

#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
async fn build() {
    let ns = Namespace {
        db: "test_db".to_string(),
        coll: "test_coll".to_string(),
    };
    let cursor_id: i64 = 123;
    let address = StreamAddress {
        hostname: "localhost".to_string(),
        port: Some(1234),
    };
    let batch_size: u32 = 123;
    let max_time = Duration::from_millis(456);

    let expected_body = doc! {
        "getMore": cursor_id,
        "collection": ns.coll.clone(),
        "batchSize": 123,
        "maxTimeMS": 456,
    };

    build_test(
        ns,
        cursor_id,
        address,
        Some(batch_size),
        Some(max_time),
        expected_body,
    );
}

#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
async fn build_batch_size() {
    let ns = Namespace {
        db: "test_db".to_string(),
        coll: "test_coll".to_string(),
    };
    let cursor_id: i64 = 123;
    let address = StreamAddress {
        hostname: "localhost".to_string(),
        port: Some(1234),
    };

    build_test(
        ns.clone(),
        cursor_id,
        address.clone(),
        Some(0),
        None,
        doc! {
            "getMore": cursor_id,
            "collection": ns.coll.clone(),
        },
    );

    build_test(
        ns.clone(),
        cursor_id,
        address.clone(),
        Some(123),
        None,
        doc! {
            "getMore": cursor_id,
            "collection": ns.coll,
            "batchSize": 123,
        },
    );

    let info = CursorInformation {
        ns: Namespace::empty(),
        address,
        id: cursor_id,
        batch_size: Some((std::i32::MAX as u32) + 1),
        max_time: None,
    };
    let op = GetMore::new(info);
    assert!(op.build(&StreamDescription::new_testing()).is_err())
}

#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
async fn op_selection_criteria() {
    let address = StreamAddress {
        hostname: "myhost.com".to_string(),
        port: Some(1234),
    };

    let info = CursorInformation {
        ns: Namespace::empty(),
        address: address.clone(),
        id: 123,
        batch_size: None,
        max_time: None,
    };
    let get_more = GetMore::new(info);
    let server_description = ServerDescription {
        address,
        server_type: ServerType::Unknown,
        reply: Ok(None),
        last_update_time: None,
        average_round_trip_time: None,
    };
    let server_info = ServerInfo::new(&server_description);

    let predicate = get_more
        .selection_criteria()
        .expect("should not be none")
        .as_predicate()
        .expect("should be predicate");
    assert!(predicate(&server_info));

    let server_description = ServerDescription {
        address: StreamAddress::default(),
        ..server_description
    };
    let server_info = ServerInfo::new(&server_description);
    assert!(!predicate(&server_info));
}

#[cfg_attr(feature = "tokio-runtime", tokio::test)]
#[cfg_attr(feature = "async-std-runtime", async_std::test)]
async fn handle_success() {
    let ns = Namespace {
        db: "test_db".to_string(),
        coll: "test_coll".to_string(),
    };
    let cursor_id: i64 = 123;
    let address = StreamAddress {
        hostname: "localhost".to_string(),
        port: Some(1234),
    };

    let info = CursorInformation {
        ns,
        address,
        id: cursor_id,
        batch_size: None,
        max_time: None,
    };
    let get_more = GetMore::new(info);

    let batch = vec![doc! { "_id": 1 }, doc! { "_id": 2 }, doc! { "_id": 3 }];

    let response = CommandResponse::with_document(doc! {
        "cursor": {
            "id": 123,
            "ns": "test_db.test_coll",
            "nextBatch": bson_util::to_bson_array(&batch),
        },
        "ok": 1
    });

    let result = get_more
        .handle_response(response)
        .expect("handle success case failed");
    assert!(!result.exhausted);
    assert_eq!(result.batch, batch);

    let response = CommandResponse::with_document(doc! {
        "cursor": {
            "id": 0,
            "ns": "test_db.test_coll",
            "nextBatch": bson_util::to_bson_array(&batch),
        },
        "ok": 1
    });
    let result = get_more
        .handle_response(response)
        .expect("handle success case failed");
    assert!(result.exhausted);
    assert_eq!(result.batch, batch);
}