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

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


The following commit(s) were added to refs/heads/main by this push:
     new 226774b0 Core: Add policy content and validator for more maintenance 
policies (#1261)
226774b0 is described below

commit 226774b04d9a0de697856d20277f62c53496d4ea
Author: Yufei Gu <[email protected]>
AuthorDate: Thu Mar 27 13:08:02 2025 -0700

    Core: Add policy content and validator for more maintenance policies (#1261)
---
 .../core/policy/{ => content}/PolicyContent.java   |   2 +-
 .../PolicyContentUtil.java}                        |   4 +-
 .../StrictBooleanDeserializer.java                 |   3 +-
 .../maintenance/BaseMaintenancePolicyContent.java} |  46 ++----
 .../maintenance}/DataCompactionPolicyContent.java  |  63 ++-----
 .../MetadataCompactionPolicyContent.java           |  54 ++++++
 .../OrphanFileRemovalPolicyContent.java            |  84 ++++++++++
 .../SnapshotRetentionPolicyContent.java            |  54 ++++++
 .../core/policy/validator/PolicyValidators.java    |  26 ++-
 .../BaseMaintenancePolicyValidator.java}           |  11 +-
 .../system/orphan-file-removal/2025-02-03.json     |   2 +-
 .../core/policy/MaintenancePolicyContentTest.java  | 181 +++++++++++++++++++++
 .../polaris/core/policy/PolicyValidatorsTest.java  | 132 +++++++++++++++
 .../validator/DataCompactionPolicyContentTest.java |  97 -----------
 .../DataCompactionPolicyValidatorTest.java         |  88 ----------
 .../policy/validator/PolicyValidatorsTest.java     |  65 --------
 16 files changed, 561 insertions(+), 351 deletions(-)

diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/PolicyContent.java 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/PolicyContent.java
similarity index 94%
rename from 
polaris-core/src/main/java/org/apache/polaris/core/policy/PolicyContent.java
rename to 
polaris-core/src/main/java/org/apache/polaris/core/policy/content/PolicyContent.java
index a1e82f7b..21186f6c 100644
--- 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/PolicyContent.java
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/PolicyContent.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.polaris.core.policy;
+package org.apache.polaris.core.policy.content;
 
 /** A marker interface for policy content */
 public interface PolicyContent {}
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/PolicyValidatorUtil.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/PolicyContentUtil.java
similarity index 94%
rename from 
polaris-core/src/main/java/org/apache/polaris/core/policy/validator/PolicyValidatorUtil.java
rename to 
polaris-core/src/main/java/org/apache/polaris/core/policy/content/PolicyContentUtil.java
index a08cdfd7..2ba025a6 100644
--- 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/PolicyValidatorUtil.java
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/PolicyContentUtil.java
@@ -16,12 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.polaris.core.policy.validator;
+package org.apache.polaris.core.policy.content;
 
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-public class PolicyValidatorUtil {
+public class PolicyContentUtil {
   public static final ObjectMapper MAPPER = configureMapper();
 
   private static ObjectMapper configureMapper() {
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/StrictBooleanDeserializer.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/StrictBooleanDeserializer.java
similarity index 92%
rename from 
polaris-core/src/main/java/org/apache/polaris/core/policy/validator/StrictBooleanDeserializer.java
rename to 
polaris-core/src/main/java/org/apache/polaris/core/policy/content/StrictBooleanDeserializer.java
index f6da87e7..3c54d10d 100644
--- 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/StrictBooleanDeserializer.java
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/StrictBooleanDeserializer.java
@@ -16,12 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.polaris.core.policy.validator;
+package org.apache.polaris.core.policy.content;
 
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonDeserializer;
 import java.io.IOException;
+import org.apache.polaris.core.policy.validator.InvalidPolicyException;
 
 public class StrictBooleanDeserializer extends JsonDeserializer<Boolean> {
   @Override
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/datacompaction/DataCompactionPolicyContent.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/BaseMaintenancePolicyContent.java
similarity index 57%
copy from 
polaris-core/src/main/java/org/apache/polaris/core/policy/validator/datacompaction/DataCompactionPolicyContent.java
copy to 
polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/BaseMaintenancePolicyContent.java
index efdd158b..a825e87b 100644
--- 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/datacompaction/DataCompactionPolicyContent.java
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/BaseMaintenancePolicyContent.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.polaris.core.policy.validator.datacompaction;
+package org.apache.polaris.core.policy.content.maintenance;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -24,15 +24,11 @@ import 
com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.google.common.base.Strings;
 import java.util.Map;
 import java.util.Set;
-import org.apache.polaris.core.policy.PolicyContent;
+import org.apache.polaris.core.policy.content.PolicyContent;
+import org.apache.polaris.core.policy.content.StrictBooleanDeserializer;
 import org.apache.polaris.core.policy.validator.InvalidPolicyException;
-import org.apache.polaris.core.policy.validator.PolicyValidatorUtil;
-import org.apache.polaris.core.policy.validator.StrictBooleanDeserializer;
-
-public class DataCompactionPolicyContent implements PolicyContent {
-  private static final String DEFAULT_POLICY_SCHEMA_VERSION = "2025-02-03";
-  private static final Set<String> POLICY_SCHEMA_VERSIONS = 
Set.of(DEFAULT_POLICY_SCHEMA_VERSION);
 
+public abstract class BaseMaintenancePolicyContent implements PolicyContent {
   @JsonDeserialize(using = StrictBooleanDeserializer.class)
   private Boolean enable;
 
@@ -40,7 +36,7 @@ public class DataCompactionPolicyContent implements 
PolicyContent {
   private Map<String, String> config;
 
   @JsonCreator
-  public DataCompactionPolicyContent(
+  public BaseMaintenancePolicyContent(
       @JsonProperty(value = "enable", required = true) boolean enable) {
     this.enable = enable;
   }
@@ -69,29 +65,21 @@ public class DataCompactionPolicyContent implements 
PolicyContent {
     this.config = config;
   }
 
-  public static DataCompactionPolicyContent fromString(String content) {
-    if (Strings.isNullOrEmpty(content)) {
-      throw new InvalidPolicyException("Policy is empty");
+  static void validateVersion(
+      String content,
+      BaseMaintenancePolicyContent policy,
+      String defaultVersion,
+      Set<String> allVersions) {
+    if (policy == null) {
+      throw new InvalidPolicyException("Invalid policy: " + content);
     }
 
-    try {
-      DataCompactionPolicyContent policy =
-          PolicyValidatorUtil.MAPPER.readValue(content, 
DataCompactionPolicyContent.class);
-      if (policy == null) {
-        throw new InvalidPolicyException("Invalid policy");
-      }
-
-      if (Strings.isNullOrEmpty(policy.getVersion())) {
-        policy.setVersion(DEFAULT_POLICY_SCHEMA_VERSION);
-      }
-
-      if (!POLICY_SCHEMA_VERSIONS.contains(policy.getVersion())) {
-        throw new InvalidPolicyException("Invalid policy version: " + 
policy.getVersion());
-      }
+    if (Strings.isNullOrEmpty(policy.getVersion())) {
+      policy.setVersion(defaultVersion);
+    }
 
-      return policy;
-    } catch (Exception e) {
-      throw new InvalidPolicyException(e);
+    if (!allVersions.contains(policy.getVersion())) {
+      throw new InvalidPolicyException("Invalid policy version: " + 
policy.getVersion());
     }
   }
 }
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/datacompaction/DataCompactionPolicyContent.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/DataCompactionPolicyContent.java
similarity index 51%
rename from 
polaris-core/src/main/java/org/apache/polaris/core/policy/validator/datacompaction/DataCompactionPolicyContent.java
rename to 
polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/DataCompactionPolicyContent.java
index efdd158b..a5a4012c 100644
--- 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/datacompaction/DataCompactionPolicyContent.java
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/DataCompactionPolicyContent.java
@@ -16,57 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.polaris.core.policy.validator.datacompaction;
+package org.apache.polaris.core.policy.content.maintenance;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.google.common.base.Strings;
-import java.util.Map;
 import java.util.Set;
-import org.apache.polaris.core.policy.PolicyContent;
+import org.apache.polaris.core.policy.content.PolicyContentUtil;
 import org.apache.polaris.core.policy.validator.InvalidPolicyException;
-import org.apache.polaris.core.policy.validator.PolicyValidatorUtil;
-import org.apache.polaris.core.policy.validator.StrictBooleanDeserializer;
 
-public class DataCompactionPolicyContent implements PolicyContent {
+public class DataCompactionPolicyContent extends BaseMaintenancePolicyContent {
   private static final String DEFAULT_POLICY_SCHEMA_VERSION = "2025-02-03";
   private static final Set<String> POLICY_SCHEMA_VERSIONS = 
Set.of(DEFAULT_POLICY_SCHEMA_VERSION);
 
-  @JsonDeserialize(using = StrictBooleanDeserializer.class)
-  private Boolean enable;
-
-  private String version;
-  private Map<String, String> config;
-
   @JsonCreator
   public DataCompactionPolicyContent(
       @JsonProperty(value = "enable", required = true) boolean enable) {
-    this.enable = enable;
-  }
-
-  public String getVersion() {
-    return version;
-  }
-
-  public void setVersion(String version) {
-    this.version = version;
-  }
-
-  public Boolean enabled() {
-    return enable;
-  }
-
-  public void setEnabled(Boolean enable) {
-    this.enable = enable;
-  }
-
-  public Map<String, String> getConfig() {
-    return config;
-  }
-
-  public void setConfig(Map<String, String> config) {
-    this.config = config;
+    super(enable);
   }
 
   public static DataCompactionPolicyContent fromString(String content) {
@@ -74,24 +40,15 @@ public class DataCompactionPolicyContent implements 
PolicyContent {
       throw new InvalidPolicyException("Policy is empty");
     }
 
+    DataCompactionPolicyContent policy;
     try {
-      DataCompactionPolicyContent policy =
-          PolicyValidatorUtil.MAPPER.readValue(content, 
DataCompactionPolicyContent.class);
-      if (policy == null) {
-        throw new InvalidPolicyException("Invalid policy");
-      }
-
-      if (Strings.isNullOrEmpty(policy.getVersion())) {
-        policy.setVersion(DEFAULT_POLICY_SCHEMA_VERSION);
-      }
-
-      if (!POLICY_SCHEMA_VERSIONS.contains(policy.getVersion())) {
-        throw new InvalidPolicyException("Invalid policy version: " + 
policy.getVersion());
-      }
-
-      return policy;
+      policy = PolicyContentUtil.MAPPER.readValue(content, 
DataCompactionPolicyContent.class);
     } catch (Exception e) {
       throw new InvalidPolicyException(e);
     }
+
+    validateVersion(content, policy, DEFAULT_POLICY_SCHEMA_VERSION, 
POLICY_SCHEMA_VERSIONS);
+
+    return policy;
   }
 }
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/MetadataCompactionPolicyContent.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/MetadataCompactionPolicyContent.java
new file mode 100644
index 00000000..a22ca2d5
--- /dev/null
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/MetadataCompactionPolicyContent.java
@@ -0,0 +1,54 @@
+/*
+ * 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.polaris.core.policy.content.maintenance;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Strings;
+import java.util.Set;
+import org.apache.polaris.core.policy.content.PolicyContentUtil;
+import org.apache.polaris.core.policy.validator.InvalidPolicyException;
+
+public class MetadataCompactionPolicyContent extends 
BaseMaintenancePolicyContent {
+  private static final String DEFAULT_POLICY_SCHEMA_VERSION = "2025-02-03";
+  private static final Set<String> POLICY_SCHEMA_VERSIONS = 
Set.of(DEFAULT_POLICY_SCHEMA_VERSION);
+
+  @JsonCreator
+  public MetadataCompactionPolicyContent(
+      @JsonProperty(value = "enable", required = true) boolean enable) {
+    super(enable);
+  }
+
+  public static MetadataCompactionPolicyContent fromString(String content) {
+    if (Strings.isNullOrEmpty(content)) {
+      throw new InvalidPolicyException("Policy is empty");
+    }
+
+    MetadataCompactionPolicyContent policy;
+    try {
+      policy = PolicyContentUtil.MAPPER.readValue(content, 
MetadataCompactionPolicyContent.class);
+    } catch (Exception e) {
+      throw new InvalidPolicyException(e);
+    }
+
+    validateVersion(content, policy, DEFAULT_POLICY_SCHEMA_VERSION, 
POLICY_SCHEMA_VERSIONS);
+
+    return policy;
+  }
+}
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/OrphanFileRemovalPolicyContent.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/OrphanFileRemovalPolicyContent.java
new file mode 100644
index 00000000..0adf0ed5
--- /dev/null
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/OrphanFileRemovalPolicyContent.java
@@ -0,0 +1,84 @@
+/*
+ * 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.polaris.core.policy.content.maintenance;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Strings;
+import java.util.List;
+import java.util.Set;
+import org.apache.polaris.core.policy.content.PolicyContentUtil;
+import org.apache.polaris.core.policy.validator.InvalidPolicyException;
+
+public class OrphanFileRemovalPolicyContent extends 
BaseMaintenancePolicyContent {
+  private static final String DEFAULT_POLICY_SCHEMA_VERSION = "2025-02-03";
+  private static final Set<String> POLICY_SCHEMA_VERSIONS = 
Set.of(DEFAULT_POLICY_SCHEMA_VERSION);
+
+  @JsonProperty(value = "max_orphan_file_age_in_days")
+  private int maxOrphanFileAgeInDays;
+
+  private List<String> locations;
+
+  @JsonCreator
+  public OrphanFileRemovalPolicyContent(
+      @JsonProperty(value = "enable", required = true) boolean enable) {
+    super(enable);
+  }
+
+  public int getMaxOrphanFileAgeInDays() {
+    return maxOrphanFileAgeInDays;
+  }
+
+  public void setMaxOrphanFileAgeInDays(int maxOrphanFileAgeInDays) {
+    this.maxOrphanFileAgeInDays = maxOrphanFileAgeInDays;
+  }
+
+  public List<String> getLocations() {
+    return locations;
+  }
+
+  public void setLocations(List<String> locations) {
+    this.locations = locations;
+  }
+
+  public static OrphanFileRemovalPolicyContent fromString(String content) {
+    if (Strings.isNullOrEmpty(content)) {
+      throw new InvalidPolicyException("Policy is empty");
+    }
+
+    OrphanFileRemovalPolicyContent policy;
+    try {
+      policy = PolicyContentUtil.MAPPER.readValue(content, 
OrphanFileRemovalPolicyContent.class);
+    } catch (Exception e) {
+      throw new InvalidPolicyException(e);
+    }
+
+    validateVersion(content, policy, DEFAULT_POLICY_SCHEMA_VERSION, 
POLICY_SCHEMA_VERSIONS);
+
+    int maxAge = policy.getMaxOrphanFileAgeInDays();
+    if (maxAge < 0) {
+      throw new InvalidPolicyException(
+          "Invalid max_orphan_file_age_in_days: "
+              + maxAge
+              + ". It must be greater than or equal to 0");
+    }
+
+    return policy;
+  }
+}
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/SnapshotRetentionPolicyContent.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/SnapshotRetentionPolicyContent.java
new file mode 100644
index 00000000..ecb3e3d6
--- /dev/null
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/content/maintenance/SnapshotRetentionPolicyContent.java
@@ -0,0 +1,54 @@
+/*
+ * 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.polaris.core.policy.content.maintenance;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Strings;
+import java.util.Set;
+import org.apache.polaris.core.policy.content.PolicyContentUtil;
+import org.apache.polaris.core.policy.validator.InvalidPolicyException;
+
+public class SnapshotRetentionPolicyContent extends 
BaseMaintenancePolicyContent {
+  private static final String DEFAULT_POLICY_SCHEMA_VERSION = "2025-02-03";
+  private static final Set<String> POLICY_SCHEMA_VERSIONS = 
Set.of(DEFAULT_POLICY_SCHEMA_VERSION);
+
+  @JsonCreator
+  public SnapshotRetentionPolicyContent(
+      @JsonProperty(value = "enable", required = true) boolean enable) {
+    super(enable);
+  }
+
+  public static SnapshotRetentionPolicyContent fromString(String content) {
+    if (Strings.isNullOrEmpty(content)) {
+      throw new InvalidPolicyException("Policy is empty");
+    }
+
+    SnapshotRetentionPolicyContent policy;
+    try {
+      policy = PolicyContentUtil.MAPPER.readValue(content, 
SnapshotRetentionPolicyContent.class);
+    } catch (Exception e) {
+      throw new InvalidPolicyException(e);
+    }
+
+    validateVersion(content, policy, DEFAULT_POLICY_SCHEMA_VERSION, 
POLICY_SCHEMA_VERSIONS);
+
+    return policy;
+  }
+}
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/PolicyValidators.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/PolicyValidators.java
index ed37f127..3ccb2f6d 100644
--- 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/PolicyValidators.java
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/PolicyValidators.java
@@ -22,7 +22,11 @@ import com.google.common.base.Preconditions;
 import org.apache.polaris.core.entity.PolarisEntity;
 import org.apache.polaris.core.policy.PolicyEntity;
 import org.apache.polaris.core.policy.PredefinedPolicyTypes;
-import 
org.apache.polaris.core.policy.validator.datacompaction.DataCompactionPolicyValidator;
+import 
org.apache.polaris.core.policy.content.maintenance.DataCompactionPolicyContent;
+import 
org.apache.polaris.core.policy.content.maintenance.MetadataCompactionPolicyContent;
+import 
org.apache.polaris.core.policy.content.maintenance.OrphanFileRemovalPolicyContent;
+import 
org.apache.polaris.core.policy.content.maintenance.SnapshotRetentionPolicyContent;
+import 
org.apache.polaris.core.policy.validator.maintenance.BaseMaintenancePolicyValidator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -50,15 +54,19 @@ public class PolicyValidators {
 
     switch (type) {
       case DATA_COMPACTION:
-        DataCompactionPolicyValidator.INSTANCE.validate(policy.getContent());
+        DataCompactionPolicyContent.fromString(policy.getContent());
         break;
-
-      // To support additional policy types in the future, add cases here.
       case METADATA_COMPACTION:
+        MetadataCompactionPolicyContent.fromString(policy.getContent());
+        break;
       case SNAPSHOT_RETENTION:
+        SnapshotRetentionPolicyContent.fromString(policy.getContent());
+        break;
       case ORPHAN_FILE_REMOVAL:
+        OrphanFileRemovalPolicyContent.fromString(policy.getContent());
+        break;
       default:
-        throw new InvalidPolicyException("Unsupported policy type: " + 
type.getName());
+        throw new IllegalArgumentException("Unsupported policy type: " + 
type.getName());
     }
 
     LOGGER.info("Policy validated successfully: {}", type.getName());
@@ -79,14 +87,16 @@ public class PolicyValidators {
     Preconditions.checkArgument(
         policyType != null, "Unknown policy type: " + 
policy.getPolicyTypeCode());
 
+    var entityType = targetEntity.getType();
+    var entitySubType = targetEntity.getSubType();
+
     switch (policyType) {
       case DATA_COMPACTION:
-        return DataCompactionPolicyValidator.INSTANCE.canAttach(
-            targetEntity.getType(), targetEntity.getSubType());
-      // To support additional policy types in the future, add cases here.
       case METADATA_COMPACTION:
       case SNAPSHOT_RETENTION:
       case ORPHAN_FILE_REMOVAL:
+        return BaseMaintenancePolicyValidator.INSTANCE.canAttach(entityType, 
entitySubType);
+
       default:
         LOGGER.warn("Attachment not supported for policy type: {}", 
policyType.getName());
         return false;
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/datacompaction/DataCompactionPolicyValidator.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/maintenance/BaseMaintenancePolicyValidator.java
similarity index 86%
rename from 
polaris-core/src/main/java/org/apache/polaris/core/policy/validator/datacompaction/DataCompactionPolicyValidator.java
rename to 
polaris-core/src/main/java/org/apache/polaris/core/policy/validator/maintenance/BaseMaintenancePolicyValidator.java
index 344c72d9..86308f55 100644
--- 
a/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/datacompaction/DataCompactionPolicyValidator.java
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/policy/validator/maintenance/BaseMaintenancePolicyValidator.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.polaris.core.policy.validator.datacompaction;
+package org.apache.polaris.core.policy.validator.maintenance;
 
 import static org.apache.polaris.core.entity.PolarisEntityType.CATALOG;
 import static org.apache.polaris.core.entity.PolarisEntityType.NAMESPACE;
@@ -28,16 +28,15 @@ import org.apache.polaris.core.entity.PolarisEntityType;
 import org.apache.polaris.core.policy.validator.InvalidPolicyException;
 import org.apache.polaris.core.policy.validator.PolicyValidator;
 
-public class DataCompactionPolicyValidator implements PolicyValidator {
-  public static final DataCompactionPolicyValidator INSTANCE = new 
DataCompactionPolicyValidator();
+public class BaseMaintenancePolicyValidator implements PolicyValidator {
+  public static final BaseMaintenancePolicyValidator INSTANCE =
+      new BaseMaintenancePolicyValidator();
 
   private static final Set<PolarisEntityType> ATTACHABLE_ENTITY_TYPES =
       Set.of(CATALOG, NAMESPACE, TABLE_LIKE);
 
   @Override
-  public void validate(String content) throws InvalidPolicyException {
-    DataCompactionPolicyContent.fromString(content);
-  }
+  public void validate(String content) throws InvalidPolicyException {}
 
   @Override
   public boolean canAttach(PolarisEntityType entityType, PolarisEntitySubType 
entitySubType) {
diff --git 
a/polaris-core/src/main/resources/schemas/policies/system/orphan-file-removal/2025-02-03.json
 
b/polaris-core/src/main/resources/schemas/policies/system/orphan-file-removal/2025-02-03.json
index 19f35dec..f7b00d1e 100644
--- 
a/polaris-core/src/main/resources/schemas/policies/system/orphan-file-removal/2025-02-03.json
+++ 
b/polaris-core/src/main/resources/schemas/policies/system/orphan-file-removal/2025-02-03.json
@@ -40,7 +40,7 @@
       "version": "2025-02-03",
       "enable": true,
       "max_orphan_file_age_in_days": 30,
-      "location": "s3://my-bucket/my-table-location",
+      "locations": ["s3://my-bucket/my-table-location"],
       "config": {
         "prefix_mismatch_mode": "ignore",
         "key1": "value1"
diff --git 
a/polaris-core/src/test/java/org/apache/polaris/core/policy/MaintenancePolicyContentTest.java
 
b/polaris-core/src/test/java/org/apache/polaris/core/policy/MaintenancePolicyContentTest.java
new file mode 100644
index 00000000..1b5972a0
--- /dev/null
+++ 
b/polaris-core/src/test/java/org/apache/polaris/core/policy/MaintenancePolicyContentTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.polaris.core.policy;
+
+import static 
org.apache.polaris.core.policy.content.maintenance.OrphanFileRemovalPolicyContent.fromString;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.util.function.Function;
+import java.util.stream.Stream;
+import 
org.apache.polaris.core.policy.content.maintenance.BaseMaintenancePolicyContent;
+import 
org.apache.polaris.core.policy.content.maintenance.DataCompactionPolicyContent;
+import 
org.apache.polaris.core.policy.content.maintenance.MetadataCompactionPolicyContent;
+import 
org.apache.polaris.core.policy.content.maintenance.OrphanFileRemovalPolicyContent;
+import 
org.apache.polaris.core.policy.content.maintenance.SnapshotRetentionPolicyContent;
+import org.apache.polaris.core.policy.validator.InvalidPolicyException;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class MaintenancePolicyContentTest {
+  static Stream<Arguments> policyTypes() {
+    return Stream.of(
+        Arguments.of(PredefinedPolicyTypes.DATA_COMPACTION),
+        Arguments.of(PredefinedPolicyTypes.METADATA_COMPACTION),
+        Arguments.of(PredefinedPolicyTypes.ORPHAN_FILE_REMOVAL),
+        Arguments.of(PredefinedPolicyTypes.METADATA_COMPACTION));
+  }
+
+  Function<String, BaseMaintenancePolicyContent> 
getParser(PredefinedPolicyTypes policyType) {
+    switch (policyType) {
+      case DATA_COMPACTION:
+        return DataCompactionPolicyContent::fromString;
+      case METADATA_COMPACTION:
+        return MetadataCompactionPolicyContent::fromString;
+      case ORPHAN_FILE_REMOVAL:
+        return OrphanFileRemovalPolicyContent::fromString;
+      case SNAPSHOT_RETENTION:
+        return SnapshotRetentionPolicyContent::fromString;
+      default:
+        throw new IllegalArgumentException("Unknown policy type: " + 
policyType);
+    }
+  }
+
+  @ParameterizedTest
+  @MethodSource("policyTypes")
+  public void testValidPolicyContent(PredefinedPolicyTypes policyType) {
+    var parser = getParser(policyType);
+
+    assertThat(parser.apply("{\"enable\": false}").enabled()).isFalse();
+    assertThat(parser.apply("{\"enable\": true}").enabled()).isTrue();
+
+    var validJson = "{\"version\":\"2025-02-03\", \"enable\": true}";
+    assertThat(parser.apply(validJson).getVersion()).isEqualTo("2025-02-03");
+
+    validJson = "{\"enable\": true, \"config\": {\"key1\": \"value1\", 
\"key2\": true}}";
+    
assertThat(parser.apply(validJson).getConfig().get("key1")).isEqualTo("value1");
+  }
+
+  @ParameterizedTest
+  @MethodSource("policyTypes")
+  void testIsValidEmptyString(PredefinedPolicyTypes policyTypes) {
+    var parser = getParser(policyTypes);
+    assertThatThrownBy(() -> parser.apply(""))
+        .as("Validating empty string should throw InvalidPolicyException")
+        .isInstanceOf(InvalidPolicyException.class)
+        .hasMessageContaining("Policy is empty");
+  }
+
+  @ParameterizedTest
+  @MethodSource("policyTypes")
+  void testIsValidJSONLiteralNull(PredefinedPolicyTypes policyTypes) {
+    var parser = getParser(policyTypes);
+    assertThatThrownBy(() -> parser.apply("null"))
+        .isInstanceOf(InvalidPolicyException.class)
+        .hasMessageContaining("Invalid policy: null");
+  }
+
+  @ParameterizedTest
+  @MethodSource("policyTypes")
+  void testIsValidEmptyJson(PredefinedPolicyTypes policyTypes) {
+    var parser = getParser(policyTypes);
+    assertThatThrownBy(() -> parser.apply("{}"))
+        .as("Validating empty JSON '{}' should throw InvalidPolicyException")
+        .isInstanceOf(InvalidPolicyException.class)
+        .hasMessageContaining("Invalid policy");
+  }
+
+  @ParameterizedTest
+  @MethodSource("policyTypes")
+  void testIsValidInvalidVersionFormat(PredefinedPolicyTypes policyTypes) {
+    var parser = getParser(policyTypes);
+    String invalidPolicy = "{\"enable\": true, \"version\": \"fdafds\"}";
+    assertThatThrownBy(() -> parser.apply(invalidPolicy))
+        .as("Validating policy with invalid version format should throw 
InvalidPolicyException")
+        .isInstanceOf(InvalidPolicyException.class);
+  }
+
+  @ParameterizedTest
+  @MethodSource("policyTypes")
+  void testIsValidInvalidKeyInPolicy(PredefinedPolicyTypes policyTypes) {
+    var parser = getParser(policyTypes);
+    String invalidPolicy = "{\"version\":\"2025-02-03\", \"enable\": true, 
\"invalid_key\": 12342}";
+    assertThatThrownBy(() -> parser.apply(invalidPolicy))
+        .as("Validating policy with an unknown key should throw 
InvalidPolicyException")
+        .isInstanceOf(InvalidPolicyException.class)
+        .hasMessageContaining("Invalid policy");
+  }
+
+  @ParameterizedTest
+  @MethodSource("policyTypes")
+  void testIsValidUnrecognizedToken(PredefinedPolicyTypes policyTypes) {
+    var parser = getParser(policyTypes);
+    var invalidPolicy = "{\"enable\": invalidToken}";
+    assertThatThrownBy(() -> parser.apply(invalidPolicy))
+        .isInstanceOf(InvalidPolicyException.class)
+        .hasMessageContaining("Invalid policy");
+  }
+
+  @ParameterizedTest
+  @MethodSource("policyTypes")
+  void testIsValidNullValue(PredefinedPolicyTypes policyTypes) {
+    var parser = getParser(policyTypes);
+    var invalidPolicy = "{\"enable\": null}";
+    assertThatThrownBy(() -> parser.apply(invalidPolicy))
+        .isInstanceOf(InvalidPolicyException.class)
+        .hasMessageContaining("Invalid policy");
+  }
+
+  @ParameterizedTest
+  @MethodSource("policyTypes")
+  void testIsValidWrongString(PredefinedPolicyTypes policyTypes) {
+    var parser = getParser(policyTypes);
+    var invalidPolicy = "{\"enable\": \"invalid\"}";
+    assertThatThrownBy(() -> parser.apply(invalidPolicy))
+        .isInstanceOf(InvalidPolicyException.class)
+        .hasMessageContaining("Invalid policy");
+  }
+
+  @Test
+  public void testValidOrphanFileRemovalPolicyContent() {
+    assertThat(
+            fromString("{\"enable\": true, \"max_orphan_file_age_in_days\": 
3}")
+                .getMaxOrphanFileAgeInDays())
+        .isEqualTo(3);
+    assertThat(
+            fromString(
+                    "{\"enable\": true, \"max_orphan_file_age_in_days\": 3, 
\"locations\": ["
+                        + "    \"s3://my-bucket/ns/my_table/\","
+                        + "    \"s3://my-bucket/ns/my_table/my-data/\","
+                        + "    \"s3://my-bucket/ns/my_table/my-metadata\""
+                        + "  ]}")
+                .getLocations()
+                .get(0))
+        .isEqualTo("s3://my-bucket/ns/my_table/");
+  }
+
+  @Test
+  public void testInvalidOrphanFileRemovalPolicyContent() {
+    assertThatThrownBy(() -> fromString("{\"enable\": true, 
\"max_orphan_file_age_in_days\": -3}"))
+        .isInstanceOf(InvalidPolicyException.class)
+        .hasMessageContaining("Invalid max_orphan_file_age_in_days");
+  }
+}
diff --git 
a/polaris-core/src/test/java/org/apache/polaris/core/policy/PolicyValidatorsTest.java
 
b/polaris-core/src/test/java/org/apache/polaris/core/policy/PolicyValidatorsTest.java
new file mode 100644
index 00000000..04435ba7
--- /dev/null
+++ 
b/polaris-core/src/test/java/org/apache/polaris/core/policy/PolicyValidatorsTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.polaris.core.policy;
+
+import static 
org.apache.polaris.core.entity.PolarisEntitySubType.ICEBERG_TABLE;
+import static org.apache.polaris.core.entity.PolarisEntitySubType.ICEBERG_VIEW;
+import static 
org.apache.polaris.core.policy.PredefinedPolicyTypes.DATA_COMPACTION;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.iceberg.catalog.Namespace;
+import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.polaris.core.entity.CatalogEntity;
+import org.apache.polaris.core.entity.NamespaceEntity;
+import org.apache.polaris.core.entity.PrincipalEntity;
+import org.apache.polaris.core.entity.table.IcebergTableLikeEntity;
+import org.apache.polaris.core.policy.validator.InvalidPolicyException;
+import org.apache.polaris.core.policy.validator.PolicyValidators;
+import org.junit.jupiter.api.Test;
+
+public class PolicyValidatorsTest {
+  Namespace ns = Namespace.of("NS1");
+  TableIdentifier tableIdentifier = TableIdentifier.of(ns, "table1");
+  PolicyEntity policyEntity = new PolicyEntity.Builder(ns, "pn", 
DATA_COMPACTION).build();
+
+  @Test
+  public void testInvalidPolicy() {
+    var policyEntity =
+        new PolicyEntity.Builder(ns, "testPolicy", DATA_COMPACTION)
+            .setContent("InvalidContent")
+            .setPolicyVersion(0)
+            .build();
+    assertThatThrownBy(() -> PolicyValidators.validate(policyEntity))
+        .as("Validating empty JSON '{}' should throw InvalidPolicyException")
+        .isInstanceOf(InvalidPolicyException.class)
+        .hasMessageContaining("Invalid policy");
+  }
+
+  @Test
+  public void testUnsupportedPolicyType() {
+    var newPolicyType =
+        new PolicyType() {
+          @Override
+          public int getCode() {
+            return Integer.MAX_VALUE;
+          }
+
+          @Override
+          public String getName() {
+            return "";
+          }
+
+          @Override
+          public boolean isInheritable() {
+            return false;
+          }
+        };
+
+    var policyEntity = new PolicyEntity.Builder(ns, "testPolicy", 
newPolicyType).build();
+
+    assertThatThrownBy(() -> PolicyValidators.validate(policyEntity))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessageContaining("Unknown policy type:");
+  }
+
+  @Test
+  public void testValidPolicy() {
+    var policyEntity =
+        new PolicyEntity.Builder(ns, "testPolicy", DATA_COMPACTION)
+            .setContent("{\"enable\": false}")
+            .setPolicyVersion(0)
+            .build();
+    PolicyValidators.validate(policyEntity);
+  }
+
+  @Test
+  public void testCanAttachReturnsTrueForCatalogType() {
+    var targetEntity = new CatalogEntity.Builder().build();
+    var result = PolicyValidators.canAttach(policyEntity, targetEntity);
+    assertThat(result).isTrue().as("Expected canAttach() to return true for 
CATALOG type");
+  }
+
+  @Test
+  public void testCanAttachReturnsTrueForNamespaceType() {
+    var targetEntity = new NamespaceEntity.Builder(ns).build();
+    var result = PolicyValidators.canAttach(policyEntity, targetEntity);
+    assertThat(result).isTrue().as("Expected canAttach() to return true for 
CATALOG type");
+  }
+
+  @Test
+  public void testCanAttachReturnsTrueForIcebergTableLikeWithTableSubtype() {
+    var targetEntity =
+        new IcebergTableLikeEntity.Builder(tableIdentifier, 
"").setSubType(ICEBERG_TABLE).build();
+    var result = PolicyValidators.canAttach(policyEntity, targetEntity);
+    assertThat(result)
+        .isTrue()
+        .as("Expected canAttach() to return true for ICEBERG_TABLE_LIKE with 
TABLE subtype");
+  }
+
+  @Test
+  public void 
testCanAttachReturnsFalseForIcebergTableLikeWithNonTableSubtype() {
+    var targetEntity =
+        new IcebergTableLikeEntity.Builder(tableIdentifier, 
"").setSubType(ICEBERG_VIEW).build();
+    var result = PolicyValidators.canAttach(policyEntity, targetEntity);
+    assertThat(result)
+        .isFalse()
+        .as("Expected canAttach() to return false for ICEBERG_TABLE_LIKE with 
non-TABLE subtype");
+  }
+
+  @Test
+  public void testCanAttachReturnsFalseForUnattachableType() {
+    var targetEntity = new PrincipalEntity.Builder().build();
+    var result = PolicyValidators.canAttach(policyEntity, targetEntity);
+    assertThat(result).isFalse().as("Expected canAttach() to return false for 
null");
+  }
+}
diff --git 
a/polaris-core/src/test/java/org/apache/polaris/core/policy/validator/DataCompactionPolicyContentTest.java
 
b/polaris-core/src/test/java/org/apache/polaris/core/policy/validator/DataCompactionPolicyContentTest.java
deleted file mode 100644
index 3d70fbee..00000000
--- 
a/polaris-core/src/test/java/org/apache/polaris/core/policy/validator/DataCompactionPolicyContentTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.polaris.core.policy.validator;
-
-import static 
org.apache.polaris.core.policy.validator.datacompaction.DataCompactionPolicyContent.fromString;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.junit.jupiter.api.Test;
-
-public class DataCompactionPolicyContentTest {
-  @Test
-  public void testValidPolicies() {
-    assertThat(fromString("{\"enable\": false}").enabled()).isFalse();
-    assertThat(fromString("{\"enable\": true}").enabled()).isTrue();
-
-    var validJson = "{\"version\":\"2025-02-03\", \"enable\": true}";
-    assertThat(fromString(validJson).getVersion()).isEqualTo("2025-02-03");
-
-    validJson = "{\"enable\": true, \"config\": {\"key1\": \"value1\", 
\"key2\": true}}";
-    
assertThat(fromString(validJson).getConfig().get("key1")).isEqualTo("value1");
-  }
-
-  @Test
-  void testIsValidEmptyString() {
-    assertThatThrownBy(() -> fromString(""))
-        .as("Validating empty string should throw InvalidPolicyException")
-        .isInstanceOf(InvalidPolicyException.class)
-        .hasMessageContaining("Policy is empty");
-  }
-
-  @Test
-  void testIsValidEmptyJson() {
-    assertThatThrownBy(() -> fromString("{}"))
-        .as("Validating empty JSON '{}' should throw InvalidPolicyException")
-        .isInstanceOf(InvalidPolicyException.class)
-        .hasMessageContaining("Invalid policy");
-  }
-
-  @Test
-  void testIsValidInvalidVersionFormat() {
-    String invalidPolicy1 = "{\"enable\": true, \"version\": \"fdafds\"}";
-    assertThatThrownBy(() -> fromString(invalidPolicy1))
-        .as("Validating policy with invalid version format should throw 
InvalidPolicyException")
-        .isInstanceOf(InvalidPolicyException.class);
-  }
-
-  @Test
-  void testIsValidInvalidKeyInPolicy() {
-    String invalidPolicy2 =
-        "{\"version\":\"2025-02-03\", \"enable\": true, \"invalid_key\": 
12342}";
-    assertThatThrownBy(() -> fromString(invalidPolicy2))
-        .as("Validating policy with an unknown key should throw 
InvalidPolicyException")
-        .isInstanceOf(InvalidPolicyException.class)
-        .hasMessageContaining("Invalid policy");
-  }
-
-  @Test
-  void testIsValidUnrecognizedToken() {
-    var invalidPolicy = "{\"enable\": invalidToken}";
-    assertThatThrownBy(() -> fromString(invalidPolicy))
-        .isInstanceOf(InvalidPolicyException.class)
-        .hasMessageContaining("Invalid policy");
-  }
-
-  @Test
-  void testIsValidNullValue() {
-    var invalidPolicy = "{\"enable\": null}";
-    assertThatThrownBy(() -> fromString(invalidPolicy))
-        .isInstanceOf(InvalidPolicyException.class)
-        .hasMessageContaining("Invalid policy");
-  }
-
-  @Test
-  void testIsValidWrongString() {
-    var invalidPolicy = "{\"enable\": \"invalid\"}";
-    assertThatThrownBy(() -> fromString(invalidPolicy))
-        .isInstanceOf(InvalidPolicyException.class)
-        .hasMessageContaining("Invalid policy");
-  }
-}
diff --git 
a/polaris-core/src/test/java/org/apache/polaris/core/policy/validator/DataCompactionPolicyValidatorTest.java
 
b/polaris-core/src/test/java/org/apache/polaris/core/policy/validator/DataCompactionPolicyValidatorTest.java
deleted file mode 100644
index 18205763..00000000
--- 
a/polaris-core/src/test/java/org/apache/polaris/core/policy/validator/DataCompactionPolicyValidatorTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.polaris.core.policy.validator;
-
-import static org.apache.polaris.core.entity.PolarisEntitySubType.ANY_SUBTYPE;
-import static 
org.apache.polaris.core.entity.PolarisEntitySubType.ICEBERG_TABLE;
-import static org.apache.polaris.core.entity.PolarisEntitySubType.ICEBERG_VIEW;
-import static org.apache.polaris.core.entity.PolarisEntityType.CATALOG;
-import static org.apache.polaris.core.entity.PolarisEntityType.NAMESPACE;
-import static org.apache.polaris.core.entity.PolarisEntityType.PRINCIPAL;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.apache.polaris.core.entity.PolarisEntityType;
-import 
org.apache.polaris.core.policy.validator.datacompaction.DataCompactionPolicyValidator;
-import org.junit.jupiter.api.Test;
-
-public class DataCompactionPolicyValidatorTest {
-  private final DataCompactionPolicyValidator validator = new 
DataCompactionPolicyValidator();
-
-  @Test
-  public void testValidPolicies() {
-    var validJson = "{\"version\":\"2025-02-03\", \"enable\": true}";
-    validator.validate(validJson);
-
-    assertThatThrownBy(() -> validator.validate(""))
-        .as("Validating empty string should throw InvalidPolicyException")
-        .isInstanceOf(InvalidPolicyException.class)
-        .hasMessageContaining("Policy is empty");
-  }
-
-  @Test
-  public void testCanAttachReturnsTrueForCatalogType() {
-    var result = validator.canAttach(CATALOG, ANY_SUBTYPE); // using any valid 
subtype
-    assertThat(result).isTrue().as("Expected canAttach() to return true for 
CATALOG type");
-  }
-
-  @Test
-  public void testCanAttachReturnsTrueForNamespaceType() {
-    var result = validator.canAttach(NAMESPACE, ANY_SUBTYPE); // using any 
valid subtype
-    assertThat(result).isTrue().as("Expected canAttach() to return true for 
CATALOG type");
-  }
-
-  @Test
-  public void testCanAttachReturnsTrueForIcebergTableLikeWithTableSubtype() {
-    var result = validator.canAttach(PolarisEntityType.TABLE_LIKE, 
ICEBERG_TABLE);
-    assertThat(result)
-        .isTrue()
-        .as("Expected canAttach() to return true for ICEBERG_TABLE_LIKE with 
TABLE subtype");
-  }
-
-  @Test
-  public void 
testCanAttachReturnsFalseForIcebergTableLikeWithNonTableSubtype() {
-    // For ICEBERG_TABLE_LIKE, any subtype other than TABLE should return 
false.
-    boolean result = validator.canAttach(PolarisEntityType.TABLE_LIKE, 
ICEBERG_VIEW);
-    assertThat(result)
-        .isFalse()
-        .as("Expected canAttach() to return false for ICEBERG_TABLE_LIKE with 
non-TABLE subtype");
-  }
-
-  @Test
-  public void testCanAttachReturnsFalseForNull() {
-    var result = validator.canAttach(null, null); // using any valid subtype
-    assertThat(result).isFalse().as("Expected canAttach() to return false for 
null");
-  }
-
-  @Test
-  public void testCanAttachReturnsFalseForUnattachableType() {
-    var result = validator.canAttach(PRINCIPAL, null); // using any valid 
subtype
-    assertThat(result).isFalse().as("Expected canAttach() to return false for 
null");
-  }
-}
diff --git 
a/polaris-core/src/test/java/org/apache/polaris/core/policy/validator/PolicyValidatorsTest.java
 
b/polaris-core/src/test/java/org/apache/polaris/core/policy/validator/PolicyValidatorsTest.java
deleted file mode 100644
index 24d122a1..00000000
--- 
a/polaris-core/src/test/java/org/apache/polaris/core/policy/validator/PolicyValidatorsTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.polaris.core.policy.validator;
-
-import static 
org.apache.polaris.core.policy.PredefinedPolicyTypes.DATA_COMPACTION;
-import static 
org.apache.polaris.core.policy.PredefinedPolicyTypes.METADATA_COMPACTION;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import org.apache.iceberg.catalog.Namespace;
-import org.apache.polaris.core.policy.PolicyEntity;
-import org.junit.jupiter.api.Test;
-
-public class PolicyValidatorsTest {
-  @Test
-  public void testInvalidPolicy() {
-    var policyEntity =
-        new PolicyEntity.Builder(Namespace.of("NS1"), "testPolicy", 
DATA_COMPACTION)
-            .setContent("InvalidContent")
-            .setPolicyVersion(0)
-            .build();
-    assertThatThrownBy(() -> PolicyValidators.validate(policyEntity))
-        .as("Validating empty JSON '{}' should throw InvalidPolicyException")
-        .isInstanceOf(InvalidPolicyException.class)
-        .hasMessageContaining("Invalid policy");
-  }
-
-  @Test
-  public void testUnsupportedPolicyType() {
-    var policyEntity =
-        new PolicyEntity.Builder(Namespace.of("NS1"), "testPolicy", 
METADATA_COMPACTION)
-            .setContent("InvalidContent")
-            .setPolicyVersion(0)
-            .build();
-
-    assertThatThrownBy(() -> PolicyValidators.validate(policyEntity))
-        .isInstanceOf(InvalidPolicyException.class)
-        .hasMessageContaining("Unsupported policy type");
-  }
-
-  @Test
-  public void testValidPolicy() {
-    var policyEntity =
-        new PolicyEntity.Builder(Namespace.of("NS1"), "testPolicy", 
DATA_COMPACTION)
-            .setContent("{\"enable\": false}")
-            .setPolicyVersion(0)
-            .build();
-    PolicyValidators.validate(policyEntity);
-  }
-}

Reply via email to