mongodb 3.6.0

The official MongoDB driver for Rust
Documentation
use std::sync::LazyLock;

use crate::{cmap::StreamDescription, options::AuthMechanism};

use super::sasl::SaslStart;

static MECHS: LazyLock<[String; 2]> = LazyLock::new(|| {
    [
        AuthMechanism::ScramSha1.as_str().to_string(),
        AuthMechanism::ScramSha256.as_str().to_string(),
    ]
});

#[test]
fn negotiate_both_scram() {
    let description_both = StreamDescription {
        sasl_supported_mechs: Some(MECHS.to_vec()),
        ..Default::default()
    };
    assert_eq!(
        AuthMechanism::from_stream_description(&description_both),
        AuthMechanism::ScramSha256
    );
}

#[test]
fn negotiate_sha1_only() {
    let description_sha1 = StreamDescription {
        sasl_supported_mechs: Some(MECHS[0..=0].to_vec()),
        ..Default::default()
    };
    assert_eq!(
        AuthMechanism::from_stream_description(&description_sha1),
        AuthMechanism::ScramSha1
    );
}

#[test]
fn negotiate_sha256_only() {
    let description_sha256 = StreamDescription {
        sasl_supported_mechs: Some(MECHS[1..=1].to_vec()),
        ..Default::default()
    };
    assert_eq!(
        AuthMechanism::from_stream_description(&description_sha256),
        AuthMechanism::ScramSha256
    );
}

#[test]
fn negotiate_none() {
    let description_none: StreamDescription = Default::default();
    assert_eq!(
        AuthMechanism::from_stream_description(&description_none),
        AuthMechanism::ScramSha1
    );
}

#[test]
fn negotiate_mangled() {
    let description_mangled = StreamDescription {
        sasl_supported_mechs: Some(["NOT A MECHANISM".to_string(), "OTHER".to_string()].to_vec()),
        ..Default::default()
    };
    assert_eq!(
        AuthMechanism::from_stream_description(&description_mangled),
        AuthMechanism::ScramSha1
    );
}

fn scram_sasl_first_options(mechanism: AuthMechanism) {
    let sasl_first = SaslStart::new(String::new(), mechanism, Vec::new(), None);
    let command = sasl_first.into_command().unwrap();
    let options = match command.body.get_document("options") {
        Ok(options) => options,
        Err(_) => panic!("SaslStart should contain options document"),
    };
    match options.get_bool("skipEmptyExchange") {
        Ok(skip_empty_exchange) => assert!(
            skip_empty_exchange,
            "skipEmptyExchange should be true for SCRAM authentication"
        ),
        Err(_) => panic!("SaslStart options should contain skipEmptyExchange"),
    }
}

#[test]
fn sasl_first_options_specified() {
    scram_sasl_first_options(AuthMechanism::ScramSha1);
    scram_sasl_first_options(AuthMechanism::ScramSha256);
}

#[test]
fn sasl_first_options_not_specified() {
    let sasl_first = SaslStart::new(String::new(), AuthMechanism::MongoDbX509, Vec::new(), None);
    let command = sasl_first.into_command().unwrap();
    assert!(
        command.body.get_document("options").is_err(),
        "SaslStart should not contain options document for X.509 authentication"
    );
}