Add support for common rules across types 95/78495/1
authormark.j.leonard <mark.j.leonard@gmail.com>
Thu, 14 Feb 2019 13:51:11 +0000 (13:51 +0000)
committermark.j.leonard <mark.j.leonard@gmail.com>
Thu, 14 Feb 2019 13:51:11 +0000 (13:51 +0000)
Refactor the Rule Driven Validator implementation for loading the Groovy
rules. Firstly, support multiple top-level folders. Secondly, include
each top-level folder in the set of paths to search for *.groovy files.

Pull a common rule up to the top-level to remove the duplication.

Add tests to exercise the exception handling code.

Change-Id: Ib70fe03ab5b1f2f8c1711760236b07850aaaa398
Issue-ID: AAI-2155
Signed-off-by: mark.j.leonard <mark.j.leonard@gmail.com>
bundleconfig/etc/rules/aai-event/entity-generic-vnf.groovy
bundleconfig/etc/rules/common_rules.groovy [moved from bundleconfig/etc/rules/spike-event/common_rules.groovy with 57% similarity]
src/main/java/org/onap/aai/validation/ruledriven/RuleDrivenValidator.java
src/main/java/org/onap/aai/validation/ruledriven/rule/GroovyRule.java
src/main/java/org/onap/aai/validation/ruledriven/rule/Rule.java
src/test/java/org/onap/aai/validation/ruledriven/mock/TestDefaultRules.java
src/test/java/org/onap/aai/validation/ruledriven/rule/TestConfigurationLoader.java
src/test/java/org/onap/aai/validation/ruledriven/validator/TestDataDictionary.java
src/test/java/org/onap/aai/validation/ruledriven/validator/TestRuleDrivenValidator.java
src/test/resources/rule-driven-validator/missing_oxm/dummy_event_type/oxm_missing.groovy [moved from bundleconfig/etc/rules/gizmo-event/common_rules.groovy with 58% similarity]

index d813eea..71bbd6a 100644 (file)
@@ -1,7 +1,10 @@
 /*
- * ============LICENSE_START===================================================
- * Copyright (c) 2018 Amdocs
- * ============================================================================
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright (c) 2018-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (c) 2018-2019 European Software Marketing Ltd.
+ * ================================================================================
  * Licensed 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
@@ -13,7 +16,7 @@
  * 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.
- * ============LICENSE_END=====================================================
+ * ============LICENSE_END=========================================================
  */
 
 entity {
@@ -34,16 +37,6 @@ entity {
        }
 }
 
-rule {
-       name        'valid_ipv4_addr'
-       category    'INVALID_VALUE'
-       description 'Validate an IPv4 address'
-       errorText   'Invalid value - attribute is not a valid IPv4 address'
-       severity    'MINOR'
-       attributes  'ipaddr'
-       validate    'ipaddr != null && ipaddr.matches("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])")'
-}
-
 // If generic-vnf.equipment-role="UCPE" and there is an l-interface - then there must be an IPV4 address related to the l-interface
 rule {
        name        'ipv4_addr_present'
@@ -1,7 +1,10 @@
 /*
- * ============LICENSE_START===================================================
- * Copyright (c) 2018 Amdocs
- * ============================================================================
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright (c) 2018-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (c) 2018-2019 European Software Marketing Ltd.
+ * ================================================================================
  * Licensed 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
  * 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.
- * ============LICENSE_END=====================================================
+ * ============LICENSE_END=========================================================
  */
 
 rule {
-    name 'valid_ipv4_addr'
+    name        'valid_ipv4_addr'
     category    'INVALID_VALUE'
     description 'Validate an IPv4 address'
-    errorText 'Invalid value - attribute is not a valid IPv4 address'
-    severity 'MINOR'
-    attributes 'ipaddr'
-    validate 'ipaddr != null && ipaddr.matches("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])")'
-}
+    errorText   'Invalid value - attribute is not a valid IPv4 address'
+    severity    'MINOR'
+    attributes  'ipaddr'
+    validate    'ipaddr != null && ipaddr.matches("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])")'
+}
\ No newline at end of file
index 4b22d77..5ca7025 100644 (file)
@@ -27,7 +27,6 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -36,6 +35,8 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
 import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.commons.lang.StringUtils;
 import org.onap.aai.validation.Validator;
@@ -68,7 +69,12 @@ public class RuleDrivenValidator implements Validator {
 
     private static final String RULES_CONFIG_FILE_SUFFIX = ".groovy";
 
-    private Path configurationPath;
+    /**
+     * The set of directories/folders containing the rules configuration files. Rules that are common to all event types
+     * will be stored in a file directly under a specified Path. Rules which are specific to a named event type will be
+     * stored in a direct child directory.
+     */
+    private Collection<Path> configurationPaths;
     private OxmReader oxmReader;
     private EventReader eventReader;
     private Optional<RuleIndexingConfig> ruleIndexingConfig;
@@ -78,17 +84,17 @@ public class RuleDrivenValidator implements Validator {
     /**
      * Construct a Validator that is configured using rule files.
      *
-     * @param configurationPath
-     *            path to the Groovy rules files
+     * @param configurationPaths
+     *            paths to the Groovy rules files and sub-folders
      * @param oxmReader
      *            required for validating entity types
      * @param eventReader
      *            a reader for extracting entities from each event to be validated
      * @param ruleIndexingConfig
      */
-    public RuleDrivenValidator(final Path configurationPath, final OxmReader oxmReader, final EventReader eventReader,
-            final RuleIndexingConfig ruleIndexingConfig) {
-        this.configurationPath = configurationPath;
+    public RuleDrivenValidator(final List<Path> configurationPaths, final OxmReader oxmReader,
+            final EventReader eventReader, final RuleIndexingConfig ruleIndexingConfig) {
+        this.configurationPaths = configurationPaths;
         this.oxmReader = oxmReader;
         this.eventReader = eventReader;
         this.ruleIndexingConfig = Optional.ofNullable(ruleIndexingConfig);
@@ -104,38 +110,6 @@ public class RuleDrivenValidator implements Validator {
         validateRulesConfiguration();
     }
 
-    private RuleManager loadRulesConfiguration(String eventType) throws ValidationServiceException {
-        StringBuilder rulesText = new StringBuilder();
-        try (Stream<Path> paths = Files.find(configurationPath.resolve(eventType), 1,
-                (path, basicFileAttributes) -> path.toFile().getName().matches(".*\\" + RULES_CONFIG_FILE_SUFFIX));) {
-            paths.forEach(appendFileContent(rulesText));
-        } catch (IOException e) {
-            throw new ValidationServiceException(ValidationServiceError.RULES_FILE_ERROR,
-                    configurationPath.toAbsolutePath(), e);
-        }
-
-        try {
-            return RulesConfigurationLoader.loadConfiguration(rulesText.toString());
-        } catch (GroovyConfigurationException e) {
-            throw new ValidationServiceException(ValidationServiceError.RULES_FILE_ERROR, e,
-                    configurationPath.toAbsolutePath() + File.separator + "*" + RULES_CONFIG_FILE_SUFFIX);
-        }
-    }
-
-    private void validateRulesConfiguration() throws ValidationServiceException {
-        for (RuleManager ruleManager : ruleManagers.values()) {
-            for (EntitySection entity : ruleManager.getEntities()) {
-                if (ruleIndexingConfig.isPresent() && ruleIndexingConfig.get().skipOxmValidation(entity.getName())) {
-                    continue;
-                }
-                if (oxmReader != null && oxmReader.getPrimaryKeys(entity.getName()).isEmpty()) {
-                    throw new ValidationServiceException(ValidationServiceError.OXM_MISSING_KEY,
-                            entity.getName() + " defined in " + configurationPath.toAbsolutePath() + File.separator
-                                    + "*" + RULES_CONFIG_FILE_SUFFIX);
-                }
-            }
-        }
-    }
 
     /**
      * Helper method to expose the configured rules. This simplifies testing of the validator.
@@ -203,6 +177,81 @@ public class RuleDrivenValidator implements Validator {
         return validationResults;
     }
 
+    /**
+     * Find and concatenate the text content of all common rules files, and all the eventType-specific rules files.
+     * Invoke the Configuration Loader to parse the text content and create the Groovy Rules.
+     * 
+     * @param eventType
+     * @return
+     * @throws ValidationServiceException
+     */
+    private RuleManager loadRulesConfiguration(String eventType) throws ValidationServiceException {
+        StringBuilder rulesText = new StringBuilder();
+
+        Stream.concat(getGroovyRulePaths(Optional.of(eventType)), getGroovyRulePaths(Optional.empty()))
+                .forEach(appendFileContent(rulesText));
+
+        try {
+            return RulesConfigurationLoader.loadConfiguration(rulesText.toString());
+        } catch (GroovyConfigurationException e) {
+            throw new ValidationServiceException(ValidationServiceError.RULES_FILE_ERROR, e,
+                    getConfigurationPathWildcards());
+        }
+    }
+
+    /**
+     * For logging and error reporting purposes, create a set of Strings each formatted to document the top-level
+     * folders for rules configuration files, and also the file suffix used to find the files.
+     * 
+     * @return all the wildcard patterns used for matching rules files
+     */
+    private List<String> getConfigurationPathWildcards() {
+        return configurationPaths.stream()
+                .map(path -> path.toAbsolutePath() + File.separator + "*" + RULES_CONFIG_FILE_SUFFIX)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Find all files matching the rules configuration file suffix that are immediate children of a rules configuration
+     * Path. If a subPath is specified then only find files in the resolved subPath.
+     * 
+     * @param subPath
+     *            an optional subPath (e.g. for an event type)
+     * @return all rules configuration files as Paths
+     * @throws ValidationServiceException
+     *             if an I/O error occurs when accessing the file system
+     */
+    private Stream<Path> getGroovyRulePaths(Optional<String> subPath) throws ValidationServiceException {
+        List<Path> groovyRules = new ArrayList<>();
+        for (Path configPath : configurationPaths) {
+            Path rulesPath = subPath.map(configPath::resolve).orElse(configPath);
+            if (rulesPath.toFile().exists()) {
+                try (Stream<Path> stream = Files.find(rulesPath, 1, (path, basicFileAttributes) -> path.toFile()
+                        .getName().matches(".*\\" + RULES_CONFIG_FILE_SUFFIX));) {
+                    groovyRules.addAll(stream.collect(Collectors.toList()));
+                } catch (IOException e) {
+                    throw new ValidationServiceException(ValidationServiceError.RULES_FILE_ERROR,
+                            configPath.toAbsolutePath(), e);
+                }
+            }
+        }
+        return groovyRules.stream();
+    }
+
+    private void validateRulesConfiguration() throws ValidationServiceException {
+        for (RuleManager ruleManager : ruleManagers.values()) {
+            for (EntitySection entity : ruleManager.getEntities()) {
+                if (ruleIndexingConfig.isPresent() && ruleIndexingConfig.get().skipOxmValidation(entity.getName())) {
+                    continue;
+                }
+                if (oxmReader != null && oxmReader.getPrimaryKeys(entity.getName()).isEmpty()) {
+                    throw new ValidationServiceException(ValidationServiceError.OXM_MISSING_KEY,
+                            entity.getName() + " defined in " + getConfigurationPathWildcards());
+                }
+            }
+        }
+    }
+
     private Optional<List<Rule>> getRulesToApply(Entity entity, Optional<String> eventType)
             throws ValidationServiceException {
         Optional<List<Rule>> rules = Optional.empty();
@@ -292,7 +341,17 @@ public class RuleDrivenValidator implements Validator {
     }
 
     private Collection<String> getSupportedEventTypes() {
-        String[] list = configurationPath.toFile().list((current, name) -> new File(current, name).isDirectory());
-        return list == null ? Collections.emptyList() : Arrays.asList(list);
+        Function<? super Path, ? extends Stream<? extends Path>> getDirectories = path -> {
+            try {
+                return Files.walk(path, 1).filter(Files::isDirectory);
+            } catch (IOException e) {
+                applicationLogger.error(ApplicationMsgs.READ_FILE_ERROR, e, "from the rules configuration path");
+                return Stream.empty();
+            }
+        };
+        return configurationPaths.stream() //
+                .flatMap(getDirectories) //
+                .map(path -> path.getFileName().toString()) //
+                .collect(Collectors.toList());
     }
 }
index df15791..d8cd61e 100644 (file)
@@ -1,19 +1,22 @@
-/*
- * ============LICENSE_START===================================================
- * Copyright (c) 2018 Amdocs
- * ============================================================================
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright (c) 2018-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (c) 2018-2019 European Software Marketing Ltd.
+ * ================================================================================
  * Licensed 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
+ *       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.
- * ============LICENSE_END=====================================================
+ * ============LICENSE_END=========================================================
  */
 package org.onap.aai.validation.ruledriven.rule;
 
@@ -65,7 +68,6 @@ public class GroovyRule implements Rule {
     private boolean ruleIsValid = true;
     private String name;
 
-
     /**
      * @param ruleConfig
      * @throws InstantiationException
index 60705ea..e4c2078 100644 (file)
@@ -1,19 +1,22 @@
-/*
- * ============LICENSE_START===================================================
- * Copyright (c) 2018 Amdocs
- * ============================================================================
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright (c) 2018-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (c) 2018-2019 European Software Marketing Ltd.
+ * ================================================================================
  * Licensed 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
+ *       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.
- * ============LICENSE_END=====================================================
+ * ============LICENSE_END=========================================================
  */
 package org.onap.aai.validation.ruledriven.rule;
 
index c3b36ed..41721ba 100644 (file)
@@ -1,20 +1,24 @@
-/*
- * ============LICENSE_START===================================================
- * Copyright (c) 2018 Amdocs
- * ============================================================================
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright (c) 2018-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (c) 2018-2019 European Software Marketing Ltd.
+ * ================================================================================
  * Licensed 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
+ *       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.
- * ============LICENSE_END=====================================================
+ * ============LICENSE_END=========================================================
  */
+
 package org.onap.aai.validation.ruledriven.mock;
 
 import static org.hamcrest.CoreMatchers.equalTo;
@@ -61,8 +65,7 @@ public class TestDefaultRules {
     }
 
     enum TestCase {
-        NULL,
-        VSERVER;
+        NULL, VSERVER;
     }
 
     // Data returned by the mocked EventReader
@@ -125,8 +128,8 @@ public class TestDefaultRules {
 
     @Before
     public void createRuleDrivenValidator() throws ValidationServiceException {
-        Path configurationPath = Paths.get("bundleconfig/etc/rules");
-        ruleDrivenValidator = new RuleDrivenValidator(configurationPath, null, eventReader, null);
+        List<Path> configurationPaths = Collections.singletonList(Paths.get("bundleconfig/etc/rules"));
+        ruleDrivenValidator = new RuleDrivenValidator(configurationPaths, null, eventReader, null);
     }
 
     @Test
index a70d908..e074bcd 100644 (file)
@@ -1,20 +1,24 @@
-/*
- * ============LICENSE_START===================================================
- * Copyright (c) 2018 Amdocs
- * ============================================================================
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright (c) 2018-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (c) 2018-2019 European Software Marketing Ltd.
+ * ================================================================================
  * Licensed 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
+ *       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.
- * ============LICENSE_END=====================================================
+ * ============LICENSE_END=========================================================
  */
+
 package org.onap.aai.validation.ruledriven.rule;
 
 import com.google.gson.JsonArray;
@@ -22,7 +26,9 @@ import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Collections;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
 import javax.inject.Inject;
 import org.junit.Test;
@@ -36,25 +42,24 @@ public class TestConfigurationLoader {
         System.setProperty("APP_HOME", ".");
     }
 
-    private enum AAIRelation {
+    private static final String RULES_PATH = "bundleconfig/etc/rules";
+
+    private enum AaiRelation {
+        // @formatter:off
         RELATED_TO("related-to"),
         PROPERTY_KEY("property-key"),
         PROPERTY_VALUE("property-value");
+        // @formatter:on
 
         private final String text;
 
         /**
          * @param text
          */
-        private AAIRelation(final String text) {
+        private AaiRelation(final String text) {
             this.text = text;
         }
 
-        /*
-         * (non-Javadoc)
-         *
-         * @see java.lang.Enum#toString()
-         */
         @Override
         public String toString() {
             return text;
@@ -69,9 +74,8 @@ public class TestConfigurationLoader {
      */
     @Test
     public void testTrinityRule() throws Exception {
-        Path configurationPath = Paths.get("bundleconfig/etc/rules");
-
-        RuleDrivenValidator validator = new RuleDrivenValidator(configurationPath, oxmReader, null, null);
+        List<Path> configurationPaths = Collections.singletonList(Paths.get(RULES_PATH));
+        RuleDrivenValidator validator = new RuleDrivenValidator(configurationPaths, oxmReader, null, null);
         validator.initialise();
 
         // Find the trinity rule
@@ -91,18 +95,18 @@ public class TestConfigurationLoader {
         RuleTester ruleTester = new RuleTester(trinityRule, attributeValues);
         ruleTester.test(true);
 
-        JsonObject genericVnfData = createRelationshipData(relationships, "generic-vnf");
+        final JsonObject genericVnfData = createRelationshipData(relationships, "generic-vnf");
         ruleTester.test(true);
 
         // Add a new object for the image relationship
-        JsonObject imageData = createRelationshipData(relationships, "image");
+        final JsonObject imageData = createRelationshipData(relationships, "image");
         ruleTester.test(true);
 
         createRelationshipData(relationships, "pserver");
         ruleTester.test(true);
 
         // Add a new JSON object for the image name
-        JsonObject imageNameProperty = createRelatedToProperty(imageData);
+        final JsonObject imageNameProperty = createRelatedToProperty(imageData);
         ruleTester.test(true);
 
         setPropertyKey(imageNameProperty, "image.image-name");
@@ -174,16 +178,16 @@ public class TestConfigurationLoader {
      */
     private JsonObject createRelationshipData(Set<JsonObject> relationships, String relatedObject) {
         JsonObject relationData = new JsonObject();
-        relationData.addProperty(AAIRelation.RELATED_TO.toString(), relatedObject);
+        relationData.addProperty(AaiRelation.RELATED_TO.toString(), relatedObject);
         relationships.add(relationData);
         return relationData;
     }
 
     private void setPropertyKey(JsonObject objectMap, String propertyKeyName) {
-        objectMap.addProperty(AAIRelation.PROPERTY_KEY.toString(), propertyKeyName);
+        objectMap.addProperty(AaiRelation.PROPERTY_KEY.toString(), propertyKeyName);
     }
 
     private void setPropertyValue(JsonObject objectMap, String propertyValue) {
-        objectMap.addProperty(AAIRelation.PROPERTY_VALUE.toString(), propertyValue);
+        objectMap.addProperty(AaiRelation.PROPERTY_VALUE.toString(), propertyValue);
     }
 }
index c3bad43..7292dec 100644 (file)
@@ -40,6 +40,11 @@ import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
+/**
+ * Use Spring XML to create an instance of {@link RuleDrivenValidator} that is configured to use Data Dictionary based
+ * Validation Rules.
+ * 
+ */
 @RunWith(SpringJUnit4ClassRunner.class)
 @TestPropertySource(locations = {"classpath:oxm-reader/schemaIngest.properties"})
 @ContextConfiguration(locations = {"classpath:data-dictionary/test-data-dictionary-beans.xml"})
index 643e2e3..27039a0 100644 (file)
@@ -1,20 +1,24 @@
-/*
- * ============LICENSE_START===================================================
- * Copyright (c) 2018-2019 Amdocs
- * ============================================================================
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright (c) 2018-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (c) 2018-2019 European Software Marketing Ltd.
+ * ================================================================================
  * Licensed 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
+ *       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.
- * ============LICENSE_END=====================================================
+ * ============LICENSE_END=========================================================
  */
+
 package org.onap.aai.validation.ruledriven.validator;
 
 import static org.hamcrest.CoreMatchers.is;
@@ -24,11 +28,13 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import com.google.gson.JsonSyntaxException;
 import java.io.IOException;
 import java.net.URISyntaxException;
+import java.net.URL;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.function.BiPredicate;
 import java.util.stream.Stream;
@@ -36,6 +42,8 @@ import javax.inject.Inject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.onap.aai.validation.exception.ValidationServiceException;
+import org.onap.aai.validation.reader.EventReader;
+import org.onap.aai.validation.reader.OxmReader;
 import org.onap.aai.validation.result.ValidationResult;
 import org.onap.aai.validation.ruledriven.RuleDrivenValidator;
 import org.onap.aai.validation.test.util.TestEntity;
@@ -45,9 +53,9 @@ import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
 @RunWith(SpringJUnit4ClassRunner.class)
-@TestPropertySource(locations = { "classpath:oxm-reader/schemaIngest.properties" })
-@ContextConfiguration(locations = {
-        "classpath:" + TestRuleDrivenValidator.UNIT_TEST_FOLDER + "/test-rule-driven-validator-beans.xml" })
+@TestPropertySource(locations = {"classpath:oxm-reader/schemaIngest.properties"})
+@ContextConfiguration(
+        locations = {"classpath:" + TestRuleDrivenValidator.UNIT_TEST_FOLDER + "/test-rule-driven-validator-beans.xml"})
 public class TestRuleDrivenValidator {
 
     static {
@@ -60,16 +68,12 @@ public class TestRuleDrivenValidator {
     @Inject
     private RuleDrivenValidator validator;
 
-    /**
-     * @param testEntitiesPath
-     * @param testEventsPath
-     * @param resultsPath
-     * @return all test entities
-     * @throws URISyntaxException
-     */
+    @Inject
+    private OxmReader oxmReader;
+
     public static List<TestEntity> getEntities(String testEntitiesPath, String testEventsPath, String resultsPath)
             throws URISyntaxException {
-        Path testEvents = Paths.get(ClassLoader.getSystemResource(testEntitiesPath + testEventsPath).toURI());
+        Path testEvents = findResource(testEntitiesPath, testEventsPath);
 
         BiPredicate<Path, BasicFileAttributes> jsonMatcher =
                 (path, basicFileAttributes) -> path.toFile().getName().matches(".*\\.json");
@@ -84,26 +88,44 @@ public class TestRuleDrivenValidator {
         return entitiesList;
     }
 
-    /**
-     * @throws ValidationServiceException
-     * @throws JsonSyntaxException
-     * @throws URISyntaxException
-     * @throws IOException
-     */
+    @Test
+    public void testInvalidRulesPath() throws ValidationServiceException, URISyntaxException {
+        validator = buildValidator(null, "/non-existent-folder");
+        validator.initialise();
+    }
+
+    @Test
+    public void testNoRulesFilesExist() throws ValidationServiceException, URISyntaxException {
+        validator = buildValidator(null, "/test_events");
+        validator.initialise();
+    }
+
+    @Test(expected = ValidationServiceException.class)
+    public void testEntityMissingFromOxm() throws ValidationServiceException, URISyntaxException {
+        validator = buildValidator(oxmReader, "/missing_oxm");
+        validator.initialise();
+    }
+
     @Test
     public void testValidateUnitTestInstances()
             throws ValidationServiceException, JsonSyntaxException, URISyntaxException, IOException {
         validateEntities(UNIT_TEST_FOLDER, TEST_EVENTS_PATH, "/results/expected");
     }
 
-    /**
-     * @param inputEventsFolder
-     * @param testEventsPath
-     * @param resultsPath
-     * @throws URISyntaxException
-     * @throws ValidationServiceException
-     * @throws IOException
-     */
+    private static Path findResource(String path, String subPath) throws URISyntaxException {
+        URL resource = ClassLoader.getSystemResource(path + subPath);
+        if (resource == null) {
+            return Paths.get(path, subPath);
+        } else {
+            return Paths.get(resource.toURI());
+        }
+    }
+
+    private RuleDrivenValidator buildValidator(OxmReader oxmReader, String rulesFolder) throws URISyntaxException {
+        return new RuleDrivenValidator(Collections.singletonList(findResource(UNIT_TEST_FOLDER, rulesFolder)),
+                oxmReader, new EventReader(null, null, null), null);
+    }
+
     private void validateEntities(String inputEventsFolder, String testEventsPath, String resultsPath)
             throws URISyntaxException, ValidationServiceException, IOException {
         for (TestEntity entity : getEntities(inputEventsFolder, testEventsPath, resultsPath)) {
@@ -111,7 +133,7 @@ public class TestRuleDrivenValidator {
             assertThat(results.size(), is(1));
             ValidationResult expectedResult = entity.getExpectedValidationResult();
             if (expectedResult == null) {
-                Path testEvents = Paths.get(ClassLoader.getSystemResource(inputEventsFolder + resultsPath).toURI());
+                Path testEvents = findResource(inputEventsFolder, resultsPath);
                 StringBuilder sb = new StringBuilder();
                 Files.walk(testEvents).forEach((path) -> sb.append(path).append("\n"));
                 assertThat("Expected results missing (" + entity.expectedResultsFile + ")\n" + sb.toString(),
@@ -1,7 +1,10 @@
 /*
- * ============LICENSE_START===================================================
- * Copyright (c) 2018 Amdocs
- * ============================================================================
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright (c) 2018-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (c) 2018-2019 European Software Marketing Ltd.
+ * ================================================================================
  * Licensed 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
  * 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.
- * ============LICENSE_END=====================================================
+ * ============LICENSE_END=========================================================
  */
 
+entity {
+    type 'unknown_type'
+    validation {
+        useRule {
+            name 'dummy'
+            attributes 'dummy'
+        }
+    }
+}
+
 rule {
-    name 'valid_ipv4_addr'
+    name        'dummy'
     category    'INVALID_VALUE'
-    description 'Validate an IPv4 address'
-    errorText 'Invalid value - attribute is not a valid IPv4 address'
-    severity 'MINOR'
-    attributes 'ipaddr'
-    validate 'ipaddr != null && ipaddr.matches("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])")'
+    description 'for test purposes'
+    errorText   ''
+    severity    'MINOR'
+    validate    'return false'
 }