diff --git a/src/coll/options.rs b/src/coll/options.rs index cfc99ff04..8987eac0f 100644 --- a/src/coll/options.rs +++ b/src/coll/options.rs @@ -509,7 +509,7 @@ pub struct CountOptions { /// /// This options maps to the `maxTimeMS` MongoDB query option, so the duration will be sent /// across the wire as an integer number of milliseconds. - #[serde(deserialize_with = "deserialize_duration_from_u64_millis")] + #[serde(default, deserialize_with = "deserialize_duration_from_u64_millis")] pub max_time: Option, /// The number of documents to skip before counting. @@ -548,6 +548,7 @@ pub struct EstimatedDocumentCountOptions { /// This options maps to the `maxTimeMS` MongoDB query option, so the duration will be sent /// across the wire as an integer number of milliseconds. #[serde( + default, serialize_with = "serialize_duration_as_int_millis", rename = "maxTimeMS", deserialize_with = "deserialize_duration_from_u64_millis" @@ -577,6 +578,7 @@ pub struct DistinctOptions { /// This options maps to the `maxTimeMS` MongoDB query option, so the duration will be sent /// across the wire as an integer number of milliseconds. #[serde( + default, serialize_with = "serialize_duration_as_int_millis", rename = "maxTimeMS", deserialize_with = "deserialize_duration_from_u64_millis" diff --git a/src/error.rs b/src/error.rs index c14d33a7b..c9463cde2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,10 @@ //! Contains the `Error` and `Result` types that `mongodb` uses. -use std::{collections::{HashMap, HashSet}, fmt::{self, Debug}, sync::Arc}; +use std::{ + collections::{HashMap, HashSet}, + fmt::{self, Debug}, + sync::Arc, +}; use bson::Bson; use serde::Deserialize; @@ -58,8 +62,7 @@ impl Error { ErrorKind::ConnectionPoolCleared { message: format!( "Connection pool for {} cleared because another operation failed with: {}", - address, - cause + address, cause ), } .into() diff --git a/src/is_master.rs b/src/is_master.rs index 553a3824f..3f94c1060 100644 --- a/src/is_master.rs +++ b/src/is_master.rs @@ -14,7 +14,11 @@ use crate::{ /// Construct an isMaster command. pub(crate) fn is_master_command(api: Option<&ServerApi>) -> Command { let command_name = if api.is_some() { "hello" } else { "isMaster" }; - let mut command = Command::new(command_name.into(), "admin".into(), doc! { command_name: 1 }); + let mut command = Command::new( + command_name.into(), + "admin".into(), + doc! { command_name: 1 }, + ); if let Some(server_api) = api { command.set_server_api(server_api); } @@ -28,8 +32,8 @@ pub(crate) async fn run_is_master( command: Command, conn: &mut Connection, ) -> Result { - if !command.name.eq_ignore_ascii_case("ismaster") && - !command.name.eq_ignore_ascii_case("hello") { + if !command.name.eq_ignore_ascii_case("ismaster") && !command.name.eq_ignore_ascii_case("hello") + { return Err(ErrorKind::Internal { message: format!("invalid ismaster command: {}", command.name), } diff --git a/src/results.rs b/src/results.rs index e568f892b..e2d7292d9 100644 --- a/src/results.rs +++ b/src/results.rs @@ -147,7 +147,10 @@ pub struct DatabaseSpecification { pub name: String, /// The amount of disk space in bytes that is consumed by the database. - #[serde(deserialize_with = "crate::bson_util::deserialize_u64_from_bson_number")] + #[serde( + deserialize_with = "crate::bson_util::deserialize_u64_from_bson_number", + serialize_with = "crate::bson::serde_helpers::serialize_u64_as_i64" + )] pub size_on_disk: u64, /// Whether the database has any data. diff --git a/src/test/mod.rs b/src/test/mod.rs index d6f96b0f3..c09b39a11 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -11,7 +11,7 @@ mod spec; mod util; pub(crate) use self::{ - spec::{run_spec_test, RunOn, Topology}, + spec::{run_single_test, run_spec_test, RunOn, Topology}, util::{ assert_matches, CmapEvent, diff --git a/src/test/spec/json/unified-runner-examples/example-insertOne.json b/src/test/spec/json/unified-test-format/examples/example-insertOne.json similarity index 100% rename from src/test/spec/json/unified-runner-examples/example-insertOne.json rename to src/test/spec/json/unified-test-format/examples/example-insertOne.json diff --git a/src/test/spec/json/unified-runner-examples/poc-crud.json b/src/test/spec/json/unified-test-format/examples/poc-crud.json similarity index 100% rename from src/test/spec/json/unified-runner-examples/poc-crud.json rename to src/test/spec/json/unified-test-format/examples/poc-crud.json diff --git a/src/test/spec/json/unified-runner-examples/poc-retryable-reads.json b/src/test/spec/json/unified-test-format/examples/poc-retryable-reads.json similarity index 100% rename from src/test/spec/json/unified-runner-examples/poc-retryable-reads.json rename to src/test/spec/json/unified-test-format/examples/poc-retryable-reads.json diff --git a/src/test/spec/json/unified-runner-examples/poc-retryable-writes.json b/src/test/spec/json/unified-test-format/examples/poc-retryable-writes.json similarity index 100% rename from src/test/spec/json/unified-runner-examples/poc-retryable-writes.json rename to src/test/spec/json/unified-test-format/examples/poc-retryable-writes.json diff --git a/src/test/spec/json/unified-runner-examples/poc-sessions.json b/src/test/spec/json/unified-test-format/examples/poc-sessions.json similarity index 100% rename from src/test/spec/json/unified-runner-examples/poc-sessions.json rename to src/test/spec/json/unified-test-format/examples/poc-sessions.json diff --git a/src/test/spec/json/unified-runner-examples/poc-sessions.yml b/src/test/spec/json/unified-test-format/examples/poc-sessions.yml similarity index 100% rename from src/test/spec/json/unified-runner-examples/poc-sessions.yml rename to src/test/spec/json/unified-test-format/examples/poc-sessions.yml diff --git a/src/test/spec/json/unified-runner-examples/poc-transactions.json b/src/test/spec/json/unified-test-format/examples/poc-transactions.json similarity index 100% rename from src/test/spec/json/unified-runner-examples/poc-transactions.json rename to src/test/spec/json/unified-test-format/examples/poc-transactions.json diff --git a/src/test/spec/json/unified-runner-examples/poc-transactions.yml b/src/test/spec/json/unified-test-format/examples/poc-transactions.yml similarity index 100% rename from src/test/spec/json/unified-runner-examples/poc-transactions.yml rename to src/test/spec/json/unified-test-format/examples/poc-transactions.yml diff --git a/src/test/spec/json/unified-test-format/valid-fail/assertNumberConnectionsCheckedOut.json b/src/test/spec/json/unified-test-format/valid-fail/assertNumberConnectionsCheckedOut.json new file mode 100644 index 000000000..9799bb2f6 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/assertNumberConnectionsCheckedOut.json @@ -0,0 +1,63 @@ +{ + "description": "assertNumberConnectionsCheckedOut", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true + } + } + ], + "tests": [ + { + "description": "operation fails if client field is not specified", + "operations": [ + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "connections": 1 + } + } + ] + }, + { + "description": "operation fails if connections field is not specified", + "operations": [ + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0" + } + } + ] + }, + { + "description": "operation fails if client entity does not exist", + "operations": [ + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client1" + } + } + ] + }, + { + "description": "operation fails if number of connections is incorrect", + "operations": [ + { + "name": "assertNumberConnectionsCheckedOut", + "object": "testRunner", + "arguments": { + "client": "client0", + "connections": 1 + } + } + ] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/assertNumberConnectionsCheckedOut.yml b/src/test/spec/json/unified-test-format/valid-fail/assertNumberConnectionsCheckedOut.yml new file mode 100644 index 000000000..8a8aa6366 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/assertNumberConnectionsCheckedOut.yml @@ -0,0 +1,38 @@ +description: assertNumberConnectionsCheckedOut + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + +tests: + - description: operation fails if client field is not specified + operations: + - name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + connections: 1 + + - description: operation fails if connections field is not specified + operations: + - name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: *client0 + + - description: operation fails if client entity does not exist + operations: + - name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: client1 + + - description: operation fails if number of connections is incorrect + operations: + - name: assertNumberConnectionsCheckedOut + object: testRunner + arguments: + client: *client0 + connections: 1 diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-bucket-database-undefined.json b/src/test/spec/json/unified-test-format/valid-fail/entity-bucket-database-undefined.json new file mode 100644 index 000000000..7f7f1978c --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-bucket-database-undefined.json @@ -0,0 +1,18 @@ +{ + "description": "entity-bucket-database-undefined", + "schemaVersion": "1.0", + "createEntities": [ + { + "bucket": { + "id": "bucket0", + "database": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-bucket-database-undefined.yml b/src/test/spec/json/unified-test-format/valid-fail/entity-bucket-database-undefined.yml new file mode 100644 index 000000000..7aeda8e1a --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-bucket-database-undefined.yml @@ -0,0 +1,12 @@ +description: "entity-bucket-database-undefined" + +schemaVersion: "1.0" + +createEntities: + - bucket: + id: &bucket0 "bucket0" + database: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-client-apiVersion-unsupported.json b/src/test/spec/json/unified-test-format/valid-fail/entity-client-apiVersion-unsupported.json new file mode 100644 index 000000000..d92d23dca --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-client-apiVersion-unsupported.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-apiVersion-unsupported", + "schemaVersion": "1.1", + "createEntities": [ + { + "client": { + "id": "client0", + "serverApi": { + "version": "server_will_never_support_this_api_version" + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-client-apiVersion-unsupported.yml b/src/test/spec/json/unified-test-format/valid-fail/entity-client-apiVersion-unsupported.yml new file mode 100644 index 000000000..7c626a308 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-client-apiVersion-unsupported.yml @@ -0,0 +1,13 @@ +description: "entity-client-apiVersion-unsupported" + +schemaVersion: "1.1" + +createEntities: + - client: + id: &client0 client0 + serverApi: + version: "server_will_never_support_this_api_version" + +tests: + - description: "foo" + operations: [] diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.json b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.json new file mode 100644 index 000000000..8c0c4d204 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.json @@ -0,0 +1,28 @@ +{ + "description": "entity-client-storeEventsAsEntities-conflict_with_client_id", + "schemaVersion": "1.2", + "createEntities": [ + { + "client": { + "id": "client0", + "storeEventsAsEntities": [ + { + "id": "client0", + "events": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent" + ] + } + ] + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.yml b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.yml new file mode 100644 index 000000000..b7055c9db --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.yml @@ -0,0 +1,16 @@ +description: "entity-client-storeEventsAsEntities-conflict_with_client_id" + +schemaVersion: "1.2" + +createEntities: + - client: + id: &client0 client0 + storeEventsAsEntities: + # Using the client ID here will ensure that test runners also detect + # conflicts with the same entity being defined + - id: *client0 + events: ["PoolCreatedEvent", "PoolReadyEvent", "PoolClearedEvent", "PoolClosedEvent"] + +tests: + - description: "foo" + operations: [] diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.json b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.json new file mode 100644 index 000000000..77bc4abf2 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.json @@ -0,0 +1,43 @@ +{ + "description": "entity-client-storeEventsAsEntities-conflict_within_different_array", + "schemaVersion": "1.2", + "createEntities": [ + { + "client": { + "id": "client0", + "storeEventsAsEntities": [ + { + "id": "events", + "events": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent" + ] + } + ] + } + }, + { + "client": { + "id": "client1", + "storeEventsAsEntities": [ + { + "id": "events", + "events": [ + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + ] + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.yml b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.yml new file mode 100644 index 000000000..8836445c9 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.yml @@ -0,0 +1,19 @@ +description: "entity-client-storeEventsAsEntities-conflict_within_different_array" + +schemaVersion: "1.2" + +createEntities: + - client: + id: &client0 client0 + storeEventsAsEntities: + - id: &events events + events: ["PoolCreatedEvent", "PoolReadyEvent", "PoolClearedEvent", "PoolClosedEvent"] + - client: + id: &client1 client1 + storeEventsAsEntities: + - id: *events + events: ["CommandStartedEvent", "CommandSucceededEvent", "CommandFailedEvent"] + +tests: + - description: "foo" + operations: [] diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.json b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.json new file mode 100644 index 000000000..e1a949988 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.json @@ -0,0 +1,36 @@ +{ + "description": "entity-client-storeEventsAsEntities-conflict_within_same_array", + "schemaVersion": "1.2", + "createEntities": [ + { + "client": { + "id": "client0", + "storeEventsAsEntities": [ + { + "id": "events", + "events": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolClosedEvent" + ] + }, + { + "id": "events", + "events": [ + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent" + ] + } + ] + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.yml b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.yml new file mode 100644 index 000000000..25ee7400d --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.yml @@ -0,0 +1,16 @@ +description: "entity-client-storeEventsAsEntities-conflict_within_same_array" + +schemaVersion: "1.2" + +createEntities: + - client: + id: &client0 client0 + storeEventsAsEntities: + - id: &events events + events: ["PoolCreatedEvent", "PoolReadyEvent", "PoolClearedEvent", "PoolClosedEvent"] + - id: *events + events: ["CommandStartedEvent", "CommandSucceededEvent", "CommandFailedEvent"] + +tests: + - description: "foo" + operations: [] diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-collection-database-undefined.json b/src/test/spec/json/unified-test-format/valid-fail/entity-collection-database-undefined.json new file mode 100644 index 000000000..20b0733e3 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-collection-database-undefined.json @@ -0,0 +1,19 @@ +{ + "description": "entity-collection-database-undefined", + "schemaVersion": "1.0", + "createEntities": [ + { + "collection": { + "id": "collection0", + "database": "foo", + "collectionName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-collection-database-undefined.yml b/src/test/spec/json/unified-test-format/valid-fail/entity-collection-database-undefined.yml new file mode 100644 index 000000000..a66b6ed29 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-collection-database-undefined.yml @@ -0,0 +1,13 @@ +description: "entity-collection-database-undefined" + +schemaVersion: "1.0" + +createEntities: + - collection: + id: &collection0 "collection0" + database: "foo" + collectionName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-database-client-undefined.json b/src/test/spec/json/unified-test-format/valid-fail/entity-database-client-undefined.json new file mode 100644 index 000000000..0f8110e6d --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-database-client-undefined.json @@ -0,0 +1,19 @@ +{ + "description": "entity-database-client-undefined", + "schemaVersion": "1.0", + "createEntities": [ + { + "database": { + "id": "database0", + "client": "foo", + "databaseName": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-database-client-undefined.yml b/src/test/spec/json/unified-test-format/valid-fail/entity-database-client-undefined.yml new file mode 100644 index 000000000..34b58cece --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-database-client-undefined.yml @@ -0,0 +1,13 @@ +description: "entity-database-client-undefined" + +schemaVersion: "1.0" + +createEntities: + - database: + id: &database0 "database0" + client: "foo" + databaseName: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-find-cursor.json b/src/test/spec/json/unified-test-format/valid-fail/entity-find-cursor.json new file mode 100644 index 000000000..f4c5bcdf4 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-find-cursor.json @@ -0,0 +1,62 @@ +{ + "description": "entity-find-cursor", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "databaseName": "database0Name", + "collectionName": "coll0", + "documents": [] + } + ], + "tests": [ + { + "description": "createFindCursor fails if filter is not specified", + "operations": [ + { + "name": "createFindCursor", + "object": "collection0", + "saveResultAsEntity": "cursor0" + } + ] + }, + { + "description": "iterateUntilDocumentOrError fails if it references a nonexistent entity", + "operations": [ + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0" + } + ] + }, + { + "description": "close fails if it references a nonexistent entity", + "operations": [ + { + "name": "close", + "object": "cursor0" + } + ] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-find-cursor.yml b/src/test/spec/json/unified-test-format/valid-fail/entity-find-cursor.yml new file mode 100644 index 000000000..ac35615fe --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-find-cursor.yml @@ -0,0 +1,37 @@ +description: entity-find-cursor + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - databaseName: *database0Name + collectionName: *collection0Name + documents: [] + +tests: + - description: createFindCursor fails if filter is not specified + operations: + - name: createFindCursor + object: *collection0 + saveResultAsEntity: &cursor0 cursor0 + + - description: iterateUntilDocumentOrError fails if it references a nonexistent entity + operations: + - name: iterateUntilDocumentOrError + object: cursor0 + + - description: close fails if it references a nonexistent entity + operations: + - name: close + object: cursor0 diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-session-client-undefined.json b/src/test/spec/json/unified-test-format/valid-fail/entity-session-client-undefined.json new file mode 100644 index 000000000..260356436 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-session-client-undefined.json @@ -0,0 +1,18 @@ +{ + "description": "entity-session-client-undefined", + "schemaVersion": "1.0", + "createEntities": [ + { + "session": { + "id": "session0", + "client": "foo" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/entity-session-client-undefined.yml b/src/test/spec/json/unified-test-format/valid-fail/entity-session-client-undefined.yml new file mode 100644 index 000000000..16eaea46b --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/entity-session-client-undefined.yml @@ -0,0 +1,12 @@ +description: "entity-session-client-undefined" + +schemaVersion: "1.0" + +createEntities: + - session: + id: &session0 "session0" + client: "foo" + +tests: + - description: "foo" + operations: [] diff --git a/src/test/spec/json/unified-test-format/valid-fail/ignoreResultAndError.json b/src/test/spec/json/unified-test-format/valid-fail/ignoreResultAndError.json new file mode 100644 index 000000000..4457040b4 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/ignoreResultAndError.json @@ -0,0 +1,72 @@ +{ + "description": "ignoreResultAndError", + "schemaVersion": "1.3", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0Name" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "database0Name", + "documents": [] + } + ], + "tests": [ + { + "description": "operation errors are not ignored if ignoreResultAndError is false", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + } + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1 + } + }, + "ignoreResultAndError": false + } + ] + }, + { + "description": "malformed operation fails if ignoreResultAndError is true", + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "foo": "bar" + }, + "ignoreResultAndError": true + } + ] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/ignoreResultAndError.yml b/src/test/spec/json/unified-test-format/valid-fail/ignoreResultAndError.yml new file mode 100644 index 000000000..52498627a --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/ignoreResultAndError.yml @@ -0,0 +1,43 @@ +description: ignoreResultAndError + +schemaVersion: '1.3' + +createEntities: + - client: + id: &client0 client0 + useMultipleMongoses: true + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name database0Name + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: + - collectionName: *collection0Name + databaseName: *database0Name + documents: [] + +tests: + - description: operation errors are not ignored if ignoreResultAndError is false + operations: + - name: insertOne + object: *collection0 + arguments: + document: &insertDocument { _id: 1 } + - name: insertOne + object: *collection0 + arguments: + # Insert the same document to force a DuplicateKey error. + document: *insertDocument + ignoreResultAndError: false + + - description: malformed operation fails if ignoreResultAndError is true + operations: + - name: insertOne + object: *collection0 + arguments: + foo: bar + ignoreResultAndError: true diff --git a/src/test/spec/json/unified-test-format/valid-fail/operation-failure.json b/src/test/spec/json/unified-test-format/valid-fail/operation-failure.json new file mode 100644 index 000000000..8f6cae152 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/operation-failure.json @@ -0,0 +1,56 @@ +{ + "description": "operation-failure", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "operation-failure" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "tests": [ + { + "description": "Unsupported command", + "operations": [ + { + "name": "runCommand", + "object": "database0", + "arguments": { + "commandName": "unsupportedCommand", + "command": { + "unsupportedCommand": 1 + } + } + } + ] + }, + { + "description": "Unsupported query operator", + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "$unsupportedQueryOperator": 1 + } + } + } + ] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/operation-failure.yml b/src/test/spec/json/unified-test-format/valid-fail/operation-failure.yml new file mode 100644 index 000000000..b80f7794d --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/operation-failure.yml @@ -0,0 +1,31 @@ +description: "operation-failure" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: operation-failure + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: coll0 + +tests: + - description: "Unsupported command" + operations: + - name: runCommand + object: *database0 + arguments: + commandName: unsupportedCommand + command: { unsupportedCommand: 1 } + + - description: "Unsupported query operator" + operations: + - name: find + object: *collection0 + arguments: + filter: { $unsupportedQueryOperator: 1 } diff --git a/src/test/spec/json/unified-test-format/valid-fail/returnDocument-enum-invalid.json b/src/test/spec/json/unified-test-format/valid-fail/returnDocument-enum-invalid.json new file mode 100644 index 000000000..ea425fb56 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/returnDocument-enum-invalid.json @@ -0,0 +1,66 @@ +{ + "description": "returnDocument-enum-invalid", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "test" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll" + } + } + ], + "tests": [ + { + "description": "FindOneAndReplace returnDocument invalid enum value", + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": 111 + }, + "returnDocument": "invalid" + } + } + ] + }, + { + "description": "FindOneAndUpdate returnDocument invalid enum value", + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$inc": { + "x": 1 + } + }, + "returnDocument": "invalid" + } + } + ] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/returnDocument-enum-invalid.yml b/src/test/spec/json/unified-test-format/valid-fail/returnDocument-enum-invalid.yml new file mode 100644 index 000000000..b877f5e96 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/returnDocument-enum-invalid.yml @@ -0,0 +1,34 @@ +description: "returnDocument-enum-invalid" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &databaseName test + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collectionName coll + +tests: + - description: "FindOneAndReplace returnDocument invalid enum value" + operations: + - name: findOneAndReplace + object: *collection0 + arguments: + filter: { _id: 1 } + replacement: { _id: 1, x: 111 } + returnDocument: invalid + + - description: "FindOneAndUpdate returnDocument invalid enum value" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: { _id: 1 } + update: { $inc: { x : 1 }} + returnDocument: invalid diff --git a/src/test/spec/json/unified-test-format/valid-fail/schemaVersion-unsupported.json b/src/test/spec/json/unified-test-format/valid-fail/schemaVersion-unsupported.json new file mode 100644 index 000000000..ceb553291 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/schemaVersion-unsupported.json @@ -0,0 +1,10 @@ +{ + "description": "schemaVersion-unsupported", + "schemaVersion": "0.1", + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/src/test/spec/json/unified-test-format/valid-fail/schemaVersion-unsupported.yml b/src/test/spec/json/unified-test-format/valid-fail/schemaVersion-unsupported.yml new file mode 100644 index 000000000..0cb6994d2 --- /dev/null +++ b/src/test/spec/json/unified-test-format/valid-fail/schemaVersion-unsupported.yml @@ -0,0 +1,7 @@ +description: "schemaVersion-unsupported" + +schemaVersion: "0.1" + +tests: + - description: "foo" + operations: [] diff --git a/src/test/spec/mod.rs b/src/test/spec/mod.rs index 4c53f5f1f..d854f1f04 100644 --- a/src/test/spec/mod.rs +++ b/src/test/spec/mod.rs @@ -62,22 +62,26 @@ where } let test_file_full_path = base_path.join(&test_file_path); - let json: Value = - serde_json::from_reader(File::open(test_file_full_path.as_path()).unwrap()).unwrap(); + run_single_test(test_file_full_path, &run_test_file).await; + } +} - // Printing the name of the test file makes it easier to debug deserialization errors. - println!( - "Running tests from {}", - test_file_full_path.display().to_string() - ); +pub(crate) async fn run_single_test(path: PathBuf, run_test_file: &F) +where + F: Fn(T) -> G, + G: Future, + T: DeserializeOwned, +{ + let json: Value = serde_json::from_reader(File::open(path.as_path()).unwrap()).unwrap(); - run_test_file( - bson::from_bson( - Bson::try_from(json) - .unwrap_or_else(|_| panic!("{}", test_file_full_path.display().to_string())), - ) - .unwrap_or_else(|e| panic!("{}: {}", test_file_full_path.display().to_string(), e)), + // Printing the name of the test file makes it easier to debug deserialization errors. + println!("Running tests from {}", path.display().to_string()); + + run_test_file( + bson::from_bson( + Bson::try_from(json).unwrap_or_else(|_| panic!("{}", path.display().to_string())), ) - .await - } + .unwrap_or_else(|e| panic!("{}: {}", path.display().to_string(), e)), + ) + .await } diff --git a/src/test/spec/unified_runner/mod.rs b/src/test/spec/unified_runner/mod.rs index 2f23d33e3..4c3277624 100644 --- a/src/test/spec/unified_runner/mod.rs +++ b/src/test/spec/unified_runner/mod.rs @@ -5,16 +5,16 @@ mod test_event; mod test_file; mod test_runner; -use std::time::Duration; +use std::{fs::read_dir, path::PathBuf, time::Duration}; -use futures::stream::TryStreamExt; +use futures::{future::FutureExt, stream::TryStreamExt}; use semver::Version; use tokio::sync::RwLockWriteGuard; use crate::{ bson::{doc, Document}, options::{CollectionOptions, FindOptions, ReadConcern, ReadPreference, SelectionCriteria}, - test::{run_spec_test, LOCK}, + test::{run_single_test, run_spec_test, LOCK}, RUNTIME, }; @@ -165,40 +165,41 @@ pub async fn run_unified_format_test(test_file: TestFile) { } } - if let Some(ref expect_result) = operation.expect_result { - let result = result - .unwrap_or_else(|e| { - panic!( - "{} should succeed, but the following error: {}", - operation.name, e - ) - }) - .unwrap_or_else(|| { + if let Some(expect_error) = operation.expect_error { + let error = result + .expect_err(&format!("{} should return an error", operation.name)); + expect_error.verify_result(error); + } else { + let result = result.unwrap_or_else(|e| { + panic!( + "{} should succeed, but the following error: {}", + operation.name, e + ) + }); + if let Some(ref expect_result) = operation.expect_result { + let result = result.unwrap_or_else(|| { panic!("{} should return an entity", operation.name) }); - match result { - Entity::Bson(ref result) => { - assert!( - results_match( - Some(result), + match result { + Entity::Bson(ref result) => { + assert!( + results_match( + Some(result), + expect_result, + operation.returns_root_documents(), + Some(&test_runner.entities), + ), + "result mismatch, expected = {:#?} actual = {:#?}", expect_result, - operation.returns_root_documents(), - Some(&test_runner.entities), - ), - "result mismatch, expected = {:#?} actual = {:#?}", - expect_result, - result - ); + result + ); + } + _ => panic!( + "Incorrect entity type returned from {}, expected BSON", + operation.name + ), } - _ => panic!( - "Incorrect entity type returned from {}, expected BSON", - operation.name - ), } - } else if let Some(expect_error) = operation.expect_error { - let error = result - .expect_err(&format!("{} should return an error", operation.name)); - expect_error.verify_result(error); } } } @@ -272,5 +273,38 @@ pub async fn run_unified_format_test(test_file: TestFile) { #[cfg_attr(feature = "async-std-runtime", async_std::test)] async fn test_examples() { let _guard: RwLockWriteGuard<_> = LOCK.run_exclusively().await; - run_spec_test(&["unified-runner-examples"], run_unified_format_test).await; + run_spec_test( + &["unified-test-format", "examples"], + run_unified_format_test, + ) + .await; +} + +#[cfg_attr(feature = "tokio-runtime", tokio::test(flavor = "multi_thread"))] +#[cfg_attr(feature = "async-std-runtime", async_std::test)] +async fn valid_fail() { + let _guard: RwLockWriteGuard<_> = LOCK.run_exclusively().await; + + let path: PathBuf = [ + env!("CARGO_MANIFEST_DIR"), + "src", + "test", + "spec", + "json", + "unified-test-format", + "valid-fail", + ] + .iter() + .collect(); + + for entry in read_dir(&path).unwrap() { + let test_file_path = PathBuf::from(entry.unwrap().file_name()); + let path = path.join(&test_file_path); + let path_display = path.display().to_string(); + + std::panic::AssertUnwindSafe(run_single_test(path, &run_unified_format_test)) + .catch_unwind() + .await + .expect_err(&format!("tests from {} should have failed", path_display)); + } } diff --git a/src/test/spec/unified_runner/operation.rs b/src/test/spec/unified_runner/operation.rs index 96524a192..ae667d4ba 100644 --- a/src/test/spec/unified_runner/operation.rs +++ b/src/test/spec/unified_runner/operation.rs @@ -246,7 +246,7 @@ impl Deref for Operation { pub(super) struct DeleteMany { filter: Document, #[serde(flatten)] - options: Option, + options: DeleteOptions, } #[async_trait] @@ -274,7 +274,7 @@ impl TestOperation for DeleteMany { pub(super) struct DeleteOne { filter: Document, #[serde(flatten)] - options: Option, + options: DeleteOptions, } #[async_trait] @@ -302,7 +302,7 @@ impl TestOperation for DeleteOne { pub(super) struct Find { filter: Option, #[serde(flatten)] - options: Option, + options: FindOptions, } #[async_trait] @@ -334,7 +334,7 @@ impl TestOperation for Find { pub(super) struct InsertMany { documents: Vec, #[serde(flatten)] - options: Option, + options: InsertManyOptions, } #[async_trait] @@ -368,7 +368,7 @@ pub(super) struct InsertOne { document: Document, session: Option, #[serde(flatten)] - options: Option, + options: InsertOneOptions, } #[async_trait] @@ -410,7 +410,7 @@ pub(super) struct UpdateMany { filter: Document, update: UpdateModifications, #[serde(flatten)] - options: Option, + options: UpdateOptions, } #[async_trait] @@ -443,7 +443,7 @@ pub(super) struct UpdateOne { filter: Document, update: UpdateModifications, #[serde(flatten)] - options: Option, + options: UpdateOptions, session: Option, } @@ -490,7 +490,7 @@ impl TestOperation for UpdateOne { pub(super) struct Aggregate { pipeline: Vec, #[serde(flatten)] - options: Option, + options: AggregateOptions, } #[async_trait] @@ -531,7 +531,7 @@ pub(super) struct Distinct { field_name: String, filter: Option, #[serde(flatten)] - options: Option, + options: DistinctOptions, } #[async_trait] @@ -558,7 +558,7 @@ impl TestOperation for Distinct { pub(super) struct CountDocuments { filter: Document, #[serde(flatten)] - options: Option, + options: CountOptions, } #[async_trait] @@ -584,7 +584,7 @@ impl TestOperation for CountDocuments { #[serde(rename_all = "camelCase", deny_unknown_fields)] pub(super) struct EstimatedDocumentCount { #[serde(flatten)] - options: Option, + options: EstimatedDocumentCountOptions, } #[async_trait] @@ -611,7 +611,7 @@ impl TestOperation for EstimatedDocumentCount { pub(super) struct FindOne { filter: Option, #[serde(flatten)] - options: Option, + options: FindOneOptions, } #[async_trait] @@ -641,7 +641,7 @@ impl TestOperation for FindOne { pub(super) struct ListDatabases { filter: Option, #[serde(flatten)] - options: Option, + options: ListDatabasesOptions, } #[async_trait] @@ -668,7 +668,7 @@ impl TestOperation for ListDatabases { pub(super) struct ListDatabaseNames { filter: Option, #[serde(flatten)] - options: Option, + options: ListDatabasesOptions, } #[async_trait] @@ -696,7 +696,7 @@ impl TestOperation for ListDatabaseNames { pub(super) struct ListCollections { filter: Option, #[serde(flatten)] - options: Option, + options: ListCollectionsOptions, } #[async_trait] @@ -753,7 +753,7 @@ pub(super) struct ReplaceOne { filter: Document, replacement: Document, #[serde(flatten)] - options: Option, + options: ReplaceOptions, } #[async_trait] @@ -786,7 +786,7 @@ pub(super) struct FindOneAndUpdate { filter: Document, update: UpdateModifications, #[serde(flatten)] - options: Option, + options: FindOneAndUpdateOptions, } #[async_trait] @@ -819,7 +819,7 @@ pub(super) struct FindOneAndReplace { filter: Document, replacement: Document, #[serde(flatten)] - options: Option, + options: FindOneAndReplaceOptions, } #[async_trait] @@ -851,7 +851,7 @@ impl TestOperation for FindOneAndReplace { pub(super) struct FindOneAndDelete { filter: Document, #[serde(flatten)] - options: Option, + options: FindOneAndDeleteOptions, } #[async_trait] @@ -956,7 +956,7 @@ impl TestOperation for AssertCollectionNotExists { pub(super) struct CreateCollection { collection: String, #[serde(flatten)] - options: Option, + options: CreateCollectionOptions, session: Option, } @@ -995,7 +995,7 @@ impl TestOperation for CreateCollection { pub(super) struct DropCollection { collection: String, #[serde(flatten)] - options: Option, + options: DropCollectionOptions, session: Option, }