From 4bf19acc78b36dac7f8dd2326fd4ed2264bc4ec4 Mon Sep 17 00:00:00 2001
From: Dave Waltermire <david.waltermire@gsa.gov>
Date: Tue, 28 May 2024 10:58:20 -0400
Subject: [PATCH] Improved the CLI tests. Moved generated content to a folder
 in target.

---
 .../metaschema/MetaschemaCommand.java         |   8 +-
 .../commands/profile/ResolveSubcommand.java   |  81 +++++++-----
 .../secauto/oscal/tools/cli/core/CLITest.java | 123 +++++++++++++-----
 ...ap_invalid.yml => example_ap_invalid.yaml} |   0
 ...ple_ap_valid.yml => example_ap_valid.yaml} |   0
 ...ar_invalid.yml => example_ar_invalid.yaml} |   0
 ...ple_ar_valid.yml => example_ar_valid.yaml} |   0
 ...valid.yml => example_catalog_invalid.yaml} |   0
 ...g_valid.yml => example_catalog_valid.yaml} |   0
 ...example_component-definition_invalid.yaml} |   0
 ...> example_component-definition_valid.yaml} |   0
 ..._invalid.yml => example_poam_invalid.yaml} |   0
 ...poam_valid.yml => example_poam_valid.yaml} |   0
 ...valid.yml => example_profile_invalid.yaml} |   0
 ...e_valid.yml => example_profile_valid.yaml} |   0
 ...p_invalid.yml => example_ssp_invalid.yaml} |   0
 ...e_ssp_valid.yml => example_ssp_valid.yaml} |   0
 17 files changed, 137 insertions(+), 75 deletions(-)
 rename src/test/resources/cli/{example_ap_invalid.yml => example_ap_invalid.yaml} (100%)
 rename src/test/resources/cli/{example_ap_valid.yml => example_ap_valid.yaml} (100%)
 rename src/test/resources/cli/{example_ar_invalid.yml => example_ar_invalid.yaml} (100%)
 rename src/test/resources/cli/{example_ar_valid.yml => example_ar_valid.yaml} (100%)
 rename src/test/resources/cli/{example_catalog_invalid.yml => example_catalog_invalid.yaml} (100%)
 rename src/test/resources/cli/{example_catalog_valid.yml => example_catalog_valid.yaml} (100%)
 rename src/test/resources/cli/{example_component-definition_invalid.yml => example_component-definition_invalid.yaml} (100%)
 rename src/test/resources/cli/{example_component-definition_valid.yml => example_component-definition_valid.yaml} (100%)
 rename src/test/resources/cli/{example_poam_invalid.yml => example_poam_invalid.yaml} (100%)
 rename src/test/resources/cli/{example_poam_valid.yml => example_poam_valid.yaml} (100%)
 rename src/test/resources/cli/{example_profile_invalid.yml => example_profile_invalid.yaml} (100%)
 rename src/test/resources/cli/{example_profile_valid.yml => example_profile_valid.yaml} (100%)
 rename src/test/resources/cli/{example_ssp_invalid.yml => example_ssp_invalid.yaml} (100%)
 rename src/test/resources/cli/{example_ssp_valid.yml => example_ssp_valid.yaml} (100%)

diff --git a/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/metaschema/MetaschemaCommand.java b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/metaschema/MetaschemaCommand.java
index f7165e0..9c1f0a3 100644
--- a/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/metaschema/MetaschemaCommand.java
+++ b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/metaschema/MetaschemaCommand.java
@@ -28,9 +28,7 @@
 
 import com.google.auto.service.AutoService;
 
-import gov.nist.secauto.metaschema.cli.commands.GenerateSchemaCommand;
-import gov.nist.secauto.metaschema.cli.commands.ValidateContentUsingModuleCommand;
-import gov.nist.secauto.metaschema.cli.commands.ValidateModuleCommand;
+import gov.nist.secauto.metaschema.cli.commands.MetaschemaCommands;
 import gov.nist.secauto.metaschema.cli.processor.command.AbstractParentCommand;
 import gov.nist.secauto.metaschema.cli.processor.command.ICommand;
 
@@ -41,9 +39,7 @@ public class MetaschemaCommand
 
   public MetaschemaCommand() {
     super(true);
-    addCommandHandler(new GenerateSchemaCommand());
-    addCommandHandler(new ValidateModuleCommand());
-    addCommandHandler(new ValidateContentUsingModuleCommand());
+    MetaschemaCommands.COMMANDS.forEach(this::addCommandHandler);
   }
 
   @Override
diff --git a/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/profile/ResolveSubcommand.java b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/profile/ResolveSubcommand.java
index 3c682b6..0491681 100644
--- a/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/profile/ResolveSubcommand.java
+++ b/src/main/java/gov/nist/secauto/oscal/tools/cli/core/commands/profile/ResolveSubcommand.java
@@ -274,49 +274,58 @@ protected ExitStatus executeCommand(
       return ExitCode.IO_ERROR.exit().withThrowable(ex);
     }
     Object object = document.getValue();
+    if (object == null) {
+      return ExitCode.INVALID_ARGUMENTS.exitMessage("The target profile contained no data");
+    }
+
     if (object instanceof Catalog) {
       // this is a catalog
-      return ExitCode.INVALID_ARGUMENTS.exitMessage("The target file is already a catalog");
-    } else if (object instanceof Profile) {
-      // this is a profile
-      URI sourceUri = ObjectUtils.notNull(source.toUri());
+      return ExitCode.INVALID_ARGUMENTS.exitMessage("The target is already a catalog");
+    }
 
-      DynamicContext dynamicContext = new DynamicContext(
-          StaticContext.builder()
-              .baseUri(sourceUri)
-              .defaultModelNamespace(document.getNamespace())
-              .build());
-      dynamicContext.setDocumentLoader(loader);
-      ProfileResolver resolver = new ProfileResolver();
-      resolver.setDynamicContext(dynamicContext);
+    if (!(object instanceof Profile)) {
+      // this is something else
+      return ExitCode.INVALID_ARGUMENTS.exitMessage("The target is not a profile");
+    }
 
-      IDocumentNodeItem resolvedProfile;
-      try {
-        resolvedProfile = resolver.resolve(document);
-      } catch (IOException | ProfileResolutionException ex) {
-        return ExitCode.PROCESSING_ERROR
-            .exitMessage(
-                String.format("Unable to resolve profile '%s'. %s", document.getDocumentUri(), ex.getMessage()))
-            .withThrowable(ex);
-      }
+    // this is a profile
+    URI sourceUri = ObjectUtils.notNull(source.toUri());
 
-      // DefaultConstraintValidator validator = new
-      // DefaultConstraintValidator(dynamicContext);
-      // ((IBoundXdmNodeItem)resolvedProfile).validate(validator);
-      // validator.finalizeValidation();
+    DynamicContext dynamicContext = new DynamicContext(
+        StaticContext.builder()
+            .baseUri(sourceUri)
+            .defaultModelNamespace(document.getNamespace())
+            .build());
+    dynamicContext.setDocumentLoader(loader);
+    ProfileResolver resolver = new ProfileResolver();
+    resolver.setDynamicContext(dynamicContext);
 
-      ISerializer<Catalog> serializer
-          = OscalBindingContext.instance().newSerializer(toFormat, Catalog.class);
-      try {
-        if (destination == null) {
-          @SuppressWarnings({ "resource", "PMD.CloseResource" }) PrintStream stdOut = ObjectUtils.notNull(System.out);
-          serializer.serialize((Catalog) INodeItem.toValue(resolvedProfile), stdOut);
-        } else {
-          serializer.serialize((Catalog) INodeItem.toValue(resolvedProfile), destination);
-        }
-      } catch (IOException ex) {
-        return ExitCode.PROCESSING_ERROR.exit().withThrowable(ex);
+    IDocumentNodeItem resolvedProfile;
+    try {
+      resolvedProfile = resolver.resolve(document);
+    } catch (IOException | ProfileResolutionException ex) {
+      return ExitCode.PROCESSING_ERROR
+          .exitMessage(
+              String.format("Unable to resolve profile '%s'. %s", document.getDocumentUri(), ex.getMessage()))
+          .withThrowable(ex);
+    }
+
+    // DefaultConstraintValidator validator = new
+    // DefaultConstraintValidator(dynamicContext);
+    // ((IBoundXdmNodeItem)resolvedProfile).validate(validator);
+    // validator.finalizeValidation();
+
+    ISerializer<Catalog> serializer
+        = OscalBindingContext.instance().newSerializer(toFormat, Catalog.class);
+    try {
+      if (destination == null) {
+        @SuppressWarnings({ "resource", "PMD.CloseResource" }) PrintStream stdOut = ObjectUtils.notNull(System.out);
+        serializer.serialize((Catalog) INodeItem.toValue(resolvedProfile), stdOut);
+      } else {
+        serializer.serialize((Catalog) INodeItem.toValue(resolvedProfile), destination);
       }
+    } catch (IOException ex) {
+      return ExitCode.PROCESSING_ERROR.exit().withThrowable(ex);
     }
     return ExitCode.OK.exit();
   }
diff --git a/src/test/java/gov/nist/secauto/oscal/tools/cli/core/CLITest.java b/src/test/java/gov/nist/secauto/oscal/tools/cli/core/CLITest.java
index 80351e4..d2fa91b 100644
--- a/src/test/java/gov/nist/secauto/oscal/tools/cli/core/CLITest.java
+++ b/src/test/java/gov/nist/secauto/oscal/tools/cli/core/CLITest.java
@@ -30,15 +30,18 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
 
-import gov.nist.secauto.metaschema.binding.io.Format;
 import gov.nist.secauto.metaschema.cli.processor.ExitCode;
 import gov.nist.secauto.metaschema.cli.processor.ExitStatus;
+import gov.nist.secauto.metaschema.core.util.ObjectUtils;
+import gov.nist.secauto.metaschema.databind.io.Format;
 import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionException;
 
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
 
+import java.io.IOException;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -61,13 +64,26 @@ void evaluateResult(@NonNull ExitStatus status, @NonNull ExitCode expectedCode,
       @NonNull Class<? extends Throwable> thrownClass) {
     status.generateMessage(true);
     Throwable thrown = status.getThrowable();
-    assert thrown != null;
     assertAll(
         () -> assertEquals(expectedCode, status.getExitCode(), "exit code mismatch"),
-        () -> assertEquals(thrownClass, thrown.getClass(), "expected Throwable mismatch"));
+        () -> assertEquals(
+            thrownClass,
+            thrown == null ? null : thrown.getClass(),
+            "Throwable mismatch"));
   }
 
-  private static Stream<Arguments> providesValues() {
+  private static String generateOutputPath(@NonNull Path source, @NonNull Format targetFormat) throws IOException {
+    String filename = ObjectUtils.notNull(source.getFileName()).toString();
+
+    int pos = filename.lastIndexOf('.');
+    filename = filename.substring(0, pos) + "_converted" + targetFormat.getDefaultExtension();
+
+    Path dir = Files.createDirectories(Path.of("target/oscal-cli-convert"));
+
+    return dir.resolve(filename).toString();
+  }
+
+  private static Stream<Arguments> providesValues() throws IOException {
     final String[] commands = { "ap", "ar", "catalog", "component-definition", "profile", "poam", "ssp" };
     final Map<Format, List<Format>> formatEntries = Map.of(
         Format.XML, Arrays.asList(Format.JSON, Format.YAML),
@@ -76,42 +92,83 @@ private static Stream<Arguments> providesValues() {
     List<Arguments> values = new ArrayList<>();
 
     values.add(Arguments.of(new String[] { "--version" }, ExitCode.OK, null));
-    // TODO: Test all data formats once usnistgov/oscal-cli#216 fix merged.
-    Path path = Paths.get("src/test/resources/cli/example_profile_invalid" + Format.XML.getDefaultExtension());
-    values.add(
-        Arguments.of(new String[] { "profile", "resolve", "--to=" + Format.XML.name().toLowerCase(), path.toString() },
-            ExitCode.PROCESSING_ERROR, ProfileResolutionException.class));
-
     for (String cmd : commands) {
+      // test helps
       values.add(Arguments.of(new String[] { cmd, "validate", "-h" }, ExitCode.OK, null));
-      // TODO: Update when usnistgov/oscal-cli#210 fix merged.
-      values.add(Arguments.of(new String[] { cmd, "convert", "-h" }, ExitCode.INVALID_COMMAND, null));
+      values.add(Arguments.of(new String[] { cmd, "convert", "-h" }, ExitCode.OK, null));
 
       for (Format format : Format.values()) {
-        path = Paths.get("src/test/resources/cli/example_" + cmd + "_invalid" + format.getDefaultExtension());
-        values.add(Arguments.of(new String[] { cmd, "validate", path.toString() }, ExitCode.FAIL, null));
-        path = Paths.get("src/test/resources/cli/example_" + cmd + "_valid" + format.getDefaultExtension());
-        values.add(Arguments.of(new String[] { cmd, "validate", path.toString() }, ExitCode.OK, null));
-        path = Paths.get("src/test/resources/cli/example_profile_valid" + format.getDefaultExtension());
-        List<Format> targetFormats = formatEntries.get(format);
-        for (Format targetFormat : targetFormats) {
-          path = Paths.get("src/test/resources/cli/example_" + cmd + "_valid" + format.getDefaultExtension());
-          String outputPath = path.toString().replace(format.getDefaultExtension(),
-              "_converted" + targetFormat.getDefaultExtension());
-          values.add(Arguments.of(new String[] { cmd, "convert", "--to=" + targetFormat.name().toLowerCase(),
-              path.toString(), outputPath, "--overwrite" }, ExitCode.OK, null));
+        String sourceExtension = format.getDefaultExtension();
+        values.add(
+            Arguments.of(
+                new String[] {
+                    cmd,
+                    "validate",
+                    Paths.get("src/test/resources/cli/example_" + cmd + "_invalid" + sourceExtension).toString()
+                },
+                ExitCode.FAIL,
+                null));
+        values.add(
+            Arguments.of(
+                new String[] {
+                    cmd,
+                    "validate",
+                    Paths.get("src/test/resources/cli/example_" + cmd + "_valid" + sourceExtension).toString()
+                },
+                ExitCode.OK,
+                null));
+
+        for (Format targetFormat : formatEntries.get(format)) {
+          Path path = Paths.get("src/test/resources/cli/example_" + cmd + "_valid" + sourceExtension);
+          values.add(
+              Arguments.of(
+                  new String[] {
+                      cmd,
+                      "convert",
+                      "--to=" + targetFormat.name().toLowerCase(),
+                      path.toString(),
+                      generateOutputPath(path, targetFormat),
+                      "--overwrite"
+                  },
+                  ExitCode.OK,
+                  null));
+
           // TODO: Update when usnistgov/oscal#217 fix merged.
-          path = Paths.get("src/test/resources/cli/example_" + cmd + "_invalid" + format.getDefaultExtension());
-          outputPath = path.toString().replace(format.getDefaultExtension(),
-              "_converted" + targetFormat.getDefaultExtension());
-          values.add(Arguments.of(new String[] { cmd, "convert", "--to=" + targetFormat.name().toLowerCase(),
-              path.toString(), outputPath, "--overwrite" }, ExitCode.OK, null));
+          path = Paths.get("src/test/resources/cli/example_" + cmd + "_invalid" + sourceExtension);
+          values.add(
+              Arguments.of(
+                  new String[] {
+                      cmd,
+                      "convert",
+                      "--to=" + targetFormat.name().toLowerCase(),
+                      path.toString(),
+                      generateOutputPath(path, targetFormat),
+                      "--overwrite"
+                  },
+                  ExitCode.OK,
+                  null));
         }
         if (cmd == "profile") {
-          path = Paths.get("src/test/resources/cli/example_profile_valid" + format.getDefaultExtension());
-          values
-              .add(Arguments.of(new String[] { cmd, "resolve", "--to=" + format.name().toLowerCase(), path.toString() },
-                  ExitCode.OK, null));
+          values.add(
+              Arguments.of(
+                  new String[] {
+                      cmd,
+                      "resolve",
+                      "--to=" + format.name().toLowerCase(),
+                      Paths.get("src/test/resources/cli/example_profile_valid" + sourceExtension).toString()
+                  },
+                  ExitCode.OK,
+                  null));
+          values.add(
+              Arguments.of(
+                  new String[] {
+                      "profile",
+                      "resolve",
+                      "--to=" + format.name().toLowerCase(),
+                      Paths.get("src/test/resources/cli/example_profile_invalid" + sourceExtension).toString()
+                  },
+                  ExitCode.PROCESSING_ERROR,
+                  ProfileResolutionException.class));
         }
       }
     }
diff --git a/src/test/resources/cli/example_ap_invalid.yml b/src/test/resources/cli/example_ap_invalid.yaml
similarity index 100%
rename from src/test/resources/cli/example_ap_invalid.yml
rename to src/test/resources/cli/example_ap_invalid.yaml
diff --git a/src/test/resources/cli/example_ap_valid.yml b/src/test/resources/cli/example_ap_valid.yaml
similarity index 100%
rename from src/test/resources/cli/example_ap_valid.yml
rename to src/test/resources/cli/example_ap_valid.yaml
diff --git a/src/test/resources/cli/example_ar_invalid.yml b/src/test/resources/cli/example_ar_invalid.yaml
similarity index 100%
rename from src/test/resources/cli/example_ar_invalid.yml
rename to src/test/resources/cli/example_ar_invalid.yaml
diff --git a/src/test/resources/cli/example_ar_valid.yml b/src/test/resources/cli/example_ar_valid.yaml
similarity index 100%
rename from src/test/resources/cli/example_ar_valid.yml
rename to src/test/resources/cli/example_ar_valid.yaml
diff --git a/src/test/resources/cli/example_catalog_invalid.yml b/src/test/resources/cli/example_catalog_invalid.yaml
similarity index 100%
rename from src/test/resources/cli/example_catalog_invalid.yml
rename to src/test/resources/cli/example_catalog_invalid.yaml
diff --git a/src/test/resources/cli/example_catalog_valid.yml b/src/test/resources/cli/example_catalog_valid.yaml
similarity index 100%
rename from src/test/resources/cli/example_catalog_valid.yml
rename to src/test/resources/cli/example_catalog_valid.yaml
diff --git a/src/test/resources/cli/example_component-definition_invalid.yml b/src/test/resources/cli/example_component-definition_invalid.yaml
similarity index 100%
rename from src/test/resources/cli/example_component-definition_invalid.yml
rename to src/test/resources/cli/example_component-definition_invalid.yaml
diff --git a/src/test/resources/cli/example_component-definition_valid.yml b/src/test/resources/cli/example_component-definition_valid.yaml
similarity index 100%
rename from src/test/resources/cli/example_component-definition_valid.yml
rename to src/test/resources/cli/example_component-definition_valid.yaml
diff --git a/src/test/resources/cli/example_poam_invalid.yml b/src/test/resources/cli/example_poam_invalid.yaml
similarity index 100%
rename from src/test/resources/cli/example_poam_invalid.yml
rename to src/test/resources/cli/example_poam_invalid.yaml
diff --git a/src/test/resources/cli/example_poam_valid.yml b/src/test/resources/cli/example_poam_valid.yaml
similarity index 100%
rename from src/test/resources/cli/example_poam_valid.yml
rename to src/test/resources/cli/example_poam_valid.yaml
diff --git a/src/test/resources/cli/example_profile_invalid.yml b/src/test/resources/cli/example_profile_invalid.yaml
similarity index 100%
rename from src/test/resources/cli/example_profile_invalid.yml
rename to src/test/resources/cli/example_profile_invalid.yaml
diff --git a/src/test/resources/cli/example_profile_valid.yml b/src/test/resources/cli/example_profile_valid.yaml
similarity index 100%
rename from src/test/resources/cli/example_profile_valid.yml
rename to src/test/resources/cli/example_profile_valid.yaml
diff --git a/src/test/resources/cli/example_ssp_invalid.yml b/src/test/resources/cli/example_ssp_invalid.yaml
similarity index 100%
rename from src/test/resources/cli/example_ssp_invalid.yml
rename to src/test/resources/cli/example_ssp_invalid.yaml
diff --git a/src/test/resources/cli/example_ssp_valid.yml b/src/test/resources/cli/example_ssp_valid.yaml
similarity index 100%
rename from src/test/resources/cli/example_ssp_valid.yml
rename to src/test/resources/cli/example_ssp_valid.yaml