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 304ef70c3 [#5861] improvement(CLI): Refactor the validation logic in 
the handle methods (#5972)
304ef70c3 is described below

commit 304ef70c39e2e6d45be8c055f2092610d36768b9
Author: Lord of Abyss <[email protected]>
AuthorDate: Thu Dec 26 06:11:31 2024 +0800

    [#5861] improvement(CLI): Refactor the validation logic in the handle 
methods (#5972)
    
    ### What changes were proposed in this pull request?
    
    refactor the validation logic of all entities and add test case, just
    like validation of table command #5906 . A hint is provided when the
    user's output is missing the required arguments. for example:
    
    ```bash
    gcli column list -m demo_metalake, --name Hive_catalog
    # Malformed entity name.
    # Missing required argument(s): schema, table
    
    gcli column details -m demo_metalake, --name Hive_catalog --audit
    # Malformed entity name.
    # Missing required argument(s): schema, table, column
    
    gcli user delete -m demo_metalake
    Missing --user option.
    ```
    Currently, the Role command needs to be refactored and opened as a
    separate issue
    
    ### Why are the changes needed?
    
    Fix: #5861
    
    ### Does this PR introduce _any_ user-facing change?
    
    NO
    
    ### How was this patch tested?
    
    local test
---
 .../org/apache/gravitino/cli/ErrorMessages.java    |   1 +
 .../apache/gravitino/cli/GravitinoCommandLine.java |  87 +++++----
 .../apache/gravitino/cli/TestCatalogCommands.java  |  33 ++++
 .../apache/gravitino/cli/TestColumnCommands.java   | 211 +++++++++++++++++++++
 .../apache/gravitino/cli/TestFilesetCommands.java  | 155 +++++++++++++++
 .../apache/gravitino/cli/TestGroupCommands.java    |  43 +++++
 .../java/org/apache/gravitino/cli/TestMain.java    |   1 -
 .../apache/gravitino/cli/TestTableCommands.java    |  76 +++++---
 .../org/apache/gravitino/cli/TestTagCommands.java  |  42 ++++
 .../apache/gravitino/cli/TestTopicCommands.java    | 154 +++++++++++++++
 .../org/apache/gravitino/cli/TestUserCommands.java |  42 ++++
 11 files changed, 778 insertions(+), 67 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 3423cee07..1d6db1a5a 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_TAG = "Missing --tag option.";
   public static final String METALAKE_EXISTS = "Metalake already exists.";
   public static final String CATALOG_EXISTS = "Catalog already exists.";
   public static final String SCHEMA_EXISTS = "Schema 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 7c8539ba1..48d972943 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
@@ -31,8 +31,6 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.Options;
@@ -255,6 +253,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String outputFormat = line.getOptionValue(GravitinoOptions.OUTPUT);
 
     Command.setAuthenticationMode(auth, userName);
+    List<String> missingEntities = Lists.newArrayList();
 
     // Handle the CommandActions.LIST action separately as it doesn't use 
`catalog`
     if (CommandActions.LIST.equals(command)) {
@@ -263,6 +262,8 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     }
 
     String catalog = name.getCatalogName();
+    if (catalog == null) missingEntities.add(CommandEntities.CATALOG);
+    checkEntities(missingEntities);
 
     switch (command) {
       case CommandActions.DETAILS:
@@ -343,29 +344,21 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String catalog = name.getCatalogName();
 
     Command.setAuthenticationMode(auth, userName);
+
     List<String> missingEntities = Lists.newArrayList();
     if (metalake == null) missingEntities.add(CommandEntities.METALAKE);
     if (catalog == null) missingEntities.add(CommandEntities.CATALOG);
 
     // Handle the CommandActions.LIST action separately as it doesn't use 
`schema`
     if (CommandActions.LIST.equals(command)) {
-      if (!missingEntities.isEmpty()) {
-        System.err.println("Missing required argument(s): " + 
COMMA_JOINER.join(missingEntities));
-        Main.exit(-1);
-      }
+      checkEntities(missingEntities);
       newListSchema(url, ignore, metalake, catalog).handle();
       return;
     }
 
     String schema = name.getSchemaName();
-    if (schema == null) {
-      missingEntities.add(CommandEntities.SCHEMA);
-    }
-
-    if (!missingEntities.isEmpty()) {
-      System.err.println("Missing required argument(s): " + 
COMMA_JOINER.join(missingEntities));
-      Main.exit(-1);
-    }
+    if (schema == null) missingEntities.add(CommandEntities.SCHEMA);
+    checkEntities(missingEntities);
 
     switch (command) {
       case CommandActions.DETAILS:
@@ -421,33 +414,20 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     String schema = name.getSchemaName();
 
     Command.setAuthenticationMode(auth, userName);
-    List<String> missingEntities =
-        Stream.of(
-                catalog == null ? CommandEntities.CATALOG : null,
-                schema == null ? CommandEntities.SCHEMA : null)
-            .filter(Objects::nonNull)
-            .collect(Collectors.toList());
+    List<String> missingEntities = Lists.newArrayList();
+    if (catalog == null) missingEntities.add(CommandEntities.CATALOG);
+    if (schema == null) missingEntities.add(CommandEntities.SCHEMA);
 
     // Handle CommandActions.LIST action separately as it doesn't require the 
`table`
     if (CommandActions.LIST.equals(command)) {
-      if (!missingEntities.isEmpty()) {
-        System.err.println(
-            "Missing required argument(s): " + Joiner.on(", 
").join(missingEntities));
-        Main.exit(-1);
-      }
+      checkEntities(missingEntities);
       newListTables(url, ignore, metalake, catalog, schema).handle();
       return;
     }
 
     String table = name.getTableName();
-    if (table == null) {
-      missingEntities.add(CommandEntities.TABLE);
-    }
-
-    if (!missingEntities.isEmpty()) {
-      System.err.println("Missing required argument(s): " + Joiner.on(", 
").join(missingEntities));
-      Main.exit(-1);
-    }
+    if (table == null) missingEntities.add(CommandEntities.TABLE);
+    checkEntities(missingEntities);
 
     switch (command) {
       case CommandActions.DETAILS:
@@ -527,7 +507,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
 
     if (user == null && !CommandActions.LIST.equals(command)) {
       System.err.println(ErrorMessages.MISSING_USER);
-      return;
+      Main.exit(-1);
     }
 
     switch (command) {
@@ -588,7 +568,7 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
 
     if (group == null && !CommandActions.LIST.equals(command)) {
       System.err.println(ErrorMessages.MISSING_GROUP);
-      return;
+      Main.exit(-1);
     }
 
     switch (command) {
@@ -647,6 +627,13 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     Command.setAuthenticationMode(auth, userName);
 
     String[] tags = line.getOptionValues(GravitinoOptions.TAG);
+    if (tags == null
+        && !((CommandActions.REMOVE.equals(command) && 
line.hasOption(GravitinoOptions.FORCE))
+            || CommandActions.LIST.equals(command))) {
+      System.err.println(ErrorMessages.MISSING_TAG);
+      Main.exit(-1);
+    }
+
     if (tags != null) {
       tags = Arrays.stream(tags).distinct().toArray(String[]::new);
     }
@@ -790,12 +777,20 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
 
     Command.setAuthenticationMode(auth, userName);
 
+    List<String> missingEntities = Lists.newArrayList();
+    if (catalog == null) missingEntities.add(CommandEntities.CATALOG);
+    if (schema == null) missingEntities.add(CommandEntities.SCHEMA);
+    if (table == null) missingEntities.add(CommandEntities.TABLE);
+
     if (CommandActions.LIST.equals(command)) {
+      checkEntities(missingEntities);
       newListColumns(url, ignore, metalake, catalog, schema, table).handle();
       return;
     }
 
     String column = name.getColumnName();
+    if (column == null) missingEntities.add(CommandEntities.COLUMN);
+    checkEntities(missingEntities);
 
     switch (command) {
       case CommandActions.DETAILS:
@@ -965,12 +960,19 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
 
     Command.setAuthenticationMode(auth, userName);
 
+    List<String> missingEntities = Lists.newArrayList();
+    if (catalog == null) missingEntities.add(CommandEntities.CATALOG);
+    if (schema == null) missingEntities.add(CommandEntities.SCHEMA);
+
     if (CommandActions.LIST.equals(command)) {
+      checkEntities(missingEntities);
       newListTopics(url, ignore, metalake, catalog, schema).handle();
       return;
     }
 
     String topic = name.getTopicName();
+    if (topic == null) missingEntities.add(CommandEntities.TOPIC);
+    checkEntities(missingEntities);
 
     switch (command) {
       case CommandActions.DETAILS:
@@ -1040,12 +1042,20 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
 
     Command.setAuthenticationMode(auth, userName);
 
+    List<String> missingEntities = Lists.newArrayList();
+    if (catalog == null) missingEntities.add(CommandEntities.CATALOG);
+    if (schema == null) missingEntities.add(CommandEntities.SCHEMA);
+
+    // Handle CommandActions.LIST action separately as it doesn't require the 
`fileset`
     if (CommandActions.LIST.equals(command)) {
+      checkEntities(missingEntities);
       newListFilesets(url, ignore, metalake, catalog, schema).handle();
       return;
     }
 
     String fileset = name.getFilesetName();
+    if (fileset == null) missingEntities.add(CommandEntities.FILESET);
+    checkEntities(missingEntities);
 
     switch (command) {
       case CommandActions.DETAILS:
@@ -1183,4 +1193,11 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
 
     return null;
   }
+
+  private void checkEntities(List<String> entities) {
+    if (!entities.isEmpty()) {
+      System.err.println("Missing required argument(s): " + 
COMMA_JOINER.join(entities));
+      Main.exit(-1);
+    }
+  }
 }
diff --git 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestCatalogCommands.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestCatalogCommands.java
index d751d6717..44e553795 100644
--- 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestCatalogCommands.java
+++ 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestCatalogCommands.java
@@ -19,6 +19,7 @@
 
 package org.apache.gravitino.cli;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
@@ -30,6 +31,7 @@ import static org.mockito.Mockito.when;
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Options;
@@ -37,6 +39,7 @@ import org.apache.gravitino.cli.commands.CatalogAudit;
 import org.apache.gravitino.cli.commands.CatalogDetails;
 import org.apache.gravitino.cli.commands.CatalogDisable;
 import org.apache.gravitino.cli.commands.CatalogEnable;
+import org.apache.gravitino.cli.commands.Command;
 import org.apache.gravitino.cli.commands.CreateCatalog;
 import org.apache.gravitino.cli.commands.DeleteCatalog;
 import org.apache.gravitino.cli.commands.ListCatalogProperties;
@@ -318,6 +321,36 @@ class TestCatalogCommands {
     verify(mockUpdateName).handle();
   }
 
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testCatalogDetailsCommandWithoutCatalog() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(false);
+
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.CATALOG, 
CommandActions.DETAILS));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newCatalogDetails(
+            GravitinoCommandLine.DEFAULT_URL,
+            false,
+            Command.OUTPUT_FORMAT_TABLE,
+            "metalake_demo",
+            "catalog");
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        "Missing --name option."
+            + "\n"
+            + "Missing required argument(s): "
+            + CommandEntities.CATALOG);
+  }
+
   @Test
   void testEnableCatalogCommand() {
     CatalogEnable mockEnable = mock(CatalogEnable.class);
diff --git 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestColumnCommands.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestColumnCommands.java
index 2eb4c5364..b6159343e 100644
--- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestColumnCommands.java
+++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestColumnCommands.java
@@ -28,9 +28,11 @@ import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.google.common.base.Joiner;
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Options;
 import org.apache.gravitino.cli.commands.AddColumn;
@@ -64,6 +66,11 @@ class TestColumnCommands {
     System.setErr(new PrintStream(errContent));
   }
 
+  @AfterEach
+  void restoreExitFlg() {
+    Main.useExit = true;
+  }
+
   @AfterEach
   public void restoreStreams() {
     System.setOut(originalOut);
@@ -435,4 +442,208 @@ class TestColumnCommands {
     commandLine.handleCommandLine();
     verify(mockUpdateDefault).handle();
   }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testDeleteColumnCommandWithoutCatalog() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(false);
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.COLUMN, 
CommandActions.DELETE));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newDeleteColumn(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", null, 
null, null, null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MISSING_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ")
+                .join(
+                    Arrays.asList(
+                        CommandEntities.CATALOG,
+                        CommandEntities.SCHEMA,
+                        CommandEntities.TABLE,
+                        CommandEntities.COLUMN)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testDeleteColumnCommandWithoutSchema() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.COLUMN, 
CommandActions.DELETE));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newDeleteColumn(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", 
"catalog", null, null, null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ")
+                .join(
+                    Arrays.asList(
+                        CommandEntities.SCHEMA, CommandEntities.TABLE, 
CommandEntities.COLUMN)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testDeleteColumnCommandWithoutTable() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog.schema");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.COLUMN, 
CommandActions.DELETE));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newDeleteColumn(
+            GravitinoCommandLine.DEFAULT_URL,
+            false,
+            "metalake_demo",
+            "catalog",
+            "schema",
+            null,
+            null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ").join(Arrays.asList(CommandEntities.TABLE, 
CommandEntities.COLUMN)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testDeleteColumnCommandWithoutColumn() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog.schema.users");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.COLUMN, 
CommandActions.DELETE));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newDeleteColumn(
+            GravitinoCommandLine.DEFAULT_URL,
+            false,
+            "metalake_demo",
+            "catalog",
+            "schema",
+            "users",
+            null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ").join(Arrays.asList(CommandEntities.COLUMN)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testListColumnCommandWithoutCatalog() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(false);
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.COLUMN, 
CommandActions.LIST));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newListColumns(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", 
"catalog", "schema", null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MISSING_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ")
+                .join(
+                    Arrays.asList(
+                        CommandEntities.CATALOG, CommandEntities.SCHEMA, 
CommandEntities.TABLE)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testListColumnCommandWithoutSchema() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.COLUMN, 
CommandActions.LIST));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newListColumns(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", 
"catalog", "schema", null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ").join(Arrays.asList(CommandEntities.SCHEMA, 
CommandEntities.TABLE)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testListColumnCommandWithoutTable() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog.schema");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.COLUMN, 
CommandActions.LIST));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newListColumns(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", 
"catalog", "schema", null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + CommandEntities.TABLE);
+  }
 }
diff --git 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestFilesetCommands.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestFilesetCommands.java
index 314e118c7..b46b73cc3 100644
--- 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestFilesetCommands.java
+++ 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestFilesetCommands.java
@@ -19,14 +19,22 @@
 
 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 com.google.common.base.Joiner;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Options;
 import org.apache.gravitino.cli.commands.CreateFileset;
@@ -38,17 +46,35 @@ import 
org.apache.gravitino.cli.commands.RemoveFilesetProperty;
 import org.apache.gravitino.cli.commands.SetFilesetProperty;
 import org.apache.gravitino.cli.commands.UpdateFilesetComment;
 import org.apache.gravitino.cli.commands.UpdateFilesetName;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 class TestFilesetCommands {
   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
+  void restoreExitFlg() {
+    Main.useExit = true;
+  }
+
+  @AfterEach
+  public void restoreStreams() {
+    System.setOut(originalOut);
+    System.setErr(originalErr);
   }
 
   @Test
@@ -322,4 +348,133 @@ class TestFilesetCommands {
     commandLine.handleCommandLine();
     verify(mockSetProperties).handle();
   }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testListFilesetCommandWithoutCatalog() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(false);
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.FILESET, 
CommandActions.LIST));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newListFilesets(GravitinoCommandLine.DEFAULT_URL, false, 
"metalake_demo", null, null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MISSING_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ").join(Arrays.asList(CommandEntities.CATALOG, 
CommandEntities.SCHEMA)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testListFilesetCommandWithoutSchema() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.FILESET, 
CommandActions.LIST));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newListFilesets(GravitinoCommandLine.DEFAULT_URL, false, 
"metalake_demo", "catalog", null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ").join(Arrays.asList(CommandEntities.SCHEMA)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testFilesetDetailCommandWithoutCatalog() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(false);
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.FILESET, 
CommandActions.DETAILS));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newFilesetDetails(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", null, 
null, null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MISSING_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ")
+                .join(
+                    Arrays.asList(
+                        CommandEntities.CATALOG, CommandEntities.SCHEMA, 
CommandEntities.FILESET)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testFilesetDetailCommandWithoutSchema() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.FILESET, 
CommandActions.DETAILS));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newFilesetDetails(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", 
"catalog", null, null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ").join(Arrays.asList(CommandEntities.SCHEMA, 
CommandEntities.FILESET)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testFilesetDetailCommandWithoutFileset() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog.schema");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.FILESET, 
CommandActions.DETAILS));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newFilesetDetails(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", 
"catalog", "schema", null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ").join(Arrays.asList(CommandEntities.FILESET)));
+  }
 }
diff --git 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestGroupCommands.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestGroupCommands.java
index 3f1c4a4cb..98e3ea910 100644
--- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestGroupCommands.java
+++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestGroupCommands.java
@@ -19,12 +19,18 @@
 
 package org.apache.gravitino.cli;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.doReturn;
 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.AddRoleToGroup;
@@ -34,17 +40,35 @@ import org.apache.gravitino.cli.commands.GroupAudit;
 import org.apache.gravitino.cli.commands.GroupDetails;
 import org.apache.gravitino.cli.commands.ListGroups;
 import org.apache.gravitino.cli.commands.RemoveRoleFromGroup;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 class TestGroupCommands {
   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
+  void restoreExitFlg() {
+    Main.useExit = true;
+  }
+
+  @AfterEach
+  public void restoreStreams() {
+    System.setOut(originalOut);
+    System.setErr(originalErr);
   }
 
   @Test
@@ -260,4 +284,23 @@ class TestGroupCommands {
     verify(mockAddSecondRole).handle();
     verify(mockAddFirstRole).handle();
   }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testDeleteGroupCommandWithoutGroupOption() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.GROUP)).thenReturn(false);
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.GROUP, 
CommandActions.DELETE));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newDeleteGroup(GravitinoCommandLine.DEFAULT_URL, false, false, 
"metalake_demo", null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(output, ErrorMessages.MISSING_GROUP);
+  }
 }
diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestMain.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestMain.java
index 377e569aa..1d1ffded0 100644
--- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestMain.java
+++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestMain.java
@@ -192,7 +192,6 @@ public class TestMain {
     assertTrue(errContent.toString().contains(ErrorMessages.TAG_EMPTY)); // 
Expect error
   }
 
-  @Test
   @SuppressWarnings("DefaultCharset")
   public void DeleteTagWithNoTag() {
     String[] args = {"tag", "delete", "--metalake", "metalake_test_no_tag", 
"-f"};
diff --git 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestTableCommands.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestTableCommands.java
index 32c289cfd..c4a8223dd 100644
--- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestTableCommands.java
+++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestTableCommands.java
@@ -19,8 +19,8 @@
 
 package org.apache.gravitino.cli;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -30,6 +30,7 @@ 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.CreateTable;
@@ -451,14 +452,15 @@ class TestTableCommands {
     assertThrows(RuntimeException.class, commandLine::handleCommandLine);
     verify(commandLine, never())
         .newListTables(GravitinoCommandLine.DEFAULT_URL, false, 
"metalake_demo", null, null);
-    assertTrue(
-        errContent
-            .toString()
-            .contains(
-                "Missing required argument(s): "
-                    + CommandEntities.CATALOG
-                    + ", "
-                    + CommandEntities.SCHEMA));
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MISSING_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + CommandEntities.CATALOG
+            + ", "
+            + CommandEntities.SCHEMA);
   }
 
   @Test
@@ -478,8 +480,13 @@ class TestTableCommands {
     assertThrows(RuntimeException.class, commandLine::handleCommandLine);
     verify(commandLine, never())
         .newListTables(GravitinoCommandLine.DEFAULT_URL, false, 
"metalake_demo", "catalog", null);
-    assertTrue(
-        errContent.toString().contains("Missing required argument(s): " + 
CommandEntities.SCHEMA));
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + CommandEntities.SCHEMA);
   }
 
   @Test
@@ -498,16 +505,17 @@ class TestTableCommands {
     verify(commandLine, never())
         .newTableDetails(
             GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", null, 
null, null);
-    assertTrue(
-        errContent
-            .toString()
-            .contains(
-                "Missing required argument(s): "
-                    + CommandEntities.CATALOG
-                    + ", "
-                    + CommandEntities.SCHEMA
-                    + ", "
-                    + CommandEntities.TABLE));
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MISSING_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + CommandEntities.CATALOG
+            + ", "
+            + CommandEntities.SCHEMA
+            + ", "
+            + CommandEntities.TABLE);
   }
 
   @Test
@@ -526,14 +534,15 @@ class TestTableCommands {
     verify(commandLine, never())
         .newTableDetails(
             GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", 
"catalog", null, null);
-    assertTrue(
-        errContent
-            .toString()
-            .contains(
-                "Missing required argument(s): "
-                    + CommandEntities.SCHEMA
-                    + ", "
-                    + CommandEntities.TABLE));
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + CommandEntities.SCHEMA
+            + ", "
+            + CommandEntities.TABLE);
   }
 
   @Test
@@ -554,7 +563,12 @@ class TestTableCommands {
     verify(commandLine, never())
         .newTableDetails(
             GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", 
"catalog", "schema", null);
-    assertTrue(
-        errContent.toString().contains("Missing required argument(s): " + 
CommandEntities.TABLE));
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + CommandEntities.TABLE);
   }
 }
diff --git 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestTagCommands.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestTagCommands.java
index 58beb02a8..8d7ce17bd 100644
--- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestTagCommands.java
+++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestTagCommands.java
@@ -19,15 +19,21 @@
 
 package org.apache.gravitino.cli;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.argThat;
 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.CreateTag;
@@ -43,6 +49,7 @@ import org.apache.gravitino.cli.commands.TagEntity;
 import org.apache.gravitino.cli.commands.UntagEntity;
 import org.apache.gravitino.cli.commands.UpdateTagComment;
 import org.apache.gravitino.cli.commands.UpdateTagName;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -51,11 +58,28 @@ import org.mockito.ArgumentMatcher;
 class TestTagCommands {
   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
+  void restoreExitFlg() {
+    Main.useExit = true;
+  }
+
+  @AfterEach
+  public void restoreStreams() {
+    System.setOut(originalOut);
+    System.setErr(originalErr);
   }
 
   @Test
@@ -528,4 +552,22 @@ class TestTagCommands {
     commandLine.handleCommandLine();
     verify(mockUntagEntity).handle();
   }
+
+  @Test
+  void testDeleteTagCommandWithoutTagOption() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.TAG)).thenReturn(false);
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.TAG, 
CommandActions.REMOVE));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newDeleteTag(GravitinoCommandLine.DEFAULT_URL, false, false, 
"metalake", null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(output, ErrorMessages.MISSING_TAG);
+  }
 }
diff --git 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestTopicCommands.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestTopicCommands.java
index 50b580eaf..7fa2e453f 100644
--- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestTopicCommands.java
+++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestTopicCommands.java
@@ -19,12 +19,20 @@
 
 package org.apache.gravitino.cli;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.doReturn;
 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 com.google.common.base.Joiner;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Options;
 import org.apache.gravitino.cli.commands.CreateTopic;
@@ -35,17 +43,35 @@ import 
org.apache.gravitino.cli.commands.RemoveTopicProperty;
 import org.apache.gravitino.cli.commands.SetTopicProperty;
 import org.apache.gravitino.cli.commands.TopicDetails;
 import org.apache.gravitino.cli.commands.UpdateTopicComment;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 class TestTopicCommands {
   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
+  void restoreExitFlg() {
+    Main.useExit = true;
+  }
+
+  @AfterEach
+  public void restoreStreams() {
+    System.setOut(originalOut);
+    System.setErr(originalErr);
   }
 
   @Test
@@ -271,4 +297,132 @@ class TestTopicCommands {
     commandLine.handleCommandLine();
     verify(mockSetProperties).handle();
   }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testListTopicCommandWithoutCatalog() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.TOPIC, 
CommandActions.LIST));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newListTopics(GravitinoCommandLine.DEFAULT_URL, false, 
"metalake_demo", null, null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MISSING_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ").join(Arrays.asList(CommandEntities.CATALOG, 
CommandEntities.SCHEMA)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testListTopicCommandWithoutSchema() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.TOPIC, 
CommandActions.LIST));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newListTopics(GravitinoCommandLine.DEFAULT_URL, false, 
"metalake_demo", "catalog", null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ").join(Arrays.asList(CommandEntities.SCHEMA)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testTopicDetailsCommandWithoutCatalog() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(false);
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.TOPIC, 
CommandActions.DETAILS));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newTopicDetails(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", null, 
null, null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MISSING_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ")
+                .join(
+                    Arrays.asList(
+                        CommandEntities.CATALOG, CommandEntities.SCHEMA, 
CommandEntities.TOPIC)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testTopicDetailsCommandWithoutSchema() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.TOPIC, 
CommandActions.DETAILS));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newTopicDetails(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", 
"catalog", null, null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ").join(Arrays.asList(CommandEntities.SCHEMA, 
CommandEntities.TOPIC)));
+  }
+
+  @Test
+  @SuppressWarnings("DefaultCharset")
+  void testTopicDetailsCommandWithoutTopic() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog.schema");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.TOPIC, 
CommandActions.DETAILS));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newTopicDetails(
+            GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", 
"schema", null, null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        output,
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + Joiner.on(", ").join(Arrays.asList(CommandEntities.TOPIC)));
+  }
 }
diff --git 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestUserCommands.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestUserCommands.java
index e8a1864b9..e8630ce97 100644
--- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestUserCommands.java
+++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestUserCommands.java
@@ -19,12 +19,18 @@
 
 package org.apache.gravitino.cli;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.doReturn;
 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.AddRoleToUser;
@@ -34,17 +40,35 @@ import org.apache.gravitino.cli.commands.ListUsers;
 import org.apache.gravitino.cli.commands.RemoveRoleFromUser;
 import org.apache.gravitino.cli.commands.UserAudit;
 import org.apache.gravitino.cli.commands.UserDetails;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 class TestUserCommands {
   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
+  void restoreExitFlg() {
+    Main.useExit = true;
+  }
+
+  @AfterEach
+  public void restoreStreams() {
+    System.setOut(originalOut);
+    System.setErr(originalErr);
   }
 
   @Test
@@ -262,4 +286,22 @@ class TestUserCommands {
     verify(mockAddFirstRole).handle();
     verify(mockAddSecondRole).handle();
   }
+
+  @Test
+  void testDeleteUserWithoutUserOption() {
+    Main.useExit = false;
+    
when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true);
+    
when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo");
+    when(mockCommandLine.hasOption(GravitinoOptions.USER)).thenReturn(false);
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.USER, 
CommandActions.DELETE));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newDeleteUser(GravitinoCommandLine.DEFAULT_URL, false, false, 
"metalake_demo", null);
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(output, ErrorMessages.MISSING_USER);
+  }
 }

Reply via email to