From 70203713048a4484e0f6f94843dff2b91f2cddca Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Mon, 13 Nov 2023 13:58:56 +0000 Subject: [PATCH] Add configuration test --- .../src/configuration/version1.rs | 40 +++-------- .../tests/configuration_tests.rs | 71 +++++++++++++++++++ 2 files changed, 80 insertions(+), 31 deletions(-) create mode 100644 crates/ndc-sqlserver/tests/configuration_tests.rs diff --git a/crates/ndc-sqlserver/src/configuration/version1.rs b/crates/ndc-sqlserver/src/configuration/version1.rs index e115a1ab..67866efc 100644 --- a/crates/ndc-sqlserver/src/configuration/version1.rs +++ b/crates/ndc-sqlserver/src/configuration/version1.rs @@ -115,9 +115,7 @@ async fn select_first_row( let stream = select.query(&mut connection).await.unwrap(); // Nothing is fetched, the first result set starts. - let row = stream.into_row().await.unwrap().unwrap(); - - row + stream.into_row().await.unwrap().unwrap() } /// Construct the deployment configuration by introspecting the database. @@ -171,37 +169,16 @@ async fn get_comparison_operators( database::ComparisonOperators(comparison_operators) } +const CHARACTER_STRINGS: [&str; 3] = ["char", "text", "varchar"]; +const UNICODE_CHARACTER_STRINGS: [&str; 3] = ["nchar", "ntext", "nvarchar"]; +const CANNOT_COMPARE: [&str; 3] = ["text", "ntext", "image"]; + // we hard code these, essentially // we look up available types in `sys.types` but hard code their behaviour by looking them up below // categories taken from https://learn.microsoft.com/en-us/sql/t-sql/data-types/data-types-transact-sql fn get_comparison_operators_for_type( type_name: database::ScalarType, ) -> BTreeMap { - let _exact_numerics = vec![ - "bigint", - "bit", - "decimal", - "int", - "money", - "numeric", - "smallint", - "smallmoney", - "tinyint", - ]; - let _approx_numerics = vec!["float", "real"]; - let _date_and_time = vec![ - "date", - "datetime2", - "datetime", - "datetimeoffset", - "smalldatetime", - "time", - ]; - let character_strings = vec!["char", "text", "varchar"]; - let unicode_character_strings = vec!["nchar", "ntext", "nvarchar"]; - let _binary_strings = vec!["binary", "image", "varbinary"]; - let cannot_compare = vec!["text", "ntext", "image"]; // https://learn.microsoft.com/en-us/sql/t-sql/language-elements/comparison-operators-transact-sql?view=sql-server-ver16 - let mut comparison_operators = BTreeMap::new(); // in ndc-spec, all things can be `==` @@ -214,8 +191,8 @@ fn get_comparison_operators_for_type( ); // include LIKE and NOT LIKE for string-ish types - if character_strings.contains(&type_name.0.as_str()) - || unicode_character_strings.contains(&type_name.0.as_str()) + if CHARACTER_STRINGS.contains(&type_name.0.as_str()) + || UNICODE_CHARACTER_STRINGS.contains(&type_name.0.as_str()) { comparison_operators.insert( "_like".to_string(), @@ -234,7 +211,8 @@ fn get_comparison_operators_for_type( } // some things cannot be compared - if !cannot_compare.contains(&type_name.0.as_str()) { + // https://learn.microsoft.com/en-us/sql/t-sql/language-elements/comparison-operators-transact-sql?view=sql-server-ver16 + if !CANNOT_COMPARE.contains(&type_name.0.as_str()) { comparison_operators.insert( "_neq".to_string(), database::ComparisonOperator { diff --git a/crates/ndc-sqlserver/tests/configuration_tests.rs b/crates/ndc-sqlserver/tests/configuration_tests.rs new file mode 100644 index 00000000..f8f209a8 --- /dev/null +++ b/crates/ndc-sqlserver/tests/configuration_tests.rs @@ -0,0 +1,71 @@ +use std::fs; +use std::path::{Path, PathBuf}; + +use ndc_sqlserver::configuration; +use similar_asserts::assert_eq; + +const CONNECTION_STRING: &str ="DRIVER={ODBC Driver 18 for SQL Server};SERVER=127.0.0.1,64003;Uid=SA;Database=Chinook;Pwd=Password!"; + +const CHINOOK_DEPLOYMENT_PATH: &str = "static/chinook-deployment.json"; + +#[tokio::test] +async fn test_configure_is_idempotent() { + configure_is_idempotent(CONNECTION_STRING, CHINOOK_DEPLOYMENT_PATH).await +} + +// Tests that configuration generation has not changed. +// +// This test does not use insta snapshots because it checks the deployment file that is shared with +// other tests. +// +// If you have changed it intentionally, run `just generate-chinook-configuration`. +pub async fn configure_is_idempotent( + connection_string: &str, + chinook_deployment_path: impl AsRef, +) { + let expected_value = read_configuration(chinook_deployment_path); + + let mut args: configuration::RawConfiguration = serde_json::from_value(expected_value.clone()) + .expect("Unable to deserialize as RawConfiguration"); + + args.mssql_connection_string = connection_string.to_string(); + + let actual = configuration::configure(&args) + .await + .expect("configuration::configure"); + + let actual_value = serde_json::to_value(actual).expect("serde_json::to_value"); + + assert_eq!(expected_value, actual_value); +} + +pub async fn configure_initial_configuration_is_unchanged( + connection_string: &str, +) -> ndc_sqlserver::configuration::RawConfiguration { + let args = configuration::RawConfiguration { + mssql_connection_string: connection_string.to_string(), + + ..configuration::RawConfiguration::empty() + }; + + configuration::configure(&args) + .await + .expect("configuration::configure") +} + +fn read_configuration(chinook_deployment_path: impl AsRef) -> serde_json::Value { + let file = fs::File::open(get_path_from_project_root(chinook_deployment_path)) + .expect("fs::File::open"); + serde_json::from_reader(file).expect("serde_json::from_reader") +} + +/// Find the project root via the crate root provided by `cargo test`, +/// and get our single static configuration file. +/// This depends on the convention that all our crates live in `/crates/` +/// and will break in the unlikely case that we change this +pub fn get_path_from_project_root(deployment_path: impl AsRef) -> PathBuf { + let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + d.push("../../"); + d.push(deployment_path); + d +}