Adobe-Consulting-Services/acs-aem-commons

View on GitHub
bundle/src/test/java/com/adobe/acs/commons/redirects/servlets/ImportRedirectMapServletTest.java

Summary

Maintainability
A
0 mins
Test Coverage
/*
 * ACS AEM Commons
 *
 * Copyright (C) 2013 - 2023 Adobe
 *
 * Licensed 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 com.adobe.acs.commons.redirects.servlets;

import com.adobe.acs.commons.redirects.RedirectResourceBuilder;
import com.adobe.acs.commons.redirects.models.ExportColumn;
import com.adobe.acs.commons.redirects.models.ImportLog;
import com.adobe.acs.commons.redirects.models.RedirectRule;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.testing.mock.sling.ResourceResolverType;
import org.apache.sling.testing.mock.sling.junit.SlingContext;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import javax.servlet.ServletException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Calendar;
import java.util.Arrays;
import java.util.Collections;
import java.util.Collection;

import static com.adobe.acs.commons.redirects.Asserts.assertDateEquals;
import static com.adobe.acs.commons.redirects.filter.RedirectFilter.REDIRECT_RULE_RESOURCE_TYPE;
import static org.junit.Assert.*;

public class ImportRedirectMapServletTest {
    @Rule
    public SlingContext context = new SlingContext(
            ResourceResolverType.RESOURCERESOLVER_MOCK);

    private ImportRedirectMapServlet servlet;
    private String redirectStoragePath = "/conf/acs-commons/redirects";

    @Before
    public void setUp() {
        servlet = new ImportRedirectMapServlet();
        context.request().addRequestParameter("path", redirectStoragePath);
        context.addModelsForClasses(RedirectRule.class);
        context.build().resource(redirectStoragePath);
    }

    /**
     * Import a spreadsheet with ony 3 required columns:
     *   - Cell A - source
     *   - Cell B - target
     *   - Cell C - statusCode
     */
    @Test
    public void testImportOnlyRequiredColumns() throws ServletException, IOException {

        XSSFWorkbook wb = new XSSFWorkbook();
        CellStyle dateStyle = wb.createCellStyle();
        dateStyle.setDataFormat(
                wb.createDataFormat().getFormat("mmm d, yyyy"));
        Sheet sheet = wb.createSheet();
        Row headerRow = sheet.createRow(0);
        headerRow.createCell(0).setCellValue(ExportColumn.SOURCE.getTitle());
        headerRow.createCell(1).setCellValue(ExportColumn.TARGET.getTitle());
        headerRow.createCell(2).setCellValue(ExportColumn.STATUS_CODE.getTitle());

        Row row1 = sheet.createRow(1);
        row1.createCell(0).setCellValue("/content/1");
        row1.createCell(1).setCellValue("/en/we-retail");
        row1.createCell(2).setCellValue(301);

        Row row2 = sheet.createRow(2);
        row2.createCell(0).setCellValue("/content/2");
        row2.createCell(1).setCellValue("/en/we-retail");
        row2.createCell(2).setCellValue(302);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        wb.write(out);
        out.close();
        byte[] excelBytes = out.toByteArray();

        MockSlingHttpServletRequest request = context.request();
        MockSlingHttpServletResponse response = context.response();

        request.addRequestParameter("file", excelBytes, "binary/data");

        servlet.doPost(request, response);

        Resource storageRoot = context.resourceResolver().getResource(redirectStoragePath);
        Map<String, Resource> rules = servlet.getRules(storageRoot); // rules keyed by source
        assertEquals("number of redirects after import ", 2, rules.size());

        RedirectRule rule1 = rules.get("/content/1").adaptTo(RedirectRule.class);
        assertEquals("/content/1", rule1.getSource());
        assertEquals("/en/we-retail", rule1.getTarget());
        assertEquals(301, rule1.getStatusCode());

        RedirectRule rule2 = rules.get("/content/2").adaptTo(RedirectRule.class);
        assertEquals("/content/2", rule2.getSource());
        assertEquals("/en/we-retail", rule2.getTarget());
        assertEquals(302, rule2.getStatusCode());

        // read ImportLog from the output json and assert there were no issues
        ImportLog importLog = new ObjectMapper().readValue(response.getOutputAsString(), ImportLog.class);
        assertEquals("ImportLog",0, importLog.getLog().size());
        assertTrue(importLog.getPath(), context.resourceResolver().getResource(importLog.getPath()) != null);
    }

    /**
     * Rows that don't have valid source/target/statusCode should be skipped
     */
    @Test
    public void testIgnoreInvalidRows() throws ServletException, IOException {

        XSSFWorkbook wb = new XSSFWorkbook();
        CellStyle dateStyle = wb.createCellStyle();
        dateStyle.setDataFormat(
                wb.createDataFormat().getFormat("mmm d, yyyy"));
        Sheet sheet = wb.createSheet();
        Row headerRow = sheet.createRow(0);
        headerRow.createCell(0).setCellValue(ExportColumn.SOURCE.getTitle());
        headerRow.createCell(1).setCellValue(ExportColumn.TARGET.getTitle());
        headerRow.createCell(2).setCellValue(ExportColumn.STATUS_CODE.getTitle());

        Row row1 = sheet.createRow(1);
        //row1.createCell(0).setCellValue("/content/1");
        row1.createCell(1).setCellValue("/en/we-retail");
        row1.createCell(2).setCellValue(301);

        Row row2 = sheet.createRow(2);
        row2.createCell(0).setCellValue("/content/1");
        //row2.createCell(1).setCellValue("/en/we-retail");
        row2.createCell(2).setCellValue(301);

        Row row3 = sheet.createRow(3);
        row3.createCell(0).setCellValue("/content/1");
        row3.createCell(1).setCellValue("/en/we-retail");
        //row3.createCell(2).setCellValue(301);

        Row row4 = sheet.createRow(4);
        row4.createCell(0).setCellValue(true);
        row4.createCell(1).setCellValue("/en/we-retail");
        row4.createCell(2).setCellValue(301);

        Row row5 = sheet.createRow(5);
        row5.createCell(0).setCellValue("/content/1");
        row5.createCell(1).setCellValue(true);
        row5.createCell(2).setCellValue(301);

        Row row6 = sheet.createRow(6);
        row6.createCell(0).setCellValue("/content/1");
        row6.createCell(1).setCellValue("/en/we-retail");
        row6.createCell(2).setCellValue(true);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        wb.write(out);
        out.close();
        byte[] excelBytes = out.toByteArray();

        MockSlingHttpServletRequest request = context.request();
        MockSlingHttpServletResponse response = context.response();

        request.addRequestParameter("file", excelBytes, "binary/data");

        servlet.doPost(request, response);

        Resource storageRoot = context.resourceResolver().getResource(redirectStoragePath);
        Map<String, Resource> rules = servlet.getRules(storageRoot); // rules keyed by source
        assertEquals("number of redirects after import ", 0, rules.size());

        // read ImportLog from the output json and assert that every 6 input rows had issues
        ImportLog importLog = new ObjectMapper().readValue(response.getOutputAsString(), ImportLog.class);
        List<ImportLog.Entry> logEntries = importLog.getLog();
        assertEquals(6, logEntries.size());
        assertTrue(importLog.getPath(), context.resourceResolver().getResource(importLog.getPath()) != null);
    }

    /**
     * redirects in the input spreadsheet are keyed by source path. Dupes will be collapsed.
     */
    @Test
    public void testDuplicatesRedirects() throws ServletException, IOException {

        XSSFWorkbook wb = new XSSFWorkbook();
        CellStyle dateStyle = wb.createCellStyle();
        dateStyle.setDataFormat(
                wb.createDataFormat().getFormat("mmm d, yyyy"));
        Sheet sheet = wb.createSheet();
        Row headerRow = sheet.createRow(0);
        headerRow.createCell(0).setCellValue(ExportColumn.SOURCE.getTitle());
        headerRow.createCell(1).setCellValue(ExportColumn.TARGET.getTitle());
        headerRow.createCell(2).setCellValue(ExportColumn.STATUS_CODE.getTitle());

        Row row1 = sheet.createRow(1);
        row1.createCell(0).setCellValue("/en/we-retail/about");
        row1.createCell(1).setCellValue("/en/we-retail");
        row1.createCell(2).setCellValue(302);

        Row row2 = sheet.createRow(2);
        row2.createCell(0).setCellValue("/en/we-retail/about");
        row2.createCell(1).setCellValue("/en/we-retail/contact-us");
        row2.createCell(2).setCellValue(302);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        wb.write(out);
        out.close();
        byte[] excelBytes = out.toByteArray();

        MockSlingHttpServletRequest request = context.request();
        MockSlingHttpServletResponse response = context.response();

        request.addRequestParameter("file", excelBytes, "binary/data");

        servlet.doPost(request, response);

        Resource storageRoot = context.resourceResolver().getResource(redirectStoragePath);
        Map<String, Resource> rules = servlet.getRules(storageRoot); // rules keyed by source
        assertEquals("number of redirects after import ", 1, rules.size());

        // read ImportLog from the output json and assert there were no issues
        ImportLog importLog = new ObjectMapper().readValue(response.getOutputAsString(), ImportLog.class);
        assertEquals("ImportLog",0, importLog.getLog().size());
        assertTrue(importLog.getPath(), context.resourceResolver().getResource(importLog.getPath()) != null);
    }

    /**
     * User can change the order of optional columns (starting with D), e.g.
     *   - Cell D - Evaluate URI
     *   - Cell E - Notes
     *   ...
     */
    @Test
    public void testImportMixedColumns() throws ServletException, IOException {

        XSSFWorkbook wb = new XSSFWorkbook();
        CellStyle dateStyle = wb.createCellStyle();
        dateStyle.setDataFormat(
                wb.createDataFormat().getFormat("mmm d, yyyy"));
        Sheet sheet = wb.createSheet();
        Row headerRow = sheet.createRow(0);
        headerRow.createCell(0).setCellValue(ExportColumn.SOURCE.getTitle());
        headerRow.createCell(1).setCellValue(ExportColumn.TARGET.getTitle());
        headerRow.createCell(2).setCellValue(ExportColumn.STATUS_CODE.getTitle());
        headerRow.createCell(3).setCellValue(ExportColumn.EVALUATE_URI.getTitle());
        headerRow.createCell(4).setCellValue(ExportColumn.OFF_TIME.getTitle());
        headerRow.createCell(5).setCellValue(ExportColumn.ON_TIME.getTitle());
        headerRow.createCell(6).setCellValue(ExportColumn.NOTES.getTitle());
        headerRow.createCell(7).setCellValue(ExportColumn.IGNORE_CONTEXT_PREFIX.getTitle());
        headerRow.createCell(8).setCellValue(ExportColumn.TAGS.getTitle());

        Row row1 = sheet.createRow(1);
        row1.createCell(0).setCellValue("/content/1");
        row1.createCell(1).setCellValue("/en/we-retail");
        row1.createCell(2).setCellValue(301);
        row1.createCell(3).setCellValue(true);
        row1.createCell(4).setCellValue(new Calendar.Builder().setDate(1974, 01, 16).build());
        row1.getCell(4).setCellStyle(dateStyle);
        row1.createCell(5).setCellValue(new Calendar.Builder().setDate(2025, 02, 02).build());
        row1.getCell(5).setCellStyle(dateStyle);
        row1.createCell(6).setCellValue("note-abc");
        row1.createCell(7).setCellValue(true);
        row1.createCell(8).setCellValue("redirects:tag1\nredirects:tag2");

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        wb.write(out);
        out.close();
        byte[] excelBytes = out.toByteArray();

        MockSlingHttpServletRequest request = context.request();
        MockSlingHttpServletResponse response = context.response();

        request.addRequestParameter("file", excelBytes, "binary/data");

        servlet.doPost(request, response);

        Resource storageRoot = context.resourceResolver().getResource(redirectStoragePath);
        Map<String, Resource> rules = servlet.getRules(storageRoot); // rules keyed by source
        assertEquals("number of redirects after import ", 1, rules.size());

        RedirectRule rule = rules.get("/content/1").adaptTo(RedirectRule.class);
        assertEquals("/en/we-retail", rule.getTarget());
        assertEquals(301, rule.getStatusCode());
        assertDateEquals("16 February 1974", rule.getUntilDate());
        assertDateEquals("02 March 2025", rule.getEffectiveFrom());
        assertEquals("note-abc", rule.getNote());
        assertTrue(rule.getEvaluateURI());
        assertTrue(rule.getContextPrefixIgnored());
        assertArrayEquals(new String[]{"redirects:tag1", "redirects:tag2"}, rule.getTagIds());

        // read ImportLog from the output json and assert there were no issues
        ImportLog importLog = new ObjectMapper().readValue(response.getOutputAsString(), ImportLog.class);
        assertEquals("ImportLog",0, importLog.getLog().size());
        assertTrue(importLog.getPath(), context.resourceResolver().getResource(importLog.getPath()) != null);
    }

    /**
     * Merge redirects from spreadsheet with existing redirects in the repository
     */
    @Test
    public void testImportMixedExistingAndSpreadsheet() throws ServletException, IOException {
        // existing rules
        new RedirectResourceBuilder(context, redirectStoragePath)
                .setSource("/content/one")
                .setTarget("/content/two")
                .setStatusCode(302)
                .setUntilDate(new Calendar.Builder().setDate(2022, 9, 9).build())
                .setNotes("note-1")
                .setEvaluateURI(true)
                .setContextPrefixIgnored(true)
                .setCreatedBy("john.doe")
                .setTagIds(new String[]{"redirects:tag3"})
                .setProperty("custom-1", "123")
                .setNodeName("redirect-saved-1")
                .build();
        new RedirectResourceBuilder(context, redirectStoragePath)
                .setSource("/content/three")
                .setTarget("/content/four")
                .setStatusCode(301)
                .setNotes("note-2")
                .setModifiedBy("xyz")
                .setTagIds(new String[]{"redirects:tag3"})
                .setProperty("custom-2", "345")
                .setNodeName("redirect-saved-2")
                .build();

        // new rules in a spreadsheet. will be merged with the existing rules
        XSSFWorkbook wb = new XSSFWorkbook();
        CellStyle dateStyle = wb.createCellStyle();
        dateStyle.setDataFormat(
                wb.createDataFormat().getFormat("mmm d, yyyy"));
        Sheet sheet = wb.createSheet();
        Row headerRow = sheet.createRow(0);
        headerRow.createCell(0).setCellValue(ExportColumn.SOURCE.getTitle());
        headerRow.createCell(1).setCellValue(ExportColumn.TARGET.getTitle());
        headerRow.createCell(2).setCellValue(ExportColumn.STATUS_CODE.getTitle());
        headerRow.createCell(3).setCellValue(ExportColumn.OFF_TIME.getTitle());
        headerRow.createCell(4).setCellValue(ExportColumn.NOTES.getTitle());
        headerRow.createCell(5).setCellValue(ExportColumn.EVALUATE_URI.getTitle());
        headerRow.createCell(6).setCellValue(ExportColumn.IGNORE_CONTEXT_PREFIX.getTitle());
        headerRow.createCell(7).setCellValue(ExportColumn.TAGS.getTitle());
        headerRow.createCell(8).setCellValue(ExportColumn.CREATED.getTitle());
        headerRow.createCell(9).setCellValue(ExportColumn.CREATED_BY.getTitle());
        headerRow.createCell(10).setCellValue(ExportColumn.MODIFIED.getTitle());
        headerRow.createCell(11).setCellValue(ExportColumn.MODIFIED_BY.getTitle());
        headerRow.createCell(12).setCellValue(ExportColumn.ON_TIME.getTitle());

        Row row1 = sheet.createRow(1);
        row1.createCell(0).setCellValue("/content/1");
        row1.createCell(1).setCellValue("/en/we-retail");
        row1.createCell(2).setCellValue(301);
        row1.createCell(3).setCellValue(new Calendar.Builder().setDate(1974, 01, 16).build());
        row1.getCell(3).setCellStyle(dateStyle);
        row1.createCell(4).setCellValue("note-abc");
        row1.createCell(7).setCellValue("redirects:tag1\nredirects:tag2");
        row1.createCell(12).setCellValue(new Calendar.Builder().setDate(2025, 02, 02).build());
        row1.getCell(12).setCellStyle(dateStyle);

        Row row2 = sheet.createRow(2);
        row2.createCell(0).setCellValue("/content/2");
        row2.createCell(1).setCellValue("/en/we-retail");
        row2.createCell(2).setCellValue(301);

        Row row3 = sheet.createRow(3);
        row3.createCell(0).setCellValue("/content/three");
        row3.createCell(1).setCellValue("/en/we-retail");
        row3.createCell(2).setCellValue(301);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        wb.write(out);
        out.close();
        byte[] excelBytes = out.toByteArray();

        MockSlingHttpServletRequest request = context.request();
        MockSlingHttpServletResponse response = context.response();

        request.addRequestParameter("file", excelBytes, "binary/data");

        servlet.doPost(request, response);

        Resource storageRoot = context.resourceResolver().getResource(redirectStoragePath);
        Map<String, Resource> rules = servlet.getRules(storageRoot); // rules keyed by source
        assertEquals("number of redirects after import ", 4, rules.size());

        Resource res1 = rules.get("/content/one");
        assertEquals("redirect-saved-1", res1.getName()); // node name is preserved
        RedirectRule rule1 = res1.adaptTo(RedirectRule.class);
        assertEquals("/content/two", rule1.getTarget());
        assertDateEquals("09 October 2022", rule1.getUntilDate());
        assertEquals("note-1", rule1.getNote());
        assertEquals("john.doe", rule1.getCreatedBy());
        assertEquals("123", res1.getValueMap().get("custom-1"));
        assertTrue(rule1.getEvaluateURI());
        assertTrue(rule1.getContextPrefixIgnored());
        assertArrayEquals(new String[]{"redirects:tag3"}, rule1.getTagIds());

        Resource res2 = rules.get("/content/three");
        assertEquals("redirect-saved-2", res2.getName()); // node name is preserved
        RedirectRule rule2 = res2.adaptTo(RedirectRule.class);
        assertEquals("/en/we-retail", rule2.getTarget());
        assertEquals(301, rule2.getStatusCode());
        assertFalse(rule2.getEvaluateURI());
        assertFalse(rule2.getContextPrefixIgnored());
        assertEquals("xyz", rule2.getModifiedBy());
        assertEquals("345", res2.getValueMap().get("custom-2"));

        RedirectRule rule3 = rules.get("/content/1").adaptTo(RedirectRule.class);
        assertEquals("/en/we-retail", rule3.getTarget());
        assertDateEquals("16 February 1974", rule3.getUntilDate());
        assertEquals("note-abc", rule3.getNote());
        assertFalse(rule3.getEvaluateURI());
        assertFalse(rule3.getContextPrefixIgnored());
        assertArrayEquals(new String[]{"redirects:tag1", "redirects:tag2"}, rule3.getTagIds());
        assertDateEquals("02 March 2025", rule3.getEffectiveFrom());

        RedirectRule rule4 = rules.get("/content/2").adaptTo(RedirectRule.class);
        assertEquals("/en/we-retail", rule4.getTarget());
        assertEquals(null, rule4.getUntilDate());

        // read ImportLog from the output json and assert there were no issues
        ImportLog importLog = new ObjectMapper().readValue(response.getOutputAsString(), ImportLog.class);
        assertEquals("ImportLog",0, importLog.getLog().size());
    }

    @Test
    public void testUpdate() throws IOException {
        Map<String, Object> rule1 = new HashMap<>();
        rule1.put("sling:resourceType", REDIRECT_RULE_RESOURCE_TYPE);
        rule1.put(RedirectRule.SOURCE_PROPERTY_NAME, "/a1");
        rule1.put(RedirectRule.TARGET_PROPERTY_NAME, "/b1");
        rule1.put(RedirectRule.STATUS_CODE_PROPERTY_NAME, 301);

        Map<String, Object> rule2 = new HashMap<>();
        rule2.put("sling:resourceType", REDIRECT_RULE_RESOURCE_TYPE);
        rule2.put(RedirectRule.SOURCE_PROPERTY_NAME, "/a2");
        rule2.put(RedirectRule.TARGET_PROPERTY_NAME, "/b2");
        rule2.put(RedirectRule.STATUS_CODE_PROPERTY_NAME, 302);
        rule2.put(RedirectRule.UNTIL_DATE_PROPERTY_NAME, Calendar.getInstance());
        rule2.put(RedirectRule.NOTE_PROPERTY_NAME, "note");
        rule2.put(RedirectRule.EVALUATE_URI_PROPERTY_NAME, true);
        rule2.put(RedirectRule.CONTEXT_PREFIX_IGNORED_PROPERTY_NAME, true);

        Collection<Map<String, Object>> rules = Arrays.asList(rule1, rule2);

        Resource root = context.resourceResolver().getResource(redirectStoragePath);
        servlet.update(root, rules, Collections.emptyMap());

        Map<String, Resource> redirects = servlet.getRules(root);
        ValueMap vm1 = redirects.get(rule1.get(RedirectRule.SOURCE_PROPERTY_NAME)).getValueMap();

        assertEquals(vm1.get(RedirectRule.SOURCE_PROPERTY_NAME), rule1.get(RedirectRule.SOURCE_PROPERTY_NAME));
        assertEquals(vm1.get(RedirectRule.TARGET_PROPERTY_NAME), rule1.get(RedirectRule.TARGET_PROPERTY_NAME));
        assertFalse(vm1.containsKey(RedirectRule.UNTIL_DATE_PROPERTY_NAME));
        assertFalse(vm1.containsKey(RedirectRule.NOTE_PROPERTY_NAME));
        assertFalse(vm1.containsKey(RedirectRule.EVALUATE_URI_PROPERTY_NAME));
        assertFalse(vm1.containsKey(RedirectRule.CONTEXT_PREFIX_IGNORED_PROPERTY_NAME));

        ValueMap vm2 = redirects.get(rule2.get(RedirectRule.SOURCE_PROPERTY_NAME)).getValueMap();
        assertEquals(vm2.get(RedirectRule.SOURCE_PROPERTY_NAME), rule2.get(RedirectRule.SOURCE_PROPERTY_NAME));
        assertEquals(vm2.get(RedirectRule.TARGET_PROPERTY_NAME), rule2.get(RedirectRule.TARGET_PROPERTY_NAME));
        assertEquals(vm2.get(RedirectRule.NOTE_PROPERTY_NAME), rule2.get(RedirectRule.NOTE_PROPERTY_NAME));
        assertEquals(vm2.get(RedirectRule.EVALUATE_URI_PROPERTY_NAME), rule2.get(RedirectRule.EVALUATE_URI_PROPERTY_NAME));
        assertEquals(vm2.get(RedirectRule.CONTEXT_PREFIX_IGNORED_PROPERTY_NAME), rule2.get(RedirectRule.CONTEXT_PREFIX_IGNORED_PROPERTY_NAME));
    }

}