This is an automated email from the ASF dual-hosted git repository.

spetz 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 bbdc54ccc fix(cli): resolve server address for ws transport (#4482)
bbdc54ccc is described below

commit bbdc54ccc50dc6fdd6e1963c74971e9c01531811
Author: Alex <[email protected]>
AuthorDate: Sat Oct 10 21:30:27 2026 +0300

    fix(cli): resolve server address for ws transport (#4482)
    
    Closes #4444
---
 core/cli/src/credentials.rs                        | 42 ++++++--------
 core/cli/src/error.rs                              |  6 --
 core/cli/src/main.rs                               | 27 +++++----
 core/common/src/types/args/mod.rs                  | 60 ++++++++++++++++----
 core/integration/tests/cli/common/mod.rs           | 41 ++++++++++++++
 core/integration/tests/cli/general/mod.rs          |  1 +
 .../tests/cli/general/test_unknown_transport.rs    | 65 ++++++++++++++++++++++
 .../test_pat_create_command.rs                     | 17 +++++-
 .../test_pat_delete_command.rs                     | 15 ++++-
 core/integration/tests/cli/system/mod.rs           |  2 +
 .../tests/cli/system/test_cli_session_scenario.rs  | 42 +++++++++++++-
 .../tests/cli/system/test_me_command.rs            | 36 ++++--------
 .../tests/cli/system/test_session_status_cmd.rs    | 65 ++++++++++++++++++++++
 13 files changed, 336 insertions(+), 83 deletions(-)

diff --git a/core/cli/src/credentials.rs b/core/cli/src/credentials.rs
index 4b38a77a1..f45067772 100644
--- a/core/cli/src/credentials.rs
+++ b/core/cli/src/credentials.rs
@@ -19,7 +19,7 @@ use crate::args::CliOptions;
 use crate::error::{CmdToolError, IggyCmdError};
 use anyhow::{Context, bail};
 use iggy::clients::client::IggyClient;
-use iggy::prelude::{Args, IggyError, PersonalAccessTokenClient, UserClient};
+use iggy::prelude::{IggyError, PersonalAccessTokenClient, UserClient};
 use iggy_cli::commands::binary_system::session::{ServerSession, 
ensure_default_store};
 use passterm::{Stream, isatty, prompt_password_tty};
 use secrecy::{ExposeSecret, SecretString};
@@ -58,7 +58,7 @@ pub(crate) struct IggyCredentials<'a> {
 impl<'a> IggyCredentials<'a> {
     pub(crate) fn new(
         cli_options: &CliOptions,
-        iggy_args: &Args,
+        server_address: &str,
         login_required: bool,
         prefer_explicit_credentials: bool,
     ) -> anyhow::Result<Self, anyhow::Error> {
@@ -71,11 +71,10 @@ impl<'a> IggyCredentials<'a> {
         }
 
         let session_credentials = || -> Option<Credentials> {
-            let server_address = iggy_args.get_server_address()?;
-            let token = 
ServerSession::new(server_address.clone()).get_token()?;
+            let token = 
ServerSession::new(server_address.to_string()).get_token()?;
             Some(Credentials::SessionWithToken(
                 SecretString::from(token),
-                server_address,
+                server_address.to_string(),
             ))
         };
 
@@ -93,25 +92,18 @@ impl<'a> IggyCredentials<'a> {
 
         #[cfg(feature = "login-session")]
         if let Some(token_name) = &cli_options.token_name {
-            return match iggy_args.get_server_address() {
-                Some(server_address) => {
-                    let server_address = format!("iggy:{server_address}");
-                    event!(target: PRINT_TARGET, Level::DEBUG,"Checking token 
presence under service: {} and name: {}",
-                    server_address, token_name);
-                    ensure_default_store()?;
-                    let entry = Entry::new(&server_address, token_name)?;
-                    let token = entry.get_password()?;
-
-                    Ok(Self {
-                        credentials: 
Some(Credentials::PersonalAccessToken(SecretString::from(
-                            token,
-                        ))),
-                        iggy_client: None,
-                        login_required,
-                    })
-                }
-                None => 
Err(IggyCmdError::CmdToolError(CmdToolError::MissingServerAddress).into()),
-            };
+            let server_address = format!("iggy:{server_address}");
+            event!(target: PRINT_TARGET, Level::DEBUG,"Checking token presence 
under service: {} and name: {}",
+            server_address, token_name);
+            ensure_default_store()?;
+            let entry = Entry::new(&server_address, token_name)?;
+            let token = entry.get_password()?;
+
+            return Ok(Self {
+                credentials: 
Some(Credentials::PersonalAccessToken(SecretString::from(token))),
+                iggy_client: None,
+                login_required,
+            });
         }
 
         if let Some(token) = &cli_options.token {
@@ -154,7 +146,7 @@ impl<'a> IggyCredentials<'a> {
                 iggy_client: None,
                 login_required,
             })
-        } else if let Some(credentials) = session_credentials() {
+        } else if prefer_explicit_credentials && let Some(credentials) = 
session_credentials() {
             // `iggy login` with no explicit credentials: reuse the cached
             // session token so a still-valid session reports "already logged 
in"
             // instead of demanding credentials for a no-op.
diff --git a/core/cli/src/error.rs b/core/cli/src/error.rs
index 1e51eec72..4ea26fc39 100644
--- a/core/cli/src/error.rs
+++ b/core/cli/src/error.rs
@@ -22,8 +22,6 @@ use thiserror::Error;
 pub(crate) enum CmdToolError {
     MissingCredentials,
     InvalidEncryptionKey,
-    #[cfg(feature = "login-session")]
-    MissingServerAddress,
 }
 
 impl Display for CmdToolError {
@@ -35,10 +33,6 @@ impl Display for CmdToolError {
             Self::InvalidEncryptionKey => {
                 write!(f, "Invalid encryption key provided")
             }
-            #[cfg(feature = "login-session")]
-            Self::MissingServerAddress => {
-                write!(f, "Missing iggy server address")
-            }
         }
     }
 }
diff --git a/core/cli/src/main.rs b/core/cli/src/main.rs
index d5e8c1710..4fe065eb8 100644
--- a/core/cli/src/main.rs
+++ b/core/cli/src/main.rs
@@ -38,8 +38,8 @@ use args::{CliOptions, IggyMergedConsoleArgs};
 use clap::Parser;
 use iggy::client_provider::{self, ClientProviderConfig};
 use iggy::clients::client::IggyClient;
-use iggy::prelude::{Aes256GcmEncryptor, Args, EncryptorKind, 
PersonalAccessTokenExpiry};
-use iggy_cli::commands::binary_context::common::ContextManager;
+use iggy::prelude::{Aes256GcmEncryptor, ClientError, EncryptorKind, 
PersonalAccessTokenExpiry};
+use iggy_cli::commands::binary_context::common::{ContextManager, 
validate_transport};
 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;
@@ -107,7 +107,7 @@ use main_login_session::*;
 fn get_command(
     command: Command,
     cli_options: &CliOptions,
-    iggy_args: &Args,
+    server_address: &str,
 ) -> Box<dyn CliCommand> {
     #[warn(clippy::let_and_return)]
     match command {
@@ -190,13 +190,13 @@ fn get_command(
                     
PersonalAccessTokenExpiry::new(pat_create_args.expiry.clone()),
                     cli_options.quiet,
                     pat_create_args.store_token,
-                    iggy_args.get_server_address().unwrap(),
+                    server_address.to_string(),
                 ))
             }
             PersonalAccessTokenAction::Delete(pat_delete_args) => {
                 Box::new(DeletePersonalAccessTokenCmd::new(
                     pat_delete_args.name.clone(),
-                    iggy_args.get_server_address().unwrap(),
+                    server_address.to_string(),
                 ))
             }
             PersonalAccessTokenAction::List(pat_list_args) => Box::new(
@@ -336,16 +336,14 @@ fn get_command(
         },
         #[cfg(feature = "login-session")]
         Command::Login(login_args) => Box::new(LoginCmd::new(
-            iggy_args.get_server_address().unwrap(),
+            server_address.to_string(),
             LoginSessionExpiry::new(login_args.expiry.clone()),
         )),
         #[cfg(feature = "login-session")]
-        Command::Logout => 
Box::new(LogoutCmd::new(iggy_args.get_server_address().unwrap())),
+        Command::Logout => 
Box::new(LogoutCmd::new(server_address.to_string())),
         #[cfg(feature = "login-session")]
         Command::Session(command) => match command {
-            SessionAction::Status => Box::new(SessionStatusCmd::new(
-                iggy_args.get_server_address().unwrap(),
-            )),
+            SessionAction::Status => 
Box::new(SessionStatusCmd::new(server_address.to_string())),
         },
     }
 }
@@ -384,13 +382,18 @@ async fn main() -> Result<(), IggyCmdError> {
     let iggy_args = merged_args.iggy;
     let cli_options = merged_args.cli;
 
+    validate_transport(&iggy_args.transport)?;
+    let server_address = iggy_args
+        .get_server_address()
+        .ok_or_else(|| 
ClientError::InvalidTransport(iggy_args.transport.clone()))?;
+
     // Get command based on command line arguments
-    let mut command = get_command(command, &cli_options, &iggy_args);
+    let mut command = get_command(command, &cli_options, &server_address);
 
     // Create credentials based on command line arguments and command
     let mut credentials = IggyCredentials::new(
         &cli_options,
-        &iggy_args,
+        &server_address,
         command.login_required(),
         command.prefer_explicit_credentials(),
     )?;
diff --git a/core/common/src/types/args/mod.rs 
b/core/common/src/types/args/mod.rs
index 4dc5ff389..1555fc4d2 100644
--- a/core/common/src/types/args/mod.rs
+++ b/core/common/src/types/args/mod.rs
@@ -15,9 +15,11 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use crate::TransportProtocol;
 use crate::defaults::{DEFAULT_ROOT_PASSWORD, DEFAULT_ROOT_USERNAME};
 use clap::Parser;
 use serde::{Deserialize, Serialize};
+use std::str::FromStr;
 
 /// The arguments used by the `ClientProviderConfig` to create a client.
 /// We are not using default values here because we want to be able to
@@ -353,19 +355,17 @@ pub struct Args {
     pub websocket_tls_validate_certificate: bool,
 }
 
-const QUIC_TRANSPORT: &str = "quic";
-const HTTP_TRANSPORT: &str = "http";
-const TCP_TRANSPORT: &str = "tcp";
-const WEBSOCKET_TRANSPORT: &str = "websocket";
-
 impl Args {
+    /// Returns the server address for `tcp`, `quic`, `http`, or `ws`.
+    ///
+    /// Transport names are case-sensitive. Returns `None` for an unsupported 
name.
+    /// For HTTP, removes `http://` from the API URL.
     pub fn get_server_address(&self) -> Option<String> {
-        match self.transport.as_str() {
-            QUIC_TRANSPORT => Some(self.quic_server_address.clone()),
-            HTTP_TRANSPORT => 
Some(self.http_api_url.clone().replace("http://";, "")),
-            TCP_TRANSPORT => Some(self.tcp_server_address.clone()),
-            WEBSOCKET_TRANSPORT => Some(self.websocket_server_address.clone()),
-            _ => None,
+        match TransportProtocol::from_str(&self.transport).ok()? {
+            TransportProtocol::Quic => Some(self.quic_server_address.clone()),
+            TransportProtocol::Http => 
Some(self.http_api_url.replace("http://";, "")),
+            TransportProtocol::Tcp => Some(self.tcp_server_address.clone()),
+            TransportProtocol::WebSocket => 
Some(self.websocket_server_address.clone()),
         }
     }
 }
@@ -522,3 +522,41 @@ impl From<Vec<ArgsOptional>> for Args {
         args
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    fn args_with_transport(transport: &str) -> Args {
+        Args {
+            transport: transport.to_string(),
+            ..Args::default()
+        }
+    }
+
+    #[test]
+    fn should_return_server_address_for_each_transport() {
+        assert_eq!(
+            args_with_transport("tcp").get_server_address().as_deref(),
+            Some("127.0.0.1:8090")
+        );
+        assert_eq!(
+            args_with_transport("quic").get_server_address().as_deref(),
+            Some("127.0.0.1:8080")
+        );
+        assert_eq!(
+            args_with_transport("http").get_server_address().as_deref(),
+            Some("localhost:3000")
+        );
+        assert_eq!(
+            args_with_transport("ws").get_server_address().as_deref(),
+            Some("127.0.0.1:8092")
+        );
+    }
+
+    #[test]
+    fn should_return_none_for_unknown_transport() {
+        assert_eq!(args_with_transport("websocket").get_server_address(), 
None);
+        assert_eq!(args_with_transport("foo").get_server_address(), None);
+    }
+}
diff --git a/core/integration/tests/cli/common/mod.rs 
b/core/integration/tests/cli/common/mod.rs
index 31a04fdf3..2c3c59325 100644
--- a/core/integration/tests/cli/common/mod.rs
+++ b/core/integration/tests/cli/common/mod.rs
@@ -92,6 +92,39 @@ pub(crate) trait IggyCmdTestCase {
     }
 }
 
+/// Runs the wrapped test case with `--transport ws` against the server's 
WebSocket address.
+pub(crate) struct OverWebSocket<T>(pub(crate) T);
+
+#[async_trait]
+impl<T: IggyCmdTestCase + Send + Sync> IggyCmdTestCase for OverWebSocket<T> {
+    async fn prepare_server_state(&mut self, client: &dyn Client) {
+        self.0.prepare_server_state(client).await;
+    }
+
+    fn get_command(&self) -> IggyCmdCommand {
+        self.0.get_command().opts(vec!["--transport", "ws"])
+    }
+
+    fn provide_stdin_input(&self) -> Option<Vec<String>> {
+        self.0.provide_stdin_input()
+    }
+
+    fn verify_command(&self, command_state: Assert) {
+        self.0.verify_command(command_state);
+    }
+
+    async fn verify_server_state(&self, client: &dyn Client) {
+        self.0.verify_server_state(client).await;
+    }
+
+    fn protocol(&self, server: &ServerHandle) -> Vec<String> {
+        vec![
+            "--websocket-server-address".into(),
+            server.websocket_addr().unwrap().to_string(),
+        ]
+    }
+}
+
 pub(crate) struct IggyCmdTest {
     harness: TestHarness,
     start_server: bool,
@@ -268,6 +301,14 @@ impl IggyCmdTest {
     pub(crate) fn get_tcp_server_address(&self) -> Option<String> {
         self.harness.server().raw_tcp_addr()
     }
+
+    #[cfg(not(target_os = "macos"))]
+    pub(crate) fn get_websocket_server_address(&self) -> Option<String> {
+        self.harness
+            .server()
+            .websocket_addr()
+            .map(|addr| addr.to_string())
+    }
 }
 
 impl Default for IggyCmdTest {
diff --git a/core/integration/tests/cli/general/mod.rs 
b/core/integration/tests/cli/general/mod.rs
index 0424c963a..802a7ff06 100644
--- a/core/integration/tests/cli/general/mod.rs
+++ b/core/integration/tests/cli/general/mod.rs
@@ -19,3 +19,4 @@ mod test_help_command;
 mod test_missing_credentials;
 mod test_overview_command;
 mod test_quiet_mode;
+mod test_unknown_transport;
diff --git a/core/integration/tests/cli/general/test_unknown_transport.rs 
b/core/integration/tests/cli/general/test_unknown_transport.rs
new file mode 100644
index 000000000..0ea6ffba5
--- /dev/null
+++ b/core/integration/tests/cli/general/test_unknown_transport.rs
@@ -0,0 +1,65 @@
+// 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::cli::common::{IggyCmdCommand, IggyCmdTest, IggyCmdTestCase};
+use assert_cmd::assert::Assert;
+use async_trait::async_trait;
+use iggy::prelude::Client;
+use predicates::str::contains;
+use serial_test::parallel;
+
+struct TestUnknownTransportCmd {
+    args: Vec<&'static str>,
+}
+
+#[async_trait]
+impl IggyCmdTestCase for TestUnknownTransportCmd {
+    async fn prepare_server_state(&mut self, _client: &dyn Client) {}
+
+    fn get_command(&self) -> IggyCmdCommand {
+        IggyCmdCommand::new()
+            .opts(vec!["--transport", "foo"])
+            .args(self.args.clone())
+    }
+
+    fn verify_command(&self, command_state: Assert) {
+        command_state.failure().stderr(contains(
+            "invalid transport 'foo' - valid values are: tcp, quic, http, ws",
+        ));
+    }
+
+    async fn verify_server_state(&self, _client: &dyn Client) {}
+}
+
+#[tokio::test]
+#[parallel]
+pub async fn should_fail_with_error_message() {
+    let mut iggy_cmd_test = IggyCmdTest::default();
+
+    iggy_cmd_test.setup().await;
+    for args in [
+        vec!["login"],
+        vec!["logout"],
+        vec!["pat", "create", "name"],
+        vec!["pat", "delete", "name"],
+        vec!["session", "status"],
+    ] {
+        iggy_cmd_test
+            .execute_test(TestUnknownTransportCmd { args })
+            .await;
+    }
+}
diff --git 
a/core/integration/tests/cli/personal_access_token/test_pat_create_command.rs 
b/core/integration/tests/cli/personal_access_token/test_pat_create_command.rs
index 613e42789..b2629c882 100644
--- 
a/core/integration/tests/cli/personal_access_token/test_pat_create_command.rs
+++ 
b/core/integration/tests/cli/personal_access_token/test_pat_create_command.rs
@@ -16,7 +16,8 @@
 // under the License.
 
 use crate::cli::common::{
-    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, 
USAGE_PREFIX,
+    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, OverWebSocket, 
TestHelpCmd,
+    USAGE_PREFIX,
 };
 use assert_cmd::assert::Assert;
 use async_trait::async_trait;
@@ -117,6 +118,20 @@ pub async fn should_be_successful() {
         .await;
 }
 
+#[tokio::test]
+#[parallel]
+pub async fn should_be_successful_using_transport_ws() {
+    let mut iggy_cmd_test = IggyCmdTest::default();
+
+    iggy_cmd_test.setup().await;
+    iggy_cmd_test
+        .execute_test(OverWebSocket(TestPatCreateCmd::new(
+            String::from("main"),
+            None,
+        )))
+        .await;
+}
+
 #[tokio::test]
 #[parallel]
 pub async fn should_help_match() {
diff --git 
a/core/integration/tests/cli/personal_access_token/test_pat_delete_command.rs 
b/core/integration/tests/cli/personal_access_token/test_pat_delete_command.rs
index f76b1ee54..aea59a46b 100644
--- 
a/core/integration/tests/cli/personal_access_token/test_pat_delete_command.rs
+++ 
b/core/integration/tests/cli/personal_access_token/test_pat_delete_command.rs
@@ -15,7 +15,9 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use crate::cli::common::{IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, 
TestHelpCmd, USAGE_PREFIX};
+use crate::cli::common::{
+    IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, OverWebSocket, TestHelpCmd, 
USAGE_PREFIX,
+};
 use assert_cmd::assert::Assert;
 use async_trait::async_trait;
 use iggy::prelude::Client;
@@ -86,6 +88,17 @@ pub async fn should_be_successful() {
         .await;
 }
 
+#[tokio::test]
+#[parallel]
+pub async fn should_be_successful_using_transport_ws() {
+    let mut iggy_cmd_test = IggyCmdTest::default();
+
+    iggy_cmd_test.setup().await;
+    iggy_cmd_test
+        
.execute_test(OverWebSocket(TestPatDeleteCmd::new(String::from("name"))))
+        .await;
+}
+
 #[tokio::test]
 #[parallel]
 pub async fn should_help_match() {
diff --git a/core/integration/tests/cli/system/mod.rs 
b/core/integration/tests/cli/system/mod.rs
index 7a3d6066a..4fc3dce6d 100644
--- a/core/integration/tests/cli/system/mod.rs
+++ b/core/integration/tests/cli/system/mod.rs
@@ -29,5 +29,7 @@ mod test_logout_command;
 #[cfg(not(any(target_os = "macos", target_env = "musl")))]
 mod test_me_command;
 mod test_ping_command;
+#[cfg(not(any(target_os = "macos", target_env = "musl")))]
+mod test_session_status_cmd;
 mod test_snapshot_cmd;
 mod test_stats_command;
diff --git a/core/integration/tests/cli/system/test_cli_session_scenario.rs 
b/core/integration/tests/cli/system/test_cli_session_scenario.rs
index 8b7d245af..2c6bc59df 100644
--- a/core/integration/tests/cli/system/test_cli_session_scenario.rs
+++ b/core/integration/tests/cli/system/test_cli_session_scenario.rs
@@ -15,10 +15,11 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use crate::cli::common::IggyCmdTest;
+use crate::cli::common::{IggyCmdTest, OverWebSocket};
 use crate::cli::system::test_login_cmd::{TestLoginCmd, TestLoginCmdType};
 use crate::cli::system::test_logout_cmd::TestLogoutCmd;
 use crate::cli::system::test_me_command::{Scenario, TestMeCmd};
+use crate::cli::system::test_session_status_cmd::TestSessionStatusCmd;
 use iggy_common::TransportProtocol;
 use serial_test::serial;
 
@@ -105,3 +106,42 @@ pub async fn should_be_successful() {
         ))
         .await;
 }
+
+#[tokio::test]
+#[serial]
+pub async fn should_be_successful_using_transport_ws() {
+    let mut iggy_cmd_test = IggyCmdTest::default();
+
+    iggy_cmd_test.setup().await;
+    let server_address = iggy_cmd_test.get_websocket_server_address();
+    assert!(server_address.is_some());
+    let server_address = server_address.unwrap();
+
+    iggy_cmd_test
+        .execute_test(OverWebSocket(TestLoginCmd::new(
+            server_address.clone(),
+            TestLoginCmdType::Success,
+        )))
+        .await;
+    iggy_cmd_test
+        .execute_test(OverWebSocket(TestSessionStatusCmd::new(
+            server_address.clone(),
+            true,
+        )))
+        .await;
+    iggy_cmd_test
+        .execute_test(TestMeCmd::new(
+            TransportProtocol::WebSocket,
+            Scenario::SuccessWithoutCredentials,
+        ))
+        .await;
+    iggy_cmd_test
+        
.execute_test(OverWebSocket(TestLogoutCmd::new(server_address.clone())))
+        .await;
+    iggy_cmd_test
+        .execute_test(OverWebSocket(TestSessionStatusCmd::new(
+            server_address,
+            false,
+        )))
+        .await;
+}
diff --git a/core/integration/tests/cli/system/test_me_command.rs 
b/core/integration/tests/cli/system/test_me_command.rs
index cb92c9cee..8c1540bb1 100644
--- a/core/integration/tests/cli/system/test_me_command.rs
+++ b/core/integration/tests/cli/system/test_me_command.rs
@@ -33,25 +33,6 @@ pub(super) enum Scenario {
     FailureDueToSessionTimeout,
 }
 
-// Helper trait to add command-specific methods to TransportProtocol
-trait TransportProtocolExt {
-    fn as_arg(&self) -> Vec<&str>;
-}
-
-impl TransportProtocolExt for TransportProtocol {
-    fn as_arg(&self) -> Vec<&str> {
-        match self {
-            TransportProtocol::Tcp => vec!["--transport", "tcp"],
-            TransportProtocol::Quic => vec!["--transport", "quic"],
-            TransportProtocol::WebSocket => vec!["--transport", "websocket"],
-            // Note: HTTP is not supported for the 'me' command
-            TransportProtocol::Http => {
-                panic!("HTTP transport is not supported for the 'me' command")
-            }
-        }
-    }
-}
-
 #[derive(Debug, Default)]
 pub(super) struct TestMeCmd {
     protocol: TransportProtocol,
@@ -61,8 +42,8 @@ pub(super) struct TestMeCmd {
 impl TestMeCmd {
     pub(super) fn new(protocol: TransportProtocol, scenario: Scenario) -> Self 
{
         assert!(
-            protocol == TransportProtocol::Tcp || protocol == 
TransportProtocol::Quic,
-            "Only TCP and QUIC protocols are supported for the 'me' command"
+            protocol != TransportProtocol::Http,
+            "HTTP transport is not supported for the 'me' command"
         );
         Self { protocol, scenario }
     }
@@ -73,7 +54,9 @@ impl IggyCmdTestCase for TestMeCmd {
     async fn prepare_server_state(&mut self, _client: &dyn Client) {}
 
     fn get_command(&self) -> IggyCmdCommand {
-        let command = 
IggyCmdCommand::new().opts(self.protocol.as_arg()).arg("me");
+        let command = IggyCmdCommand::new()
+            .opts(vec!["--transport", self.protocol.as_str()])
+            .arg("me");
 
         match &self.scenario {
             Scenario::SuccessWithCredentials => command.with_env_credentials(),
@@ -86,13 +69,14 @@ impl IggyCmdTestCase for TestMeCmd {
     fn verify_command(&self, command_state: Assert) {
         match &self.scenario {
             Scenario::SuccessWithCredentials | 
Scenario::SuccessWithoutCredentials => {
+                let transport = match self.protocol {
+                    TransportProtocol::WebSocket => "WebSocket".to_string(),
+                    _ => self.protocol.as_str().to_uppercase(),
+                };
                 command_state
                     .success()
                     .stdout(starts_with("Executing me command\n"))
-                    .stdout(contains(format!(
-                        "Transport | {}",
-                        self.protocol.as_str().to_uppercase()
-                    )));
+                    .stdout(contains(format!("Transport | {transport}")));
             }
             Scenario::FailureWithoutCredentials => {
                 command_state
diff --git a/core/integration/tests/cli/system/test_session_status_cmd.rs 
b/core/integration/tests/cli/system/test_session_status_cmd.rs
new file mode 100644
index 000000000..a7263c818
--- /dev/null
+++ b/core/integration/tests/cli/system/test_session_status_cmd.rs
@@ -0,0 +1,65 @@
+// 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::cli::common::{IggyCmdCommand, IggyCmdTestCase, 
ensure_keyring_store};
+use assert_cmd::assert::Assert;
+use async_trait::async_trait;
+use iggy::prelude::Client;
+use predicates::str::{contains, starts_with};
+
+#[derive(Debug)]
+pub(super) struct TestSessionStatusCmd {
+    server_address: String,
+    active: bool,
+}
+
+impl TestSessionStatusCmd {
+    pub(super) fn new(server_address: String, active: bool) -> Self {
+        ensure_keyring_store();
+        Self {
+            server_address,
+            active,
+        }
+    }
+}
+
+#[async_trait]
+impl IggyCmdTestCase for TestSessionStatusCmd {
+    async fn prepare_server_state(&mut self, _client: &dyn Client) {}
+
+    fn get_command(&self) -> IggyCmdCommand {
+        IggyCmdCommand::new().arg("session").arg("status")
+    }
+
+    fn verify_command(&self, command_state: Assert) {
+        let active = if self.active {
+            "Yes (token presence only, freshness not verified)"
+        } else {
+            "No"
+        };
+        command_state
+            .success()
+            .stdout(starts_with("Executing session status command\n"))
+            .stdout(contains(format!(
+                "Server Address | {}",
+                self.server_address
+            )))
+            .stdout(contains(format!("Session Active | {active}")));
+    }
+
+    async fn verify_server_state(&self, _client: &dyn Client) {}
+}

Reply via email to