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

jmclean pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new f6e201bfce [#5985] improvement(CLI): Fix role command that supports 
handling multiple values (#5988)
f6e201bfce is described below

commit f6e201bfce3de6611a32802e3880bd2f81831f13
Author: Lord of Abyss <103809695+abyss-l...@users.noreply.github.com>
AuthorDate: Sat Dec 28 22:11:05 2024 +0800

    [#5985] improvement(CLI): Fix role command that supports handling multiple 
values (#5988)
    
    ### What changes were proposed in this pull request?
    
    In `GravitinoOptions`, the role option supports multiple values, but the
    handleRoleCommand implementation currently only processes a single role
    value. CLI should support create and delete multiple roles
    simultaneously.
    
    
    ### Why are the changes needed?
    
    Fix: #5985
    
    ### Does this PR introduce _any_ user-facing change?
    
    NO
    
    ### How was this patch tested?
    
    local test + UT
    
    ```bash
    gcli role create -m demo_metalake --role role1 role2 role3
    # role1, role2, role3 created
    
    gcli role delete -m demo_metalake --role role1 role2 role3
    # role1, role2, role3 deleted.
    
    gcli role delete -m demo_metalake --role role1 role2 role3 unknown
    # role1, role2, role3 deleted, but unknown is not deleted.
    
    gcli role details -m demo_metalake
    # Missing --role option.
    
    gcli role details -m demo_metalake --role roleA roleB
    # Exception in thread "main" java.lang.IllegalArgumentException: details 
requires only one role, but multiple are currently passed.
    ```
---
 .../org/apache/gravitino/cli/ErrorMessages.java    |   1 +
 .../apache/gravitino/cli/GravitinoCommandLine.java |  33 +++--
 .../apache/gravitino/cli/TestableCommandLine.java  |   8 +-
 .../apache/gravitino/cli/commands/CreateRole.java  |  15 ++-
 .../apache/gravitino/cli/commands/DeleteRole.java  |  32 +++--
 .../org/apache/gravitino/cli/TestRoleCommands.java | 133 +++++++++++++++++++--
 6 files changed, 184 insertions(+), 38 deletions(-)

diff --git 
a/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java
index 1d6db1a5ac..a525366450 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java
@@ -32,6 +32,7 @@ public class ErrorMessages {
   public static final String MISSING_NAME = "Missing --name option.";
   public static final String MISSING_GROUP = "Missing --group option.";
   public static final String MISSING_USER = "Missing --user option.";
+  public static final String MISSING_ROLE = "Missing --role option.";
   public static final String MISSING_TAG = "Missing --tag option.";
   public static final String METALAKE_EXISTS = "Metalake already exists.";
   public static final String CATALOG_EXISTS = "Catalog already exists.";
diff --git 
a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java
index 48d9729435..06ac09d673 100644
--- 
a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java
+++ 
b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java
@@ -720,17 +720,26 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String userName = line.getOptionValue(GravitinoOptions.LOGIN);
     FullName name = new FullName(line);
     String metalake = name.getMetalakeName();
-    String role = line.getOptionValue(GravitinoOptions.ROLE);
     String[] privileges = line.getOptionValues(GravitinoOptions.PRIVILEGE);
 
     Command.setAuthenticationMode(auth, userName);
 
+    String[] roles = line.getOptionValues(GravitinoOptions.ROLE);
+    if (roles == null && !CommandActions.LIST.equals(command)) {
+      System.err.println(ErrorMessages.MISSING_ROLE);
+      Main.exit(-1);
+    }
+
+    if (roles != null) {
+      roles = Arrays.stream(roles).distinct().toArray(String[]::new);
+    }
+
     switch (command) {
       case CommandActions.DETAILS:
         if (line.hasOption(GravitinoOptions.AUDIT)) {
-          newRoleAudit(url, ignore, metalake, role).handle();
+          newRoleAudit(url, ignore, metalake, getOneRole(roles, 
CommandActions.DETAILS)).handle();
         } else {
-          newRoleDetails(url, ignore, metalake, role).handle();
+          newRoleDetails(url, ignore, metalake, getOneRole(roles, 
CommandActions.DETAILS)).handle();
         }
         break;
 
@@ -739,20 +748,24 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
         break;
 
       case CommandActions.CREATE:
-        newCreateRole(url, ignore, metalake, role).handle();
+        newCreateRole(url, ignore, metalake, roles).handle();
         break;
 
       case CommandActions.DELETE:
         boolean forceDelete = line.hasOption(GravitinoOptions.FORCE);
-        newDeleteRole(url, ignore, forceDelete, metalake, role).handle();
+        newDeleteRole(url, ignore, forceDelete, metalake, roles).handle();
         break;
 
       case CommandActions.GRANT:
-        newGrantPrivilegesToRole(url, ignore, metalake, role, name, 
privileges).handle();
+        newGrantPrivilegesToRole(
+                url, ignore, metalake, getOneRole(roles, 
CommandActions.GRANT), name, privileges)
+            .handle();
         break;
 
       case CommandActions.REVOKE:
-        newRevokePrivilegesFromRole(url, ignore, metalake, role, name, 
privileges).handle();
+        newRevokePrivilegesFromRole(
+                url, ignore, metalake, getOneRole(roles, 
CommandActions.REMOVE), name, privileges)
+            .handle();
         break;
 
       default:
@@ -762,6 +775,12 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     }
   }
 
+  private String getOneRole(String[] roles, String command) {
+    Preconditions.checkArgument(
+        roles.length == 1, command + " requires only one role, but multiple 
are currently passed.");
+    return roles[0];
+  }
+
   /**
    * Handles the command execution for Columns based on command type and the 
command line options.
    */
diff --git 
a/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java
index effe0da1f1..f07244c005 100644
--- 
a/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java
+++ 
b/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java
@@ -468,13 +468,13 @@ public class TestableCommandLine {
     return new RoleAudit(url, ignore, metalake, role);
   }
 
-  protected CreateRole newCreateRole(String url, boolean ignore, String 
metalake, String role) {
-    return new CreateRole(url, ignore, metalake, role);
+  protected CreateRole newCreateRole(String url, boolean ignore, String 
metalake, String[] roles) {
+    return new CreateRole(url, ignore, metalake, roles);
   }
 
   protected DeleteRole newDeleteRole(
-      String url, boolean ignore, boolean force, String metalake, String role) 
{
-    return new DeleteRole(url, ignore, force, metalake, role);
+      String url, boolean ignore, boolean force, String metalake, String[] 
roles) {
+    return new DeleteRole(url, ignore, force, metalake, roles);
   }
 
   protected TagDetails newTagDetails(String url, boolean ignore, String 
metalake, String tag) {
diff --git 
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateRole.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateRole.java
index fea6fe4a72..e821013471 100644
--- 
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateRole.java
+++ 
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/CreateRole.java
@@ -19,6 +19,7 @@
 
 package org.apache.gravitino.cli.commands;
 
+import com.google.common.base.Joiner;
 import java.util.Collections;
 import org.apache.gravitino.cli.ErrorMessages;
 import org.apache.gravitino.client.GravitinoClient;
@@ -27,7 +28,7 @@ import 
org.apache.gravitino.exceptions.RoleAlreadyExistsException;
 
 public class CreateRole extends Command {
   protected String metalake;
-  protected String role;
+  protected String[] roles;
 
   /**
    * Create a new role.
@@ -35,12 +36,12 @@ public class CreateRole extends Command {
    * @param url The URL of the Gravitino server.
    * @param ignoreVersions If true don't check the client/server versions 
match.
    * @param metalake The name of the metalake.
-   * @param role The name of the role.
+   * @param roles The array of roles.
    */
-  public CreateRole(String url, boolean ignoreVersions, String metalake, 
String role) {
+  public CreateRole(String url, boolean ignoreVersions, String metalake, 
String[] roles) {
     super(url, ignoreVersions);
     this.metalake = metalake;
-    this.role = role;
+    this.roles = roles;
   }
 
   /** Create a new role. */
@@ -48,7 +49,9 @@ public class CreateRole extends Command {
   public void handle() {
     try {
       GravitinoClient client = buildClient(metalake);
-      client.createRole(role, null, Collections.EMPTY_LIST);
+      for (String role : roles) {
+        client.createRole(role, null, Collections.EMPTY_LIST);
+      }
     } catch (NoSuchMetalakeException err) {
       exitWithError(ErrorMessages.UNKNOWN_METALAKE);
     } catch (RoleAlreadyExistsException err) {
@@ -57,6 +60,6 @@ public class CreateRole extends Command {
       exitWithError(exp.getMessage());
     }
 
-    System.out.println(role + " created");
+    System.out.println(Joiner.on(", ").join(roles) + " created");
   }
 }
diff --git 
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteRole.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteRole.java
index f175d95043..fa7c8cacc2 100644
--- 
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteRole.java
+++ 
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteRole.java
@@ -19,6 +19,9 @@
 
 package org.apache.gravitino.cli.commands;
 
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import java.util.List;
 import org.apache.gravitino.cli.AreYouSure;
 import org.apache.gravitino.cli.ErrorMessages;
 import org.apache.gravitino.client.GravitinoClient;
@@ -26,9 +29,9 @@ import 
org.apache.gravitino.exceptions.NoSuchMetalakeException;
 import org.apache.gravitino.exceptions.NoSuchRoleException;
 
 public class DeleteRole extends Command {
-
+  public static final Joiner COMMA_JOINER = Joiner.on(", ").skipNulls();
   protected String metalake;
-  protected String role;
+  protected String[] roles;
   protected boolean force;
 
   /**
@@ -38,28 +41,30 @@ public class DeleteRole extends Command {
    * @param ignoreVersions If true don't check the client/server versions 
match.
    * @param force Force operation.
    * @param metalake The name of the metalake.
-   * @param role The name of the role.
+   * @param roles The name of the role.
    */
   public DeleteRole(
-      String url, boolean ignoreVersions, boolean force, String metalake, 
String role) {
+      String url, boolean ignoreVersions, boolean force, String metalake, 
String[] roles) {
     super(url, ignoreVersions);
     this.metalake = metalake;
     this.force = force;
-    this.role = role;
+    this.roles = roles;
   }
 
   /** Delete a role. */
   @Override
   public void handle() {
-    boolean deleted = false;
-
     if (!AreYouSure.really(force)) {
       return;
     }
+    List<String> failedRoles = Lists.newArrayList();
+    List<String> successRoles = Lists.newArrayList();
 
     try {
       GravitinoClient client = buildClient(metalake);
-      deleted = client.deleteRole(role);
+      for (String role : roles) {
+        (client.deleteRole(role) ? successRoles : failedRoles).add(role);
+      }
     } catch (NoSuchMetalakeException err) {
       exitWithError(ErrorMessages.UNKNOWN_METALAKE);
     } catch (NoSuchRoleException err) {
@@ -68,10 +73,15 @@ public class DeleteRole extends Command {
       exitWithError(exp.getMessage());
     }
 
-    if (deleted) {
-      System.out.println(role + " deleted.");
+    if (failedRoles.isEmpty()) {
+      System.out.println(COMMA_JOINER.join(successRoles) + " deleted.");
     } else {
-      System.out.println(role + " not deleted.");
+      System.err.println(
+          COMMA_JOINER.join(successRoles)
+              + " deleted, "
+              + "but "
+              + COMMA_JOINER.join(failedRoles)
+              + " is not deleted.");
     }
   }
 }
diff --git 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestRoleCommands.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestRoleCommands.java
index 88b380d63e..0e671067e3 100644
--- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestRoleCommands.java
+++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestRoleCommands.java
@@ -19,14 +19,20 @@
 
 package org.apache.gravitino.cli;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Options;
 import org.apache.gravitino.cli.commands.CreateRole;
@@ -36,17 +42,30 @@ import org.apache.gravitino.cli.commands.ListRoles;
 import org.apache.gravitino.cli.commands.RevokePrivilegesFromRole;
 import org.apache.gravitino.cli.commands.RoleAudit;
 import org.apache.gravitino.cli.commands.RoleDetails;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 class TestRoleCommands {
   private CommandLine mockCommandLine;
   private Options mockOptions;
+  private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
+  private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
+  private final PrintStream originalOut = System.out;
+  private final PrintStream originalErr = System.err;
 
   @BeforeEach
   void setUp() {
     mockCommandLine = mock(CommandLine.class);
     mockOptions = mock(Options.class);
+    System.setOut(new PrintStream(outContent));
+    System.setErr(new PrintStream(errContent));
+  }
+
+  @AfterEach
+  public void restoreStreams() {
+    System.setOut(originalOut);
+    System.setErr(originalErr);
   }
 
   @Test
@@ -71,7 +90,7 @@ class TestRoleCommands {
     
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
     
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
     when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(true);
-    
when(mockCommandLine.getOptionValue(GravitinoOptions.ROLE)).thenReturn("admin");
+    
when(mockCommandLine.getOptionValues(GravitinoOptions.ROLE)).thenReturn(new 
String[] {"admin"});
     GravitinoCommandLine commandLine =
         spy(
             new GravitinoCommandLine(
@@ -83,13 +102,31 @@ class TestRoleCommands {
     verify(mockDetails).handle();
   }
 
+  @Test
+  void testRoleDetailsCommandWithMultipleRoles() {
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(true);
+    when(mockCommandLine.getOptionValues(GravitinoOptions.ROLE))
+        .thenReturn(new String[] {"admin", "roleA", "roleB"});
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.ROLE, 
CommandActions.DETAILS));
+
+    assertThrows(IllegalArgumentException.class, 
commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newRoleDetails(
+            eq(GravitinoCommandLine.DEFAULT_URL), eq(false), 
eq("metalake_demo"), any());
+  }
+
   @Test
   void testRoleAuditCommand() {
     RoleAudit mockAudit = mock(RoleAudit.class);
     
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
     
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
     when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(true);
-    
when(mockCommandLine.getOptionValue(GravitinoOptions.ROLE)).thenReturn("group");
+    
when(mockCommandLine.getOptionValues(GravitinoOptions.ROLE)).thenReturn(new 
String[] {"group"});
     when(mockCommandLine.hasOption(GravitinoOptions.AUDIT)).thenReturn(true);
     GravitinoCommandLine commandLine =
         spy(
@@ -108,14 +145,39 @@ class TestRoleCommands {
     
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
     
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
     when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(true);
-    
when(mockCommandLine.getOptionValue(GravitinoOptions.ROLE)).thenReturn("admin");
+    
when(mockCommandLine.getOptionValues(GravitinoOptions.ROLE)).thenReturn(new 
String[] {"admin"});
     GravitinoCommandLine commandLine =
         spy(
             new GravitinoCommandLine(
                 mockCommandLine, mockOptions, CommandEntities.ROLE, 
CommandActions.CREATE));
     doReturn(mockCreate)
         .when(commandLine)
-        .newCreateRole(GravitinoCommandLine.DEFAULT_URL, false, 
"metalake_demo", "admin");
+        .newCreateRole(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", new 
String[] {"admin"});
+    commandLine.handleCommandLine();
+    verify(mockCreate).handle();
+  }
+
+  @Test
+  void testCreateRolesCommand() {
+    CreateRole mockCreate = mock(CreateRole.class);
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(true);
+    when(mockCommandLine.getOptionValues(GravitinoOptions.ROLE))
+        .thenReturn(new String[] {"admin", "engineer", "scientist"});
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.ROLE, 
CommandActions.CREATE));
+
+    doReturn(mockCreate)
+        .when(commandLine)
+        .newCreateRole(
+            eq(GravitinoCommandLine.DEFAULT_URL),
+            eq(false),
+            eq("metalake_demo"),
+            eq(new String[] {"admin", "engineer", "scientist"}));
     commandLine.handleCommandLine();
     verify(mockCreate).handle();
   }
@@ -126,14 +188,45 @@ class TestRoleCommands {
     
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
     
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
     when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(true);
-    
when(mockCommandLine.getOptionValue(GravitinoOptions.ROLE)).thenReturn("admin");
+    
when(mockCommandLine.getOptionValues(GravitinoOptions.ROLE)).thenReturn(new 
String[] {"admin"});
     GravitinoCommandLine commandLine =
         spy(
             new GravitinoCommandLine(
                 mockCommandLine, mockOptions, CommandEntities.ROLE, 
CommandActions.DELETE));
     doReturn(mockDelete)
         .when(commandLine)
-        .newDeleteRole(GravitinoCommandLine.DEFAULT_URL, false, false, 
"metalake_demo", "admin");
+        .newDeleteRole(
+            GravitinoCommandLine.DEFAULT_URL,
+            false,
+            false,
+            "metalake_demo",
+            new String[] {"admin"});
+    commandLine.handleCommandLine();
+    verify(mockDelete).handle();
+  }
+
+  @Test
+  void testDeleteRolesCommand() {
+    DeleteRole mockDelete = mock(DeleteRole.class);
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(true);
+    when(mockCommandLine.getOptionValues(GravitinoOptions.ROLE))
+        .thenReturn(new String[] {"admin", "engineer", "scientist"});
+    when(mockCommandLine.hasOption(GravitinoOptions.FORCE)).thenReturn(false);
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.ROLE, 
CommandActions.DELETE));
+
+    doReturn(mockDelete)
+        .when(commandLine)
+        .newDeleteRole(
+            eq(GravitinoCommandLine.DEFAULT_URL),
+            eq(false),
+            eq(false),
+            eq("metalake_demo"),
+            eq(new String[] {"admin", "engineer", "scientist"}));
     commandLine.handleCommandLine();
     verify(mockDelete).handle();
   }
@@ -144,7 +237,7 @@ class TestRoleCommands {
     
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
     
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
     when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(true);
-    
when(mockCommandLine.getOptionValue(GravitinoOptions.ROLE)).thenReturn("admin");
+    
when(mockCommandLine.getOptionValues(GravitinoOptions.ROLE)).thenReturn(new 
String[] {"admin"});
     when(mockCommandLine.hasOption(GravitinoOptions.FORCE)).thenReturn(true);
     GravitinoCommandLine commandLine =
         spy(
@@ -152,7 +245,8 @@ class TestRoleCommands {
                 mockCommandLine, mockOptions, CommandEntities.ROLE, 
CommandActions.DELETE));
     doReturn(mockDelete)
         .when(commandLine)
-        .newDeleteRole(GravitinoCommandLine.DEFAULT_URL, false, true, 
"metalake_demo", "admin");
+        .newDeleteRole(
+            GravitinoCommandLine.DEFAULT_URL, false, true, "metalake_demo", 
new String[] {"admin"});
     commandLine.handleCommandLine();
     verify(mockDelete).handle();
   }
@@ -166,7 +260,7 @@ class TestRoleCommands {
     when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
     
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog");
     when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(true);
-    
when(mockCommandLine.getOptionValue(GravitinoOptions.ROLE)).thenReturn("admin");
+    
when(mockCommandLine.getOptionValues(GravitinoOptions.ROLE)).thenReturn(new 
String[] {"admin"});
     
when(mockCommandLine.hasOption(GravitinoOptions.PRIVILEGE)).thenReturn(true);
     
when(mockCommandLine.getOptionValues(GravitinoOptions.PRIVILEGE)).thenReturn(privileges);
     GravitinoCommandLine commandLine =
@@ -195,7 +289,7 @@ class TestRoleCommands {
     when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
     
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog");
     when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(true);
-    
when(mockCommandLine.getOptionValue(GravitinoOptions.ROLE)).thenReturn("admin");
+    
when(mockCommandLine.getOptionValues(GravitinoOptions.ROLE)).thenReturn(new 
String[] {"admin"});
     
when(mockCommandLine.hasOption(GravitinoOptions.PRIVILEGE)).thenReturn(true);
     
when(mockCommandLine.getOptionValues(GravitinoOptions.PRIVILEGE)).thenReturn(privileges);
     GravitinoCommandLine commandLine =
@@ -214,4 +308,23 @@ class TestRoleCommands {
     commandLine.handleCommandLine();
     verify(mockRevoke).handle();
   }
+
+  @Test
+  void testDeleteRoleCommandWithoutRole() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(false);
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.ROLE, 
CommandActions.REVOKE));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newDeleteRole(
+            eq(GravitinoCommandLine.DEFAULT_URL), eq(false), eq(false), 
eq("metalake_demo"), any());
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(output, ErrorMessages.MISSING_ROLE);
+  }
 }

Reply via email to