You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by di...@apache.org on 2021/06/25 08:38:06 UTC

[sling-whiteboard] 01/01: improve coverage

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

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

commit 37c19d4118cf9dbb0a8d12b4a36ff3aa61209a89
Author: Dirk Rudolph <di...@apache.org>
AuthorDate: Fri Jun 25 10:31:00 2021 +0200

    improve coverage
---
 sitemap/pom.xml                                    |  12 ++
 .../impl/console/SitemapInventoryPlugin.java       |  48 +++--
 .../sling/sitemap/impl/SitemapServiceImplTest.java |  23 ++-
 .../impl/console/SitemapInventoryPluginTest.java   | 214 +++++++++++++++++++++
 .../SitemapInventoryPluginTest/inventory.json      |  41 ++++
 5 files changed, 314 insertions(+), 24 deletions(-)

diff --git a/sitemap/pom.xml b/sitemap/pom.xml
index a50d532..2b96385 100644
--- a/sitemap/pom.xml
+++ b/sitemap/pom.xml
@@ -247,6 +247,18 @@
             <version>2.1</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.12.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-yaml</artifactId>
+            <version>2.12.3</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <profiles>
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPlugin.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPlugin.java
index f0f32fe..ef7f106 100644
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPlugin.java
+++ b/sitemap/src/main/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPlugin.java
@@ -29,6 +29,7 @@ import org.apache.sling.sitemap.SitemapInfo;
 import org.apache.sling.sitemap.SitemapService;
 import org.apache.sling.sitemap.common.SitemapUtil;
 import org.apache.sling.sitemap.impl.SitemapServiceConfiguration;
+import org.jetbrains.annotations.Nullable;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.component.annotations.Activate;
@@ -115,17 +116,20 @@ public class SitemapInventoryPlugin implements InventoryPrinter {
                 while (infoIt.hasNext()) {
                     SitemapInfo info = infoIt.next();
                     pw.print('{');
-                    pw.print("\"url\":\"");
+                    pw.print("\"name\":\"");
+                    pw.print(escapeDoubleQuotes(info.getName()));
+                    pw.print('"');
+                    pw.print(",\"url\":\"");
                     pw.print(escapeDoubleQuotes(info.getUrl()));
+                    pw.print("\",\"status\":\"");
+                    pw.print(info.getStatus());
                     pw.print('"');
                     if (info.getStoragePath() != null) {
                         pw.print(",\"path\":\"");
                         pw.print(escapeDoubleQuotes(info.getStoragePath()));
-                        pw.print("\",\"status\":\"");
-                        pw.print(info.getStatus());
                         pw.print("\",\"size\":");
                         pw.print(info.getSize());
-                        pw.print(",\"entries\":");
+                        pw.print(",\"urls\":");
                         pw.print(info.getEntries());
                         pw.print(",\"inLimits\":");
                         pw.print(isWithinLimits(info));
@@ -149,8 +153,9 @@ public class SitemapInventoryPlugin implements InventoryPrinter {
     }
 
     private void printText(PrintWriter pw) {
-        pw.println("Apache Sling Sitemap Schedulers");
-        pw.println("-------------------------------");
+        pw.println("# Apache Sling Sitemap Schedulers");
+        pw.println("# -------------------------------");
+        pw.println("schedulers:");
 
         for (ServiceReference<?> ref : bundleContext.getBundle().getRegisteredServices()) {
             Object schedulerExp = ref.getProperty(Scheduler.PROPERTY_SCHEDULER_EXPRESSION);
@@ -167,35 +172,40 @@ public class SitemapInventoryPlugin implements InventoryPrinter {
 
         pw.println();
         pw.println();
-        pw.println("Apache Sling Sitemap Roots");
-        pw.println("--------------------------");
+        pw.println("# Apache Sling Sitemap Roots");
+        pw.println("# --------------------------");
+        pw.println("roots:");
 
         try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
             Iterator<Resource> roots = SitemapUtil.findSitemapRoots(resolver, "/");
             while (roots.hasNext()) {
                 Resource root = roots.next();
+                pw.print("  ");
                 pw.print(root.getPath());
                 pw.print(':');
                 pw.println();
                 for (SitemapInfo info : sitemapService.getSitemapInfo(root)) {
-                    pw.print(" - Url: ");
+                    pw.print("   - Name: ");
+                    pw.print(info.getName());
+                    pw.println();
+                    pw.print("     Url: ");
                     pw.print(info.getUrl());
                     pw.println();
+                    pw.print("     Status: ");
+                    pw.print(info.getStatus());
+                    pw.println();
                     if (info.getStoragePath() != null) {
-                        pw.print("   Path: ");
+                        pw.print("     Path: ");
                         pw.print(info.getStoragePath());
                         pw.println();
-                        pw.print("   Status: ");
-                        pw.print(info.getStatus());
-                        pw.println();
-                        pw.print("   Bytes: ");
+                        pw.print("     Size: ");
                         pw.print(info.getSize());
                         pw.println();
-                        pw.print("   Urls: ");
+                        pw.print("     Urls: ");
                         pw.print(info.getEntries());
                         pw.println();
-                        pw.print("   Within Limits: ");
-                        pw.print(isWithinLimits(info) ? "yes": "no");
+                        pw.print("     Within Limits: ");
+                        pw.print(isWithinLimits(info) ? "yes" : "no");
                         pw.println();
                     }
                 }
@@ -210,7 +220,7 @@ public class SitemapInventoryPlugin implements InventoryPrinter {
         return info.getSize() <= configuration.getMaxSize() && info.getEntries() <= configuration.getMaxEntries();
     }
 
-    private static String escapeDoubleQuotes(String text) {
-        return text.replace("\"", "\\\"");
+    private static String escapeDoubleQuotes(@Nullable String text) {
+        return text == null ? "" : text.replace("\"", "\\\"");
     }
 }
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplTest.java
index 7ca09dc..dc78fae 100644
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplTest.java
+++ b/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplTest.java
@@ -60,6 +60,7 @@ public class SitemapServiceImplTest {
     private final SitemapServiceImpl subject = new SitemapServiceImpl();
     private final SitemapStorage storage = new SitemapStorage();
     private final SitemapGeneratorManagerImpl generatorManager = new SitemapGeneratorManagerImpl();
+    private final SitemapScheduler scheduler = new SitemapScheduler();
     private final SitemapServiceConfiguration sitemapServiceConfiguration = new SitemapServiceConfiguration();
 
     private TestGenerator generator = new TestGenerator() {
@@ -73,6 +74,8 @@ public class SitemapServiceImplTest {
 
     @Mock
     private ServiceUserMapped serviceUser;
+    @Mock
+    private JobManager jobManager;
 
     private Resource deRoot;
     private Resource enRoot;
@@ -105,11 +108,17 @@ public class SitemapServiceImplTest {
         noRoot = context.create().resource("/content/site/nothing");
 
         context.registerService(ServiceUserMapped.class, serviceUser, "subServiceName", "sitemap-writer");
+        context.registerService(ServiceUserMapped.class, serviceUser, "subServiceName", "sitemap-reader");
         context.registerService(SitemapGenerator.class, generator, "service.ranking", 1);
         context.registerService(SitemapGenerator.class, newsGenerator, "service.ranking", 2);
+        context.registerService(JobManager.class, jobManager);
         context.registerInjectActivateService(sitemapServiceConfiguration);
         context.registerInjectActivateService(generatorManager);
         context.registerInjectActivateService(storage);
+        context.registerInjectActivateService(scheduler,
+                "scheduler.name", "default",
+                "scheduler.expression", "never",
+                "names", new String[] { SitemapService.DEFAULT_SITEMAP_NAME });
         context.registerInjectActivateService(subject);
     }
 
@@ -137,7 +146,7 @@ public class SitemapServiceImplTest {
         assertThat(enInfo, hasSize(2));
         assertThat(enInfo, hasItems(
                 eqSitemapInfo("/site/en.sitemap-index.xml", -1, -1, SitemapInfo.Status.ON_DEMAND),
-                eqSitemapInfo("/site/en.sitemap.xml", -1, -1, SitemapInfo.Status.UNKNOWN)
+                eqSitemapInfo("/site/en.sitemap.xml", -1, -1, SitemapInfo.Status.SCHEDULED)
         ));
         assertThat(frInfo, hasSize(3));
         assertThat(frInfo, hasItems(
@@ -160,13 +169,17 @@ public class SitemapServiceImplTest {
     @Test
     public void testSitemapUrlReturnsProperSelectors() throws IOException {
         // given
-        storage.writeSitemap(deRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 100, 1);
+        storage.writeSitemap(deRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 100,
+                1);
         storage.writeSitemap(frRoot, "foobar", new ByteArrayInputStream(new byte[0]), 1, 100, 1);
-        storage.writeSitemap(enRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 100, 1);
+        storage.writeSitemap(enRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 100,
+                1);
         storage.writeSitemap(enNews, "foo", new ByteArrayInputStream(new byte[0]), 1, 100, 1);
         storage.writeSitemap(enNews, "bar", new ByteArrayInputStream(new byte[0]), 1, 100, 1);
-        storage.writeSitemap(itRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 100, 1);
-        storage.writeSitemap(itRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 2, 100, 1);
+        storage.writeSitemap(itRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 100,
+                1);
+        storage.writeSitemap(itRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 2, 100,
+                1);
 
         generator.setNames(deRoot, SitemapService.DEFAULT_SITEMAP_NAME);
         generator.setNames(frRoot, "foobar");
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPluginTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPluginTest.java
new file mode 100644
index 0000000..2503fc3
--- /dev/null
+++ b/sitemap/src/test/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPluginTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.sitemap.impl.console;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.IOUtils;
+import org.apache.felix.inventory.Format;
+import org.apache.sling.api.resource.LoginException;
+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.commons.scheduler.Scheduler;
+import org.apache.sling.sitemap.SitemapInfo;
+import org.apache.sling.sitemap.SitemapService;
+import org.apache.sling.sitemap.impl.SitemapServiceConfiguration;
+import org.apache.sling.testing.mock.jcr.MockJcr;
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.junit5.SlingContext;
+import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+import javax.jcr.Node;
+import javax.jcr.Session;
+import javax.jcr.query.Query;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.Mockito.*;
+
+@ExtendWith({SlingContextExtension.class, MockitoExtension.class})
+public class SitemapInventoryPluginTest {
+
+    public final SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK);
+    private final SitemapInventoryPlugin subject = new SitemapInventoryPlugin();
+    private final SitemapServiceConfiguration configuration = new SitemapServiceConfiguration();
+
+    // some terms are different in text then in json for better readability
+    private static Map<String, String> YAML_TO_JSON = ImmutableMap.of(
+            "within limits", "inlimits"
+    );
+
+    @Mock
+    private SitemapService sitemapService;
+    @Mock
+    private SitemapInfo deInfo;
+    @Mock
+    private SitemapInfo enInfo1;
+    @Mock
+    private SitemapInfo enInfo2;
+    @Mock
+    private ServiceReference<Runnable> schedulerReference1;
+    @Mock
+    private ServiceReference<Runnable> schedulerReference2;
+    @Mock
+    private ServiceReference<Runnable> unknownReference;
+    // we have to use a mock bundle as the osgi-mock one does not implement Bundle#getRegisteredServices()
+    @Mock
+    private Bundle bundle;
+    @Mock
+    private BundleContext bundleContext;
+    // we have to use mock rrf in order to provide the context#resourceResolver() rr to the implementation with the
+    // mocked jcr query responses
+    @Mock
+    private ResourceResolverFactory resourceResolverFactory;
+
+    @BeforeEach
+    public void setup() throws LoginException {
+        ResourceResolver resourceResolver = spy(context.resourceResolver());
+
+        context.registerService(SitemapService.class, sitemapService);
+        context.registerInjectActivateService(configuration, "maxEntries", 999);
+        context.registerService(ResourceResolverFactory.class, resourceResolverFactory, "service.ranking", 100);
+        context.registerInjectActivateService(subject);
+        subject.activate(bundleContext);
+
+        Resource deRoot = context.create().resource("/content/site/de",
+                SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE);
+        Resource enRoot = context.create().resource("/content/site/en",
+                SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE);
+
+        MockJcr.setQueryResult(
+                context.resourceResolver().adaptTo(Session.class),
+                "/jcr:root//*[@sling:sitemapRoot=true] option(index tag slingSitemaps)",
+                Query.XPATH,
+                Arrays.asList(deRoot.adaptTo(Node.class), enRoot.adaptTo(Node.class))
+        );
+
+        when(deInfo.getName()).thenReturn(SitemapService.DEFAULT_SITEMAP_NAME);
+        when(deInfo.getUrl()).thenReturn("/site/de.sitemap.xml");
+        when(deInfo.getStatus()).thenReturn(SitemapInfo.Status.STORAGE);
+        when(deInfo.getSize()).thenReturn(1000);
+        when(deInfo.getEntries()).thenReturn(10);
+        when(deInfo.getStoragePath()).thenReturn("/var/sitemaps/content/site/de/sitemap.xml");
+        when(enInfo1.getName()).thenReturn(SitemapService.SITEMAP_INDEX_NAME);
+        when(enInfo1.getUrl()).thenReturn("/site/en.sitemap-index.xml");
+        when(enInfo1.getStatus()).thenReturn(SitemapInfo.Status.ON_DEMAND);
+        when(enInfo2.getName()).thenReturn(SitemapService.DEFAULT_SITEMAP_NAME);
+        when(enInfo2.getUrl()).thenReturn("/site/en.sitemap.xml");
+        when(enInfo2.getStatus()).thenReturn(SitemapInfo.Status.STORAGE);
+        when(enInfo2.getSize()).thenReturn(10000);
+        when(enInfo2.getEntries()).thenReturn(1000);
+        when(enInfo2.getStoragePath()).thenReturn("/var/sitemaps/content/site/en/sitemap.xml");
+
+        when(bundleContext.getBundle()).thenReturn(bundle);
+        when(bundle.getRegisteredServices()).thenReturn(new ServiceReference[]{
+                schedulerReference1, schedulerReference2, unknownReference
+        });
+        when(schedulerReference1.getProperty(Scheduler.PROPERTY_SCHEDULER_NAME)).thenReturn("sitemap-default");
+        when(schedulerReference1.getProperty(Scheduler.PROPERTY_SCHEDULER_EXPRESSION)).thenReturn("0 0 0 * * * ?");
+        when(schedulerReference2.getProperty(Scheduler.PROPERTY_SCHEDULER_NAME)).thenReturn("sitemap-news");
+        when(schedulerReference2.getProperty(Scheduler.PROPERTY_SCHEDULER_EXPRESSION)).thenReturn("0 */30 * * * * ?");
+        when(resourceResolverFactory.getServiceResourceResolver(any())).thenReturn(resourceResolver);
+
+        doNothing().when(resourceResolver).close();
+        doReturn(Collections.singleton(deInfo))
+                .when(sitemapService).getSitemapInfo(argThat(resourceWithPath(deRoot.getPath())));
+        doReturn(Arrays.asList(enInfo1, enInfo2))
+                .when(sitemapService).getSitemapInfo(argThat(resourceWithPath(enRoot.getPath())));
+    }
+
+    @Test
+    public void testJson() {
+        // given
+        StringWriter writer = new StringWriter();
+
+        // when
+        subject.print(new PrintWriter(writer), Format.JSON, false);
+
+        // then
+        assertJson("SitemapInventoryPluginTest/inventory.json", writer.toString());
+    }
+
+    @Test
+    public void testText() {
+        // given
+        StringWriter writer = new StringWriter();
+
+        // when
+        subject.print(new PrintWriter(writer), Format.TEXT, false);
+
+        // then
+        assertYaml("SitemapInventoryPluginTest/inventory.json", writer.toString());
+    }
+
+    private static void assertYaml(String expected, String given) {
+        assertJson(new YAMLMapper(), expected, given);
+    }
+
+    private static void assertJson(String expected, String given) {
+        assertJson(new ObjectMapper(), expected, given);
+    }
+
+    private static void assertJson(ObjectMapper objectMapper, String expected, String given) {
+        try {
+            InputStream expectedResource = SitemapInventoryPluginTest.class.getClassLoader()
+                    .getResourceAsStream(expected);
+            StringWriter expectedContent = new StringWriter();
+            IOUtils.copy(expectedResource, expectedContent, StandardCharsets.UTF_8);
+            JsonNode expectedJson = objectMapper.readTree(normalizeJson(expectedContent.toString()));
+            JsonNode givenJson = objectMapper.readTree(normalizeJson(given));
+            assertEquals(expectedJson, givenJson);
+        } catch (IOException ex) {
+            fail(ex.getMessage());
+        }
+    }
+
+    private static String normalizeJson(String json) {
+        String lowercase = json.toLowerCase(Locale.ROOT);
+        for (Map.Entry<String, String> entry : YAML_TO_JSON.entrySet()) {
+            lowercase = lowercase.replace(entry.getKey(), entry.getValue());
+        }
+        return lowercase;
+    }
+
+    private static ArgumentMatcher<Resource> resourceWithPath(String path) {
+        return r -> r.getPath().equals(path);
+    }
+}
diff --git a/sitemap/src/test/resources/SitemapInventoryPluginTest/inventory.json b/sitemap/src/test/resources/SitemapInventoryPluginTest/inventory.json
new file mode 100644
index 0000000..bf93196
--- /dev/null
+++ b/sitemap/src/test/resources/SitemapInventoryPluginTest/inventory.json
@@ -0,0 +1,41 @@
+{
+  "schedulers": [
+    {
+      "name": "sitemap-default",
+      "expression": "0 0 0 * * * ?"
+    },
+    {
+      "name": "sitemap-news",
+      "expression": "0 */30 * * * * ?"
+    }
+  ],
+  "roots": {
+    "/content/site/de": [
+      {
+        "name": "<default>",
+        "url": "/site/de.sitemap.xml",
+        "status": "STORAGE",
+        "path": "/var/sitemaps/content/site/de/sitemap.xml",
+        "size": 1000,
+        "urls": 10,
+        "inLimits": true
+      }
+    ],
+    "/content/site/en": [
+      {
+        "name": "<sitemap-index>",
+        "url": "/site/en.sitemap-index.xml",
+        "status": "ON_DEMAND"
+      },
+      {
+        "name": "<default>",
+        "url": "/site/en.sitemap.xml",
+        "status": "STORAGE",
+        "path": "/var/sitemaps/content/site/en/sitemap.xml",
+        "size": 10000,
+        "urls": 1000,
+        "inLimits": false
+      }
+    ]
+  }
+}
\ No newline at end of file