diff --git a/diesel_cli/Cargo.toml b/diesel_cli/Cargo.toml index 842db31c4892..b157ab2a6d32 100644 --- a/diesel_cli/Cargo.toml +++ b/diesel_cli/Cargo.toml @@ -68,6 +68,7 @@ path = "../diesel_migrations/" [dev-dependencies] tempfile = "3.1.0" +temp-env = "0.3" regex = "1.3.9" insta = "1.21" diff --git a/diesel_cli/README.md b/diesel_cli/README.md index 7d327ea00990..d3402612a536 100644 --- a/diesel_cli/README.md +++ b/diesel_cli/README.md @@ -70,6 +70,9 @@ ways that you can set it: * Set it as an environment variable using [dotenv](https://github.com/dotenv-rs/dotenv#examples) * Pass it directly by adding the `--database-url` flag +To support a test database during development, passing --test-db to have +diesel load the database url from TEST_DATABASE_URL instead. + As an alternative to running migrations with the CLI, you can call [`diesel::migrations::run_pending_migrations`][pending-migrations] from `build.rs`. diff --git a/diesel_cli/src/cli.rs b/diesel_cli/src/cli.rs index 345e3e880e20..bdc12ff0b62e 100644 --- a/diesel_cli/src/cli.rs +++ b/diesel_cli/src/cli.rs @@ -356,6 +356,16 @@ pub fn build_cli() -> Command { .global(true) .num_args(1); + let test_db_arg = Arg::new("TEST_DB") + .long("test-db") + .short('t') + .help( + "Enable using a test db. This switches the database-url fall back to load from the \ + TEST_DATABASE_URL environment variable before DATABASE_URL.", + ) + .global(true) + .action(ArgAction::SetTrue); + let locked_schema_arg = Arg::new("LOCKED_SCHEMA") .long("locked-schema") .help("Require that the schema file is up to date.") @@ -384,6 +394,7 @@ pub fn build_cli() -> Command { ) .arg(database_arg) .arg(config_arg) + .arg(test_db_arg) .arg(locked_schema_arg) .subcommand(migration_subcommand) .subcommand(setup_subcommand) diff --git a/diesel_cli/src/database.rs b/diesel_cli/src/database.rs index 32046ee3854d..3758e0772b70 100644 --- a/diesel_cli/src/database.rs +++ b/diesel_cli/src/database.rs @@ -413,7 +413,14 @@ pub fn database_url(matches: &ArgMatches) -> Result("DATABASE_URL") .cloned() - .or_else(|| env::var("DATABASE_URL").ok()) + .or_else(|| { + let is_test_db = matches.get_one::("TEST_DB").cloned().unwrap_or(false); + if is_test_db { + env::var("TEST_DATABASE_URL").ok() + } else { + env::var("DATABASE_URL").ok() + } + }) .ok_or(crate::errors::Error::DatabaseUrlMissing) } @@ -469,44 +476,183 @@ fn path_from_sqlite_url(database_url: &str) -> Result), + ("TEST_DATABASE_URL", None::<&str>), + ], + || { + let matches = cli + .clone() + .try_get_matches_from(["diesel", "setup"]) + .unwrap(); + let ret = database_url(&matches); + assert!(ret.is_err()); + assert!(matches!( + ret.unwrap_err(), + crate::errors::Error::DatabaseUrlMissing + )); + }, + ); + } + + #[test] + fn when_database_url_arg_returns_database_url_arg() { + let cli = build_cli(); + temp_env::with_vars( + [ + ("DATABASE_URL", Some("sqlite:///prod.sqlite")), + ("TEST_DATABASE_URL", Some("sqlite:///test.sqlite")), + ], + || { + let matches = cli + .clone() + .try_get_matches_from([ + "diesel", + "setup", + "--database-url", + "sqlite:///arg.sqlite", + ]) + .unwrap(); + let ret = database_url(&matches); + assert!(ret.is_ok()); + assert_eq!(ret.unwrap(), "sqlite:///arg.sqlite"); + }, + ); + } + + #[test] + fn when_test_db_and_no_database_url_arg_returns_test_database_url_env() { + let cli = build_cli(); + temp_env::with_vars( + [ + ("DATABASE_URL", Some("sqlite:///prod.sqlite")), + ("TEST_DATABASE_URL", Some("sqlite:///test.sqlite")), + ], + || { + let matches = cli + .clone() + .try_get_matches_from(["diesel", "setup", "--test-db"]) + .unwrap(); + let ret = database_url(&matches); + assert!(ret.is_ok()); + assert_eq!(ret.unwrap(), "sqlite:///test.sqlite"); + }, + ); + } - #[test] - fn split_pg_connection_string_handles_user_and_password() { - let database = "database".to_owned(); - let base_url = "postgresql://user:password@localhost:5432".to_owned(); - let database_url = format!("{base_url}/{database}"); - let postgres_url = format!("{}/{}", base_url, "postgres"); - assert_eq!( - (database, postgres_url), - change_database_of_url(&database_url, "postgres").unwrap() - ); + #[test] + fn when_test_db_and_no_test_url_env_returns_error() { + let cli = build_cli(); + temp_env::with_vars([("DATABASE_URL", Some("sqlite:///prod.sqlite"))], || { + let matches = cli + .clone() + .try_get_matches_from(["diesel", "setup", "--test-db"]) + .unwrap(); + let ret = database_url(&matches); + assert!(ret.is_err()); + assert!(matches!( + ret.unwrap_err(), + crate::errors::Error::DatabaseUrlMissing + )); + }); + } + + #[test] + fn when_test_db_and_database_url_arg_returns_database_url_arg() { + let cli = build_cli(); + temp_env::with_vars( + [ + ("DATABASE_URL", Some("sqlite:///prod.sqlite")), + ("TEST_DATABASE_URL", Some("sqlite:///test.sqlite")), + ], + || { + let matches = cli + .clone() + .try_get_matches_from([ + "diesel", + "setup", + "--test-db", + "--database-url", + "sqlite:///arg.sqlite", + ]) + .unwrap(); + let ret = database_url(&matches); + assert!(ret.is_ok()); + assert_eq!(ret.unwrap(), "sqlite:///arg.sqlite"); + }, + ); + } } - #[test] - fn split_pg_connection_string_handles_query_string() { - let database = "database".to_owned(); - let query = "?sslmode=true".to_owned(); - let base_url = "postgresql://user:password@localhost:5432".to_owned(); - let database_url = format!("{base_url}/{database}{query}"); - let postgres_url = format!("{}/{}{}", base_url, "postgres", query); - assert_eq!( - (database, postgres_url), - change_database_of_url(&database_url, "postgres").unwrap() - ); + #[cfg(any(feature = "postgres", feature = "mysql"))] + mod change_of_url_tests { + use super::super::change_database_of_url; + + #[test] + fn split_pg_connection_string_returns_postgres_url_and_database() { + let database = "database".to_owned(); + let base_url = "postgresql://localhost:5432".to_owned(); + let database_url = format!("{base_url}/{database}"); + let postgres_url = format!("{}/{}", base_url, "postgres"); + assert_eq!( + (database, postgres_url), + change_database_of_url(&database_url, "postgres").unwrap() + ); + } + + #[test] + fn split_pg_connection_string_handles_user_and_password() { + let database = "database".to_owned(); + let base_url = "postgresql://user:password@localhost:5432".to_owned(); + let database_url = format!("{base_url}/{database}"); + let postgres_url = format!("{}/{}", base_url, "postgres"); + assert_eq!( + (database, postgres_url), + change_database_of_url(&database_url, "postgres").unwrap() + ); + } + + #[test] + fn split_pg_connection_string_handles_query_string() { + let database = "database".to_owned(); + let query = "?sslmode=true".to_owned(); + let base_url = "postgresql://user:password@localhost:5432".to_owned(); + let database_url = format!("{base_url}/{database}{query}"); + let postgres_url = format!("{}/{}{}", base_url, "postgres", query); + assert_eq!( + (database, postgres_url), + change_database_of_url(&database_url, "postgres").unwrap() + ); + } } }