This is an automated email from the ASF dual-hosted git repository. andysch pushed a commit to branch feature/SLING-7768 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git
commit f3b9a004426c59d32ea669bc177116612bc53379 Author: Andreas Schaefer <[email protected]> AuthorDate: Mon Jul 30 13:20:45 2018 -0700 SLING-7768: Added /etc/map tests to the resource resolver and extracted some common test code into MockTestUtil class --- .../impl/EtcMappingResourceResolverTest.java | 276 +++++++++++++++++++++ .../resourceresolver/impl/SimpleValueMapImpl.java | 7 +- .../mapping/AbstractMappingMapEntriesTest.java | 77 ------ .../impl/mapping/EtcMappingMapEntriesTest.java | 11 +- .../mapping/StringInterpolationMapEntriesTest.java | 7 +- .../sling/resourceresolver/util/MockTestUtil.java | 198 +++++++++++++-- 6 files changed, 472 insertions(+), 104 deletions(-) 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 class SimpleValueMapImpl implements ValueMap { 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.resource.path.Path; 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 org.osgi.service.event.EventAdmin; 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.ExecutorService; 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 @@ public abstract class AbstractMappingMapEntriesTest { } } - 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 javax.servlet.http.HttpServletRequest; 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 @@ package org.apache.sling.resourceresolver.impl.mapping; 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.Mockito; 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 @@ public class MockTestUtil { 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 @@ public class MockTestUtil { }); // 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 @@ public class MockTestUtil { }); 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 @@ public class MockTestUtil { 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 class MockTestUtil { 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()); + } + } }
