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 539ac3634 [#5961] feat(CLI): Add details and list command to CLI for 
model. (#6053)
539ac3634 is described below

commit 539ac3634bd142d63e8878408dd858ca7ccb5216
Author: Lord of Abyss <103809695+abyss-l...@users.noreply.github.com>
AuthorDate: Thu Jan 2 07:02:10 2025 +0800

    [#5961] feat(CLI): Add details and list command to CLI for model. (#6053)
    
    ### What changes were proposed in this pull request?
    
    add get and list command to model.
    
    - get: `gcli model get -m demo_metalake --name catalog.schema`, Displays
    the name of the Model along with all versions.
    - list:`gcli model list -m demo_metalake --name catalog.schema`, Display
    all models in this schema.
    
    ### Why are the changes needed?
    
    Fix: #5961
    
    ### Does this PR introduce _any_ user-facing change?
    
    NO
    
    ### How was this patch tested?
    
    ut
---
 .../org/apache/gravitino/cli/CommandEntities.java  |   2 +
 .../org/apache/gravitino/cli/ErrorMessages.java    |   1 +
 .../java/org/apache/gravitino/cli/FullName.java    |   9 +
 .../apache/gravitino/cli/GravitinoCommandLine.java |  39 +++
 .../apache/gravitino/cli/TestableCommandLine.java  |  12 +
 .../apache/gravitino/cli/commands/ListModel.java   |  80 ++++++
 .../gravitino/cli/commands/ModelDetails.java       |  92 +++++++
 .../org/apache/gravitino/cli/TestModelCommand.java | 270 +++++++++++++++++++++
 8 files changed, 505 insertions(+)

diff --git 
a/clients/cli/src/main/java/org/apache/gravitino/cli/CommandEntities.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/CommandEntities.java
index dc0332029..2dd50974e 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/CommandEntities.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/CommandEntities.java
@@ -37,6 +37,7 @@ public class CommandEntities {
   public static final String TOPIC = "topic";
   public static final String FILESET = "fileset";
   public static final String ROLE = "role";
+  public static final String MODEL = "model";
 
   private static final HashSet<String> VALID_ENTITIES = new HashSet<>();
 
@@ -52,6 +53,7 @@ public class CommandEntities {
     VALID_ENTITIES.add(TOPIC);
     VALID_ENTITIES.add(FILESET);
     VALID_ENTITIES.add(ROLE);
+    VALID_ENTITIES.add(MODEL);
   }
 
   /**
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 757e7c2cb..4bd523ec2 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
@@ -28,6 +28,7 @@ public class ErrorMessages {
   public static final String UNKNOWN_CATALOG = "Unknown catalog name.";
   public static final String UNKNOWN_SCHEMA = "Unknown schema name.";
   public static final String UNKNOWN_TABLE = "Unknown table name.";
+  public static final String UNKNOWN_MODEL = "Unknown model name.";
   public static final String MALFORMED_NAME = "Malformed entity name.";
   public static final String MISSING_NAME = "Missing --name option.";
   public static final String MISSING_METALAKE = "Missing --metalake option.";
diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java
index 8af7322dc..c21d21af4 100644
--- a/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java
@@ -96,6 +96,15 @@ public class FullName {
     return getNamePart(1);
   }
 
+  /**
+   * Retrieves the model name from the second part of the full name option.
+   *
+   * @return The model name, or null if not found
+   */
+  public String getModelName() {
+    return getNamePart(2);
+  }
+
   /**
    * Retrieves the table name from the third part of the full name option.
    *
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 7869dd97b..8cd335beb 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
@@ -152,6 +152,8 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
       handleTagCommand();
     } else if (entity.equals(CommandEntities.ROLE)) {
       handleRoleCommand();
+    } else if (entity.equals(CommandEntities.MODEL)) {
+      handleModelCommand();
     }
   }
 
@@ -1150,6 +1152,43 @@ public class GravitinoCommandLine extends 
TestableCommandLine {
     }
   }
 
+  private void handleModelCommand() {
+    String url = getUrl();
+    String auth = getAuth();
+    String userName = line.getOptionValue(GravitinoOptions.LOGIN);
+    FullName name = new FullName(line);
+    String metalake = name.getMetalakeName();
+    String catalog = name.getCatalogName();
+    String schema = name.getSchemaName();
+
+    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 
`model`
+    if (CommandActions.LIST.equals(command)) {
+      checkEntities(missingEntities);
+      newListModel(url, ignore, metalake, catalog, schema).handle();
+      return;
+    }
+
+    String model = name.getModelName();
+    if (model == null) missingEntities.add(CommandEntities.MODEL);
+    checkEntities(missingEntities);
+
+    switch (command) {
+      case CommandActions.DETAILS:
+        newModelDetails(url, ignore, metalake, catalog, schema, 
model).handle();
+        break;
+
+      default:
+        System.err.println(ErrorMessages.UNSUPPORTED_ACTION);
+        break;
+    }
+  }
+
   /**
    * Retrieves the Gravitinno URL from the command line options or the 
GRAVITINO_URL environment
    * variable or the Gravitio config file.
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 f07244c00..3cfd84ad8 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
@@ -66,6 +66,7 @@ import org.apache.gravitino.cli.commands.ListGroups;
 import org.apache.gravitino.cli.commands.ListIndexes;
 import org.apache.gravitino.cli.commands.ListMetalakeProperties;
 import org.apache.gravitino.cli.commands.ListMetalakes;
+import org.apache.gravitino.cli.commands.ListModel;
 import org.apache.gravitino.cli.commands.ListRoles;
 import org.apache.gravitino.cli.commands.ListSchema;
 import org.apache.gravitino.cli.commands.ListSchemaProperties;
@@ -79,6 +80,7 @@ import org.apache.gravitino.cli.commands.MetalakeAudit;
 import org.apache.gravitino.cli.commands.MetalakeDetails;
 import org.apache.gravitino.cli.commands.MetalakeDisable;
 import org.apache.gravitino.cli.commands.MetalakeEnable;
+import org.apache.gravitino.cli.commands.ModelDetails;
 import org.apache.gravitino.cli.commands.OwnerDetails;
 import org.apache.gravitino.cli.commands.RemoveAllTags;
 import org.apache.gravitino.cli.commands.RemoveCatalogProperty;
@@ -907,4 +909,14 @@ public class TestableCommandLine {
       String url, boolean ignore, String metalake, String catalog) {
     return new CatalogDisable(url, ignore, metalake, catalog);
   }
+
+  protected ListModel newListModel(
+      String url, boolean ignore, String metalake, String catalog, String 
schema) {
+    return new ListModel(url, ignore, metalake, catalog, schema);
+  }
+
+  protected ModelDetails newModelDetails(
+      String url, boolean ignore, String metalake, String catalog, String 
schema, String model) {
+    return new ModelDetails(url, ignore, metalake, catalog, schema, model);
+  }
 }
diff --git 
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListModel.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListModel.java
new file mode 100644
index 000000000..1528e954b
--- /dev/null
+++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListModel.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+package org.apache.gravitino.cli.commands;
+
+import com.google.common.base.Joiner;
+import java.util.Arrays;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.cli.ErrorMessages;
+import org.apache.gravitino.client.GravitinoClient;
+import org.apache.gravitino.exceptions.NoSuchCatalogException;
+import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NoSuchSchemaException;
+
+/** List the names of all models in a schema. */
+public class ListModel extends Command {
+  protected final String metalake;
+  protected final String catalog;
+  protected final String schema;
+
+  /**
+   * List the names of all models in a schema.
+   *
+   * @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 catalog The name of the catalog.
+   * @param schema The name of schema.
+   */
+  public ListModel(
+      String url, boolean ignoreVersions, String metalake, String catalog, 
String schema) {
+    super(url, ignoreVersions);
+    this.metalake = metalake;
+    this.catalog = catalog;
+    this.schema = schema;
+  }
+
+  /** List the names of all models in a schema. */
+  @Override
+  public void handle() {
+    NameIdentifier[] models = new NameIdentifier[0];
+    Namespace name = Namespace.of(schema);
+
+    try {
+      GravitinoClient client = buildClient(metalake);
+      models = client.loadCatalog(catalog).asModelCatalog().listModels(name);
+    } catch (NoSuchMetalakeException noSuchMetalakeException) {
+      exitWithError(ErrorMessages.UNKNOWN_METALAKE);
+    } catch (NoSuchCatalogException noSuchCatalogException) {
+      exitWithError(ErrorMessages.UNKNOWN_CATALOG);
+    } catch (NoSuchSchemaException noSuchSchemaException) {
+      exitWithError(ErrorMessages.UNKNOWN_SCHEMA);
+    } catch (Exception err) {
+      exitWithError(err.getMessage());
+    }
+
+    String output =
+        models.length == 0
+            ? "No models exist."
+            : Joiner.on(",").join(Arrays.stream(models).map(model -> 
model.name()).iterator());
+
+    System.out.println(output);
+  }
+}
diff --git 
a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ModelDetails.java 
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ModelDetails.java
new file mode 100644
index 000000000..6c3aec08f
--- /dev/null
+++ 
b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ModelDetails.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package org.apache.gravitino.cli.commands;
+
+import java.util.Arrays;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.cli.ErrorMessages;
+import org.apache.gravitino.client.GravitinoClient;
+import org.apache.gravitino.exceptions.NoSuchCatalogException;
+import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.exceptions.NoSuchModelException;
+import org.apache.gravitino.exceptions.NoSuchSchemaException;
+import org.apache.gravitino.model.Model;
+import org.apache.gravitino.model.ModelCatalog;
+
+/** Displays the details of a model. */
+public class ModelDetails extends Command {
+  protected final String metalake;
+  protected final String catalog;
+  protected final String schema;
+  protected final String model;
+
+  /**
+   * Displays the details of a model.
+   *
+   * @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 catalog The name of the catalog.
+   * @param schema The name of schema.
+   * @param model The name of model.
+   */
+  public ModelDetails(
+      String url,
+      boolean ignoreVersions,
+      String metalake,
+      String catalog,
+      String schema,
+      String model) {
+    super(url, ignoreVersions);
+    this.metalake = metalake;
+    this.catalog = catalog;
+    this.schema = schema;
+    this.model = model;
+  }
+
+  /** Displays the details of a model. */
+  @Override
+  public void handle() {
+    NameIdentifier name = NameIdentifier.of(schema, model);
+    Model gModel = null;
+    int[] versions = new int[0];
+
+    try {
+      GravitinoClient client = buildClient(metalake);
+      ModelCatalog modelCatalog = client.loadCatalog(catalog).asModelCatalog();
+      gModel = modelCatalog.getModel(name);
+      versions = modelCatalog.listModelVersions(name);
+    } catch (NoSuchMetalakeException noSuchMetalakeException) {
+      exitWithError(ErrorMessages.UNKNOWN_METALAKE);
+    } catch (NoSuchCatalogException noSuchCatalogException) {
+      exitWithError(ErrorMessages.UNKNOWN_CATALOG);
+    } catch (NoSuchSchemaException noSuchSchemaException) {
+      exitWithError(ErrorMessages.UNKNOWN_SCHEMA);
+    } catch (NoSuchModelException noSuchModelException) {
+      exitWithError(ErrorMessages.UNKNOWN_MODEL);
+    } catch (Exception err) {
+      exitWithError(err.getMessage());
+    }
+    String basicInfo =
+        String.format("Model name %s, latest version: %s%n", gModel.name(), 
gModel.latestVersion());
+    String versionInfo = Arrays.toString(versions);
+    System.out.printf(basicInfo + "versions: " + versionInfo);
+  }
+}
diff --git 
a/clients/cli/src/test/java/org/apache/gravitino/cli/TestModelCommand.java 
b/clients/cli/src/test/java/org/apache/gravitino/cli/TestModelCommand.java
new file mode 100644
index 000000000..d222655b6
--- /dev/null
+++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestModelCommand.java
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+package org.apache.gravitino.cli;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+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 java.util.Arrays;
+import java.util.Collections;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Options;
+import org.apache.gravitino.cli.commands.ListModel;
+import org.apache.gravitino.cli.commands.ModelDetails;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.shaded.com.google.common.base.Joiner;
+
+public class TestModelCommand {
+  private final Joiner joiner = Joiner.on(", ").skipNulls();
+  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
+  void testListModelCommand() {
+    ListModel mockList = mock(ListModel.class);
+    
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.MODEL, 
CommandActions.LIST));
+
+    doReturn(mockList)
+        .when(commandLine)
+        .newListModel(
+            eq(GravitinoCommandLine.DEFAULT_URL),
+            eq(false),
+            eq("metalake_demo"),
+            eq("catalog"),
+            eq("schema"));
+    commandLine.handleCommandLine();
+    verify(mockList).handle();
+  }
+
+  @Test
+  void testListModelCommandWithoutCatalog() {
+    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.MODEL, 
CommandActions.LIST));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newListModel(
+            eq(GravitinoCommandLine.DEFAULT_URL),
+            eq(false),
+            eq("metalake_demo"),
+            isNull(),
+            isNull());
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        ErrorMessages.MISSING_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + joiner.join(Arrays.asList(CommandEntities.CATALOG, 
CommandEntities.SCHEMA)),
+        output);
+  }
+
+  @Test
+  void testListModelCommandWithoutSchema() {
+    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.MODEL, 
CommandActions.LIST));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+    verify(commandLine, never())
+        .newListModel(
+            eq(GravitinoCommandLine.DEFAULT_URL),
+            eq(false),
+            eq("metalake_demo"),
+            eq("catalog"),
+            isNull());
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + joiner.join(Collections.singletonList(CommandEntities.SCHEMA)),
+        output);
+  }
+
+  @Test
+  void testModelDetailsCommand() {
+    ModelDetails mockList = mock(ModelDetails.class);
+    
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.model");
+    GravitinoCommandLine commandLine =
+        spy(
+            new GravitinoCommandLine(
+                mockCommandLine, mockOptions, CommandEntities.MODEL, 
CommandActions.DETAILS));
+
+    doReturn(mockList)
+        .when(commandLine)
+        .newModelDetails(
+            eq(GravitinoCommandLine.DEFAULT_URL),
+            eq(false),
+            eq("metalake_demo"),
+            eq("catalog"),
+            eq("schema"),
+            eq("model"));
+    commandLine.handleCommandLine();
+    verify(mockList).handle();
+  }
+
+  @Test
+  void testModelDetailsCommandWithoutCatalog() {
+    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.MODEL, 
CommandActions.DETAILS));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+
+    verify(commandLine, never())
+        .newModelDetails(
+            eq(GravitinoCommandLine.DEFAULT_URL),
+            eq(false),
+            eq("metalake_demo"),
+            isNull(),
+            isNull(),
+            isNull());
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        ErrorMessages.MISSING_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + joiner.join(
+                Arrays.asList(
+                    CommandEntities.CATALOG, CommandEntities.SCHEMA, 
CommandEntities.MODEL)),
+        output);
+  }
+
+  @Test
+  void testModelDetailsCommandWithoutSchema() {
+    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.MODEL, 
CommandActions.DETAILS));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+
+    verify(commandLine, never())
+        .newModelDetails(
+            eq(GravitinoCommandLine.DEFAULT_URL),
+            eq(false),
+            eq("metalake_demo"),
+            eq("catalog"),
+            isNull(),
+            isNull());
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + joiner.join(Arrays.asList(CommandEntities.SCHEMA, 
CommandEntities.MODEL)),
+        output);
+  }
+
+  @Test
+  void testModelDetailsCommandWithoutModel() {
+    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.MODEL, 
CommandActions.DETAILS));
+
+    assertThrows(RuntimeException.class, commandLine::handleCommandLine);
+
+    verify(commandLine, never())
+        .newModelDetails(
+            eq(GravitinoCommandLine.DEFAULT_URL),
+            eq(false),
+            eq("metalake_demo"),
+            eq("catalog"),
+            eq("schema"),
+            isNull());
+    String output = new String(errContent.toByteArray(), 
StandardCharsets.UTF_8).trim();
+    assertEquals(
+        ErrorMessages.MALFORMED_NAME
+            + "\n"
+            + "Missing required argument(s): "
+            + joiner.join(Collections.singletonList(CommandEntities.MODEL)),
+        output);
+  }
+}

Reply via email to