Skip to content

RUST-1077 Update read/write concern document tests #567

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/concern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,14 @@ pub struct WriteConcern {
/// Note that an error being returned due to a write concern error does not imply that the
/// write would not have finished propagating if allowed more time to finish, and the
/// server will not roll back the writes that occurred before the timeout was reached.
#[serde(rename = "wtimeout")]
#[serde(rename = "wtimeout", alias = "wtimeoutMS")]
#[serde(serialize_with = "bson_util::serialize_duration_option_as_int_millis")]
#[serde(deserialize_with = "bson_util::deserialize_duration_option_from_u64_millis")]
#[serde(default)]
pub w_timeout: Option<Duration>,

/// Requests acknowledgement that the operation has propagated to the on-disk journal.
#[serde(rename = "j")]
#[serde(rename = "j", alias = "journal")]
pub journal: Option<bool>,
}

Expand Down Expand Up @@ -285,11 +285,12 @@ impl From<String> for Acknowledgment {
}

impl WriteConcern {
#[allow(dead_code)]
pub(crate) fn is_acknowledged(&self) -> bool {
self.w != Some(Acknowledgment::Nodes(0)) || self.journal == Some(true)
}

/// Whether the write concern was created with no values specified. If true, the write concern
/// should be considered the server's default.
pub(crate) fn is_empty(&self) -> bool {
self.w == None && self.w_timeout == None && self.journal == None
}
Expand Down
143 changes: 84 additions & 59 deletions src/test/spec/read_write_concern/document.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
use std::time::Duration;

use serde::Deserialize;

use crate::{
bson::{Bson, Document},
error::Error,
options::{Acknowledgment, WriteConcern},
bson::Document,
options::{ReadConcern, WriteConcern},
test::run_spec_test,
};

Expand All @@ -15,80 +12,108 @@ struct TestFile {
}

#[derive(Debug, Deserialize)]
// TODO RUST-1077: remove the #[allow(dead_code)] tag and add #[serde(deny_unknown_fields)] to
// ensure these tests are being fully run
#[allow(dead_code)]
#[serde(rename_all = "camelCase")]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct TestCase {
pub description: String,
pub valid: bool,
pub write_concern: Option<Document>,
pub write_concern_document: Option<Document>,
pub read_concern: Option<Document>,
pub read_concern_document: Option<Document>,
pub is_server_default: Option<bool>,
pub is_acknowledged: Option<bool>,
}

fn write_concern_from_document(write_concern_doc: Document) -> Option<WriteConcern> {
let mut write_concern = WriteConcern::default();
async fn run_document_test(test_file: TestFile) {
for test_case in test_file.tests {
let description = test_case.description.as_str();

for (key, value) in write_concern_doc {
match (&key[..], value) {
("w", Bson::Int32(i)) => {
write_concern.w = Some(Acknowledgment::from(i as u32));
}
("w", Bson::String(s)) => {
write_concern.w = Some(Acknowledgment::from(s));
}
("journal", Bson::Boolean(b)) => {
write_concern.journal = Some(b);
}
("wtimeoutMS", Bson::Int32(i)) if i > 0 => {
write_concern.w_timeout = Some(Duration::from_millis(i as u64));
if let Some(specified_write_concern_document) = test_case.write_concern {
let specified_write_concern =
match bson::from_document::<WriteConcern>(specified_write_concern_document) {
Ok(write_concern) => {
let is_valid = write_concern.validate().is_ok();
assert_eq!(
is_valid, test_case.valid,
"Write concern deserialization/validation should fail: {}",
description
);
write_concern
}
Err(err) => {
assert!(
!test_case.valid,
"Write concern deserialization should succeed but got {:?}: {}",
description, err
);
continue;
}
};

if let Some(is_server_default) = test_case.is_server_default {
assert_eq!(
specified_write_concern.is_empty(),
is_server_default,
"is_server_default is {} but write concern is {:?}: {}",
is_server_default,
&specified_write_concern,
description
);
}
("wtimeoutMS", Bson::Int32(_)) => {
// WriteConcern has an unsigned integer for the wtimeout field, so this is
// impossible to test.
return None;

if let Some(is_acknowledged) = test_case.is_acknowledged {
assert_eq!(
specified_write_concern.is_acknowledged(),
is_acknowledged,
"is_acknowledged is {} but write concern is {:?}: {}",
is_acknowledged,
&specified_write_concern,
description
);
}
_ => {}
};
}

Some(write_concern)
}
let actual_write_concern_document = bson::to_document(&specified_write_concern)
.unwrap_or_else(|_| {
panic!(
"Write concern serialization should succeed: {}",
description
)
});

async fn run_document_test(test_file: TestFile) {
for test_case in test_file.tests {
if let Some(specified_write_concern) = test_case.write_concern {
let wc = write_concern_from_document(specified_write_concern).map(|write_concern| {
write_concern.validate().map_err(Error::from).and_then(|_| {
let doc = bson::to_bson(&write_concern)?;
if let Some(expected_write_concern_document) = test_case.write_concern_document {
assert_eq!(
actual_write_concern_document, expected_write_concern_document,
"{}",
description
);
}
}

if let Some(specified_read_concern_document) = test_case.read_concern {
// It's impossible to construct an empty `ReadConcern` (i.e. the server's default) in
// the Rust driver so we skip this test.
if test_case.description == "Default" {
continue;
}

Ok(doc)
})
});
let specified_read_concern: ReadConcern =
bson::from_document(specified_read_concern_document).unwrap_or_else(|_| {
panic!(
"Read concern deserialization should succeed: {}",
description
)
});

let actual_write_concern = match wc {
Some(Ok(Bson::Document(write_concern))) => {
assert!(test_case.valid, "{}", &test_case.description);
write_concern
}
Some(Ok(x)) => panic!("wat: {:?}", x),
Some(Err(_)) => {
assert!(!test_case.valid, "{}", &test_case.description);
continue;
}
None => {
continue;
}
};
let actual_read_concern_document = bson::to_document(&specified_read_concern)
.unwrap_or_else(|_| {
panic!("Read concern serialization should succeed: {}", description)
});

if let Some(expected_write_concern) = test_case.write_concern_document {
if let Some(expected_read_concern_document) = test_case.read_concern_document {
assert_eq!(
actual_write_concern, expected_write_concern,
actual_read_concern_document, expected_read_concern_document,
"{}",
&test_case.description
description
);
}
}
Expand Down