From 282407b5493b7c4d33e164209fb591ff05481379 Mon Sep 17 00:00:00 2001 From: Jordan Padams Date: Tue, 19 May 2026 00:27:32 -0700 Subject: [PATCH 1/3] Upgrade warning.integrity.member_not_found to error by default (#1601) Add --allow-unlisted-members flag to downgrade back to warning for accumulating/incremental bundle validation use cases. Co-Authored-By: Claude Sonnet 4.6 --- .../java/gov/nasa/pds/tools/util/FlagsUtil.java | 13 +++++++++++++ .../gov/nasa/pds/tools/validate/ProblemType.java | 2 ++ .../pds4/BundleReferentialIntegrityRule.java | 8 ++++++-- .../pds4/CollectionReferentialIntegrityRule.java | 10 +++++++--- .../gov/nasa/pds/validate/ValidateLauncher.java | 16 ++++++++++++++++ .../validate/commandline/options/ConfigKey.java | 5 +++++ .../pds/validate/commandline/options/Flag.java | 4 ++++ .../commandline/options/FlagOptions.java | 1 + src/site/markdown/operate/index.md | 12 ++++++++++++ 9 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/main/java/gov/nasa/pds/tools/util/FlagsUtil.java b/src/main/java/gov/nasa/pds/tools/util/FlagsUtil.java index cda41a4a8..5fc8a39cf 100644 --- a/src/main/java/gov/nasa/pds/tools/util/FlagsUtil.java +++ b/src/main/java/gov/nasa/pds/tools/util/FlagsUtil.java @@ -38,6 +38,9 @@ public class FlagsUtil { /** Flag to enable/disable stack trace printing. */ private static boolean stackPrintingFlag = false; + /** Flag to downgrade member_not_found from ERROR to WARNING (for accumulating bundles). */ + private static boolean allowUnlistedMembers = false; + /** * Initialize flags to their default states * @@ -48,6 +51,7 @@ public static void initialize() { FlagsUtil.contentValidationFlag = true; FlagsUtil.skipProductValidation = false; FlagsUtil.stackPrintingFlag = false; + FlagsUtil.allowUnlistedMembers = false; LOG.debug("initialize:contentValidationFlag {}", FlagsUtil.contentValidationFlag); LOG.debug("initialize:skipProductValidation {}", FlagsUtil.skipProductValidation); } @@ -115,6 +119,15 @@ public static boolean getSkipProductValidation() { return (FlagsUtil.skipProductValidation); } + public static void setAllowUnlistedMembers(boolean flag) { + FlagsUtil.allowUnlistedMembers = flag; + LOG.debug("setAllowUnlistedMembers: {}", FlagsUtil.allowUnlistedMembers); + } + + public static boolean getAllowUnlistedMembers() { + return FlagsUtil.allowUnlistedMembers; + } + /** * Set the severity value * diff --git a/src/main/java/gov/nasa/pds/tools/validate/ProblemType.java b/src/main/java/gov/nasa/pds/tools/validate/ProblemType.java index ae61c51c5..4155f961d 100644 --- a/src/main/java/gov/nasa/pds/tools/validate/ProblemType.java +++ b/src/main/java/gov/nasa/pds/tools/validate/ProblemType.java @@ -211,6 +211,8 @@ public enum ProblemType { MEMBER_NOT_FOUND("warning.integrity.member_not_found"), + MEMBER_NOT_FOUND_ERROR("error.integrity.member_not_found"), + INTEGRITY_PDS4_VERSION_MISMATCH("warning.integrity.pds4_version_mismatch", ProblemCategory.GENERAL), diff --git a/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/BundleReferentialIntegrityRule.java b/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/BundleReferentialIntegrityRule.java index 90dc8da45..c7b34d480 100644 --- a/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/BundleReferentialIntegrityRule.java +++ b/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/BundleReferentialIntegrityRule.java @@ -25,6 +25,7 @@ import gov.nasa.pds.tools.util.EveryNCounter; import gov.nasa.pds.tools.util.ReferentialIntegrityUtil; import gov.nasa.pds.tools.util.Utility; +import gov.nasa.pds.tools.util.FlagsUtil; import gov.nasa.pds.tools.util.XMLExtractor; import gov.nasa.pds.tools.validate.Identifier; import gov.nasa.pds.tools.validate.ProblemDefinition; @@ -172,9 +173,12 @@ private void getBundleMembers(URL bundle) { } if (matchingMembers.isEmpty() && "Primary".equalsIgnoreCase(memberStatus)) { LOG.debug("getBundleMembers:MATCHING_MEMBER_ID_IS_EMPTY {}", id); + boolean downgradeToWarning = FlagsUtil.getAllowUnlistedMembers(); getListener() - .addProblem(new ValidationProblem(new ProblemDefinition(ExceptionType.WARNING, - ProblemType.MEMBER_NOT_FOUND, "The member '" + id + "' could not be found in " + .addProblem(new ValidationProblem(new ProblemDefinition( + downgradeToWarning ? ExceptionType.WARNING : ExceptionType.ERROR, + downgradeToWarning ? ProblemType.MEMBER_NOT_FOUND : ProblemType.MEMBER_NOT_FOUND_ERROR, + "The member '" + id + "' could not be found in " + "any product within the given target."), bundle)); } else if (matchingMembers.size() == 1) { diff --git a/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/CollectionReferentialIntegrityRule.java b/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/CollectionReferentialIntegrityRule.java index d97c79bf7..27748aa87 100644 --- a/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/CollectionReferentialIntegrityRule.java +++ b/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/CollectionReferentialIntegrityRule.java @@ -27,6 +27,7 @@ import gov.nasa.pds.tools.inventory.reader.InventoryReaderException; import gov.nasa.pds.tools.inventory.reader.InventoryTableReader; import gov.nasa.pds.tools.label.ExceptionType; +import gov.nasa.pds.tools.util.FlagsUtil; import gov.nasa.pds.tools.util.ReferentialIntegrityUtil; import gov.nasa.pds.tools.util.Utility; import gov.nasa.pds.tools.util.XMLExtractor; @@ -205,11 +206,14 @@ private void validateEntry(URL collection, int numOfCollectionMembers, Inventory "getCollectionMembers: id,matchingMembers.isEmpty(),entry.getMemberStatus() {},{},{}", id, matchingMembers.isEmpty(), entry.getMemberStatus()); if (matchingMembers.isEmpty() && "P".equalsIgnoreCase(entry.getMemberStatus())) { + boolean downgradeToWarning = FlagsUtil.getAllowUnlistedMembers(); getListener() - .addProblem(new ValidationProblem(new ProblemDefinition(ExceptionType.WARNING, - ProblemType.MEMBER_NOT_FOUND, "The member '" + id + "' could not be found in " + .addProblem(new ValidationProblem(new ProblemDefinition( + downgradeToWarning ? ExceptionType.WARNING : ExceptionType.ERROR, + downgradeToWarning ? ProblemType.MEMBER_NOT_FOUND : ProblemType.MEMBER_NOT_FOUND_ERROR, + "The member '" + id + "' could not be found in " + "any product within the given target."), - collection)); + collection)); } else if (matchingMembers.size() == 1) { super.verifyLidPrefix(id.getLid(), this.lid, entry.getMemberStatus(), collection); getListener() diff --git a/src/main/java/gov/nasa/pds/validate/ValidateLauncher.java b/src/main/java/gov/nasa/pds/validate/ValidateLauncher.java index 0122f46dc..5999f0407 100644 --- a/src/main/java/gov/nasa/pds/validate/ValidateLauncher.java +++ b/src/main/java/gov/nasa/pds/validate/ValidateLauncher.java @@ -237,6 +237,8 @@ public class ValidateLauncher { private boolean allowUnlabeledFiles; + private boolean allowUnlistedMembers; + private File registeredProductsFile; private File nonRegisteredProductsFile; @@ -288,6 +290,7 @@ public ValidateLauncher() throws TransformerConfigurationException { contextMismatchAsWarn = true; spotCheckData = -1; allowUnlabeledFiles = false; + allowUnlistedMembers = false; registeredAndNonRegistedProducts = new HashMap<>(); registeredProductsFile = new File( System.getProperty("resources.home") + File.separator + ToolInfo.getOutputFileName()); @@ -477,6 +480,9 @@ public void query(CommandLine line) throws Exception { setSpotCheckData(value); } else if (Flag.ALLOW_UNLABELED_FILES.getLongName().equals(o.getLongOpt())) { setAllowUnlabeledFiles(true); + } else if (Flag.ALLOW_UNLISTED_MEMBERS.getLongName().equals(o.getLongOpt())) { + setAllowUnlistedMembers(true); + FlagsUtil.setAllowUnlistedMembers(true); } else if (Flag.LATEST_JSON_FILE.getLongName().equals(o.getLongOpt())) { setUpdateRegisteredProducts(true); } else if (Flag.NONREGPROD_JSON_FILE.getLongName().equals(o.getLongOpt())) { @@ -852,6 +858,10 @@ public void query(File configuration) throws ConfigurationException { if (config.containsKey(ConfigKey.ALLOW_UNLABELED_FILES)) { setAllowUnlabeledFiles(true); } + if (config.containsKey(ConfigKey.ALLOW_UNLISTED_MEMBERS)) { + setAllowUnlistedMembers(true); + FlagsUtil.setAllowUnlistedMembers(true); + } if (config.containsKey(ConfigKey.LATEST_JSON_FILE)) { setUpdateRegisteredProducts(true); } @@ -1160,6 +1170,10 @@ public void setAllowUnlabeledFiles(boolean flag) { this.allowUnlabeledFiles = flag; } + public void setAllowUnlistedMembers(boolean flag) { + this.allowUnlistedMembers = flag; + } + private void setRegisteredProducts() { URL url = null; @@ -1396,6 +1410,8 @@ public void setupReport(String cliArgs[]) throws IOException { || validationRule.equalsIgnoreCase("pds4.collection"))) { report.addParameter("allowUnlabeledFiles", "Allow Unlabeled Files", String.valueOf(allowUnlabeledFiles)); + report.addParameter("allowUnlistedMembers", "Allow Unlisted Members", + String.valueOf(allowUnlistedMembers)); } report.addParameter("maxErrors", "Max Errors", String.valueOf(maxErrors)); report.addParameter("registeredContextsFile", "Registered Contexts File", diff --git a/src/main/java/gov/nasa/pds/validate/commandline/options/ConfigKey.java b/src/main/java/gov/nasa/pds/validate/commandline/options/ConfigKey.java index 6f8c9182e..297cdcfaf 100644 --- a/src/main/java/gov/nasa/pds/validate/commandline/options/ConfigKey.java +++ b/src/main/java/gov/nasa/pds/validate/commandline/options/ConfigKey.java @@ -147,6 +147,11 @@ public class ConfigKey { */ public static final String ALLOW_UNLABELED_FILES = "validate.allowUnlabeledFiles"; + /** + * Property to downgrade member_not_found from ERROR to WARNING (for accumulating bundles). + */ + public static final String ALLOW_UNLISTED_MEMBERS = "validate.allowUnlistedMembers"; + /** * Property to download the latest Registered Context Products JSON file and replace the existing * file. diff --git a/src/main/java/gov/nasa/pds/validate/commandline/options/Flag.java b/src/main/java/gov/nasa/pds/validate/commandline/options/Flag.java index fa4d8dc01..4e876f265 100644 --- a/src/main/java/gov/nasa/pds/validate/commandline/options/Flag.java +++ b/src/main/java/gov/nasa/pds/validate/commandline/options/Flag.java @@ -171,6 +171,10 @@ public enum Flag { ALLOW_UNLABELED_FILES(null, "allow-unlabeled-files", "Tells the tool to not check for unlabeled files in a bundle or collection."), + ALLOW_UNLISTED_MEMBERS(null, "allow-unlisted-members", + "When set, collection inventory members not found in the target are reported as warnings instead of errors. " + + "Use when validating partial or accumulating bundles where prior-release products are intentionally absent."), + RULE("R", "rule", "validation rule name", String.class, "Specifies the validation rules to apply. (pds4.bundle|pds4.collection|pds4.folder|pds4.label|pds3.volume)." + " Default is pds4.label"), diff --git a/src/main/java/gov/nasa/pds/validate/commandline/options/FlagOptions.java b/src/main/java/gov/nasa/pds/validate/commandline/options/FlagOptions.java index 90752db32..c6c52ed2d 100644 --- a/src/main/java/gov/nasa/pds/validate/commandline/options/FlagOptions.java +++ b/src/main/java/gov/nasa/pds/validate/commandline/options/FlagOptions.java @@ -74,6 +74,7 @@ public class FlagOptions { options.addOption(new ToolsOption(Flag.NO_DATA)); options.addOption(new ToolsOption(Flag.SPOT_CHECK_DATA)); options.addOption(new ToolsOption(Flag.ALLOW_UNLABELED_FILES)); + options.addOption(new ToolsOption(Flag.ALLOW_UNLISTED_MEMBERS)); options.addOption(new ToolsOption(Flag.LATEST_JSON_FILE)); options.addOption(new ToolsOption(Flag.NONREGPROD_JSON_FILE)); options.addOption(new ToolsOption(Flag.SKIP_CONTEXT_VALIDATION)); diff --git a/src/site/markdown/operate/index.md b/src/site/markdown/operate/index.md index 934a309ec..1eeb4f66e 100644 --- a/src/site/markdown/operate/index.md +++ b/src/site/markdown/operate/index.md @@ -451,6 +451,18 @@ and is in line with the other bundle data. % validate --skip-product-validation --rule pds4.bundle --target path/to/staged/bundle_v2 --alternate_file_paths path/to/online/bundle_v1 ``` +#### Validating Partial/Incremental Bundle Deliveries + +By default, validate reports an **error** (`error.integrity.member_not_found`) when a collection inventory lists a primary member product that cannot be found in the validation target. This is the correct behavior for complete bundles. + +When validating a partial or incremental delivery of an accumulating bundle — where prior-release products are intentionally absent from the local target — use the `--allow-unlisted-members` flag to downgrade this condition to a warning: + +``` +% validate --allow-unlisted-members --rule pds4.bundle --target path/to/incremental/bundle +``` + +This flag should only be used when you have a legitimate reason for missing members (e.g., products delivered in a previous release). For standard bundle validation, the default error behavior is correct and should not be suppressed. + #### Example of using multiple-flags to improve performance Here is one example of how to From b549fc3ae5fb7c5f7a929d9bc13a02c6e2de56d9 Mon Sep 17 00:00:00 2001 From: Jordan Padams Date: Tue, 19 May 2026 01:39:13 -0700 Subject: [PATCH 2/3] Rename flag to --skip-strict-collection-membership (#1601) Co-Authored-By: Claude Sonnet 4.6 --- .../gov/nasa/pds/tools/util/FlagsUtil.java | 14 +++++------ .../pds4/BundleReferentialIntegrityRule.java | 2 +- .../CollectionReferentialIntegrityRule.java | 2 +- .../nasa/pds/validate/ValidateLauncher.java | 24 +++++++++---------- .../commandline/options/ConfigKey.java | 2 +- .../validate/commandline/options/Flag.java | 2 +- .../commandline/options/FlagOptions.java | 2 +- src/site/markdown/operate/index.md | 4 ++-- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/main/java/gov/nasa/pds/tools/util/FlagsUtil.java b/src/main/java/gov/nasa/pds/tools/util/FlagsUtil.java index 5fc8a39cf..facfa0da9 100644 --- a/src/main/java/gov/nasa/pds/tools/util/FlagsUtil.java +++ b/src/main/java/gov/nasa/pds/tools/util/FlagsUtil.java @@ -39,7 +39,7 @@ public class FlagsUtil { private static boolean stackPrintingFlag = false; /** Flag to downgrade member_not_found from ERROR to WARNING (for accumulating bundles). */ - private static boolean allowUnlistedMembers = false; + private static boolean skipStrictCollectionMembership = false; /** * Initialize flags to their default states @@ -51,7 +51,7 @@ public static void initialize() { FlagsUtil.contentValidationFlag = true; FlagsUtil.skipProductValidation = false; FlagsUtil.stackPrintingFlag = false; - FlagsUtil.allowUnlistedMembers = false; + FlagsUtil.skipStrictCollectionMembership = false; LOG.debug("initialize:contentValidationFlag {}", FlagsUtil.contentValidationFlag); LOG.debug("initialize:skipProductValidation {}", FlagsUtil.skipProductValidation); } @@ -119,13 +119,13 @@ public static boolean getSkipProductValidation() { return (FlagsUtil.skipProductValidation); } - public static void setAllowUnlistedMembers(boolean flag) { - FlagsUtil.allowUnlistedMembers = flag; - LOG.debug("setAllowUnlistedMembers: {}", FlagsUtil.allowUnlistedMembers); + public static void setSkipStrictCollectionMembership(boolean flag) { + FlagsUtil.skipStrictCollectionMembership = flag; + LOG.debug("setSkipStrictCollectionMembership: {}", FlagsUtil.skipStrictCollectionMembership); } - public static boolean getAllowUnlistedMembers() { - return FlagsUtil.allowUnlistedMembers; + public static boolean getSkipStrictCollectionMembership() { + return FlagsUtil.skipStrictCollectionMembership; } /** diff --git a/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/BundleReferentialIntegrityRule.java b/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/BundleReferentialIntegrityRule.java index c7b34d480..1f9ec6c54 100644 --- a/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/BundleReferentialIntegrityRule.java +++ b/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/BundleReferentialIntegrityRule.java @@ -173,7 +173,7 @@ private void getBundleMembers(URL bundle) { } if (matchingMembers.isEmpty() && "Primary".equalsIgnoreCase(memberStatus)) { LOG.debug("getBundleMembers:MATCHING_MEMBER_ID_IS_EMPTY {}", id); - boolean downgradeToWarning = FlagsUtil.getAllowUnlistedMembers(); + boolean downgradeToWarning = FlagsUtil.getSkipStrictCollectionMembership(); getListener() .addProblem(new ValidationProblem(new ProblemDefinition( downgradeToWarning ? ExceptionType.WARNING : ExceptionType.ERROR, diff --git a/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/CollectionReferentialIntegrityRule.java b/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/CollectionReferentialIntegrityRule.java index 27748aa87..00572e4e7 100644 --- a/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/CollectionReferentialIntegrityRule.java +++ b/src/main/java/gov/nasa/pds/tools/validate/rule/pds4/CollectionReferentialIntegrityRule.java @@ -206,7 +206,7 @@ private void validateEntry(URL collection, int numOfCollectionMembers, Inventory "getCollectionMembers: id,matchingMembers.isEmpty(),entry.getMemberStatus() {},{},{}", id, matchingMembers.isEmpty(), entry.getMemberStatus()); if (matchingMembers.isEmpty() && "P".equalsIgnoreCase(entry.getMemberStatus())) { - boolean downgradeToWarning = FlagsUtil.getAllowUnlistedMembers(); + boolean downgradeToWarning = FlagsUtil.getSkipStrictCollectionMembership(); getListener() .addProblem(new ValidationProblem(new ProblemDefinition( downgradeToWarning ? ExceptionType.WARNING : ExceptionType.ERROR, diff --git a/src/main/java/gov/nasa/pds/validate/ValidateLauncher.java b/src/main/java/gov/nasa/pds/validate/ValidateLauncher.java index 5999f0407..30c3034fa 100644 --- a/src/main/java/gov/nasa/pds/validate/ValidateLauncher.java +++ b/src/main/java/gov/nasa/pds/validate/ValidateLauncher.java @@ -237,7 +237,7 @@ public class ValidateLauncher { private boolean allowUnlabeledFiles; - private boolean allowUnlistedMembers; + private boolean skipStrictCollectionMembership; private File registeredProductsFile; @@ -290,7 +290,7 @@ public ValidateLauncher() throws TransformerConfigurationException { contextMismatchAsWarn = true; spotCheckData = -1; allowUnlabeledFiles = false; - allowUnlistedMembers = false; + skipStrictCollectionMembership = false; registeredAndNonRegistedProducts = new HashMap<>(); registeredProductsFile = new File( System.getProperty("resources.home") + File.separator + ToolInfo.getOutputFileName()); @@ -480,9 +480,9 @@ public void query(CommandLine line) throws Exception { setSpotCheckData(value); } else if (Flag.ALLOW_UNLABELED_FILES.getLongName().equals(o.getLongOpt())) { setAllowUnlabeledFiles(true); - } else if (Flag.ALLOW_UNLISTED_MEMBERS.getLongName().equals(o.getLongOpt())) { - setAllowUnlistedMembers(true); - FlagsUtil.setAllowUnlistedMembers(true); + } else if (Flag.SKIP_STRICT_COLLECTION_MEMBERSHIP.getLongName().equals(o.getLongOpt())) { + setSkipStrictCollectionMembership(true); + FlagsUtil.setSkipStrictCollectionMembership(true); } else if (Flag.LATEST_JSON_FILE.getLongName().equals(o.getLongOpt())) { setUpdateRegisteredProducts(true); } else if (Flag.NONREGPROD_JSON_FILE.getLongName().equals(o.getLongOpt())) { @@ -858,9 +858,9 @@ public void query(File configuration) throws ConfigurationException { if (config.containsKey(ConfigKey.ALLOW_UNLABELED_FILES)) { setAllowUnlabeledFiles(true); } - if (config.containsKey(ConfigKey.ALLOW_UNLISTED_MEMBERS)) { - setAllowUnlistedMembers(true); - FlagsUtil.setAllowUnlistedMembers(true); + if (config.containsKey(ConfigKey.SKIP_STRICT_COLLECTION_MEMBERSHIP)) { + setSkipStrictCollectionMembership(true); + FlagsUtil.setSkipStrictCollectionMembership(true); } if (config.containsKey(ConfigKey.LATEST_JSON_FILE)) { setUpdateRegisteredProducts(true); @@ -1170,8 +1170,8 @@ public void setAllowUnlabeledFiles(boolean flag) { this.allowUnlabeledFiles = flag; } - public void setAllowUnlistedMembers(boolean flag) { - this.allowUnlistedMembers = flag; + public void setSkipStrictCollectionMembership(boolean flag) { + this.skipStrictCollectionMembership = flag; } private void setRegisteredProducts() { @@ -1410,8 +1410,8 @@ public void setupReport(String cliArgs[]) throws IOException { || validationRule.equalsIgnoreCase("pds4.collection"))) { report.addParameter("allowUnlabeledFiles", "Allow Unlabeled Files", String.valueOf(allowUnlabeledFiles)); - report.addParameter("allowUnlistedMembers", "Allow Unlisted Members", - String.valueOf(allowUnlistedMembers)); + report.addParameter("skipStrictCollectionMembership", "Allow Unlisted Members", + String.valueOf(skipStrictCollectionMembership)); } report.addParameter("maxErrors", "Max Errors", String.valueOf(maxErrors)); report.addParameter("registeredContextsFile", "Registered Contexts File", diff --git a/src/main/java/gov/nasa/pds/validate/commandline/options/ConfigKey.java b/src/main/java/gov/nasa/pds/validate/commandline/options/ConfigKey.java index 297cdcfaf..a9a6d2be2 100644 --- a/src/main/java/gov/nasa/pds/validate/commandline/options/ConfigKey.java +++ b/src/main/java/gov/nasa/pds/validate/commandline/options/ConfigKey.java @@ -150,7 +150,7 @@ public class ConfigKey { /** * Property to downgrade member_not_found from ERROR to WARNING (for accumulating bundles). */ - public static final String ALLOW_UNLISTED_MEMBERS = "validate.allowUnlistedMembers"; + public static final String SKIP_STRICT_COLLECTION_MEMBERSHIP = "validate.skipStrictCollectionMembership"; /** * Property to download the latest Registered Context Products JSON file and replace the existing diff --git a/src/main/java/gov/nasa/pds/validate/commandline/options/Flag.java b/src/main/java/gov/nasa/pds/validate/commandline/options/Flag.java index 4e876f265..59351c1be 100644 --- a/src/main/java/gov/nasa/pds/validate/commandline/options/Flag.java +++ b/src/main/java/gov/nasa/pds/validate/commandline/options/Flag.java @@ -171,7 +171,7 @@ public enum Flag { ALLOW_UNLABELED_FILES(null, "allow-unlabeled-files", "Tells the tool to not check for unlabeled files in a bundle or collection."), - ALLOW_UNLISTED_MEMBERS(null, "allow-unlisted-members", + SKIP_STRICT_COLLECTION_MEMBERSHIP(null, "skip-strict-collection-membership", "When set, collection inventory members not found in the target are reported as warnings instead of errors. " + "Use when validating partial or accumulating bundles where prior-release products are intentionally absent."), diff --git a/src/main/java/gov/nasa/pds/validate/commandline/options/FlagOptions.java b/src/main/java/gov/nasa/pds/validate/commandline/options/FlagOptions.java index c6c52ed2d..1a914573c 100644 --- a/src/main/java/gov/nasa/pds/validate/commandline/options/FlagOptions.java +++ b/src/main/java/gov/nasa/pds/validate/commandline/options/FlagOptions.java @@ -74,7 +74,7 @@ public class FlagOptions { options.addOption(new ToolsOption(Flag.NO_DATA)); options.addOption(new ToolsOption(Flag.SPOT_CHECK_DATA)); options.addOption(new ToolsOption(Flag.ALLOW_UNLABELED_FILES)); - options.addOption(new ToolsOption(Flag.ALLOW_UNLISTED_MEMBERS)); + options.addOption(new ToolsOption(Flag.SKIP_STRICT_COLLECTION_MEMBERSHIP)); options.addOption(new ToolsOption(Flag.LATEST_JSON_FILE)); options.addOption(new ToolsOption(Flag.NONREGPROD_JSON_FILE)); options.addOption(new ToolsOption(Flag.SKIP_CONTEXT_VALIDATION)); diff --git a/src/site/markdown/operate/index.md b/src/site/markdown/operate/index.md index 1eeb4f66e..8bb368a29 100644 --- a/src/site/markdown/operate/index.md +++ b/src/site/markdown/operate/index.md @@ -455,10 +455,10 @@ and is in line with the other bundle data. By default, validate reports an **error** (`error.integrity.member_not_found`) when a collection inventory lists a primary member product that cannot be found in the validation target. This is the correct behavior for complete bundles. -When validating a partial or incremental delivery of an accumulating bundle — where prior-release products are intentionally absent from the local target — use the `--allow-unlisted-members` flag to downgrade this condition to a warning: +When validating a partial or incremental delivery of an accumulating bundle — where prior-release products are intentionally absent from the local target — use the `--skip-strict-collection-membership` flag to downgrade this condition to a warning: ``` -% validate --allow-unlisted-members --rule pds4.bundle --target path/to/incremental/bundle +% validate --skip-strict-collection-membership --rule pds4.bundle --target path/to/incremental/bundle ``` This flag should only be used when you have a legitimate reason for missing members (e.g., products delivered in a previous release). For standard bundle validation, the default error behavior is correct and should not be suppressed. From 76244d1945944b3a5c4816ee437a1f1f4d78f49e Mon Sep 17 00:00:00 2001 From: Jordan Padams Date: Tue, 19 May 2026 05:53:29 -0700 Subject: [PATCH 3/3] Add test cases for issue #1601 and fix existing tests broken by default ERROR behavior - Add src/test/resources/features/4.2.x.feature with two scenarios: subtest 1: default run expects error.integrity.member_not_found=1 subtest 2: with --skip-strict-collection-membership expects warning.integrity.member_not_found=1 - Add minimal PDS4 bundle test data in src/test/resources/github1601/ to trigger member_not_found (collection inventory references a non-existent primary member) - Update pre.3.6.x.feature: add --skip-strict-collection-membership to four existing tests (github408/1, github401/1, github368/1, github6/2) that expected the old WARNING behavior, so they continue to pass with the new ERROR default Co-Authored-By: Claude Sonnet 4.6 --- src/test/resources/features/4.2.x.feature | 15 +++++ src/test/resources/features/pre.3.6.x.feature | 8 +-- .../resources/github1601/bundle_test_1601.xml | 33 +++++++++++ .../github1601/data/collection_data.xml | 57 +++++++++++++++++++ .../data/collection_data_inventory.csv | 1 + 5 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 src/test/resources/features/4.2.x.feature create mode 100644 src/test/resources/github1601/bundle_test_1601.xml create mode 100644 src/test/resources/github1601/data/collection_data.xml create mode 100644 src/test/resources/github1601/data/collection_data_inventory.csv diff --git a/src/test/resources/features/4.2.x.feature b/src/test/resources/features/4.2.x.feature new file mode 100644 index 000000000..a2303f1e8 --- /dev/null +++ b/src/test/resources/features/4.2.x.feature @@ -0,0 +1,15 @@ +Feature: 4.2.x + Scenario Outline: NASA-PDS/validate#- + Given validate issue , test , and test data at + When execute validate with + Then compare to the expected outcome . + @4.2.x + Examples: + | issueNumber | subtest | datasrc | args | expectation | +#begin + +# github1601: member_not_found should be ERROR by default; --skip-strict-collection-membership downgrades to WARNING +| 1601 | 1 | "github1601" | "-R pds4.bundle --skip-context-validation -t {datasrc}/bundle_test_1601.xml" | "summary:totalErrors=1,summary:referentialIntegrity:failed=1,summary:messageTypes:error.integrity.member_not_found=1" | +| 1601 | 2 | "github1601" | "-R pds4.bundle --skip-context-validation --skip-strict-collection-membership -t {datasrc}/bundle_test_1601.xml" | "summary:totalWarnings=1,summary:messageTypes:warning.integrity.member_not_found=1" | + +#end diff --git a/src/test/resources/features/pre.3.6.x.feature b/src/test/resources/features/pre.3.6.x.feature index 6f248581a..8bd617330 100644 --- a/src/test/resources/features/pre.3.6.x.feature +++ b/src/test/resources/features/pre.3.6.x.feature @@ -80,8 +80,8 @@ Feature: < 3.6 | 416 | 2 | "github416" | "-R pds4.label -t {datasrc}/mix_raw_calib_mixs-c_sw_offset_table_20160301_invalid.xml" | "summary:totalErrors=5,summary:totalWarnings=5,summary:productValidation:failed=1,summary:messageTypes:error.label.invalid_object_definition=1,summary:messageTypes:error.label.schematron=2,summary:messageTypes:error.table.field_value_data_type_mismatch=2,summary:messageTypes:warning.label.context_ref_mismatch=5" | | 416 | 3 | "github416" | "-R pds4.label -t {datasrc}/phe_misc_temperature_reference_20190524.xml" | "summary:totalWarnings=5,summary:messageTypes:warning.label.context_ref_mismatch=5" | | 416 | 4 | "github416" | "-R pds4.label -t {datasrc}/phe_misc_temperature_reference_20190524_invalid.xml" | "summary:totalErrors=866,summary:totalWarnings=293,summary:productValidation:failed=1,summary:messageTypes:error.label.invalid_object_definition=1,summary:messageTypes:error.label.schematron=2,summary:messageTypes:error.table.field_value_data_type_mismatch=575,summary:messageTypes:error.table.missing_CRLF=288,summary:messageTypes:warning.label.context_ref_mismatch=5,summary:messageTypes:warning.table.field_value_format_specifier_mismatch=288" | -| 408 | 1 | "github408" | "-R pds4.bundle --skip-content-validation --skip-context-validation -t {datasrc}/valid/bundle_insight_seis.xml" | "summary:totalWarnings=14,summary:productValidation:skipped=2,summary:messageTypes:warning.integrity.member_not_found=14" | -| 401 | 1 | "github401" | "--skip-context-validation -R pds4.bundle -t {datasrc}/invalid/bundle_kaguya_derived.xml" | "summary:totalErrors=5,summary:totalWarnings=2,summary:productValidation:failed=5,summary:productValidation:skipped=1,summary:messageTypes:error.label.schema=1,summary:messageTypes:error.table.records_mismatch=1,summary:messageTypes:error.validation.invalid_field_value=3,summary:messageTypes:warning.integrity.member_not_found=1,summary:messageTypes:warning.integrity.unreferenced_member=1" | +| 408 | 1 | "github408" | "-R pds4.bundle --skip-content-validation --skip-context-validation --skip-strict-collection-membership -t {datasrc}/valid/bundle_insight_seis.xml" | "summary:totalWarnings=14,summary:productValidation:skipped=2,summary:messageTypes:warning.integrity.member_not_found=14" | +| 401 | 1 | "github401" | "--skip-context-validation --skip-strict-collection-membership -R pds4.bundle -t {datasrc}/invalid/bundle_kaguya_derived.xml" | "summary:totalErrors=5,summary:totalWarnings=2,summary:productValidation:failed=5,summary:productValidation:skipped=1,summary:messageTypes:error.label.schema=1,summary:messageTypes:error.table.records_mismatch=1,summary:messageTypes:error.validation.invalid_field_value=3,summary:messageTypes:warning.integrity.member_not_found=1,summary:messageTypes:warning.integrity.unreferenced_member=1" | | 392 | 1 | "github392" | "-R pds4.label --skip-context-validation -t {datasrc}/test1_valid.xml" | | | 392 | 2 | "github392" | "-R pds4.label --skip-context-validation -t {datasrc}/test1_invalid.xml" | "summary:totalErrors=1,summary:productValidation:failed=1,summary:messageTypes:error.table.field_value_overlap=1" | | 392 | 3 | "github392" | "-R pds4.label --skip-context-validation -t {datasrc}/INVALID_odf07155_msgr_11.xml" | "summary:totalErrors=2,summary:productValidation:failed=1,summary:messageTypes:error.table.bad_field_read=1,summary:messageTypes:error.table.field_value_overlap=1" | @@ -92,7 +92,7 @@ Feature: < 3.6 | 375 | 1 | "github375" | "-R pds4.bundle --skip-content-validation --skip-context-validation -t {datasrc}/h/bundle_gbo.ast.primass-l.spectra.xml" | | | 373 | 1 | "github240" | "-R pds4.bundle --skip-product-validation --skip-context-validation --skip-content-validation --skip-context-validation -t {datasrc}/valid/bundle_kaguya_derived.xml" | "summary:totalWarnings=5,summary:productValidation:skipped=10,summary:messageTypes:warning.integrity.pds4_version_mismatch=1,summary:messageTypes:warning.integrity.reference_not_found=3,summary:messageTypes:warning.integrity.unreferenced_member=1" | | 373 | 2 | "github240" | "-R pds4.bundle --skip-product-validation --skip-context-validation --skip-content-validation --skip-context-validation -t {datasrc}/invalid/bundle_kaguya_derived.xml" | "summary:totalWarnings=8,summary:productValidation:skipped=10,summary:messageTypes:warning.integrity.pds4_version_mismatch=1,summary:messageTypes:warning.integrity.reference_not_found=3,summary:messageTypes:warning.integrity.unreferenced_member=1,summary:messageTypes:warning.sub_directory.unallowed_name=3" | -| 368 | 1 | "github368" | "-R pds4.bundle --skip-context-reference-check --skip-product-validation -t {datasrc}/valid//bundle_kaguya_derived.xml" | "summary:totalWarnings=3,summary:productValidation:skipped=5,summary:messageTypes:warning.integrity.member_not_found=1,summary:messageTypes:warning.integrity.reference_not_found=1,summary:messageTypes:warning.integrity.unreferenced_member=1" | +| 368 | 1 | "github368" | "-R pds4.bundle --skip-context-reference-check --skip-product-validation --skip-strict-collection-membership -t {datasrc}/valid//bundle_kaguya_derived.xml" | "summary:totalWarnings=3,summary:productValidation:skipped=5,summary:messageTypes:warning.integrity.member_not_found=1,summary:messageTypes:warning.integrity.reference_not_found=1,summary:messageTypes:warning.integrity.unreferenced_member=1" | | 367 | 1 | "github367" | "--skip-context-validation -R pds4.bundle -t {datasrc}/bundle_kaguya_derived.xml" | "summary:totalErrors=8,summary:totalWarnings=19,summary:productValidation:failed=2,summary:messageTypes:error.file.not_jpeg_compliant=2,summary:messageTypes:error.file.not_png_compliant=2,summary:messageTypes:error.label.duplicate_identifier=1,summary:messageTypes:error.label.missing_file=2,summary:messageTypes:error.pdf.file.not_pdfa_compliant=1,summary:messageTypes:warning.file.not_encapsulated_postscript_mimetype=1,summary:messageTypes:warning.file.not_gif_mimetype=1,summary:messageTypes:warning.file.not_html_mimetype=2,summary:messageTypes:warning.file.not_latex_mimetype=2,summary:messageTypes:warning.file.not_mp4_mimetype=2,summary:messageTypes:warning.file.not_msexcel_mimetype=1,summary:messageTypes:warning.file.not_msword_mimetype=1,summary:messageTypes:warning.file.not_referenced_in_label=2,summary:messageTypes:warning.file.not_tiff_mimetype=2,summary:messageTypes:warning.integrity.pds4_version_mismatch=1,summary:messageTypes:warning.integrity.reference_not_found=3,summary:messageTypes:warning.integrity.unreferenced_member=1" | | 367 | 2 | "github367" | "--skip-context-validation -R pds4.bundle -t {datasrc}/bundle_kaguya_derived.xml" | "summary:totalErrors=8,summary:totalWarnings=19,summary:productValidation:failed=2,summary:messageTypes:error.file.not_jpeg_compliant=2,summary:messageTypes:error.file.not_png_compliant=2,summary:messageTypes:error.label.duplicate_identifier=1,summary:messageTypes:error.label.missing_file=2,summary:messageTypes:error.pdf.file.not_pdfa_compliant=1,summary:messageTypes:warning.file.not_encapsulated_postscript_mimetype=1,summary:messageTypes:warning.file.not_gif_mimetype=1,summary:messageTypes:warning.file.not_html_mimetype=2,summary:messageTypes:warning.file.not_latex_mimetype=2,summary:messageTypes:warning.file.not_mp4_mimetype=2,summary:messageTypes:warning.file.not_msexcel_mimetype=1,summary:messageTypes:warning.file.not_msword_mimetype=1,summary:messageTypes:warning.file.not_referenced_in_label=2,summary:messageTypes:warning.file.not_tiff_mimetype=2,summary:messageTypes:warning.integrity.pds4_version_mismatch=1,summary:messageTypes:warning.integrity.reference_not_found=3,summary:messageTypes:warning.integrity.unreferenced_member=1" | | 367 | 3 | "github367" | "--skip-context-validation -R pds4.bundle -t {datasrc}/bundle_kaguya_derived.xml" | "summary:totalErrors=8,summary:totalWarnings=19,summary:productValidation:failed=2,summary:messageTypes:error.file.not_jpeg_compliant=2,summary:messageTypes:error.file.not_png_compliant=2,summary:messageTypes:error.label.duplicate_identifier=1,summary:messageTypes:error.label.missing_file=2,summary:messageTypes:error.pdf.file.not_pdfa_compliant=1,summary:messageTypes:warning.file.not_encapsulated_postscript_mimetype=1,summary:messageTypes:warning.file.not_gif_mimetype=1,summary:messageTypes:warning.file.not_html_mimetype=2,summary:messageTypes:warning.file.not_latex_mimetype=2,summary:messageTypes:warning.file.not_mp4_mimetype=2,summary:messageTypes:warning.file.not_msexcel_mimetype=1,summary:messageTypes:warning.file.not_msword_mimetype=1,summary:messageTypes:warning.file.not_referenced_in_label=2,summary:messageTypes:warning.file.not_tiff_mimetype=2,summary:messageTypes:warning.integrity.pds4_version_mismatch=1,summary:messageTypes:warning.integrity.reference_not_found=3,summary:messageTypes:warning.integrity.unreferenced_member=1" | @@ -224,7 +224,7 @@ Feature: < 3.6 | 9 | 6 | "github09" | "--skip-context-validation -t {datasrc}/val9b.xml" | | | 7 | 1 | "github7" | "--skip-context-validation -t {datasrc}/ch2_sar_ncxs_20090107t163003745_d_sli_xx_fp_hh_pb1_19111.xml" | | | 6 | 1 | "github6" | "--skip-context-validation -R pds4.bundle {datasrc}/invalid/bundle_kaguya_derived.xml" | "summary:totalErrors=8,summary:totalWarnings=14,summary:productValidation:failed=2,summary:productValidation:skipped=6,summary:referentialIntegrity:failed=5,summary:messageTypes:error.file.name_has_invalid_characters=6,summary:messageTypes:error.file.unallowed_base_name=1,summary:messageTypes:error.pdf.file.not_pdfa_compliant=1,summary:messageTypes:warning.file.not_referenced_in_label=7,summary:messageTypes:warning.integrity.pds4_version_mismatch=1,summary:messageTypes:warning.integrity.reference_not_found=3,summary:messageTypes:warning.integrity.unreferenced_member=2,summary:messageTypes:warning.label.schematron=1" | -| 6 | 2 | "github6" | "-R pds4.bundle --skip-context-validation {datasrc}/invalid/bundle_kaguya_derived_7.xml" | "summary:totalErrors=8,summary:totalWarnings=15,summary:productValidation:failed=2,summary:productValidation:skipped=6,summary:referentialIntegrity:failed=5,summary:messageTypes:error.file.name_has_invalid_characters=6,summary:messageTypes:error.file.unallowed_base_name=1,summary:messageTypes:error.pdf.file.not_pdfa_compliant=1,summary:messageTypes:warning.file.not_referenced_in_label=7,summary:messageTypes:warning.integrity.member_not_found=1,summary:messageTypes:warning.integrity.pds4_version_mismatch=1,summary:messageTypes:warning.integrity.reference_not_found=3,summary:messageTypes:warning.integrity.unreferenced_member=3" | +| 6 | 2 | "github6" | "-R pds4.bundle --skip-context-validation --skip-strict-collection-membership {datasrc}/invalid/bundle_kaguya_derived_7.xml" | "summary:totalErrors=8,summary:totalWarnings=15,summary:productValidation:failed=2,summary:productValidation:skipped=6,summary:referentialIntegrity:failed=5,summary:messageTypes:error.file.name_has_invalid_characters=6,summary:messageTypes:error.file.unallowed_base_name=1,summary:messageTypes:error.pdf.file.not_pdfa_compliant=1,summary:messageTypes:warning.file.not_referenced_in_label=7,summary:messageTypes:warning.integrity.member_not_found=1,summary:messageTypes:warning.integrity.pds4_version_mismatch=1,summary:messageTypes:warning.integrity.reference_not_found=3,summary:messageTypes:warning.integrity.unreferenced_member=3" | | 6 | 3 | "github6" | "-R pds4.bundle --skip-context-validation {datasrc}/valid/bundle_kaguya_derived.xml" | "summary:totalWarnings=7,summary:messageTypes:warning.file.not_referenced_in_label=2,summary:messageTypes:warning.integrity.pds4_version_mismatch=1,summary:messageTypes:warning.integrity.reference_not_found=3,summary:messageTypes:warning.integrity.unreferenced_member=1" | | 5 | 1 | "github5" | "-R pds4.bundle --skip-context-validation -t {datasrc}/invalid/bundle_kaguya_derived.xml" | "summary:totalErrors=7,summary:totalWarnings=13,summary:productValidation:failed=2,summary:productValidation:skipped=6,summary:referentialIntegrity:failed=4,summary:messageTypes:error.file.name_has_invalid_characters=5,summary:messageTypes:error.file.unallowed_base_name=1,summary:messageTypes:error.label.missing_file=1,summary:messageTypes:warning.file.not_referenced_in_label=6,summary:messageTypes:warning.integrity.pds4_version_mismatch=1,summary:messageTypes:warning.integrity.reference_not_found=3,summary:messageTypes:warning.integrity.unreferenced_member=2,summary:messageTypes:warning.label.schematron=1" | | 5 | 2 | "github5" | "-R pds4.bundle --skip-context-validation -t {datasrc}/valid/bundle_kaguya_derived.xml" | "summary:totalWarnings=6,summary:productValidation:skipped=6,summary:messageTypes:warning.file.not_referenced_in_label=1,summary:messageTypes:warning.integrity.pds4_version_mismatch=1,summary:messageTypes:warning.integrity.reference_not_found=3,summary:messageTypes:warning.integrity.unreferenced_member=1" | diff --git a/src/test/resources/github1601/bundle_test_1601.xml b/src/test/resources/github1601/bundle_test_1601.xml new file mode 100644 index 000000000..8ab712744 --- /dev/null +++ b/src/test/resources/github1601/bundle_test_1601.xml @@ -0,0 +1,33 @@ + + + + + + urn:nasa:pds:test_1601 + 1.0 + Test Bundle for Issue 1601 + 1.11.0.0 + Product_Bundle + + 2024 + Test bundle for issue 1601 - member_not_found default error behavior. + + + + 2024-01-01 + 1.0 + Initial version for test + + + + + Archive + + + urn:nasa:pds:test_1601:data::1.0 + Primary + bundle_has_data_collection + + diff --git a/src/test/resources/github1601/data/collection_data.xml b/src/test/resources/github1601/data/collection_data.xml new file mode 100644 index 000000000..1527dace2 --- /dev/null +++ b/src/test/resources/github1601/data/collection_data.xml @@ -0,0 +1,57 @@ + + + + + + urn:nasa:pds:test_1601:data + 1.0 + Test Data Collection for Issue 1601 + 1.11.0.0 + Product_Collection + + 2024 + Test data collection for issue 1601 - member_not_found default error behavior. + + + + 2024-01-01 + 1.0 + Initial version for test + + + + + Data + + + + collection_data_inventory.csv + + + 0 + PDS DSV 1 + 1 + Carriage-Return Line-Feed + Comma + + 2 + 0 + + Member Status + 1 + ASCII_String + 1 + + + LIDVID_LID + 2 + ASCII_LIDVID_LID + 255 + + + inventory_has_member_product + + + diff --git a/src/test/resources/github1601/data/collection_data_inventory.csv b/src/test/resources/github1601/data/collection_data_inventory.csv new file mode 100644 index 000000000..51ffc91dd --- /dev/null +++ b/src/test/resources/github1601/data/collection_data_inventory.csv @@ -0,0 +1 @@ +P,urn:nasa:pds:test_1601:data:missing_product::1.0