| /* |
| * 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.resourcemerger.impl; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.commons.collections4.iterators.IteratorIterable; |
| 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.ResourceResolverFactory; |
| import org.apache.sling.hamcrest.ResourceMatchers; |
| import org.apache.sling.resourcemerger.spi.MergedResourcePicker2; |
| import org.apache.sling.spi.resource.provider.ResolveContext; |
| import org.apache.sling.spi.resource.provider.ResourceContext; |
| import org.apache.sling.testing.resourceresolver.MockHelper; |
| import org.apache.sling.testing.resourceresolver.MockResourceResolverFactory; |
| import org.hamcrest.Matchers; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import com.google.common.collect.ImmutableMap; |
| |
| /** |
| * Tests common merging behaviour (independent of actual picker implementation) |
| * |
| */ |
| public class CommonMergedResourceProviderTest { |
| |
| private ResourceResolver resolver; |
| |
| private MergingResourceProvider provider; |
| |
| private Resource base; |
| private Resource overlay; |
| |
| private ResolveContext<Void> ctx; |
| |
| /** |
| * A very simple resource picker which will just merge two different resources (base and overlay) directly on the mount point. |
| * Supports also merging of arbitrary child resources below the mount point. |
| */ |
| private final class SimpleMergedResourcePicker implements MergedResourcePicker2 { |
| |
| private final List<Resource> rootResourcesToMerge; |
| |
| public SimpleMergedResourcePicker() { |
| rootResourcesToMerge = new ArrayList<Resource>(); |
| rootResourcesToMerge.add(base); |
| rootResourcesToMerge.add(overlay); |
| } |
| |
| @Override |
| public List<Resource> pickResources(ResourceResolver resolver, String relativePath, Resource relatedResource) { |
| // merging in the root path is always successfull |
| if (relativePath.isEmpty()) { |
| return rootResourcesToMerge; |
| } else { |
| List<Resource> resourcesToMerge = new ArrayList<Resource>(); |
| // if deeper merging is wanted, check in both locations if the relative path is available at all |
| Resource resourceToPick = base.getChild(relativePath); |
| if (resourceToPick != null) { |
| resourcesToMerge.add(resourceToPick); |
| } else { |
| resourcesToMerge.add(new NonExistingResource(resolver, base.getPath() + "/" + relativePath)); |
| } |
| resourceToPick = overlay.getChild(relativePath); |
| if (resourceToPick != null) { |
| resourcesToMerge.add(resourceToPick); |
| } else { |
| resourcesToMerge.add(new NonExistingResource(resolver, overlay.getPath() + "/" + relativePath)); |
| } |
| return resourcesToMerge; |
| } |
| } |
| } |
| |
| @Before |
| public void setup() throws LoginException, PersistenceException { |
| final ResourceResolverFactory factory = new MockResourceResolverFactory(); |
| this.resolver = factory.getResourceResolver(null); |
| this.ctx = new BasicResolveContext<Void>(resolver); |
| MockHelper.create(this.resolver) |
| .resource("/apps").resource("base") |
| .resource("/apps/overlay").commit(); |
| |
| base = this.resolver.getResource("/apps/base"); |
| overlay = this.resolver.getResource("/apps/overlay"); |
| |
| this.provider = new CRUDMergingResourceProvider("/merged", new SimpleMergedResourcePicker(), true); |
| } |
| |
| @Test |
| public void testHideChildren() throws PersistenceException { |
| // create new child nodes below base and overlay |
| MockHelper.create(this.resolver) |
| .resource("/apps/base/child1").p("property1", "frombase") |
| .resource("/apps/base/child2").p("property1", "frombase") |
| .resource("/apps/base/child1/grandchild").p("propertygrandchild1", "frombase") |
| .resource("/apps/base/child1/grandchild/grandgrandchildfrombase").p("propertygrandgrandchild1", "frombase") |
| .resource("/apps/overlay/child1").p("property1", "fromoverlay") |
| .resource("/apps/overlay/child1/grandchild").p("propertygrandchild1", "fromoverlay") |
| .resource("/apps/overlay/child1/grandchild/grandgrandchildfromoverlay").p("propertygrandgrandchild1", "fromoverlay") |
| .resource("/apps/overlay/child1/grandchild1").p("propertygrandchild1", "fromoverlay") |
| .resource("/apps/overlay/child1/grandchild1/grandgrandchild1").p("propertygrandgrandchild1", "fromoverlay") |
| .resource("/apps/overlay/child3").p("property1", "fromoverlay") |
| .commit(); |
| |
| ModifiableValueMap properties = overlay.adaptTo(ModifiableValueMap.class); |
| properties.put(MergedResourceConstants.PN_HIDE_CHILDREN, "*"); |
| resolver.commit(); |
| |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterableChildren = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| |
| // all overlay resource are still exposed, because hiding children by wildcard only hides children from underlying resources |
| Assert.assertThat(iterableChildren, Matchers.containsInAnyOrder( |
| ResourceMatchers.nameAndProps("child1", Collections.singletonMap("property1", (Object)"fromoverlay")), |
| ResourceMatchers.nameAndProps("child3", Collections.singletonMap("property1", (Object)"fromoverlay")) |
| )); |
| |
| // go down one level! |
| Resource mergedChildResource = this.provider.getResource(ctx, "/merged/child1", ResourceContext.EMPTY_CONTEXT, null); |
| |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterableGrandchildren = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedChildResource), true); |
| |
| // all overlay resource are still exposed, because hiding children by wildcard only hides children from underlying resources |
| Assert.assertThat(iterableGrandchildren, Matchers.containsInAnyOrder( |
| ResourceMatchers.nameAndProps("grandchild", Collections.singletonMap("propertygrandchild1", (Object)"fromoverlay")), |
| ResourceMatchers.nameAndProps("grandchild1", Collections.singletonMap("propertygrandchild1", (Object)"fromoverlay"))) |
| ); |
| |
| // go down two levels |
| Resource mergedGrandChildResource = this.provider.getResource(ctx, "/merged/child1/grandchild", ResourceContext.EMPTY_CONTEXT, null); |
| |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterableGrandGrandchildren = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedGrandChildResource), true); |
| |
| // all overlay resource are still exposed, because hiding children by wildcard only hides children from underlying resources |
| Assert.assertThat(iterableGrandGrandchildren, Matchers.contains( |
| ResourceMatchers.nameAndProps("grandgrandchildfromoverlay", Collections.singletonMap("propertygrandgrandchild1", (Object)"fromoverlay"))) |
| ); |
| |
| // go down two levels (in node which is only available in overlay!) |
| mergedGrandChildResource = this.provider.getResource(ctx, "/merged/child1/grandchild1", ResourceContext.EMPTY_CONTEXT, null); |
| |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| iterableGrandGrandchildren = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedGrandChildResource), true); |
| |
| // all overlay resource are still exposed, because hiding children by wildcard only hides children from underlying resources |
| Assert.assertThat(iterableGrandGrandchildren, Matchers.contains( |
| ResourceMatchers.nameAndProps("grandgrandchild1", Collections.singletonMap("propertygrandgrandchild1", (Object)"fromoverlay"))) |
| ); |
| |
| // now hide by explicit value |
| properties.put(MergedResourceConstants.PN_HIDE_CHILDREN, "child1"); |
| resolver.commit(); |
| |
| // child1 is no longer exposed from overlay, because hiding children by name hides children from underlying as well as from local resources, child2 is exposed from base |
| iterableChildren = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| Assert.assertThat(iterableChildren, Matchers.containsInAnyOrder( |
| ResourceMatchers.name("child2"), |
| ResourceMatchers.name("child3"))); |
| |
| // now hide by negated value (hide all children except for the one with name child2) |
| properties.put(MergedResourceConstants.PN_HIDE_CHILDREN, new String[]{"!child2", "*", "child3"}); |
| resolver.commit(); |
| |
| iterableChildren = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| Assert.assertThat(iterableChildren, Matchers.containsInAnyOrder( |
| ResourceMatchers.name("child2") |
| )); |
| } |
| |
| @Test |
| public void testHideChildrenWithResourceNamesStartingWithExclamationMark() throws PersistenceException { |
| // create new child nodes below base and overlay |
| MockHelper.create(this.resolver) |
| .resource("/apps/base/!child1").p("property1", "frombase") |
| .resource("/apps/overlay/!child1").p("property1", "fromoverlay") |
| .resource("/apps/overlay/!child3").p("property1", "fromoverlay") |
| .commit(); |
| |
| ModifiableValueMap properties = overlay.adaptTo(ModifiableValueMap.class); |
| properties.put(MergedResourceConstants.PN_HIDE_CHILDREN, "!!child3"); // escape the resource name with another exclamation mark |
| resolver.commit(); |
| |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterable = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| |
| // the resource named "!child3" should be hidden |
| Assert.assertThat(iterable, Matchers.contains(ResourceMatchers.nameAndProps("!child1", Collections.singletonMap("property1", (Object)"fromoverlay")))); |
| } |
| |
| @Test |
| public void testHideChildrenBeingSetOnParent() throws PersistenceException { |
| // create new child nodes below base and overlay |
| MockHelper.create(this.resolver) |
| .resource("/apps/base/child").p("property1", "frombase").resource("grandchild").p("property1", "frombase") |
| .resource("/apps/base/child2").p("property1", "frombase") |
| .resource("/apps/overlay/child").p("property1", "fromoverlay") |
| .commit(); |
| |
| ModifiableValueMap properties = overlay.adaptTo(ModifiableValueMap.class); |
| properties.put(MergedResourceConstants.PN_HIDE_CHILDREN, "*"); |
| resolver.commit(); |
| |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| |
| // the child was hidden on the parent (but only for the underlying resource), the local child from the overlay is still exposed |
| Assert.assertThat(provider.getResource(ctx, "/merged/child", ResourceContext.EMPTY_CONTEXT, mergedResource), ResourceMatchers.nameAndProps("child", Collections.singletonMap("property1", (Object)"fromoverlay"))); |
| } |
| |
| @Test |
| public void testHideProperties() { |
| ModifiableValueMap properties = base.adaptTo(ModifiableValueMap.class); |
| properties.put("property1", "frombase"); |
| properties.put("property2", "frombase"); |
| properties.put(MergedResourceConstants.PN_HIDE_CHILDREN, "some invalid resource"); |
| |
| // hide with wildcard |
| ModifiableValueMap overlayProperties = overlay.adaptTo(ModifiableValueMap.class); |
| overlayProperties.put(MergedResourceConstants.PN_HIDE_PROPERTIES, "*"); |
| Map<String, Object> expectedProperties = new HashMap<String, Object>(); |
| expectedProperties.put("property1", "fromoverlay"); |
| expectedProperties.put("property3", "fromoverlay"); |
| overlayProperties.putAll(expectedProperties); |
| this.provider = new CRUDMergingResourceProvider("/merged", new SimpleMergedResourcePicker(), false); |
| |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| // property1 is still exposed from overlay, because hiding properties by wildcard only hides children from underlying resources |
| Assert.assertThat(mergedResource, ResourceMatchers.props(expectedProperties)); |
| // all properties from underlying resource are hidden! |
| Assert.assertThat(mergedResource, Matchers.not(ResourceMatchers.props(properties))); |
| // make sure no special properties are exposed |
| Assert.assertFalse(mergedResource.getValueMap().containsKey(MergedResourceConstants.PN_HIDE_CHILDREN)); |
| Assert.assertFalse(mergedResource.getValueMap().containsKey(MergedResourceConstants.PN_HIDE_PROPERTIES)); |
| |
| // hide by value |
| overlayProperties.put(MergedResourceConstants.PN_HIDE_PROPERTIES, new String[]{"property1"}); |
| mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| expectedProperties.put("property2", "frombase"); |
| expectedProperties.remove("property1"); |
| // property2 and property 3 are still exposed |
| Assert.assertThat(mergedResource, ResourceMatchers.props(expectedProperties)); |
| // property1 is no longer exposed from overlay nor base, because hiding properties by name also hides local properties |
| Assert.assertThat(mergedResource, Matchers.not(ResourceMatchers.props(Collections.singletonMap("property1", (Object)"fromoverlay")))); |
| |
| // make sure no special properties are exposed |
| Assert.assertFalse(mergedResource.getValueMap().containsKey(MergedResourceConstants.PN_HIDE_CHILDREN)); |
| Assert.assertFalse(mergedResource.getValueMap().containsKey(MergedResourceConstants.PN_HIDE_PROPERTIES)); |
| } |
| |
| @Test |
| public void testHidePropertiesWithMultiValue() { |
| ModifiableValueMap properties = base.adaptTo(ModifiableValueMap.class); |
| properties.put("!property1", "frombase"); |
| properties.put("!!property2", "frombase"); |
| properties.put("!!!property3", "frombase"); |
| properties.put(MergedResourceConstants.PN_HIDE_CHILDREN, "some invalid resource"); |
| |
| // hide by value |
| ModifiableValueMap overlayProperties = overlay.adaptTo(ModifiableValueMap.class); |
| overlayProperties.put(MergedResourceConstants.PN_HIDE_PROPERTIES, new String[]{"!!property1", "!!!!!!property3"}); |
| |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| Map<String, Object> expectedProperties = new HashMap<String, Object>(); |
| expectedProperties.put("!!property2", "frombase"); |
| // property2 is still exposed |
| Assert.assertThat(mergedResource, ResourceMatchers.props(expectedProperties)); |
| // property1 is no longer exposed from overlay nor base, because hiding properties by name also hides local properties |
| Assert.assertThat(mergedResource, Matchers.not(ResourceMatchers.props(Collections.singletonMap("!property1", (Object)"frombase")))); |
| Assert.assertThat(mergedResource, Matchers.not(ResourceMatchers.props(Collections.singletonMap("!!!property3", (Object)"frombase")))); |
| } |
| |
| @Test |
| public void testOrderOfPartiallyOverwrittenChildren() throws PersistenceException { |
| // see https://issues.apache.org/jira/browse/SLING-4915 |
| // and https://issues.apache.org/jira/browse/SLING-6956 |
| // create new child nodes below base and overlay |
| MockHelper.create(this.resolver) |
| .resource("/apps/base/child1") |
| .resource("/apps/base/child2") |
| .resource("/apps/base/child3") |
| .resource("/apps/overlay/child4") |
| .resource("/apps/overlay/child2") |
| .resource("/apps/overlay/child3") |
| .commit(); |
| |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterable = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| |
| Assert.assertThat(iterable, Matchers.contains(ResourceMatchers.name("child1"),ResourceMatchers.name("child4"), ResourceMatchers.name("child2"), ResourceMatchers.name("child3"))); |
| |
| } |
| |
| @Test |
| public void testOrderOfPartiallyOverlappingChildren() throws PersistenceException { |
| // see https://issues.apache.org/jira/browse/SLING-4915 |
| // and https://issues.apache.org/jira/browse/SLING-6956 |
| // create new child nodes below base and overlay |
| MockHelper.create(this.resolver) |
| .resource("/apps/base/child1") |
| .resource("/apps/base/child2") |
| .resource("/apps/base/child3") |
| .resource("/apps/overlay/child4") |
| .resource("/apps/overlay/child2") |
| .resource("/apps/overlay/child3") |
| .commit(); |
| |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterable = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| |
| Assert.assertThat(iterable, Matchers.contains(ResourceMatchers.name("child1"),ResourceMatchers.name("child4"), ResourceMatchers.name("child2"), ResourceMatchers.name("child3"))); |
| } |
| |
| @Test |
| public void testOrderOfPartiallyOverlappingChildrenWithDifferentOrder() throws PersistenceException { |
| // see https://issues.apache.org/jira/browse/SLING-4915 |
| // and https://issues.apache.org/jira/browse/SLING-6956 |
| // create new child nodes below base and overlay |
| MockHelper.create(this.resolver) |
| .resource("/apps/base/child1") |
| .resource("/apps/base/child3") |
| .resource("/apps/base/child5") |
| .resource("/apps/overlay/child2") |
| .resource("/apps/overlay/child3") |
| .resource("/apps/overlay/child4") |
| .resource("/apps/overlay/child1") |
| .commit(); |
| |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterable = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| |
| Assert.assertThat(iterable, Matchers.contains(ResourceMatchers.name("child5"),ResourceMatchers.name("child2"), ResourceMatchers.name("child3"), ResourceMatchers.name("child4"),ResourceMatchers.name("child1"))); |
| } |
| |
| |
| @Test |
| public void testOrderOfNonOverlappingChildren() throws PersistenceException { |
| // create new child nodes below base and overlay |
| // https://issues.apache.org/jira/browse/SLING-6956 |
| MockHelper.create(this.resolver) |
| .resource("/apps/base/child1") |
| .resource("/apps/base/child2") |
| .resource("/apps/overlay/child3") |
| .resource("/apps/overlay/child4") |
| .commit(); |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterable = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| |
| Assert.assertThat(iterable, Matchers.contains(ResourceMatchers.name("child1"),ResourceMatchers.name("child2"), ResourceMatchers.name("child3"), ResourceMatchers.name("child4"))); |
| } |
| |
| @Test |
| public void testOrderOfFullyOverlappingChildren() throws PersistenceException { |
| // create new child nodes below base and overlay |
| // https://issues.apache.org/jira/browse/SLING-6956 |
| MockHelper.create(this.resolver) |
| .resource("/apps/base/child1") |
| .resource("/apps/base/child2") |
| .resource("/apps/base/child3") |
| .resource("/apps/overlay/child2") |
| .commit(); |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterable = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| |
| Assert.assertThat(iterable, Matchers.contains(ResourceMatchers.name("child1"),ResourceMatchers.name("child2"), ResourceMatchers.name("child3"))); |
| } |
| |
| @Test |
| public void testOrderOfFullyOverlappingChildrenWithDifferentOrder() throws PersistenceException { |
| // create new child nodes below base and overlay |
| // https://issues.apache.org/jira/browse/SLING-6956 |
| MockHelper.create(this.resolver) |
| .resource("/apps/base/child1") |
| .resource("/apps/base/child2") |
| .resource("/apps/base/child3") |
| .resource("/apps/overlay/child3") |
| .resource("/apps/overlay/child2") |
| .commit(); |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterable = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| |
| Assert.assertThat(iterable, Matchers.contains(ResourceMatchers.name("child1"),ResourceMatchers.name("child2"), ResourceMatchers.name("child3"))); |
| } |
| |
| @Test |
| public void testOrderBeingModifiedThroughOrderBefore() throws PersistenceException { |
| // create new child nodes below base and overlay |
| MockHelper.create(this.resolver) |
| .resource("/apps/base/child1") |
| .resource("/apps/base/child2") |
| .resource("/apps/base/child3") |
| .resource("/apps/base/child4") |
| .resource("/apps/overlay/child5").p(MergedResourceConstants.PN_ORDER_BEFORE, "child2") |
| .resource("/apps/overlay/child6").p(MergedResourceConstants.PN_ORDER_BEFORE, "child2") |
| .resource("/apps/overlay/child7").p(MergedResourceConstants.PN_ORDER_BEFORE, "child4") |
| .resource("/apps/overlay/child8").p(MergedResourceConstants.PN_ORDER_BEFORE, "child3") |
| .resource("/apps/overlay/child9").p(MergedResourceConstants.PN_ORDER_BEFORE, "child3") |
| .resource("/apps/overlay/child10") |
| .resource("/apps/overlay/child3") |
| .commit(); |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterable = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| |
| Assert.assertThat(iterable, Matchers.contains(ResourceMatchers.name("child1"),ResourceMatchers.name("child5"), ResourceMatchers.name("child6"), ResourceMatchers.name("child2"), ResourceMatchers.name("child8"),ResourceMatchers.name("child9"), ResourceMatchers.name("child7"),ResourceMatchers.name("child4"),ResourceMatchers.name("child10"),ResourceMatchers.name("child3"))); |
| } |
| |
| @Test |
| public void testOrderBeingModifiedThroughOrderBeforeAndFullyOverlappingChildren() throws PersistenceException { |
| |
| // create new child nodes below base and overlay |
| MockHelper.create(this.resolver) |
| .resource("/apps/base/child1") |
| .resource("/apps/base/child2") |
| .resource("/apps/overlay/child1") |
| .resource("/apps/overlay/child3").p(MergedResourceConstants.PN_ORDER_BEFORE, "child2") |
| .resource("/apps/overlay/child2") |
| .commit(); |
| |
| Resource mergedResource = this.provider.getResource(ctx, "/merged", ResourceContext.EMPTY_CONTEXT, null); |
| // convert the iterator returned by list children into an iterable (to be able to perform some tests) |
| IteratorIterable<Resource> iterable = new IteratorIterable<Resource>(provider.listChildren(ctx, mergedResource), true); |
| |
| Assert.assertThat(iterable, Matchers.contains(ResourceMatchers.name("child1"),ResourceMatchers.name("child3"),ResourceMatchers.name("child2"))); |
| } |
| |
| @Test |
| public void testNodesDefinedInBaseStructureShouldBeReflectedInOverlaidStructureWhenNotExcludedInCombinationWithWildcard() throws PersistenceException { |
| final String title = "title"; |
| final String field = "field"; |
| final String description = "description"; |
| |
| MockHelper.create(this.resolver) |
| // Set up the base structure, including a tab that is not desired in the overlaid situation |
| .resource("/apps/base/test") |
| .resource("/apps/base/test/tabs") |
| .resource("/apps/base/test/tabs/undesired-tab") |
| .p(title, "Undesired Tab") |
| .resource("/apps/base/test/tabs/desired-tab") |
| .p(title, "Desired Tab") |
| // Set up a field inside the items node in the desired tab, that should be reflected in the overlaid situation |
| .resource("/apps/base/test/tabs/desired-tab/items") |
| .resource("/apps/base/test/tabs/desired-tab/items/" + field) |
| // Define a property on the field inside the base tab, that should be reflected int he overlaid situation |
| .p(field, field) |
| // Set up the overlaid structure, defining the tabs again |
| .resource("/apps/overlay/test") |
| .resource("/apps/overlay/test/tabs") |
| // Explicitly define that *all* children need to be hidden from the base structure, except for the desired tab |
| .p(MergedResourceConstants.PN_HIDE_CHILDREN, new String[]{"!desired-tab", "*"}) |
| .resource("/apps/overlay/test/tabs/desired-tab") |
| .resource("/apps/overlay/test/tabs/desired-tab/items") |
| .resource("/apps/overlay/test/tabs/desired-tab/items/" + field) |
| // Define an additional property on a node inside the desired tab, so that it should end up getting 2 properties |
| .p(description, description) |
| .commit(); |
| |
| // Ensure that the undesired tab is not found |
| final Resource tabsResource = this.provider.getResource(ctx, "/merged/test/tabs", ResourceContext.EMPTY_CONTEXT, null); |
| Assert.assertNotNull(tabsResource); |
| final IteratorIterable<Resource> tabs = new IteratorIterable<Resource>(this.provider.listChildren(ctx, tabsResource), true); |
| Assert.assertThat(tabs, Matchers.contains( |
| ResourceMatchers.nameAndProps("desired-tab", title, "Desired Tab") |
| )); |
| |
| // Ensure that the desired tab also has the newly added description, as well as its original name |
| final Resource desiredTabItemsResource = this.provider.getResource(ctx, "/merged/test/tabs/desired-tab/items", ResourceContext.EMPTY_CONTEXT, null); |
| Assert.assertNotNull(desiredTabItemsResource); |
| final IteratorIterable<Resource> desiredTabItems = new IteratorIterable<Resource>(provider.listChildren(ctx, desiredTabItemsResource), true); |
| Assert.assertThat(desiredTabItems, Matchers.contains( |
| ResourceMatchers.nameAndProps(field, ImmutableMap.<String, Object>builder() |
| .put(field, field) |
| .put(description, description) |
| .build()) |
| ) |
| ); |
| } |
| } |