From 2dfda34dba95cc1dc73dbce412b52e6911eb455c Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Thu, 9 Apr 2026 11:36:46 -0400 Subject: [PATCH 1/2] Show database names in spacetime list --- crates/cli/src/subcommands/list.rs | 49 ++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/subcommands/list.rs b/crates/cli/src/subcommands/list.rs index d650e98f52d..5ba9c05e6f1 100644 --- a/crates/cli/src/subcommands/list.rs +++ b/crates/cli/src/subcommands/list.rs @@ -6,7 +6,9 @@ use crate::util::UNSTABLE_WARNING; use crate::Config; use anyhow::Context; use clap::{ArgMatches, Command}; +use futures::future::join_all; use serde::Deserialize; +use spacetimedb_client_api_messages::name::DatabaseName; use spacetimedb_lib::Identity; use tabled::{ settings::{object::Columns, Alignment, Modify, Style}, @@ -33,6 +35,12 @@ struct IdentityRow { pub db_identity: Identity, } +#[derive(Tabled)] +struct DatabaseRow { + pub db_names: String, + pub db_identity: Identity, +} + pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> { eprintln!("{UNSTABLE_WARNING}\n"); @@ -58,11 +66,12 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E .context("unable to retrieve databases for identity")?; if !result.identities.is_empty() { - let mut table = Table::new(result.identities); + let databases = lookup_database_names(&config, server, result.identities).await; + let mut table = Table::new(databases); table .with(Style::psql()) .with(Modify::new(Columns::first()).with(Alignment::left())); - println!("Associated database identities for {identity}:\n"); + println!("Associated databases for {identity}:\n"); println!("{table}"); } else { println!("No databases found for {identity}."); @@ -70,3 +79,39 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E Ok(()) } + +async fn lookup_database_names( + config: &Config, + server: Option<&str>, + identities: Vec, +) -> Vec { + let lookups = identities.iter().map(|row| async { + let result = util::spacetime_reverse_dns(config, &row.db_identity.to_string(), server).await; + (row.db_identity, result) + }); + + join_all(lookups) + .await + .into_iter() + .map(|(db_identity, result)| { + let db_names = match result { + Ok(response) if !response.names.is_empty() => format_database_names(response.names), + Ok(_) => "(unnamed)".to_string(), + Err(err) => { + eprintln!("Warning: failed to look up names for {db_identity}: {err}"); + "(lookup failed)".to_string() + } + }; + + DatabaseRow { db_names, db_identity } + }) + .collect() +} + +fn format_database_names(names: Vec) -> String { + names + .into_iter() + .map(|name| name.to_string()) + .collect::>() + .join(", ") +} From e232929917ad560f61f49721927a9354b419ae14 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Thu, 9 Apr 2026 11:50:43 -0400 Subject: [PATCH 2/2] Add smoketests for spacetime list names --- .../smoketests/tests/smoketests/cli/list.rs | 78 +++++++++++++++++++ crates/smoketests/tests/smoketests/cli/mod.rs | 1 + 2 files changed, 79 insertions(+) create mode 100644 crates/smoketests/tests/smoketests/cli/list.rs diff --git a/crates/smoketests/tests/smoketests/cli/list.rs b/crates/smoketests/tests/smoketests/cli/list.rs new file mode 100644 index 00000000000..b432c7c981a --- /dev/null +++ b/crates/smoketests/tests/smoketests/cli/list.rs @@ -0,0 +1,78 @@ +//! CLI list command tests + +use spacetimedb_smoketests::{require_local_server, Smoketest}; +use std::process::Output; + +fn output_stdout(output: &Output) -> String { + String::from_utf8_lossy(&output.stdout).to_string() +} + +fn output_stderr(output: &Output) -> String { + String::from_utf8_lossy(&output.stderr).to_string() +} + +fn assert_success(output: &Output, context: &str) { + assert!( + output.status.success(), + "{context} failed:\nstdout: {}\nstderr: {}", + output_stdout(output), + output_stderr(output), + ); +} + +#[test] +fn cli_list_shows_database_names_and_identities() { + require_local_server!(); + let mut test = Smoketest::builder().autopublish(false).build(); + + let primary_name = format!("list-db-{}", std::process::id()); + let alias_name = format!("{primary_name}-alias"); + let second_alias_name = format!("{primary_name}-alt"); + let identity = test.publish_module_named(&primary_name, false).unwrap(); + + let json_body = format!(r#"["{}","{}"]"#, alias_name, second_alias_name); + let response = test + .api_call_json("PUT", &format!("/v1/database/{primary_name}/names"), &json_body) + .unwrap(); + assert_eq!( + response.status_code, + 200, + "Expected 200 status when replacing names, got {}: {}", + response.status_code, + String::from_utf8_lossy(&response.body) + ); + + let output = test.spacetime_cmd(&["list", "--server", &test.server_url]); + assert_success(&output, "spacetime list"); + + let stdout = output_stdout(&output); + assert!(stdout.contains("db_names"), "missing db_names column:\n{stdout}"); + assert!(stdout.contains("db_identity"), "missing db_identity column:\n{stdout}"); + assert!(stdout.contains(&alias_name), "missing alias name in output:\n{stdout}"); + assert!( + stdout.contains(&second_alias_name), + "missing second alias name in output:\n{stdout}" + ); + assert!( + stdout.contains(&identity), + "missing database identity in output:\n{stdout}" + ); +} + +#[test] +fn cli_list_marks_unnamed_databases() { + require_local_server!(); + let mut test = Smoketest::builder().autopublish(false).build(); + + let identity = test.publish_module().unwrap(); + + let output = test.spacetime_cmd(&["list", "--server", &test.server_url]); + assert_success(&output, "spacetime list"); + + let stdout = output_stdout(&output); + assert!(stdout.contains("(unnamed)"), "missing unnamed marker:\n{stdout}"); + assert!( + stdout.contains(&identity), + "missing database identity in output:\n{stdout}" + ); +} diff --git a/crates/smoketests/tests/smoketests/cli/mod.rs b/crates/smoketests/tests/smoketests/cli/mod.rs index d54c9749851..60ece75d377 100644 --- a/crates/smoketests/tests/smoketests/cli/mod.rs +++ b/crates/smoketests/tests/smoketests/cli/mod.rs @@ -1,5 +1,6 @@ pub mod auth; pub mod dev; pub mod generate; +pub mod list; pub mod publish; pub mod server;