diff --git a/crates/cli/src/subcommands/delete.rs b/crates/cli/src/subcommands/delete.rs index 2f91e3ab30f..a7b9154a13a 100644 --- a/crates/cli/src/subcommands/delete.rs +++ b/crates/cli/src/subcommands/delete.rs @@ -45,6 +45,20 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E let force = args.get_flag("force"); let identity = database_identity(&config, &resolved.database, server).await?; + let delete_target = if resolved.database == identity.to_string() { + identity.to_string() + } else { + format!("{} ({identity})", resolved.database) + }; + + if !y_or_n( + force, + &format!("Are you sure you want to delete database {delete_target}? This action cannot be undone."), + )? { + println!("Aborting"); + return Ok(()); + } + let host_url = config.get_host_url(server)?; let request_path = format!("{host_url}/v1/database/{identity}"); let auth_header = get_auth_header(&mut config, false, server, !force).await?; @@ -58,7 +72,7 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E if !force { print_database_tree_info(&confirm.database_tree).await?; } - if y_or_n(force, "Do you want to proceed deleting above databases?")? { + if force || y_or_n(false, "Do you want to proceed deleting above databases?")? { send_request(&client, &request_path, &auth_header, Some(confirm.confirmation_token)) .await? .error_for_status()?; diff --git a/crates/smoketests/tests/smoketests/delete_database.rs b/crates/smoketests/tests/smoketests/delete_database.rs index c101304563f..e55152fb5d1 100644 --- a/crates/smoketests/tests/smoketests/delete_database.rs +++ b/crates/smoketests/tests/smoketests/delete_database.rs @@ -2,11 +2,41 @@ use spacetimedb_smoketests::Smoketest; use std::thread; use std::time::Duration; +fn assert_delete_prompt(output: &str, database: &str) { + assert!( + output.contains("Are you sure you want to delete database"), + "expected confirmation prompt in output:\n{output}" + ); + assert!( + output.contains(database), + "expected database name in confirmation prompt:\n{output}" + ); +} + +#[test] +fn test_delete_database_aborts_without_confirmation() { + let mut test = Smoketest::builder() + .precompiled_module("delete-database") + .autopublish(false) + .build(); + + let name = format!("test-db-{}", std::process::id()); + test.publish_module_named(&name, false).unwrap(); + + let output = test + .spacetime_with_stdin(&["delete", "--server", &test.server_url, &name], "n\n") + .unwrap(); + assert_delete_prompt(&output, &name); + assert!(output.contains("Aborting"), "expected abort message:\n{output}"); + + test.spacetime(&["logs", "--server", &test.server_url, &name]).unwrap(); +} + /// Test that deleting a database stops the module. /// The module is considered stopped if its scheduled reducer stops /// producing update events. #[test] -fn test_delete_database() { +fn test_delete_database_with_confirmation() { let mut test = Smoketest::builder() .precompiled_module("delete-database") .autopublish(false) @@ -23,8 +53,10 @@ fn test_delete_database() { thread::sleep(Duration::from_secs(2)); // Delete the database - test.spacetime(&["delete", "--server", &test.server_url, &name]) + let output = test + .spacetime_with_stdin(&["delete", "--server", &test.server_url, &name], "y\n") .unwrap(); + assert_delete_prompt(&output, &name); // Collect whatever updates we got let updates = sub.collect().unwrap(); @@ -37,3 +69,25 @@ fn test_delete_database() { updates.len() ); } + +#[test] +fn test_delete_database_yes_skips_confirmation() { + let mut test = Smoketest::builder() + .precompiled_module("delete-database") + .autopublish(false) + .build(); + + let name = format!("test-db-{}", std::process::id()); + test.publish_module_named(&name, false).unwrap(); + + let output = test + .spacetime(&["delete", "--server", &test.server_url, "--yes", &name]) + .unwrap(); + assert!( + output.contains("Skipping confirmation due to --yes"), + "expected --yes skip message:\n{output}" + ); + + let result = test.spacetime(&["logs", "--server", &test.server_url, &name]); + assert!(result.is_err(), "expected database to be deleted"); +}