SLING-7768: Added /etc/map tests to the resource resolver and extracted some common test code into MockTestUtil class
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
new file mode 100644
index 0000000..b75b456
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
@@ -0,0 +1,276 @@
+/*
+ * 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.resourceresolver.impl;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.observation.ResourceChange;
+import org.apache.sling.api.resource.path.Path;
+import org.apache.sling.resourceresolver.impl.mapping.MapConfigurationProvider;
+import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
+import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProviderConfiguration;
+import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProviderImpl;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker;
+import org.apache.sling.serviceusermapping.ServiceUserMapper;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.event.EventAdmin;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+import static org.apache.sling.resourceresolver.impl.MockedResourceResolverImplTest.createRPHandler;
+import static org.apache.sling.resourceresolver.impl.ResourceResolverImpl.PROP_REDIRECT_INTERNAL;
+import static org.apache.sling.resourceresolver.impl.mapping.MapEntries.PROP_REDIRECT_EXTERNAL;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.ExpectedEtcMapping;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.buildResource;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.callInaccessibleMethod;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.checkInternalResource;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.checkRedirectResource;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.createRequestFromUrl;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.setInaccessibleField;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+/**
+ * These are the same tests as in the EtcMappingMapEntriesTest but in this
+ * class we are actually mocking the Resource Resolver Factory and its classes
+ * and we test the mapping and resource resolution through the resource resolver
+ * rather the MapEntries.
+ */
+public class EtcMappingResourceResolverTest {
+
+ static final String PROP_REG_EXP = "sling:match";
+
+ @Mock
+ ResourceResolverFactory resourceResolverFactory;
+
+ @Mock
+ BundleContext bundleContext;
+
+ @Mock
+ Bundle bundle;
+
+ @Mock
+ EventAdmin eventAdmin;
+
+ @Mock
+ ResourceResolver resourceResolver;
+
+ @Mock
+ ResourceProvider<?> resourceProvider;
+
+ @Mock
+ StringInterpolationProviderConfiguration stringInterpolationProviderConfiguration;
+
+ StringInterpolationProviderImpl stringInterpolationProvider = new StringInterpolationProviderImpl();
+ MapEntries mapEntries;
+
+ File vanityBloomFilterFile;
+
+ CommonResourceResolverFactoryImpl commonFactory;
+
+ Resource etc;
+ Resource map;
+ Resource http;
+
+ Map<String, Map<String, String>> aliasMap;
+
+ @SuppressWarnings({"unchecked"})
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ List<MapConfigurationProvider.VanityPathConfig> configs = getVanityPathConfigs();
+ vanityBloomFilterFile = new File("target/test-classes/resourcesvanityBloomFilter.txt");
+ List<ResourceProviderHandler> handlers = asList(createRPHandler(resourceProvider, "rp1", 0, "/"));
+ ResourceProviderTracker resourceProviderTracker = mock(ResourceProviderTracker.class);
+ ResourceProviderStorage storage = new ResourceProviderStorage(handlers);
+ when(resourceProviderTracker.getResourceProviderStorage()).thenReturn(storage);
+ ResourceResolverFactoryActivator activator = new ResourceResolverFactoryActivator();
+ // These fields on the Activator a package private so we need reflection to access them
+ setInaccessibleField("resourceProviderTracker", activator, resourceProviderTracker);
+ setInaccessibleField("resourceAccessSecurityTracker", activator, new ResourceAccessSecurityTracker());
+ setInaccessibleField("bundleContext", activator, bundleContext);
+ setInaccessibleField("stringInterpolationProvider", activator, stringInterpolationProvider);
+ setInaccessibleField("mapRoot", activator, "/etc/map");
+ setInaccessibleField("mapRootPrefix", activator, "/etc/map");
+ setInaccessibleField("observationPaths", activator, new Path[] {new Path("/")});
+ ServiceUserMapper serviceUserMapper = mock(ServiceUserMapper.class);
+ setInaccessibleField("serviceUserMapper", activator, serviceUserMapper);
+ commonFactory = spy(new CommonResourceResolverFactoryImpl(activator));
+ when(bundleContext.getBundle()).thenReturn(bundle);
+ when(bundleContext.getDataFile("vanityBloomFilter.txt")).thenReturn(vanityBloomFilterFile);
+ when(serviceUserMapper.getServiceUserID(any(Bundle.class),anyString())).thenReturn("mapping");
+ // Activate method is package private so we use reflection to to call it
+ callInaccessibleMethod("activate", commonFactory, BundleContext.class, bundleContext);
+ final Bundle usingBundle = mock(Bundle.class);
+ resourceResolverFactory = new ResourceResolverFactoryImpl(commonFactory, usingBundle, null);
+ resourceResolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
+
+ etc = buildResource("/etc", null, resourceResolver, resourceProvider);
+ map = buildResource("/etc/map", etc, resourceResolver, resourceProvider);
+ http = buildResource("/etc/map/http", map, resourceResolver, resourceProvider);
+ }
+
+ List<MapConfigurationProvider.VanityPathConfig> getVanityPathConfigs() {
+ return new ArrayList<>();
+ }
+
+ void refreshMapEntries(String path, boolean isExternal) {
+ ((MapEntries) commonFactory.getMapEntries()).onChange(
+ asList(
+ new ResourceChange(ResourceChange.ChangeType.ADDED, path, isExternal)
+ )
+ );
+ }
+
+ @Test
+ public void root_node_to_content_mapping() throws Exception {
+ buildResource(http.getPath() + "/localhost.8080", http, resourceResolver, resourceProvider, PROP_REDIRECT_EXTERNAL, "/content/simple-node");
+ // This updates the map entries so that the newly added resources are added.
+ // ATTENTION: only call this after all etc-mapping resources are defined as this lock their Resource Meta Data and prevents a re-update
+ refreshMapEntries("/etc/map", true);
+
+ ExpectedEtcMapping expectedEtcMapping = new ExpectedEtcMapping("^http/localhost.8080/", "/content/simple-node/");
+ expectedEtcMapping.assertEtcMap("Etc Mapping for root node to content", commonFactory.getMapEntries().getResolveMaps());
+
+ HttpServletRequest request = createRequestFromUrl("http://localhost:8080/");
+ Resource resolvedResource = resourceResolver.resolve(request, "/");
+ checkRedirectResource(resolvedResource, "/content/simple-node/", 302);
+ }
+
+ @Test
+ public void match_to_content_mapping() throws Exception {
+ buildResource("test-node", http, resourceResolver, resourceProvider,
+ PROP_REG_EXP, "localhost.8080/",
+ PROP_REDIRECT_EXTERNAL, "/content/simple-match/"
+ );
+ refreshMapEntries("/etc/map", true);
+
+ ExpectedEtcMapping expectedEtcMapping = new ExpectedEtcMapping("^http/localhost.8080/", "/content/simple-match/");
+ expectedEtcMapping.assertEtcMap("Etc Mapping for match to content", commonFactory.getMapEntries().getResolveMaps());
+
+ HttpServletRequest request = createRequestFromUrl("http://localhost:8080/");
+ Resource resolvedResource = resourceResolver.resolve(request, "/");
+ checkRedirectResource(resolvedResource, "/content/simple-match/", 302);
+ }
+
+ // The following tests are based on the example from the https://sling.apache.org/documentation/the-sling-engine/mappings-for-resource-resolution.html page
+
+ @Test
+ public void internal_to_external_node_mapping() throws Exception {
+ buildResource("example.com.80", http, resourceResolver, resourceProvider, PROP_REDIRECT_EXTERNAL, "http://www.example.com/");
+ refreshMapEntries("/etc/map", true);
+
+ ExpectedEtcMapping expectedEtcMapping = new ExpectedEtcMapping("^http/example.com.80/", "http://www.example.com/");
+ expectedEtcMapping.assertEtcMap("Etc Mapping for internal to external based on node", commonFactory.getMapEntries().getResolveMaps());
+
+ HttpServletRequest request = createRequestFromUrl("http://example.com/");
+ Resource resolvedResource = resourceResolver.resolve(request, "/");
+ checkRedirectResource(resolvedResource, "http://www.example.com/", 302);
+ }
+
+ @Test
+ public void internal_root_to_content_node_mapping() throws Exception {
+ buildResource("/example", null, resourceResolver, resourceProvider);
+
+ buildResource("www.example.com.80", http, resourceResolver, resourceProvider, PROP_REDIRECT_INTERNAL, "/example");
+ refreshMapEntries("/etc/map", true);
+
+ ExpectedEtcMapping expectedEtcMapping = new ExpectedEtcMapping().addEtcMapEntry("^http/www.example.com.80/", true, "/example/");
+ expectedEtcMapping.assertEtcMap("Etc Mapping for internal root to content", commonFactory.getMapEntries().getResolveMaps());
+
+ HttpServletRequest request = createRequestFromUrl("http://www.example.com:80/");
+ Resource resolvedResource = resourceResolver.resolve(request, "/");
+ checkInternalResource(resolvedResource, "/example");
+ }
+
+ @Test
+ public void host_redirect_match_mapping() throws Exception {
+ buildResource("any_example.com.80", http, resourceResolver, resourceProvider,
+ PROP_REG_EXP, ".+\\.example\\.com\\.80",
+ PROP_REDIRECT_EXTERNAL, "http://www.example.com/"
+ );
+ refreshMapEntries("/etc/map", true);
+
+ ExpectedEtcMapping expectedEtcMapping = new ExpectedEtcMapping().addEtcMapEntry("^http/.+\\.example\\.com\\.80", false, "http://www.example.com/");
+ expectedEtcMapping.assertEtcMap("Etc Mapping for host redirect match mapping", commonFactory.getMapEntries().getResolveMaps());
+
+ HttpServletRequest request = createRequestFromUrl("http://www.example.com");
+ Resource resolvedResource = resourceResolver.resolve(request, "/");
+ checkRedirectResource(resolvedResource, "http://www.example.com//", 302);
+ }
+
+ @Test
+ public void nested_internal_mixed_mapping() throws Exception {
+ Resource localhost = buildResource("localhost_any", http, resourceResolver, resourceProvider,
+ PROP_REG_EXP, "localhost\\.\\d*",
+ PROP_REDIRECT_INTERNAL, "/content"
+ );
+ buildResource("cgi-bin", localhost, resourceResolver, resourceProvider,PROP_REDIRECT_INTERNAL, "/scripts");
+ buildResource("gateway", localhost, resourceResolver, resourceProvider,PROP_REDIRECT_INTERNAL, "http://gbiv.com");
+ buildResource("(stories)", localhost, resourceResolver, resourceProvider,PROP_REDIRECT_INTERNAL, "/anecdotes/$1");
+
+ refreshMapEntries("/etc/map", true);
+
+ ExpectedEtcMapping expectedEtcMapping = new ExpectedEtcMapping()
+ .addEtcMapEntry("^http/localhost\\.\\d*", true, "/content")
+ .addEtcMapEntry("^http/localhost\\.\\d*/cgi-bin/", true, "/scripts/")
+ .addEtcMapEntry("^http/localhost\\.\\d*/gateway/", true, "http://gbiv.com/")
+ .addEtcMapEntry("^http/localhost\\.\\d*/(stories)/", true, "/anecdotes/$1/");
+ expectedEtcMapping.assertEtcMap("Etc Mapping for nested internal mixed mapping", commonFactory.getMapEntries().getResolveMaps());
+
+ buildResource("/content", null, resourceResolver, resourceProvider);
+ Resource scripts = buildResource("/scripts", null, resourceResolver, resourceProvider);
+ Resource scriptsChild = buildResource("/scripts/child", scripts, resourceResolver, resourceProvider);
+ Resource anecdotes = buildResource("/anecdotes", null, resourceResolver, resourceProvider);
+ Resource stories = buildResource("/anecdotes/stories", anecdotes, resourceResolver, resourceProvider);
+
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.getScheme()).thenReturn("http");
+ when(request.getServerName()).thenReturn("localhost");
+ when(request.getServerPort()).thenReturn(1234);
+ Resource resolvedResource = resourceResolver.resolve(request, "/");
+ checkInternalResource(resolvedResource, "/content");
+
+ resolvedResource = resourceResolver.resolve(request, "/cgi-bin/");
+ checkInternalResource(resolvedResource, "/scripts");
+ resolvedResource = resourceResolver.resolve(request, "/cgi-bin/child/");
+ checkInternalResource(resolvedResource, "/scripts/child");
+//AS TODO: Does not redirect -> investigate later
+// resolvedResource = resourceResolver.resolve(request, "/gateway/");
+// checkRedirectResource(resolvedResource, "http://gbiv.com/", 302);
+ resolvedResource = resourceResolver.resolve(request, "/stories/");
+ checkInternalResource(resolvedResource, "/anecdotes/stories");
+ }
+}
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/SimpleValueMapImpl.java b/src/test/java/org/apache/sling/resourceresolver/impl/SimpleValueMapImpl.java
index f1ed9c1..be498aa 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/SimpleValueMapImpl.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/SimpleValueMapImpl.java
@@ -84,7 +84,12 @@
public <T> T get(String name, Class<T> type) {
Object o = delegate.get(name);
if ( type.equals(String[].class) && ! ( o instanceof String[])) {
- o = new String[] { String.valueOf(o) };
+ //AS This works fine unless o is a 'null'. It should return an empty string instead
+ if(o == null) {
+ o = new String[] {};
+ } else {
+ o = new String[]{String.valueOf(o)};
+ }
}
return (T) o;
}
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java
index 0f462e0..fdfbfab 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java
@@ -16,7 +16,6 @@
*/
package org.apache.sling.resourceresolver.impl.mapping;
-import junit.framework.TestCase;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
@@ -26,7 +25,6 @@
import org.apache.sling.api.wrappers.ValueMapDecorator;
import org.junit.After;
import org.junit.Before;
-import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
@@ -38,7 +36,6 @@
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -49,8 +46,6 @@
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
-import static org.apache.sling.resourceresolver.impl.mapping.MapEntries.PROP_REDIRECT_EXTERNAL;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyMap;
@@ -222,78 +217,6 @@
}
}
- static class ExpectedEtcMapping {
- List<ExpectedEtcMapEntry> expectedEtcMapEntries = new ArrayList<>();
-
- public ExpectedEtcMapping() {}
-
- public ExpectedEtcMapping(String...expectedMapping) {
- if(expectedMapping.length % 2 != 0) {
- throw new IllegalArgumentException("Expect an even number of strings with pattern / redirect");
- }
- int size = expectedMapping.length / 2;
- for(int i = 0; i < size; i++ ) {
- expectedEtcMapEntries.add(new ExpectedEtcMapEntry(expectedMapping[2 * i], expectedMapping[2 * i + 1]));
- }
- }
-
- public ExpectedEtcMapping addEtcMapEntry(String pattern, String redirect) {
- addEtcMapEntry(pattern, false, redirect);
- return this;
- }
- public ExpectedEtcMapping addEtcMapEntry(String pattern, boolean internal, String redirect) {
- expectedEtcMapEntries.add(new ExpectedEtcMapEntry(pattern, internal, redirect));
- return this;
- }
-
- public void assertEtcMap(String title, List<MapEntry> mapEntries) {
- assertEquals("Wrong Number of Mappings for: " + title, expectedEtcMapEntries.size(), mapEntries.size());
- ArrayList<MapEntry> actual = new ArrayList<>(mapEntries);
- ArrayList<ExpectedEtcMapEntry> expected = new ArrayList<>(expectedEtcMapEntries);
- for(MapEntry actualMapEntry: actual) {
- ExpectedEtcMapEntry expectedFound = null;
- for(ExpectedEtcMapEntry expectedEtcMapEntry: expected) {
- if(expectedEtcMapEntry.pattern.equals(actualMapEntry.getPattern())) {
- expectedFound = expectedEtcMapEntry;
- break;
- }
- }
- if(expectedFound == null) {
- TestCase.fail("This pattern (" + actualMapEntry.getPattern() + ") is not expected for: " + title);
- }
- expectedFound.assertEtcMap(title, actualMapEntry);
- expected.remove(expectedFound);
- }
- for(ExpectedEtcMapEntry expectedEtcMapEntry: expected) {
- TestCase.fail("Expected Map Entry (" + expectedEtcMapEntry.pattern + ") not provided for: " + title);
- }
- }
- }
-
- static class ExpectedEtcMapEntry {
- private String pattern;
- private boolean internal;
- private String redirect;
-
- public ExpectedEtcMapEntry(String pattern, String redirect) {
- this(pattern, false, redirect);
- }
-
- public ExpectedEtcMapEntry(String pattern, boolean internal, String redirect) {
- this.pattern = pattern;
- this.internal = internal;
- this.redirect = redirect;
- }
-
- public void assertEtcMap(String title, MapEntry mapEntry) {
- assertEquals("Wrong Pattern for " + title, pattern, mapEntry.getPattern());
- List<String> givenRedirects = new ArrayList<>(Arrays.asList(mapEntry.getRedirect()));
- assertEquals("Wrong Number of Redirects for: " + title, 1, givenRedirects.size());
- assertEquals("Wrong Redirect for: " + title, this.redirect, givenRedirects.get(0));
- assertEquals("Wrong Redirect Type (ext/int) for: " + title, this.internal, mapEntry.isInternal());
- }
- }
-
/**
* Iterator to piggyback the list of Resources onto a Resource Mock
* so that we can add children to them and create the iterators after
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java
index 5526ea0..a98ca6e 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java
@@ -17,7 +17,6 @@
package org.apache.sling.resourceresolver.impl.mapping;
import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.path.Path;
import org.apache.sling.resourceresolver.impl.CommonResourceResolverFactoryImpl;
@@ -39,23 +38,23 @@
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import static java.util.Arrays.asList;
import static org.apache.sling.resourceresolver.impl.MockedResourceResolverImplTest.createRPHandler;
import static org.apache.sling.resourceresolver.impl.ResourceResolverImpl.PROP_REDIRECT_INTERNAL;
import static org.apache.sling.resourceresolver.impl.mapping.MapEntries.PROP_REDIRECT_EXTERNAL;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.ExpectedEtcMapping;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyMap;
-import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+/**
+ * These tests are for the /etc/map setup of the Map Entries when
+ * an /etc/map is present.
+ */
public class EtcMappingMapEntriesTest extends AbstractMappingMapEntriesTest {
@Test
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationMapEntriesTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationMapEntriesTest.java
index f1635a0..d500cdc 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationMapEntriesTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/StringInterpolationMapEntriesTest.java
@@ -19,13 +19,8 @@
import org.apache.sling.api.resource.Resource;
import org.junit.Test;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static junit.framework.TestCase.fail;
import static org.apache.sling.resourceresolver.impl.mapping.MapEntries.PROP_REDIRECT_EXTERNAL;
-import static org.junit.Assert.assertEquals;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.ExpectedEtcMapping;
/**
* These are tests that are testing the Sling Interpolation Feature (SLING-7768)
diff --git a/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java b/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java
index 7db3831..4fb1e58 100644
--- a/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java
+++ b/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java
@@ -16,13 +16,16 @@
*/
package org.apache.sling.resourceresolver.util;
+import junit.framework.TestCase;
+import org.apache.sling.api.resource.NonExistingResource;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.wrappers.ValueMapDecorator;
import org.apache.sling.resourceresolver.impl.SimpleValueMapImpl;
-import org.apache.sling.resourceresolver.impl.mapping.AbstractMappingMapEntriesTest;
+import org.apache.sling.resourceresolver.impl.helper.RedirectResource;
+import org.apache.sling.resourceresolver.impl.mapping.MapEntry;
import org.apache.sling.spi.resource.provider.ResolveContext;
import org.apache.sling.spi.resource.provider.ResourceContext;
import org.apache.sling.spi.resource.provider.ResourceProvider;
@@ -30,39 +33,109 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import static java.util.Arrays.asList;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
public class MockTestUtil {
+ static final String PROP_SLING_TARGET = "sling:target";
+ static final String PROP_SLING_STATUS = "sling:status";
+
+ public static void checkRedirectResource(Resource redirect, String target, int status) {
+ assertThat("Not a Redirect Resource", redirect, instanceOf(RedirectResource.class));
+ RedirectResource redirectResource = (RedirectResource) redirect;
+ ValueMap values = redirectResource.adaptTo(ValueMap.class);
+ assertEquals("Redirect Target is wrong", target, values.get(PROP_SLING_TARGET, String.class));
+ assertEquals("Redirect Status is wrong", new Integer(status), values.get(PROP_SLING_STATUS, Integer.class));
+ }
+
+ public static void checkNonExistingResource(Resource redirect, String path) {
+ assertThat("Not a Non Existing Resource", redirect, instanceOf(NonExistingResource.class));
+ NonExistingResource nonExistingResource = (NonExistingResource) redirect;
+ if(path != null) {
+ assertEquals("Wrong Path for Non Existing Resource", path, nonExistingResource.getPath());
+ }
+ }
+
+ public static void checkInternalResource(Resource internal, String path) {
+// assertThat("Not a Non Existing Resource", redirect, instanceOf(NonExistingResource.class));
+// NonExistingResource nonExistingResource = (NonExistingResource) redirect;
+// if(path != null) {
+ assertEquals("Wrong Path for Resource", path, internal.getPath());
+ }
+
/**
* Extract the name from a resource path
+ *
* @param fullPath Full / Aboslute path to the resource
* @return Name of the resource
*/
public static String getResourceName(String fullPath) {
int n = fullPath.lastIndexOf("/");
- return fullPath.substring(n+1);
+ return fullPath.substring(n + 1);
+ }
+
+ /**
+ * Creates a Mock Http Servlet Request
+ * @param url Absolute URL to be used to get the method, host and port
+ * @return Http Servlet Request if the url is valid otherwise null
+ */
+ public static HttpServletRequest createRequestFromUrl(String url) {
+ int index = url.indexOf("://");
+ if(index > 0) {
+ String method = url.substring(0, index);
+ int port = 80;
+ int index2 = url.indexOf(":", index + 3);
+ int index3 = url.indexOf("/", index2 > index ? index2 : index + 3);
+ String host = "";
+ if (index2 > 0) {
+ port = new Integer(url.substring(index2 + 1, index3));
+ host = url.substring(index + 3, index2);
+ } else {
+ if(index3 > 0) {
+ host = url.substring(index + 3, index3);
+ } else {
+ host = url.substring(index + 3);
+ }
+ }
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.getScheme()).thenReturn(method);
+ when(request.getServerName()).thenReturn(host);
+ when(request.getServerPort()).thenReturn(port);
+ return request;
+ }
+ return null;
}
/**
* Build a resource with path, parent, provider and resource resolver.
- * @param fullPath Full Path of the Resource
- * @param parent Parent of this resource but it can be null
+ *
+ * @param fullPath Full Path of the Resource
+ * @param parent Parent of this resource but it can be null
* @param resourceResolver Resource Resolver of this resource
- * @param provider Resource Provider Instance
- * @param properties Key / Value pair for resource properties (the number of strings must be even)
+ * @param provider Resource Provider Instance
+ * @param properties Key / Value pair for resource properties (the number of strings must be even)
* @return
*/
@SuppressWarnings("unchecked")
- private Resource buildResource(String fullPath, Resource parent, ResourceResolver resourceResolver, ResourceProvider<?> provider, String ... properties) {
- if(properties != null && properties.length % 2 != 0) { throw new IllegalArgumentException("List of Resource Properties must be an even number: " + asList(properties)); }
+ public static Resource buildResource(String fullPath, Resource parent, ResourceResolver resourceResolver, ResourceProvider<?> provider, String... properties) {
+ if (properties != null && properties.length % 2 != 0) {
+ throw new IllegalArgumentException("List of Resource Properties must be an even number: " + asList(properties));
+ }
Resource resource = mock(Resource.class, withSettings().name(getResourceName(fullPath)).extraInterfaces(ResourceChildrenAccessor.class));
when(resource.getName()).thenReturn(getResourceName(fullPath));
when(resource.getPath()).thenReturn(fullPath);
@@ -70,7 +143,7 @@
when(resource.getResourceMetadata()).thenReturn(resourceMetadata);
when(resource.getResourceResolver()).thenReturn(resourceResolver);
- if(parent != null) {
+ if (parent != null) {
List<Resource> childList = ((ResourceChildrenAccessor) parent).getChildrenList();
childList.add(resource);
}
@@ -87,7 +160,7 @@
});
// register the resource with the provider
- if ( provider != null ) {
+ if (provider != null) {
when(provider.listChildren(Mockito.any(ResolveContext.class), Mockito.eq(resource))).thenAnswer(new Answer<Iterator<Resource>>() {
@Override
public Iterator<Resource> answer(InvocationOnMock invocation) throws Throwable {
@@ -96,11 +169,11 @@
});
when(provider.getResource(Mockito.any(ResolveContext.class), Mockito.eq(fullPath), Mockito.any(ResourceContext.class), Mockito.any(Resource.class))).thenReturn(resource);
}
- if ( properties != null ) {
+ if (properties != null) {
ValueMap vm = new SimpleValueMapImpl();
- for ( int i=0; i < properties.length; i+=2) {
- resourceMetadata.put(properties[i], properties[i+1]);
- vm.put(properties[i], properties[i+1]);
+ for (int i = 0; i < properties.length; i += 2) {
+ resourceMetadata.put(properties[i], properties[i + 1]);
+ vm.put(properties[i], properties[i + 1]);
}
when(resource.getValueMap()).thenReturn(vm);
when(resource.adaptTo(Mockito.eq(ValueMap.class))).thenReturn(vm);
@@ -112,6 +185,32 @@
return resource;
}
+ public static Object callInaccessibleMethod(String methodName, Object target, Class paramsType, Object param) throws NoSuchMethodException {
+ try {
+ Method method = target.getClass().getDeclaredMethod(methodName, paramsType);
+ method.setAccessible(true);
+ return method.invoke(target, param);
+ } catch(NoSuchMethodException e) {
+ throw new UnsupportedOperationException("Failed to find method: " + methodName, e);
+ } catch (IllegalAccessException e) {
+ throw new UnsupportedOperationException("Failed to access method: " + methodName, e);
+ } catch (InvocationTargetException e) {
+ throw new UnsupportedOperationException("Failed to invoke method: " + methodName, e);
+ }
+ }
+
+ public static void setInaccessibleField(String fieldName, Object target, Object fieldValue) throws NoSuchMethodException {
+ try {
+ Field field = target.getClass().getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(target, fieldValue);
+ } catch (IllegalAccessException e) {
+ throw new UnsupportedOperationException("Failed to access field: " + fieldName, e);
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ }
+ }
+
/**
* Iterator to piggyback the list of Resources onto a Resource Mock
* so that we can add children to them and create the iterators after
@@ -121,4 +220,75 @@
public List<Resource> getChildrenList();
}
+ public static class ExpectedEtcMapping {
+ List<ExpectedEtcMapEntry> expectedEtcMapEntries = new ArrayList<>();
+
+ public ExpectedEtcMapping() {}
+
+ public ExpectedEtcMapping(String...expectedMapping) {
+ if(expectedMapping.length % 2 != 0) {
+ throw new IllegalArgumentException("Expect an even number of strings with pattern / redirect");
+ }
+ int size = expectedMapping.length / 2;
+ for(int i = 0; i < size; i++ ) {
+ expectedEtcMapEntries.add(new ExpectedEtcMapEntry(expectedMapping[2 * i], expectedMapping[2 * i + 1]));
+ }
+ }
+
+ public ExpectedEtcMapping addEtcMapEntry(String pattern, String redirect) {
+ addEtcMapEntry(pattern, false, redirect);
+ return this;
+ }
+ public ExpectedEtcMapping addEtcMapEntry(String pattern, boolean internal, String redirect) {
+ expectedEtcMapEntries.add(new ExpectedEtcMapEntry(pattern, internal, redirect));
+ return this;
+ }
+
+ public void assertEtcMap(String title, List<MapEntry> mapEntries) {
+ assertEquals("Wrong Number of Mappings for: " + title, expectedEtcMapEntries.size(), mapEntries.size());
+ ArrayList<MapEntry> actual = new ArrayList<>(mapEntries);
+ ArrayList<ExpectedEtcMapEntry> expected = new ArrayList<>(expectedEtcMapEntries);
+ for(MapEntry actualMapEntry: actual) {
+ ExpectedEtcMapEntry expectedFound = null;
+ for(ExpectedEtcMapEntry expectedEtcMapEntry: expected) {
+ if(expectedEtcMapEntry.pattern.equals(actualMapEntry.getPattern())) {
+ expectedFound = expectedEtcMapEntry;
+ break;
+ }
+ }
+ if(expectedFound == null) {
+ TestCase.fail("This pattern (" + actualMapEntry.getPattern() + ") is not expected for: " + title);
+ }
+ expectedFound.assertEtcMap(title, actualMapEntry);
+ expected.remove(expectedFound);
+ }
+ for(ExpectedEtcMapEntry expectedEtcMapEntry: expected) {
+ TestCase.fail("Expected Map Entry (" + expectedEtcMapEntry.pattern + ") not provided for: " + title);
+ }
+ }
+ }
+
+ public static class ExpectedEtcMapEntry {
+ private String pattern;
+ private boolean internal;
+ private String redirect;
+
+ public ExpectedEtcMapEntry(String pattern, String redirect) {
+ this(pattern, false, redirect);
+ }
+
+ public ExpectedEtcMapEntry(String pattern, boolean internal, String redirect) {
+ this.pattern = pattern;
+ this.internal = internal;
+ this.redirect = redirect;
+ }
+
+ public void assertEtcMap(String title, MapEntry mapEntry) {
+ assertEquals("Wrong Pattern for " + title, pattern, mapEntry.getPattern());
+ List<String> givenRedirects = new ArrayList<>(Arrays.asList(mapEntry.getRedirect()));
+ assertEquals("Wrong Number of Redirects for: " + title, 1, givenRedirects.size());
+ assertEquals("Wrong Redirect for: " + title, this.redirect, givenRedirects.get(0));
+ assertEquals("Wrong Redirect Type (ext/int) for: " + title, this.internal, mapEntry.isInternal());
+ }
+ }
}