This is an automated email from the ASF dual-hosted git repository.

reschke pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git


The following commit(s) were added to refs/heads/master by this push:
     new 52a802d6 SLING-12804: alias refactoring - improve test coverage of 
query statements (#181)
52a802d6 is described below

commit 52a802d6adf294f4f3e3e678abd49bc101dc1005
Author: Julian Reschke <[email protected]>
AuthorDate: Wed Jun 4 18:09:26 2025 +0200

    SLING-12804: alias refactoring - improve test coverage of query statements 
(#181)
---
 .../impl/mapping/AliasHandler.java                 |  37 ++---
 .../impl/mapping/AliasMapEntriesTest.java          | 162 +++++++++++++++++----
 2 files changed, 148 insertions(+), 51 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/AliasHandler.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/AliasHandler.java
index da158284..fb2efff0 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/AliasHandler.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/AliasHandler.java
@@ -32,6 +32,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
 
 import org.apache.sling.api.resource.LoginException;
 import org.apache.sling.api.resource.QuerySyntaxException;
@@ -166,7 +167,7 @@ class AliasHandler {
     }
 
     boolean doAddAlias(@NotNull Resource resource) {
-        if (this.aliasMapsMap != UNITIALIZED_MAP) {
+        if (usesCache()) {
             return loadAlias(resource, this.aliasMapsMap, null, null);
         } else {
             return false;
@@ -185,7 +186,7 @@ class AliasHandler {
             @NotNull String contentPath,
             @Nullable String path,
             @NotNull Runnable notifyOfChange) {
-        if (this.aliasMapsMap != UNITIALIZED_MAP) {
+        if (usesCache()) {
             return removeAliasInMap(resolver, contentPath, path, 
notifyOfChange);
         } else {
             return false;
@@ -279,7 +280,7 @@ class AliasHandler {
      * @return {@code true} if any change
      */
     boolean doUpdateAlias(@NotNull Resource resource) {
-        if (this.aliasMapsMap != UNITIALIZED_MAP) {
+        if (usesCache()) {
             return doUpdateAliasInMap(resource);
         } else {
             return false;
@@ -321,16 +322,14 @@ class AliasHandler {
     }
 
     public @NotNull Map<String, Collection<String>> getAliasMap(@Nullable 
String parentPath) {
-        Map<String, Collection<String>> result = this.aliasMapsMap != 
UNITIALIZED_MAP
-                ? getAliasMapFromCache(parentPath)
-                : getAliasMapFromRepo(parentPath);
+        Map<String, Collection<String>> result =
+                usesCache() ? getAliasMapFromCache(parentPath) : 
getAliasMapFromRepo(parentPath);
         return result != null ? result : Collections.emptyMap();
     }
 
     public @NotNull Map<String, Collection<String>> getAliasMap(@NotNull 
Resource parent) {
-        Map<String, Collection<String>> result = this.aliasMapsMap != 
UNITIALIZED_MAP
-                ? getAliasMapFromCache(parent.getPath())
-                : getAliasMapFromRepo(parent);
+        Map<String, Collection<String>> result =
+                usesCache() ? getAliasMapFromCache(parent.getPath()) : 
getAliasMapFromRepo(parent);
         return result != null ? result : Collections.emptyMap();
     }
 
@@ -440,24 +439,14 @@ class AliasHandler {
     private String generateAliasQuery() {
         Set<String> allowedLocations = this.factory.getAllowedAliasLocations();
 
-        StringBuilder baseQuery = new StringBuilder("SELECT [sling:alias] FROM 
[nt:base] WHERE");
+        StringBuilder baseQuery = new StringBuilder("SELECT [sling:alias] FROM 
[nt:base] WHERE ");
 
         if (allowedLocations.isEmpty()) {
-            baseQuery.append(" ").append(QueryBuildHelper.excludeSystemPath());
+            baseQuery.append(QueryBuildHelper.excludeSystemPath());
         } else {
-            Iterator<String> pathIterator = allowedLocations.iterator();
-            baseQuery.append(" (");
-            String sep = "";
-            while (pathIterator.hasNext()) {
-                String prefix = pathIterator.next();
-                baseQuery
-                        .append(sep)
-                        .append("isdescendantnode('")
-                        .append(QueryBuildHelper.escapeString(prefix))
-                        .append("')");
-                sep = " OR ";
-            }
-            baseQuery.append(")");
+            baseQuery.append(allowedLocations.stream()
+                    .map(location -> "isdescendantnode('" + 
QueryBuildHelper.escapeString(location) + "')")
+                    .collect(Collectors.joining(" OR ", "(", ")")));
         }
 
         baseQuery.append(" AND [sling:alias] IS NOT NULL");
diff --git 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AliasMapEntriesTest.java
 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AliasMapEntriesTest.java
index 68f42126..53a8cc9d 100644
--- 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AliasMapEntriesTest.java
+++ 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AliasMapEntriesTest.java
@@ -18,12 +18,14 @@
  */
 package org.apache.sling.resourceresolver.impl.mapping;
 
+import java.io.IOException;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -31,7 +33,11 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.QuerySyntaxException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.path.Path;
@@ -170,23 +176,34 @@ public class AliasMapEntriesTest extends 
AbstractMappingMapEntriesTest {
         method.invoke(mapEntries, path, bool);
     }
 
-    private void internal_test_simple_alias_support(boolean onJcrContent) {
-        prepareMapEntriesForAlias(onJcrContent, false, "alias");
+    private void internal_test_simple_alias_support(boolean onJcrContent, 
boolean cached) {
+        Resource parent = prepareMapEntriesForAlias(onJcrContent, false, 
!cached, false, "alias");
         mapEntries.ah.initializeAliases();
-        Map<String, Collection<String>> aliasMap = 
mapEntries.getAliasMap("/parent");
-        assertNotNull(aliasMap);
-        assertTrue(aliasMap.containsKey("child"));
-        assertEquals(List.of("alias"), aliasMap.get("child"));
+
+        Map<String, Collection<String>> aliasMapString = 
mapEntries.getAliasMap("/parent");
+        assertNotNull(aliasMapString);
+        assertTrue(aliasMapString.containsKey("child"));
+        assertEquals(List.of("alias"), aliasMapString.get("child"));
+
+        Map<String, Collection<String>> aliasMapResource = 
mapEntries.getAliasMap(parent);
+        assertNotNull(aliasMapResource);
+        assertTrue(aliasMapResource.containsKey("child"));
+        assertEquals(List.of("alias"), aliasMapResource.get("child"));
     }
 
     @Test
     public void test_simple_alias_support() {
-        internal_test_simple_alias_support(false);
+        internal_test_simple_alias_support(false, true);
+    }
+
+    @Test
+    public void test_simple_alias_support_uncached() {
+        internal_test_simple_alias_support(false, false);
     }
 
     @Test
     public void test_simple_alias_support_on_jcr_content() {
-        internal_test_simple_alias_support(true);
+        internal_test_simple_alias_support(true, true);
     }
 
     private void internal_test_simple_multi_alias_support(boolean 
onJcrContent) {
@@ -200,11 +217,18 @@ public class AliasMapEntriesTest extends 
AbstractMappingMapEntriesTest {
 
     @Test
     public void 
internal_test_simple_alias_support_throwing_unsupported_operation_exception_exception()
 {
-        prepareMapEntriesForAlias(false, false, 
UnsupportedOperationException.class, "foo", "bar");
+        prepareMapEntriesForAlias(false, false, true, false, "foo", "bar");
         mapEntries.ah.initializeAliases();
         assertFalse(mapEntries.ah.usesCache());
     }
 
+    @Test
+    public void 
internal_test_simple_alias_support_throwing_query_syntax_exception_exception() {
+        prepareMapEntriesForAlias(false, false, false, true, "foo", "bar");
+        mapEntries.ah.initializeAliases();
+        assertTrue(mapEntries.ah.usesCache());
+    }
+
     @Test
     public void test_simple_multi_alias_support() {
         internal_test_simple_multi_alias_support(false);
@@ -250,43 +274,69 @@ public class AliasMapEntriesTest extends 
AbstractMappingMapEntriesTest {
     }
 
     private void prepareMapEntriesForAlias(boolean onJcrContent, boolean 
withNullParent, String... aliases) {
-        prepareMapEntriesForAlias(onJcrContent, withNullParent, null, aliases);
+        prepareMapEntriesForAlias(onJcrContent, withNullParent, false, false, 
aliases);
     }
 
-    private void prepareMapEntriesForAlias(
+    private Resource prepareMapEntriesForAlias(
             boolean onJcrContent,
             boolean withNullParent,
-            Class<? extends Exception> queryThrowsWith,
+            boolean queryAlwaysThrows,
+            boolean pagedQueryThrows,
             String... aliases) {
         Resource parent = mock(Resource.class);
-        when(parent.getPath()).thenReturn("/parent");
+        Resource result = mock(Resource.class);
+        Resource content = mock(Resource.class);
 
-        final Resource result = mock(Resource.class);
-        final Resource content = mock(Resource.class);
-        final Resource aliasResource = onJcrContent ? content : result;
+        when(parent.getChildren()).thenReturn(Set.of(result));
+        when(result.getParent()).thenReturn(null); // should be root
+        when(parent.getPath()).thenReturn("/parent");
+        when(parent.getName()).thenReturn("parent");
 
+        when(result.getChildren()).thenReturn(Set.of(content));
         when(result.getParent()).thenReturn(withNullParent && !onJcrContent ? 
null : parent);
         when(result.getPath()).thenReturn("/parent/child");
         when(result.getName()).thenReturn("child");
 
+        when(content.getChildren()).thenReturn(Set.of());
         when(content.getParent()).thenReturn(withNullParent && onJcrContent ? 
null : result);
         when(content.getPath()).thenReturn("/parent/child/jcr:content");
         when(content.getName()).thenReturn("jcr:content");
 
+        Resource aliasResource = onJcrContent ? content : result;
+
         
when(aliasResource.getValueMap()).thenReturn(buildValueMap(ResourceResolverImpl.PROP_ALIAS,
 aliases));
 
-        if (queryThrowsWith == null) {
-            when(resourceResolver.findResources(anyString(), eq("JCR-SQL2")))
-                    .thenAnswer((Answer<Iterator<Resource>>) invocation -> {
-                        if 
(invocation.getArguments()[0].toString().contains(ResourceResolverImpl.PROP_ALIAS))
 {
+        when(resourceResolver.getResource(anyString())).thenAnswer(invocation 
-> {
+            String path = invocation.getArgument(0);
+            if (path.equals(parent.getPath())) {
+                return parent;
+            } else if (path.equals(result.getPath())) {
+                return result;
+            } else if (path.equals(content.getPath())) {
+                return content;
+            } else {
+                return null;
+            }
+        });
+
+        when(resourceResolver.findResources(anyString(), eq("JCR-SQL2")))
+                .thenAnswer((Answer<Iterator<Resource>>) invocation -> {
+                    String query = invocation.getArgument(0);
+                    if (queryAlwaysThrows) {
+                        throw new UnsupportedOperationException("test case 
configured to always throw: " + query);
+                    } else {
+                        if (pagedQueryThrows && matchesPagedQuery(query)) {
+                            throw new QuerySyntaxException(
+                                    "test case configured to throw for paged 
queries", query, "JCR-SQL2");
+                        } else if (query.equals(AQ_SIMPLE) || 
matchesPagedQuery(query)) {
                             return List.of(aliasResource).iterator();
                         } else {
-                            return Collections.emptyIterator();
+                            throw new RuntimeException("unexpected query: " + 
query);
                         }
-                    });
-        } else {
-            when(resourceResolver.findResources(anyString(), 
eq("JCR-SQL2"))).thenThrow(queryThrowsWith);
-        }
+                    }
+                });
+
+        return parent;
     }
 
     @Test
@@ -308,7 +358,8 @@ public class AliasMapEntriesTest extends 
AbstractMappingMapEntriesTest {
 
         when(resourceResolver.findResources(anyString(), eq("JCR-SQL2")))
                 .thenAnswer((Answer<Iterator<Resource>>) invocation -> {
-                    if 
(invocation.getArguments()[0].toString().contains(ResourceResolverImpl.PROP_ALIAS))
 {
+                    String query = invocation.getArgument(0);
+                    if (query.equals(AQ_SIMPLE) || matchesPagedQuery(query)) {
                         return Arrays.asList(result, secondResult).iterator();
                     } else {
                         return Collections.emptyIterator();
@@ -345,7 +396,8 @@ public class AliasMapEntriesTest extends 
AbstractMappingMapEntriesTest {
 
         when(resourceResolver.findResources(anyString(), eq("JCR-SQL2")))
                 .thenAnswer((Answer<Iterator<Resource>>) invocation -> {
-                    if 
(invocation.getArguments()[0].toString().contains(ResourceResolverImpl.PROP_ALIAS))
 {
+                    String query = invocation.getArgument(0);
+                    if (query.equals(AQ_SIMPLE) || matchesPagedQuery(query)) {
                         return List.of(node, content).iterator();
                     } else {
                         return Collections.emptyIterator();
@@ -380,6 +432,30 @@ public class AliasMapEntriesTest extends 
AbstractMappingMapEntriesTest {
         assertEquals("/content", actualContent);
     }
 
+    @Test
+    public void test_allowed_locations_query() throws LoginException, 
IOException {
+        
when(resourceResolverFactory.getAllowedAliasLocations()).thenReturn(Set.of("/a",
 "/'b'"));
+        Set<String> queryMade = new HashSet<>();
+        when(resourceResolver.findResources(anyString(), eq("JCR-SQL2")))
+                .thenAnswer((Answer<Iterator<Resource>>) invocation -> {
+                    String query = invocation.getArgument(0);
+                    if (query.contains("alias")) {
+                        queryMade.add(query);
+                    }
+                    return Collections.emptyIterator();
+                });
+
+        new MapEntries(resourceResolverFactory, bundleContext, eventAdmin, 
stringInterpolationProvider, metrics);
+
+        assertTrue("seems no alias query was made", !queryMade.isEmpty());
+        String match1 = "(isdescendantnode('/a') OR 
isdescendantnode('/''b'''))";
+        String match2 = "(isdescendantnode('/''b''') OR 
isdescendantnode('/a'))";
+        String actual = queryMade.iterator().next();
+        assertTrue(
+                "query should contain '" + match1 + "' (or reversed), but was: 
'" + actual + "'",
+                actual.contains(match1) || actual.contains(match2));
+    }
+
     // SLING-3727
     @Test
     public void test_doAddAliasAttributesWithDisableAliasOptimization() throws 
Exception {
@@ -1228,4 +1304,36 @@ public class AliasMapEntriesTest extends 
AbstractMappingMapEntriesTest {
         ah.initializeAliases();
         assertFalse("alias handler should not have set up cache", 
ah.usesCache());
     }
+
+    // utilities for testing alias queries
+
+    // used for paged query of all
+    private static final String AQ_PAGED_START =
+            "SELECT [sling:alias] FROM [nt:base] WHERE NOT 
isdescendantnode('/jcr:system') AND [sling:alias] IS NOT NULL AND 
FIRST([sling:alias]) >= '";
+    private static final String AQ_PAGED_END = "' ORDER BY 
FIRST([sling:alias])";
+
+    private static final Pattern AQ_PAGED_PATTERN =
+            Pattern.compile(Pattern.quote(AQ_PAGED_START) + 
"(?<path>\\p{Alnum}*)" + Pattern.quote(AQ_PAGED_END));
+
+    // used when paged query not available
+    private static final String AQ_SIMPLE =
+            "SELECT [sling:alias] FROM [nt:base] WHERE NOT 
isdescendantnode('/jcr:system') AND [sling:alias] IS NOT NULL";
+
+    // sanity test on matcher
+    @Test
+    public void testMatcher() {
+        assertTrue(AQ_PAGED_PATTERN.matcher(AQ_PAGED_START + 
AQ_PAGED_END).matches());
+        assertTrue(
+                AQ_PAGED_PATTERN.matcher(AQ_PAGED_START + "xyz" + 
AQ_PAGED_END).matches());
+        assertEquals(
+                1,
+                AQ_PAGED_PATTERN.matcher(AQ_PAGED_START + "xyz" + 
AQ_PAGED_END).groupCount());
+        Matcher m1 = AQ_PAGED_PATTERN.matcher(AQ_PAGED_START + "xyz" + 
AQ_PAGED_END);
+        assertTrue(m1.find());
+        assertEquals("xyz", m1.group("path"));
+    }
+
+    private boolean matchesPagedQuery(String query) {
+        return AQ_PAGED_PATTERN.matcher(query).matches();
+    }
 }

Reply via email to