This is an automated email from the ASF dual-hosted git repository.
hubcio pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git
The following commit(s) were added to refs/heads/master by this push:
new 5556d30a6 feat(cli): add context show and session status commands
(#3096)
5556d30a6 is described below
commit 5556d30a6d7390bee89a60153c657aeeb0eb55a1
Author: Atharva Lade <[email protected]>
AuthorDate: Wed May 6 05:07:27 2026 -0500
feat(cli): add context show and session status commands (#3096)
---
core/cli/src/args/context.rs | 18 ++
core/cli/src/args/mod.rs | 10 +
.../cli/context/mod.rs => cli/src/args/session.rs} | 24 +-
core/cli/src/commands/binary_context/mod.rs | 1 +
.../src/commands/binary_context/show_context.rs | 252 +++++++++++++++++++
core/cli/src/commands/binary_system/mod.rs | 1 +
.../src/commands/binary_system/session_status.rs | 93 +++++++
core/cli/src/main.rs | 12 +
core/integration/tests/cli/context/mod.rs | 1 +
.../tests/cli/context/test_context_show_command.rs | 269 +++++++++++++++++++++
.../tests/cli/general/test_help_command.rs | 1 +
.../tests/cli/general/test_overview_command.rs | 1 +
12 files changed, 677 insertions(+), 6 deletions(-)
diff --git a/core/cli/src/args/context.rs b/core/cli/src/args/context.rs
index 1c7d9089c..50ef254c8 100644
--- a/core/cli/src/args/context.rs
+++ b/core/cli/src/args/context.rs
@@ -60,6 +60,17 @@ pub(crate) enum ContextAction {
/// iggy context delete production
#[clap(verbatim_doc_comment, visible_alias = "d")]
Delete(ContextDeleteArgs),
+
+ /// Show details of a specific context
+ ///
+ /// Displays the full configuration of a named context including
+ /// transport, server addresses, TLS settings, and credentials.
+ ///
+ /// Examples
+ /// iggy context show default
+ /// iggy context show production
+ #[clap(verbatim_doc_comment, visible_alias = "s")]
+ Show(ContextShowArgs),
}
#[derive(Debug, Clone, Args)]
@@ -148,6 +159,13 @@ pub(crate) struct ContextDeleteArgs {
pub(crate) context_name: String,
}
+#[derive(Debug, Clone, Args)]
+pub(crate) struct ContextShowArgs {
+ /// Name of the context to show
+ #[arg(value_parser = clap::value_parser!(String))]
+ pub(crate) context_name: String,
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/core/cli/src/args/mod.rs b/core/cli/src/args/mod.rs
index 428d4d5e4..a9ed50160 100644
--- a/core/cli/src/args/mod.rs
+++ b/core/cli/src/args/mod.rs
@@ -45,6 +45,9 @@ use crate::args::{
#[cfg(feature = "login-session")]
use crate::args::system::LoginArgs;
+#[cfg(feature = "login-session")]
+use crate::args::session::SessionAction;
+
use self::user::UserAction;
pub(crate) mod client;
@@ -63,6 +66,9 @@ pub(crate) mod system;
pub(crate) mod topic;
pub(crate) mod user;
+#[cfg(feature = "login-session")]
+pub(crate) mod session;
+
static CARGO_BIN_NAME: &str = env!("CARGO_BIN_NAME");
static CARGO_PKG_HOMEPAGE: &str = env!("CARGO_PKG_HOMEPAGE");
@@ -206,6 +212,10 @@ pub(crate) enum Command {
/// execute any command that requires authentication.
#[clap(verbatim_doc_comment, visible_alias = "lo")]
Logout,
+ #[cfg(feature = "login-session")]
+ /// login session operations
+ #[command(subcommand, visible_alias = "sess")]
+ Session(SessionAction),
}
impl IggyConsoleArgs {
diff --git a/core/integration/tests/cli/context/mod.rs
b/core/cli/src/args/session.rs
similarity index 55%
copy from core/integration/tests/cli/context/mod.rs
copy to core/cli/src/args/session.rs
index 1bc857c6c..2243a225a 100644
--- a/core/integration/tests/cli/context/mod.rs
+++ b/core/cli/src/args/session.rs
@@ -16,10 +16,22 @@
* under the License.
*/
-mod common;
+use clap::Subcommand;
-mod test_context_applied;
-mod test_context_create_command;
-mod test_context_delete_command;
-mod test_context_list_command;
-mod test_context_use_command;
+#[derive(Debug, Clone, Subcommand)]
+pub(crate) enum SessionAction {
+ /// Show current login session status
+ ///
+ /// Displays whether you have an active login session and which
+ /// server it is connected to. This checks the local credential
+ /// store without contacting the server.
+ ///
+ /// Note: only checks whether a token exists in the keyring.
+ /// A stale or expired token will still report as active.
+ /// Use 'iggy me' to verify the session is valid server-side.
+ ///
+ /// Examples
+ /// iggy session status
+ #[clap(verbatim_doc_comment, visible_alias = "s")]
+ Status,
+}
diff --git a/core/cli/src/commands/binary_context/mod.rs
b/core/cli/src/commands/binary_context/mod.rs
index bad021e21..8037ca566 100644
--- a/core/cli/src/commands/binary_context/mod.rs
+++ b/core/cli/src/commands/binary_context/mod.rs
@@ -21,4 +21,5 @@ pub mod common;
pub mod create_context;
pub mod delete_context;
pub mod get_contexts;
+pub mod show_context;
pub mod use_context;
diff --git a/core/cli/src/commands/binary_context/show_context.rs
b/core/cli/src/commands/binary_context/show_context.rs
new file mode 100644
index 000000000..882caf116
--- /dev/null
+++ b/core/cli/src/commands/binary_context/show_context.rs
@@ -0,0 +1,252 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use anyhow::bail;
+use async_trait::async_trait;
+use comfy_table::Table;
+use tracing::{Level, event};
+
+use crate::commands::cli_command::{CliCommand, PRINT_TARGET};
+use iggy_common::Client;
+
+use super::common::{ContextConfig, ContextManager};
+
+const MASKED_VALUE: &str = "********";
+
+pub struct ShowContextCmd {
+ context_name: String,
+}
+
+impl ShowContextCmd {
+ pub fn new(context_name: String) -> Self {
+ Self { context_name }
+ }
+
+ fn add_opt_str(table: &mut Table, label: &str, value: &Option<String>) {
+ if let Some(ref v) = *value {
+ table.add_row(vec![label, v]);
+ }
+ }
+
+ fn add_opt_bool(table: &mut Table, label: &str, value: Option<bool>) {
+ if let Some(v) = value {
+ table.add_row(vec![label, &v.to_string()]);
+ }
+ }
+
+ fn add_opt_u16(table: &mut Table, label: &str, value: Option<u16>) {
+ if let Some(v) = value {
+ table.add_row(vec![label, &v.to_string()]);
+ }
+ }
+
+ fn add_opt_u32(table: &mut Table, label: &str, value: Option<u32>) {
+ if let Some(v) = value {
+ table.add_row(vec![label, &v.to_string()]);
+ }
+ }
+
+ fn add_opt_u64(table: &mut Table, label: &str, value: Option<u64>) {
+ if let Some(v) = value {
+ table.add_row(vec![label, &v.to_string()]);
+ }
+ }
+
+ fn add_masked(table: &mut Table, label: &str, value: &Option<String>) {
+ if value.is_some() {
+ table.add_row(vec![label, MASKED_VALUE]);
+ }
+ }
+
+ fn build_table(name: &str, is_active: bool, config: &ContextConfig) ->
Table {
+ let mut table = Table::new();
+ table.set_header(vec!["Property", "Value"]);
+
+ let display_name = if is_active {
+ format!("{name}*")
+ } else {
+ name.to_string()
+ };
+ table.add_row(vec!["Name", &display_name]);
+
+ let iggy = &config.iggy;
+
+ Self::add_opt_str(&mut table, "Transport", &iggy.transport);
+ Self::add_masked(&mut table, "Encryption Key", &iggy.encryption_key);
+ Self::add_opt_str(
+ &mut table,
+ "Credentials Username",
+ &iggy.credentials_username,
+ );
+ Self::add_masked(
+ &mut table,
+ "Credentials Password",
+ &iggy.credentials_password,
+ );
+
+ Self::add_opt_str(&mut table, "HTTP API URL", &iggy.http_api_url);
+ Self::add_opt_u32(&mut table, "HTTP Retries", iggy.http_retries);
+
+ Self::add_opt_str(&mut table, "TCP Server Address",
&iggy.tcp_server_address);
+ Self::add_opt_u32(
+ &mut table,
+ "TCP Reconnection Max Retries",
+ iggy.tcp_reconnection_max_retries,
+ );
+ Self::add_opt_str(
+ &mut table,
+ "TCP Reconnection Interval",
+ &iggy.tcp_reconnection_interval,
+ );
+ Self::add_opt_bool(&mut table, "TCP TLS Enabled",
iggy.tcp_tls_enabled);
+ Self::add_opt_str(&mut table, "TCP TLS Domain", &iggy.tcp_tls_domain);
+
+ Self::add_opt_str(&mut table, "QUIC Client Address",
&iggy.quic_client_address);
+ Self::add_opt_str(&mut table, "QUIC Server Address",
&iggy.quic_server_address);
+ Self::add_opt_str(&mut table, "QUIC Server Name",
&iggy.quic_server_name);
+ Self::add_opt_u32(
+ &mut table,
+ "QUIC Reconnection Max Retries",
+ iggy.quic_reconnection_max_retries,
+ );
+ Self::add_opt_str(
+ &mut table,
+ "QUIC Reconnection Interval",
+ &iggy.quic_reconnection_interval,
+ );
+ Self::add_opt_u64(
+ &mut table,
+ "QUIC Max Concurrent Bidi Streams",
+ iggy.quic_max_concurrent_bidi_streams,
+ );
+ Self::add_opt_u64(
+ &mut table,
+ "QUIC Datagram Send Buffer Size",
+ iggy.quic_datagram_send_buffer_size,
+ );
+ Self::add_opt_u16(&mut table, "QUIC Initial MTU",
iggy.quic_initial_mtu);
+ Self::add_opt_u64(&mut table, "QUIC Send Window",
iggy.quic_send_window);
+ Self::add_opt_u64(&mut table, "QUIC Receive Window",
iggy.quic_receive_window);
+ Self::add_opt_u64(
+ &mut table,
+ "QUIC Response Buffer Size",
+ iggy.quic_response_buffer_size,
+ );
+ Self::add_opt_u64(
+ &mut table,
+ "QUIC Keep Alive Interval",
+ iggy.quic_keep_alive_interval,
+ );
+ Self::add_opt_u64(
+ &mut table,
+ "QUIC Max Idle Timeout",
+ iggy.quic_max_idle_timeout,
+ );
+ Self::add_opt_bool(
+ &mut table,
+ "QUIC Validate Certificate",
+ iggy.quic_validate_certificate,
+ );
+
+ Self::add_opt_str(
+ &mut table,
+ "WebSocket Server Address",
+ &iggy.websocket_server_address,
+ );
+ Self::add_opt_u32(
+ &mut table,
+ "WebSocket Reconnection Max Retries",
+ iggy.websocket_reconnection_max_retries,
+ );
+ Self::add_opt_str(
+ &mut table,
+ "WebSocket Reconnection Interval",
+ &iggy.websocket_reconnection_interval,
+ );
+
+ if let Some(ref username) = config.username {
+ table.add_row(vec!["Username", username]);
+ }
+ Self::add_masked(&mut table, "Password", &config.password);
+ Self::add_masked(&mut table, "Token", &config.token);
+ Self::add_opt_str(&mut table, "Token Name", &config.token_name);
+
+ for (key, value) in &config.extra {
+ table.add_row(vec![key.as_str(), &value.to_string()]);
+ }
+
+ table
+ }
+}
+
+#[async_trait]
+impl CliCommand for ShowContextCmd {
+ fn explain(&self) -> String {
+ let context_name = &self.context_name;
+ format!("show context {context_name}")
+ }
+
+ fn login_required(&self) -> bool {
+ false
+ }
+
+ fn connection_required(&self) -> bool {
+ false
+ }
+
+ async fn execute_cmd(&mut self, _client: &dyn Client) ->
anyhow::Result<(), anyhow::Error> {
+ let mut context_mgr = ContextManager::default();
+ let contexts_map = context_mgr.get_contexts().await?;
+ let active_context_key = context_mgr.get_active_context_key().await?;
+
+ let config = match contexts_map.get(&self.context_name) {
+ Some(config) => config,
+ None => bail!("context '{}' not found", self.context_name),
+ };
+
+ let is_active = self.context_name == active_context_key;
+ let table = Self::build_table(&self.context_name, is_active, config);
+
+ event!(target: PRINT_TARGET, Level::INFO, "{table}");
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn should_return_explain_message() {
+ let cmd = ShowContextCmd::new("production".to_string());
+ assert_eq!(cmd.explain(), "show context production");
+ }
+
+ #[test]
+ fn should_not_require_login() {
+ let cmd = ShowContextCmd::new("test".to_string());
+ assert!(!cmd.login_required());
+ }
+
+ #[test]
+ fn should_not_require_connection() {
+ let cmd = ShowContextCmd::new("test".to_string());
+ assert!(!cmd.connection_required());
+ }
+}
diff --git a/core/cli/src/commands/binary_system/mod.rs
b/core/cli/src/commands/binary_system/mod.rs
index 18e57b5c3..42c0bc1dc 100644
--- a/core/cli/src/commands/binary_system/mod.rs
+++ b/core/cli/src/commands/binary_system/mod.rs
@@ -21,5 +21,6 @@ pub mod logout;
pub mod me;
pub mod ping;
pub mod session;
+pub mod session_status;
pub mod snapshot;
pub mod stats;
diff --git a/core/cli/src/commands/binary_system/session_status.rs
b/core/cli/src/commands/binary_system/session_status.rs
new file mode 100644
index 000000000..94ee0ee16
--- /dev/null
+++ b/core/cli/src/commands/binary_system/session_status.rs
@@ -0,0 +1,93 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use crate::commands::binary_system::session::ServerSession;
+use crate::commands::cli_command::{CliCommand, PRINT_TARGET};
+use async_trait::async_trait;
+use comfy_table::Table;
+use iggy_common::Client;
+use tracing::{Level, event};
+
+pub struct SessionStatusCmd {
+ server_session: ServerSession,
+}
+
+impl SessionStatusCmd {
+ pub fn new(server_address: String) -> Self {
+ Self {
+ server_session: ServerSession::new(server_address),
+ }
+ }
+}
+
+#[async_trait]
+impl CliCommand for SessionStatusCmd {
+ fn explain(&self) -> String {
+ "session status command".to_owned()
+ }
+
+ fn login_required(&self) -> bool {
+ false
+ }
+
+ fn connection_required(&self) -> bool {
+ false
+ }
+
+ async fn execute_cmd(&mut self, _client: &dyn Client) ->
anyhow::Result<(), anyhow::Error> {
+ let is_active = self.server_session.is_active();
+ let server_address = self.server_session.get_server_address();
+
+ let mut table = Table::new();
+ table.set_header(vec!["Property", "Value"]);
+ table.add_row(vec!["Server Address", server_address]);
+ let active = if is_active {
+ "Yes (token presence only, freshness not verified)"
+ } else {
+ "No"
+ };
+ table.add_row(vec!["Session Active", active]);
+
+ event!(target: PRINT_TARGET, Level::INFO, "{table}");
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn should_return_explain_message() {
+ let cmd = SessionStatusCmd::new("127.0.0.1:8090".to_string());
+ assert_eq!(cmd.explain(), "session status command");
+ }
+
+ #[test]
+ fn should_not_require_login() {
+ let cmd = SessionStatusCmd::new("127.0.0.1:8090".to_string());
+ assert!(!cmd.login_required());
+ }
+
+ #[test]
+ fn should_not_require_connection() {
+ let cmd = SessionStatusCmd::new("127.0.0.1:8090".to_string());
+ assert!(!cmd.connection_required());
+ }
+}
diff --git a/core/cli/src/main.rs b/core/cli/src/main.rs
index 98c300ada..543003dac 100644
--- a/core/cli/src/main.rs
+++ b/core/cli/src/main.rs
@@ -43,6 +43,7 @@ use iggy::prelude::{Aes256GcmEncryptor, Args, EncryptorKind,
PersonalAccessToken
use iggy_cli::commands::binary_context::common::ContextManager;
use iggy_cli::commands::binary_context::create_context::CreateContextCmd;
use iggy_cli::commands::binary_context::delete_context::DeleteContextCmd;
+use iggy_cli::commands::binary_context::show_context::ShowContextCmd;
use iggy_cli::commands::binary_context::use_context::UseContextCmd;
use iggy_cli::commands::binary_segments::delete_segments::DeleteSegmentsCmd;
use iggy_cli::commands::binary_system::snapshot::GetSnapshotCmd;
@@ -95,6 +96,8 @@ use tracing::{Level, event};
#[cfg(feature = "login-session")]
mod main_login_session {
+ pub(crate) use crate::args::session::SessionAction;
+ pub(crate) use
iggy_cli::commands::binary_system::session_status::SessionStatusCmd;
pub(crate) use iggy_cli::commands::binary_system::{login::LoginCmd,
logout::LogoutCmd};
pub(crate) use
iggy_cli::commands::utils::login_session_expiry::LoginSessionExpiry;
}
@@ -337,6 +340,9 @@ fn get_command(
ContextAction::Delete(delete_args) => {
Box::new(DeleteContextCmd::new(delete_args.context_name.clone()))
}
+ ContextAction::Show(show_args) => {
+ Box::new(ShowContextCmd::new(show_args.context_name.clone()))
+ }
},
#[cfg(feature = "login-session")]
Command::Login(login_args) => Box::new(LoginCmd::new(
@@ -345,6 +351,12 @@ fn get_command(
)),
#[cfg(feature = "login-session")]
Command::Logout =>
Box::new(LogoutCmd::new(iggy_args.get_server_address().unwrap())),
+ #[cfg(feature = "login-session")]
+ Command::Session(command) => match command {
+ SessionAction::Status => Box::new(SessionStatusCmd::new(
+ iggy_args.get_server_address().unwrap(),
+ )),
+ },
}
}
diff --git a/core/integration/tests/cli/context/mod.rs
b/core/integration/tests/cli/context/mod.rs
index 1bc857c6c..01ca1a747 100644
--- a/core/integration/tests/cli/context/mod.rs
+++ b/core/integration/tests/cli/context/mod.rs
@@ -22,4 +22,5 @@ mod test_context_applied;
mod test_context_create_command;
mod test_context_delete_command;
mod test_context_list_command;
+mod test_context_show_command;
mod test_context_use_command;
diff --git a/core/integration/tests/cli/context/test_context_show_command.rs
b/core/integration/tests/cli/context/test_context_show_command.rs
new file mode 100644
index 000000000..14fd72cb4
--- /dev/null
+++ b/core/integration/tests/cli/context/test_context_show_command.rs
@@ -0,0 +1,269 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use std::collections::BTreeMap;
+
+use crate::cli::common::{
+ CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd,
USAGE_PREFIX,
+};
+use assert_cmd::assert::Assert;
+use async_trait::async_trait;
+use iggy::prelude::Client;
+use iggy_cli::commands::binary_context::common::ContextConfig;
+use iggy_common::ArgsOptional;
+use predicates::str::contains;
+use serial_test::parallel;
+
+use super::common::TestIggyContext;
+
+struct TestContextShowCmd {
+ test_iggy_context: TestIggyContext,
+ context_to_show: String,
+ expected_rows: Vec<String>,
+}
+
+impl TestContextShowCmd {
+ fn new(
+ test_iggy_context: TestIggyContext,
+ context_to_show: String,
+ expected_rows: Vec<String>,
+ ) -> Self {
+ Self {
+ test_iggy_context,
+ context_to_show,
+ expected_rows,
+ }
+ }
+}
+
+#[async_trait]
+impl IggyCmdTestCase for TestContextShowCmd {
+ async fn prepare_server_state(&mut self, _client: &dyn Client) {
+ self.test_iggy_context.prepare().await;
+ }
+
+ fn get_command(&self) -> IggyCmdCommand {
+ IggyCmdCommand::new()
+ .env(
+ "IGGY_HOME",
+ self.test_iggy_context.get_iggy_home().to_str().unwrap(),
+ )
+ .arg("context")
+ .arg("show")
+ .arg(self.context_to_show.clone())
+ .with_env_credentials()
+ }
+
+ fn verify_command(&self, command_state: Assert) {
+ let mut command_state = command_state.success();
+
+ for row in &self.expected_rows {
+ command_state = command_state.stdout(contains(row.as_str()));
+ }
+ }
+
+ async fn verify_server_state(&self, _client: &dyn Client) {}
+}
+
+#[tokio::test]
+#[parallel]
+pub async fn should_show_context_with_all_fields() {
+ let mut iggy_cmd_test = IggyCmdTest::default();
+ iggy_cmd_test.setup().await;
+
+ let config = ContextConfig {
+ username: Some("admin".to_string()),
+ password: Some("secret".to_string()),
+ token: None,
+ token_name: None,
+ iggy: ArgsOptional {
+ transport: Some("tcp".to_string()),
+ tcp_server_address: Some("10.0.0.1:8090".to_string()),
+ tcp_tls_enabled: Some(true),
+ ..Default::default()
+ },
+ extra: Default::default(),
+ };
+
+ iggy_cmd_test
+ .execute_test(TestContextShowCmd::new(
+ TestIggyContext::new(
+ Some(BTreeMap::from([
+ ("default".to_string(), ContextConfig::default()),
+ ("production".to_string(), config),
+ ])),
+ None,
+ ),
+ "production".to_string(),
+ vec![
+ "production".to_string(),
+ "tcp".to_string(),
+ "10.0.0.1:8090".to_string(),
+ "true".to_string(),
+ "admin".to_string(),
+ "********".to_string(),
+ ],
+ ))
+ .await;
+}
+
+#[tokio::test]
+#[parallel]
+pub async fn should_show_default_context() {
+ let mut iggy_cmd_test = IggyCmdTest::default();
+ iggy_cmd_test.setup().await;
+
+ iggy_cmd_test
+ .execute_test(TestContextShowCmd::new(
+ TestIggyContext::new(None, None),
+ "default".to_string(),
+ vec!["default*".to_string()],
+ ))
+ .await;
+}
+
+#[tokio::test]
+#[parallel]
+pub async fn should_show_active_context_with_asterisk() {
+ let mut iggy_cmd_test = IggyCmdTest::default();
+ iggy_cmd_test.setup().await;
+
+ iggy_cmd_test
+ .execute_test(TestContextShowCmd::new(
+ TestIggyContext::new(
+ Some(BTreeMap::from([
+ ("default".to_string(), ContextConfig::default()),
+ ("dev".to_string(), ContextConfig::default()),
+ ])),
+ Some("dev".to_string()),
+ ),
+ "dev".to_string(),
+ vec!["dev*".to_string()],
+ ))
+ .await;
+}
+
+struct TestContextShowNotFoundCmd {
+ test_iggy_context: TestIggyContext,
+ context_to_show: String,
+}
+
+impl TestContextShowNotFoundCmd {
+ fn new(test_iggy_context: TestIggyContext, context_to_show: String) ->
Self {
+ Self {
+ test_iggy_context,
+ context_to_show,
+ }
+ }
+}
+
+#[async_trait]
+impl IggyCmdTestCase for TestContextShowNotFoundCmd {
+ async fn prepare_server_state(&mut self, _client: &dyn Client) {
+ self.test_iggy_context.prepare().await;
+ }
+
+ fn get_command(&self) -> IggyCmdCommand {
+ IggyCmdCommand::new()
+ .env(
+ "IGGY_HOME",
+ self.test_iggy_context.get_iggy_home().to_str().unwrap(),
+ )
+ .arg("context")
+ .arg("show")
+ .arg(self.context_to_show.clone())
+ .with_env_credentials()
+ }
+
+ fn verify_command(&self, command_state: Assert) {
+ command_state.failure().stderr(contains("not found"));
+ }
+
+ async fn verify_server_state(&self, _client: &dyn Client) {}
+}
+
+#[tokio::test]
+#[parallel]
+pub async fn should_fail_for_nonexistent_context() {
+ let mut iggy_cmd_test = IggyCmdTest::default();
+ iggy_cmd_test.setup().await;
+
+ iggy_cmd_test
+ .execute_test(TestContextShowNotFoundCmd::new(
+ TestIggyContext::new(None, None),
+ "nonexistent".to_string(),
+ ))
+ .await;
+}
+
+#[tokio::test]
+#[parallel]
+pub async fn should_help_match() {
+ let mut iggy_cmd_test = IggyCmdTest::default();
+
+ iggy_cmd_test
+ .execute_test_for_help_command(TestHelpCmd::new(
+ vec!["context", "show", "--help"],
+ format!(
+ r#"Show details of a specific context
+
+Displays the full configuration of a named context including
+transport, server addresses, TLS settings, and credentials.
+
+Examples
+ iggy context show default
+ iggy context show production
+
+{USAGE_PREFIX} context show <CONTEXT_NAME>
+
+Arguments:
+ <CONTEXT_NAME>
+{CLAP_INDENT}Name of the context to show
+
+Options:
+ -h, --help
+{CLAP_INDENT}Print help (see a summary with '-h')
+"#,
+ ),
+ ))
+ .await;
+}
+
+#[tokio::test]
+#[parallel]
+pub async fn should_short_help_match() {
+ let mut iggy_cmd_test = IggyCmdTest::default();
+
+ iggy_cmd_test
+ .execute_test_for_help_command(TestHelpCmd::new(
+ vec!["context", "show", "-h"],
+ format!(
+ r#"Show details of a specific context
+
+{USAGE_PREFIX} context show <CONTEXT_NAME>
+
+Arguments:
+ <CONTEXT_NAME> Name of the context to show
+
+Options:
+ -h, --help Print help (see more with '--help')
+"#,
+ ),
+ ))
+ .await;
+}
diff --git a/core/integration/tests/cli/general/test_help_command.rs
b/core/integration/tests/cli/general/test_help_command.rs
index 8cf0735b1..365345696 100644
--- a/core/integration/tests/cli/general/test_help_command.rs
+++ b/core/integration/tests/cli/general/test_help_command.rs
@@ -51,6 +51,7 @@ Commands:
context context operations [aliases: ctx]
login login to Iggy server [aliases: li]
logout logout from Iggy server [aliases: lo]
+ session login session operations [aliases: sess]
help Print this message or the help of the given subcommand(s)
Options:
diff --git a/core/integration/tests/cli/general/test_overview_command.rs
b/core/integration/tests/cli/general/test_overview_command.rs
index a5a88d2e9..8a32f9dff 100644
--- a/core/integration/tests/cli/general/test_overview_command.rs
+++ b/core/integration/tests/cli/general/test_overview_command.rs
@@ -62,6 +62,7 @@ Commands:
context context operations [aliases: ctx]
login login to Iggy server [aliases: li]
logout logout from Iggy server [aliases: lo]
+ session login session operations [aliases: sess]
help Print this message or the help of the given subcommand(s)