/* * 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.sling.validation.impl; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import java.util.function.Predicate; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.jcr.RepositoryException; import org.apache.jackrabbit.JcrConstants; import org.apache.sling.api.resource.LoginException; import org.apache.sling.api.resource.ModifiableValueMap; import org.apache.sling.api.resource.NonExistingResource; import org.apache.sling.api.resource.PersistenceException; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceUtil; import org.apache.sling.api.resource.SyntheticResource; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.api.wrappers.ValueMapDecorator; import org.apache.sling.i18n.ResourceBundleProvider; import org.apache.sling.jcr.resource.JcrResourceConstants; import org.apache.sling.testing.mock.sling.junit.SlingContext; import org.apache.sling.validation.SlingValidationException; import org.apache.sling.validation.ValidationFailure; import org.apache.sling.validation.ValidationResult; import org.apache.sling.validation.impl.model.ChildResourceImpl; import org.apache.sling.validation.impl.model.ResourcePropertyBuilder; import org.apache.sling.validation.impl.model.ValidationModelBuilder; import org.apache.sling.validation.impl.util.examplevalidators.DateValidator; import org.apache.sling.validation.impl.validators.RegexValidator; import org.apache.sling.validation.model.ChildResource; import org.apache.sling.validation.model.ResourceProperty; import org.apache.sling.validation.model.ValidationModel; import org.apache.sling.validation.model.spi.ValidationModelRetriever; import org.apache.sling.validation.spi.ValidatorContext; import org.apache.sling.validation.spi.Validator; import org.apache.sling.validation.spi.support.DefaultValidationFailure; import org.apache.sling.validation.spi.support.DefaultValidationResult; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; import org.osgi.framework.Bundle; import org.osgi.framework.ServiceReference; @RunWith(MockitoJUnitRunner.class) public class ValidationServiceImplTest { /** * Assume the validation models are stored under (/libs|/apps) + / + VALIDATION_MODELS_RELATIVE_PATH. */ private ValidationServiceImpl validationService; private ValidationModelBuilder modelBuilder; private ResourcePropertyBuilder propertyBuilder; @Mock ValidationServiceConfiguration configuration; @Rule public SlingContext context = new SlingContext(); @Mock private ResourceBundle defaultResourceBundle; @Mock private ResourceBundleProvider resourceBundleProvider; @Mock private ValidationModelRetriever modelRetriever; private Validator<Date> dateValidator; @Mock private ServiceReference<Validator<?>> validatorServiceReference; @Mock private ServiceReference<Validator<?>> newValidatorServiceReference; @Mock private Bundle providingBundle; private static final String DATE_VALIDATOR_ID = "DateValidator"; private static final String REGEX_VALIDATOR_ID = "RegexValidator"; @Before public void setUp() throws LoginException, PersistenceException, RepositoryException { validationService = new ValidationServiceImpl(); validationService.searchPaths = Arrays.asList(context.resourceResolver().getSearchPath()); validationService.configuration = configuration; Mockito.doReturn(20).when(configuration).defaultSeverity(); validationService.resourceBundleProviders = Collections.singletonList(resourceBundleProvider); Mockito.doReturn(defaultResourceBundle).when(resourceBundleProvider).getResourceBundle(Mockito.anyObject()); modelBuilder = new ValidationModelBuilder(); propertyBuilder = new ResourcePropertyBuilder(); dateValidator = new DateValidator(); Mockito.doReturn(1l).when(providingBundle).getBundleId(); Mockito.doReturn(providingBundle).when(validatorServiceReference).getBundle(); Mockito.doReturn(providingBundle).when(newValidatorServiceReference).getBundle(); validationService.validatorMap.put(DATE_VALIDATOR_ID, dateValidator, validatorServiceReference, 10); validationService.validatorMap.put(REGEX_VALIDATOR_ID, new RegexValidator(), validatorServiceReference, 10); validationService.modelRetriever = modelRetriever; } @Test public void testGetValidationModelWithAbsolutePath() throws Exception { // check conversion to relative resource type validationService.getValidationModel("/libs/some/type", "some path", true); Mockito.verify(modelRetriever).getValidationModel("some/type", "some path", true); } @Test public void testGetValidationModelWithRelativePath() throws Exception { // check conversion to relative resource type validationService.getValidationModel("some/type", "some path", true); Mockito.verify(modelRetriever).getValidationModel("some/type", "some path", true); } @Test(expected=IllegalArgumentException.class) public void testGetValidationModelWithAbsolutePathOutsideSearchPath() throws Exception { // check conversion to relative resource type validationService.getValidationModel("/content/some/type", "some path", true); } @Test(expected = IllegalStateException.class) public void testValidateWithInvalidValidatorId() throws Exception { propertyBuilder.validator("invalidid", 10); modelBuilder.resourceProperty(propertyBuilder.build("field1")); ValidationModel vm = modelBuilder.build("sling/validation/test", "some source"); HashMap<String, Object> hashMap = new HashMap<String, Object>(); hashMap.put("field1", "1"); validationService.validate(new ValueMapDecorator(hashMap), vm); } @Test() public void testValueMapWithWrongDataType() throws Exception { propertyBuilder.validator(DATE_VALIDATOR_ID, 10); modelBuilder.resourceProperty(propertyBuilder.build("field1")); ValidationModel vm = modelBuilder.build("sling/validation/test", "some source"); HashMap<String, Object> hashMap = new HashMap<String, Object>(); hashMap.put("field1", "1"); ValidationResult vr = validationService.validate(new ValueMapDecorator(hashMap), vm); Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>contains(new DefaultValidationFailure("field1", 10, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_WRONG_PROPERTY_TYPE, Date.class))); } @Test public void testValidateNeverCalledWithNullValues() throws Exception { Validator<String> myValidator = new Validator<String>() { @Override public @Nonnull ValidationResult validate(@Nonnull String data, @Nonnull ValidatorContext context, @Nonnull ValueMap arguments) throws SlingValidationException { Assert.assertNotNull("data parameter for validate should never be null", data); Assert.assertNotNull("location of context parameter for validate should never be null", context.getLocation()); Assert.assertNotNull("valueMap of context parameter for validate should never be null", context.getValueMap()); Assert.assertNull("resource of context parameter for validate cannot be set if validate was called only with a value map", context.getResource()); Assert.assertNotNull("arguments parameter for validate should never be null", arguments); return DefaultValidationResult.VALID; } }; validationService.validatorMap.put("someId", myValidator, validatorServiceReference, 10); propertyBuilder.validator("someId", 20); modelBuilder.resourceProperty(propertyBuilder.build("field1")); ValidationModel vm = modelBuilder.build("sling/validation/test", "some source"); HashMap<String, Object> hashMap = new HashMap<String, Object>(); hashMap.put("field1", "1"); ValidationResult vr = validationService.validate(new ValueMapDecorator(hashMap), vm); Assert.assertThat(vr.getFailures(), Matchers.hasSize(0)); Assert.assertTrue(vr.isValid()); } @Test() public void testValueMapWithMissingField() throws Exception { modelBuilder.resourceProperty(propertyBuilder.build("field1")); modelBuilder.resourceProperty(propertyBuilder.build("field2")); modelBuilder.resourceProperty(propertyBuilder.build("field3")); modelBuilder.resourceProperty(propertyBuilder.build("field4")); ValidationModel vm = modelBuilder.build("sling/validation/test", "some source"); // this should not be detected as missing property HashMap<String, Object> hashMap = new HashMap<String, Object>(); hashMap.put("field1", new String[] {}); hashMap.put("field2", new String[] { "null" }); hashMap.put("field3", ""); ValidationResult vr = validationService.validate(new ValueMapDecorator(hashMap), vm); Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>contains(new DefaultValidationFailure("", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_PROPERTY_WITH_NAME, "field4"))); } @Test() public void testValueMapWithMissingOptionalValue() throws Exception { modelBuilder.resourceProperty(propertyBuilder.optional().build("field1")); ValidationModel vm = modelBuilder.build("sling/validation/test", "some source"); HashMap<String, Object> hashMap = new HashMap<String, Object>(); hashMap.put("field2", "1"); ValidationResult vr = validationService.validate(new ValueMapDecorator(hashMap), vm); Assert.assertThat(vr.getFailures(), Matchers.hasSize(0)); Assert.assertTrue(vr.isValid()); } @Test() public void testValueMapWithEmptyOptionalValue() throws Exception { propertyBuilder.optional(); propertyBuilder.validator(REGEX_VALIDATOR_ID, null, RegexValidator.REGEX_PARAM, "abc"); modelBuilder.resourceProperty(propertyBuilder.build("field1")); ValidationModel vm = modelBuilder.build("sling/validation/test", "some source"); HashMap<String, Object> hashMap = new HashMap<String, Object>(); hashMap.put("field1", ""); ValidationResult vr = validationService.validate(new ValueMapDecorator(hashMap), vm); Assert.assertFalse(vr.isValid()); Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>contains(new DefaultValidationFailure("field1", 10, defaultResourceBundle, RegexValidator.I18N_KEY_PATTERN_DOES_NOT_MATCH, "abc"))); } @Test public void testValueMapWithCorrectDataType() throws Exception { propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "abc"); modelBuilder.resourceProperty(propertyBuilder.build("field1")); propertyBuilder = new ResourcePropertyBuilder(); final String TEST_REGEX = "^test$"; propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, TEST_REGEX); modelBuilder.resourceProperty(propertyBuilder.build("field2")); ValidationModel vm = modelBuilder.build("sling/validation/test", "some source"); HashMap<String, Object> hashMap = new HashMap<String, Object>(); hashMap.put("field1", "HelloWorld"); hashMap.put("field2", "HelloWorld"); ValidationResult vr = validationService.validate(new ValueMapDecorator(hashMap), vm); Assert.assertFalse(vr.isValid()); Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure> hasItem(new DefaultValidationFailure("field2", 0, defaultResourceBundle, RegexValidator.I18N_KEY_PATTERN_DOES_NOT_MATCH, TEST_REGEX))); } // see https://issues.apache.org/jira/browse/SLING-5674 @Test public void testNonExistingResource() throws Exception { propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits ResourceProperty property = propertyBuilder.build("field1"); modelBuilder.resourceProperty(property); ChildResource modelChild = new ChildResourceImpl("child", null, true, Collections.singletonList(property), Collections.emptyList()); modelBuilder.childResource(modelChild); modelChild = new ChildResourceImpl("optionalChild", null, false, Collections.singletonList(property), Collections.emptyList()); modelBuilder.childResource(modelChild); ValidationModel vm = modelBuilder.build("sometype", "some source"); ResourceResolver rr = context.resourceResolver(); Resource nonExistingResource = new NonExistingResource(rr, "non-existing-resource"); ValidationResult vr = validationService.validate(nonExistingResource, vm); Assert.assertFalse("resource should have been considered invalid", vr.isValid()); Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>containsInAnyOrder( new DefaultValidationFailure("", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_PROPERTY_WITH_NAME, "field1"), new DefaultValidationFailure("", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_CHILD_RESOURCE_WITH_NAME, "child") )); } // see https://issues.apache.org/jira/browse/SLING-5749 @Test public void testSyntheticResource() throws Exception { propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits ResourceProperty property = propertyBuilder.build("field1"); modelBuilder.resourceProperty(property); ChildResource modelChild = new ChildResourceImpl("child", null, true, Collections.singletonList(property), Collections.emptyList()); modelBuilder.childResource(modelChild); modelChild = new ChildResourceImpl("optionalChild", null, false, Collections.singletonList(property), Collections.emptyList()); modelBuilder.childResource(modelChild); ValidationModel vm = modelBuilder.build("sometype", "some source"); ResourceResolver rr = context.resourceResolver(); Resource nonExistingResource = new SyntheticResource(rr, "someresource", "resourceType"); ValidationResult vr = validationService.validate(nonExistingResource, vm); Assert.assertFalse("resource should have been considered invalid", vr.isValid()); Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>containsInAnyOrder( new DefaultValidationFailure("", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_PROPERTY_WITH_NAME, "field1"), new DefaultValidationFailure("", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_CHILD_RESOURCE_WITH_NAME, "child") )); } @Test public void testResourceWithMissingGrandChildProperty() throws Exception { propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits ResourceProperty property = propertyBuilder.build("field1"); modelBuilder.resourceProperty(property); ChildResource modelGrandChild = new ChildResourceImpl("grandchild", null, true, Collections.singletonList(property), Collections.<ChildResource> emptyList()); ChildResource modelChild = new ChildResourceImpl("child", null, true, Collections.singletonList(property), Collections.singletonList(modelGrandChild)); modelBuilder.childResource(modelChild); ValidationModel vm = modelBuilder.build("sometype", "some source"); // create a resource ResourceResolver rr = context.resourceResolver(); Resource testResource = ResourceUtil.getOrCreateResource(rr, "/content/validation/1/resource", JcrConstants.NT_UNSTRUCTURED, JcrConstants.NT_UNSTRUCTURED, true); ModifiableValueMap mvm = testResource.adaptTo(ModifiableValueMap.class); mvm.put("field1", "1"); Map<String, Object> properties = new HashMap<String, Object>(); properties.put("field1", "1"); Resource resourceChild = rr.create(testResource, "child", properties); // resourceGrandChild is missing the mandatory field1 property rr.create(resourceChild, "grandchild", null); ValidationResult vr = validationService.validate(testResource, vm); Assert.assertFalse("resource should have been considered invalid", vr.isValid()); Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>contains(new DefaultValidationFailure("child/grandchild", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_PROPERTY_WITH_NAME, "field1"))); } @Test public void testResourceWithMissingOptionalChildResource() throws Exception { propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits ResourceProperty property = propertyBuilder.build("field1"); ChildResource child = new ChildResourceImpl("child", null, false, Collections.singletonList(property), Collections.<ChildResource> emptyList()); modelBuilder.childResource(child); ValidationModel vm = modelBuilder.build("type", "some source"); // create a resource (lacking the optional "child" sub resource) ResourceResolver rr = context.resourceResolver(); Resource testResource = ResourceUtil.getOrCreateResource(rr, "/content/validation/1/resource", JcrConstants.NT_UNSTRUCTURED, JcrConstants.NT_UNSTRUCTURED, true); ValidationResult vr = validationService.validate(testResource, vm); Assert.assertThat(vr.getFailures(), Matchers.hasSize(0)); Assert.assertTrue(vr.isValid()); } @Test public void testResourceWithNestedChildren() throws Exception { propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits ResourceProperty property = propertyBuilder.build("field1"); ChildResource modelGrandChild = new ChildResourceImpl("grandchild", null, true, Collections.singletonList(property), Collections.<ChildResource> emptyList()); ChildResource modelChild = new ChildResourceImpl("child", null, true, Collections.singletonList(property), Collections.singletonList(modelGrandChild)); modelBuilder.childResource(modelChild); ValidationModel vm = modelBuilder.build("sometype", "some source"); // create a resource ResourceResolver rr = context.resourceResolver(); Resource testResource = ResourceUtil.getOrCreateResource(rr, "/content/validation/1/resource", JcrConstants.NT_UNSTRUCTURED, JcrConstants.NT_UNSTRUCTURED, true); Map<String, Object> properties = new HashMap<String, Object>(); properties.put("field1", "1"); Resource resourceChild = rr.create(testResource, "child", properties); rr.create(resourceChild, "grandchild", properties); ValidationResult vr = validationService.validate(testResource, vm); Assert.assertThat(vr.getFailures(), Matchers.hasSize(0)); Assert.assertTrue(vr.isValid()); } @Test public void testResourceWithValidatorLeveragingTheResource() throws Exception { Validator<String> extendedValidator = new Validator<String>() { @Override @Nonnull public ValidationResult validate(@Nonnull String data, @Nonnull ValidatorContext context, @Nonnull ValueMap arguments) throws SlingValidationException { Resource resource = context.getResource(); if (resource == null) { Assert.fail("Resource must not be null"); } else { Assert.assertThat(resource.getPath(), Matchers.equalTo("/content/validation/1/resource")); } return DefaultValidationResult.VALID; } }; // register validator validationService.validatorMap.put("myid", extendedValidator, newValidatorServiceReference, null); propertyBuilder.validator("myid", null); // accept any digits modelBuilder.resourceProperty(propertyBuilder.build("field1")); ValidationModel vm = modelBuilder.build("sometype", "some source"); // create a resource ResourceResolver rr = context.resourceResolver(); Map<String, Object> properties = new HashMap<String, Object>(); properties.put("field1", "1"); Resource testResource = ResourceUtil.getOrCreateResource(rr, "/content/validation/1/resource", properties, JcrConstants.NT_UNSTRUCTURED, true); ValidationResult vr = validationService.validate(testResource, vm); Assert.assertTrue(vr.isValid()); } @Test public void testResourceWithNestedChildrenAndPatternMatching() throws Exception { propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits ResourceProperty property = propertyBuilder.build("field1"); ChildResource modelGrandChild = new ChildResourceImpl("grandchild", "grandchild.*", true, Collections.singletonList(property), Collections.<ChildResource> emptyList()); ChildResource modelChild = new ChildResourceImpl("child", "child.*", true, Collections.singletonList(property), Collections.singletonList(modelGrandChild)); ChildResource siblingChild = new ChildResourceImpl("siblingchild", "siblingchild.*", true, Collections.singletonList(property), Collections.singletonList(modelGrandChild)); modelBuilder.childResource(modelChild); modelBuilder.childResource(siblingChild); ValidationModel vm = modelBuilder.build("sometype", "some source"); ResourceResolver rr = context.resourceResolver(); Resource testResource = ResourceUtil.getOrCreateResource(rr, "/apps/validation/1/resource", JcrConstants.NT_UNSTRUCTURED, JcrConstants.NT_UNSTRUCTURED, true); Map<String, Object> properties = new HashMap<String, Object>(); properties.put("field1", "1"); Resource resourceChild = rr.create(testResource, "child1", properties); rr.create(resourceChild, "grandchild1", properties); // child2 is lacking its mandatory sub resource rr.create(testResource, "child2", properties); rr.create(testResource, "child3", null); // sibling child is not there at all (although mandatory) ValidationResult vr = validationService.validate(testResource, vm); Assert.assertFalse("resource should have been considered invalid", vr.isValid()); Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>containsInAnyOrder( new DefaultValidationFailure("child2", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_CHILD_RESOURCE_MATCHING_PATTERN, "grandchild.*"), new DefaultValidationFailure("child3", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_CHILD_RESOURCE_MATCHING_PATTERN, "grandchild.*"), new DefaultValidationFailure("child3", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_PROPERTY_WITH_NAME, "field1"), new DefaultValidationFailure("", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_CHILD_RESOURCE_MATCHING_PATTERN, "siblingchild.*"))); } @Test public void testResourceWithPropertyPatternMatching() throws Exception { propertyBuilder.validator(REGEX_VALIDATOR_ID, 1, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits propertyBuilder.nameRegex("field.*"); modelBuilder.resourceProperty(propertyBuilder.build("field")); propertyBuilder.nameRegex("otherfield.*"); modelBuilder.resourceProperty(propertyBuilder.build("otherfield")); propertyBuilder.nameRegex("optionalfield.*").optional(); modelBuilder.resourceProperty(propertyBuilder.build("optionalfield")); ValidationModel vm = modelBuilder.build("type", "some source"); // create a resource ResourceResolver rr = context.resourceResolver(); Resource testResource = ResourceUtil.getOrCreateResource(rr, "/content/validation/1/resource", JcrConstants.NT_UNSTRUCTURED, JcrConstants.NT_UNSTRUCTURED, true); ModifiableValueMap mvm = testResource.adaptTo(ModifiableValueMap.class); mvm.put("field1", "1"); mvm.put("field2", "1"); mvm.put("field3", "abc"); // does not validate // otherfield.* property is missing ValidationResult vr = validationService.validate(testResource, vm); Assert.assertFalse("resource should have been considered invalid", vr.isValid()); Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>contains( new DefaultValidationFailure("field3", 1, defaultResourceBundle, RegexValidator.I18N_KEY_PATTERN_DOES_NOT_MATCH, "\\d"), new DefaultValidationFailure("", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_PROPERTY_MATCHING_PATTERN, "otherfield.*"))); } @Test public void testResourceWithMultivalueProperties() throws Exception { propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits propertyBuilder.multiple(); modelBuilder.resourceProperty(propertyBuilder.build("field")); ValidationModel vm = modelBuilder.build("type", "some source"); ResourceResolver rr = context.resourceResolver(); Resource testResource = ResourceUtil.getOrCreateResource(rr, "/content/validation/1/resource", JcrConstants.NT_UNSTRUCTURED, JcrConstants.NT_UNSTRUCTURED, true); ModifiableValueMap mvm = testResource.adaptTo(ModifiableValueMap.class); mvm.put("field", new String[] { "1", "abc", "2" }); ValidationResult vr = validationService.validate(testResource, vm); Assert.assertFalse("resource should have been considered invalid", vr.isValid()); Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>contains(new DefaultValidationFailure("field[1]", 0, defaultResourceBundle, RegexValidator.I18N_KEY_PATTERN_DOES_NOT_MATCH, "\\d"))); } @Test() public void testValidateResourceRecursively() throws Exception { modelBuilder.resourceProperty(propertyBuilder.build("field1")); final ValidationModel vm1 = modelBuilder.build("resourcetype1", "some source"); modelBuilder = new ValidationModelBuilder(); modelBuilder.resourceProperty(propertyBuilder.build("field2")); final ValidationModel vm2 = modelBuilder.build("resourcetype2", "some source"); // set model retriever validationService.modelRetriever = new ValidationModelRetriever() { @Override public @CheckForNull ValidationModel getValidationModel(@Nonnull String resourceType, String resourcePath, boolean considerResourceSuperTypeModels) { if (resourceType.equals("resourcetype1")) { return vm1; } else if (resourceType.equals("resourcetype2")) { return vm2; } else { return null; } } }; ResourceResolver rr = context.resourceResolver(); // resource is lacking the required field (is invalid) Resource testResource = ResourceUtil.getOrCreateResource(rr, "/content/validation/1/resource", "resourcetype1", JcrConstants.NT_UNSTRUCTURED, true); // child1 is valid Map<String, Object> properties = new HashMap<String, Object>(); properties.put(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY, "resourcetype2"); properties.put("field2", "test"); rr.create(testResource, "child1", properties); // child2 is invalid properties.clear(); properties.put(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY, "resourcetype2"); rr.create(testResource, "child2", properties); // child3 has no model (but its resource type is ignored) properties.clear(); properties.put(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY, "resourcetype3"); rr.create(testResource, "child3", properties); final Predicate<Resource> ignoreResourceType3Filter = new Predicate<Resource>() { @Override public boolean test(final Resource resource) { return !"resourcetype3".equals(resource.getResourceType()); } }; ValidationResult vr = validationService.validateResourceRecursively(testResource, true, ignoreResourceType3Filter, false); Assert.assertFalse("resource should have been considered invalid", vr.isValid()); Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>contains( new DefaultValidationFailure("", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_PROPERTY_WITH_NAME, "field1"), new DefaultValidationFailure("child2", 20, defaultResourceBundle, ValidationServiceImpl.I18N_KEY_MISSING_REQUIRED_PROPERTY_WITH_NAME, "field2"))); } // see https://issues.apache.org/jira/browse/SLING-5674 @Test public void testValidateResourceRecursivelyOnNonExistingResource() throws Exception { ResourceResolver rr = context.resourceResolver(); Resource nonExistingResource = new NonExistingResource(rr, "non-existing-resource"); ValidationResult vr = validationService.validateResourceRecursively(nonExistingResource, true, null, true); Assert.assertTrue("resource should have been considered valid", vr.isValid()); } @Test(expected = IllegalArgumentException.class) public void testValidateResourceRecursivelyWithMissingValidationModel() throws Exception { // set model retriever which never retrieves anything validationService.modelRetriever = new ValidationModelRetriever() { @Override public @CheckForNull ValidationModel getValidationModel(@Nonnull String resourceType, String resourcePath, boolean considerResourceSuperTypeModels) { return null; } }; ResourceResolver rr = context.resourceResolver(); // resource is having no connected validation model Resource testResource = ResourceUtil.getOrCreateResource(rr, "/content/validation/1/resource", "resourcetype1", JcrConstants.NT_UNSTRUCTURED, true); validationService.validateResourceRecursively(testResource, true, null, false); } @Test() public void testValidateResourceRecursivelyWithMissingValidatorAndNoEnforcement() throws Exception { // set model retriever which never retrieves anything validationService.modelRetriever = new ValidationModelRetriever() { @Override public @CheckForNull ValidationModel getValidationModel(@Nonnull String resourceType, String resourcePath, boolean considerResourceSuperTypeModels) { return null; } }; ResourceResolver rr = context.resourceResolver(); // resource is having no connected validation model Resource testResource = ResourceUtil.getOrCreateResource(rr, "/content/validation/1/resource", "resourcetype1", JcrConstants.NT_UNSTRUCTURED, true); ValidationResult vr = validationService.validateResourceRecursively(testResource, false, null, false); Assert.assertTrue(vr.isValid()); } @Test public void testGetRelativeResourcePath() { // return relative paths unmodified Assert.assertThat(validationService.getRelativeResourceType("relative/path"), Matchers.equalTo("relative/path")); Assert.assertThat(validationService.getRelativeResourceType("/apps/relative/path"), Matchers.equalTo("relative/path")); Assert.assertThat(validationService.getRelativeResourceType("/libs/relative/path"), Matchers.equalTo("relative/path")); } @Test(expected = IllegalArgumentException.class) public void testGetRelativeResourcePathWithAbsolutePathOutsideOfTheSearchPaths() { validationService.getRelativeResourceType("/apps2/relative/path"); } }