blob: 5d469370e9eb2eaa83fe3f8b8ef6d10850fe3cdf [file] [log] [blame]
use crate::cli::common::{
IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, CLAP_INDENT, USAGE_PREFIX,
};
use crate::cli::user::common::PermissionsTestArgs;
use ahash::AHashMap;
use assert_cmd::assert::Assert;
use async_trait::async_trait;
use iggy::client::Client;
use iggy::models::permissions::{GlobalPermissions, StreamPermissions, TopicPermissions};
use iggy::models::{permissions::Permissions, user_status::UserStatus};
use predicates::str::diff;
use serial_test::parallel;
#[derive(Debug, Clone)]
enum UserStatusTest {
Default,
Active,
Inactive,
}
impl UserStatusTest {
fn as_arg(&self) -> Vec<String> {
match self {
Self::Default => vec![],
Self::Active => vec![String::from("--user-status"), String::from("active")],
Self::Inactive => vec![String::from("--user-status"), String::from("inactive")],
}
}
}
impl Default for UserStatusTest {
fn default() -> Self {
Self::Default
}
}
impl From<UserStatusTest> for UserStatus {
fn from(value: UserStatusTest) -> Self {
match value {
UserStatusTest::Default => Self::Active,
UserStatusTest::Active => Self::Active,
UserStatusTest::Inactive => Self::Inactive,
}
}
}
struct TestUserCreateCmd {
username: String,
password: String,
status: UserStatusTest,
permissions: PermissionsTestArgs,
}
impl TestUserCreateCmd {
fn new(
username: String,
password: String,
status: UserStatusTest,
permissions: PermissionsTestArgs,
) -> Self {
Self {
username,
password,
status,
permissions,
}
}
fn to_args(&self) -> Vec<String> {
let mut args = vec![self.username.clone(), self.password.clone()];
args.extend(self.status.as_arg());
args.extend(self.permissions.as_arg());
args
}
}
#[async_trait]
impl IggyCmdTestCase for TestUserCreateCmd {
async fn prepare_server_state(&mut self, _client: &dyn Client) {}
fn get_command(&self) -> IggyCmdCommand {
IggyCmdCommand::new()
.arg("user")
.arg("create")
.args(self.to_args())
.with_env_credentials()
}
fn verify_command(&self, command_state: Assert) {
command_state
.success()
.stdout(diff(format!("Executing create user with username: {} and password: {}\nUser with username: {} and password: {} created\n",
self.username, self.password, self.username, self.password)));
}
async fn verify_server_state(&self, client: &dyn Client) {
let user = client
.get_user(&self.username.as_str().try_into().unwrap())
.await;
assert!(user.is_ok());
let user = user.unwrap().expect("User not found");
assert_eq!(user.username, self.username);
assert_eq!(user.status, self.status.clone().into());
assert_eq!(
user.permissions,
self.permissions.expected_permissions.clone()
);
}
}
#[tokio::test]
#[parallel]
pub async fn should_be_successful() {
let mut iggy_cmd_test = IggyCmdTest::default();
iggy_cmd_test.setup().await;
iggy_cmd_test
.execute_test(TestUserCreateCmd::new(
String::from("username"),
String::from("password"),
UserStatusTest::Default,
PermissionsTestArgs::default(),
))
.await;
iggy_cmd_test
.execute_test(TestUserCreateCmd::new(
String::from("active_user"),
String::from("password"),
UserStatusTest::Active,
PermissionsTestArgs::default(),
))
.await;
iggy_cmd_test
.execute_test(TestUserCreateCmd::new(
String::from("inactive_user"),
String::from("password"),
UserStatusTest::Inactive,
PermissionsTestArgs::default(),
))
.await;
iggy_cmd_test
.execute_test(TestUserCreateCmd::new(
String::from("reader"),
String::from("password"),
UserStatusTest::Active,
PermissionsTestArgs::new(
Some(String::from("r_srv,r_usr,r_top")),
vec![],
Some(Permissions {
global: GlobalPermissions {
manage_servers: false,
read_servers: true,
manage_users: false,
read_users: true,
manage_streams: false,
read_streams: false,
manage_topics: false,
read_topics: true,
poll_messages: false,
send_messages: false,
},
streams: None,
}),
),
))
.await;
iggy_cmd_test
.execute_test(TestUserCreateCmd::new(
String::from("stream3"),
String::from("password"),
UserStatusTest::Active,
PermissionsTestArgs::new(
None,
vec![String::from("3")],
Some(Permissions {
global: GlobalPermissions::default(),
streams: Some(AHashMap::from([(3u32, StreamPermissions::default())])),
}),
),
))
.await;
iggy_cmd_test
.execute_test(TestUserCreateCmd::new(
String::from("stream1"),
String::from("password"),
UserStatusTest::Active,
PermissionsTestArgs::new(
None,
vec![String::from("1#1:m_top,r_top,p_msg,s_msg")],
Some(Permissions {
global: GlobalPermissions::default(),
streams: Some(AHashMap::from([(
1u32,
StreamPermissions {
topics: Some(AHashMap::from([(
1,
TopicPermissions {
manage_topic: true,
read_topic: true,
poll_messages: true,
send_messages: true,
},
)])),
..Default::default()
},
)])),
}),
),
))
.await;
iggy_cmd_test
.execute_test(TestUserCreateCmd::new(
String::from("giant"),
String::from("password"),
UserStatusTest::Active,
PermissionsTestArgs::new(
Some(String::from("m_srv,r_srv,m_str,r_str")),
vec![String::from("2#1:p_msg,s_msg")],
Some(Permissions {
global: GlobalPermissions {
manage_servers: true,
read_servers: true,
manage_users: false,
read_users: false,
manage_streams: true,
read_streams: true,
manage_topics: false,
read_topics: false,
poll_messages: false,
send_messages: false,
},
streams: Some(AHashMap::from([(
2u32,
StreamPermissions {
topics: Some(AHashMap::from([(
1,
TopicPermissions {
manage_topic: false,
read_topic: false,
poll_messages: true,
send_messages: true,
},
)])),
..Default::default()
},
)])),
}),
),
))
.await;
}
#[tokio::test]
#[parallel]
pub async fn should_help_match() {
let mut iggy_cmd_test = IggyCmdTest::help_message();
iggy_cmd_test
.execute_test_for_help_command(TestHelpCmd::new(
vec!["user", "create", "--help"],
format!(
r#"Create user with given username and password
Examples
iggy user create testuser pass#1%X!
iggy user create guest guess --user-status inactive
{USAGE_PREFIX} user create [OPTIONS] <USERNAME> <PASSWORD>
Arguments:
<USERNAME>
Username
{CLAP_INDENT}
Unique identifier for the user account on iggy server,
must be between 3 and 50 characters long.
<PASSWORD>
Password
{CLAP_INDENT}
Password of the user, must be between 3 and 100 characters long.
Options:
-u, --user-status <USER_STATUS>
User status
{CLAP_INDENT}
[default: active]
[possible values: active, inactive]
-g, --global-permissions <GLOBAL_PERMISSIONS>
Set global permissions for created user
{CLAP_INDENT}
All global permissions by default are set to false and this command line option
allows to set each permission individually. Permissions are separated
by comma and each permission is identified by the same name as in the iggy
SDK in iggy::models::permissions::GlobalPermissions struct. For each permission
there's long variant (same as in SDK) and short variant.
{CLAP_INDENT}
Available permissions (long and short versions): manage_servers / m_srv,
read_servers / r_srv, manage_users / m_usr, read_users / r_usr,
manage_streams / m_str, read_streams / r_str, manage_topics / m_top,
read_topics / r_top, poll_messages / p_msg, send_messages / s_msg
{CLAP_INDENT}
Examples:
iggy user create guest guess --global-permissions p_msg,s_msg
iggy user create admin pass#1%X! -g m_srv,r_srv,m_usr,r_usr,m_str,r_str,m_top,r_top,p_msg,s_msg
-s, --stream-permissions <STREAM_PERMISSIONS>
Set stream permissions for created user
{CLAP_INDENT}
Stream permissions are defined by each stream separately. Setting permission for stream
allows to set each permission individually, by default, if no permission is provided
(only stream ID is provided) all are set fo false. Stream permission format consists
of stream ID followed by colon (:) and list of permissions separated by comma (,).
For each stream permission there's long variant (same as in SDK in
iggy::models::permissions::StreamPermissions) and short variant.
{CLAP_INDENT}
Available stream permissions: manage_stream / m_str, read_stream / r_str, manage_topics / m_top,
read_topics / r_top, poll_messages / p_msg, send_messages / s_msg.
{CLAP_INDENT}
For each stream one can set permissions for each topic separately. Topic permissions
are defined for each topic separately. Setting permission for topic allows to set each
permission individually, by default, if no permission is provided (only topic ID is provided)
all are set fo false. Topic permission format consists of topic ID followed by colon (:)
and list of permissions separated by comma (,). For each topic permission there's long
variant (same as in SDK in iggy::models::permissions::TopicPermissions) and short variant.
Topic permissions are separated by hash (#) after stream permissions.
{CLAP_INDENT}
Available topic permissions: manage_topic / m_top, read_topic / r_top, poll_messages / p_msg,
send_messages / s_msg.
{CLAP_INDENT}
Permissions format: STREAM_ID[:STREAM_PERMISSIONS][#TOPIC_ID[:TOPIC_PERMISSIONS]]
{CLAP_INDENT}
Examples:
iggy user create guest guest -s 1:manage_topics,read_topics
iggy user create admin p@Ss! --stream-permissions 2:m_str,r_str,m_top,r_top,p_msg,s_msg
iggy user create sender s3n43r -s 3#1:s_msg#2:s_msg
iggy user create user1 test12 -s 4:manage_stream,r_top#1:s_msg,p_msg#2:manage_topic
-h, --help
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!["user", "create", "-h"],
format!(
r#"Create user with given username and password
{USAGE_PREFIX} user create [OPTIONS] <USERNAME> <PASSWORD>
Arguments:
<USERNAME> Username
<PASSWORD> Password
Options:
-u, --user-status <USER_STATUS>
User status [default: active] [possible values: active, inactive]
-g, --global-permissions <GLOBAL_PERMISSIONS>
Set global permissions for created user
-s, --stream-permissions <STREAM_PERMISSIONS>
Set stream permissions for created user
-h, --help
Print help (see more with '--help')
"#,
),
))
.await;
}