/*
* #%L
* ACS AEM Commons Bundle
* %%
* Copyright (C) 2013 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.
* #L%
*/
package com.adobe.acs.commons.analysis.jcrchecksum.impl;
import com.adobe.acs.commons.analysis.jcrchecksum.ChecksumGeneratorOptions;
import com.adobe.acs.commons.analysis.jcrchecksum.impl.options.CustomChecksumGeneratorOptions;
import com.adobe.acs.commons.analysis.jcrchecksum.impl.options.DefaultChecksumGeneratorOptions;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.jackrabbit.value.ValueFactoryImpl;
import org.apache.sling.testing.mock.jcr.MockJcr;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
@RunWith(MockitoJUnitRunner.class)
public class ChecksumGeneratorImplTest {
ChecksumGeneratorImpl checksumGenerator = new ChecksumGeneratorImpl();
StringWriter sw;
PrintWriter pw;
Session session;
@Before
public void setUp() {
sw = new StringWriter();
pw = new PrintWriter(sw);
session = MockJcr.newSession();
}
@After
public void tearDown() throws IOException {
sw.close();
pw.close();
session.logout();
}
public Node setupPage1() throws RepositoryException {
// Set up page1
Node page =
session.getRootNode()
.addNode("content")
.addNode("test-page", "cq:Page")
.addNode("jcr:content", "cq:PageContent");
page.setProperty("jcr:title", "test title");
session.save();
return page;
}
public Node setupAsset1() throws RepositoryException {
// Set up page1
Node asset1 =
session.getRootNode()
.addNode("content")
.addNode("dam", "sling:Folder")
.addNode("foo.jpg", "dam:Asset")
.addNode("jcr:content", "dam:AssetContent");
asset1
.addNode("metadata").setProperty("dc:title", "Foo");
asset1
.addNode("renditions", "nt:folder")
.addNode("original", "nt:file")
.addNode("jcr:content", "nt:resource")
.setProperty("data", ValueFactoryImpl.getInstance().createBinary(new ByteArrayInputStream("test binary string".getBytes())));
asset1
.setProperty("text", "t");
// Set a property
Calendar c = Calendar.getInstance();
Date d = new Date();
d.setTime(0);
c.setTime(d);
asset1.setProperty("jcr:lastModified", c);
return asset1;
}
@Test
public void testGetChecksumKey() {
String expected = "jcr:content";
String actual = checksumGenerator.getChecksumKey("/content/page/jcr:content", "/content/page/jcr:content");
assertEquals(expected, actual);
expected = "jcr:content/foo";
actual = checksumGenerator.getChecksumKey("/content/page/jcr:content", "/content/page/jcr:content/foo");
assertEquals(expected, actual);
expected = "jcr:content/foo/bar";
actual = checksumGenerator.getChecksumKey("/content/page/jcr:content", "/content/page/jcr:content/foo/bar");
assertEquals(expected, actual);
expected = "/";
actual = checksumGenerator.getChecksumKey("/", "/");
assertEquals(expected, actual);
expected = "/etc/workflow";
actual = checksumGenerator.getChecksumKey("/", "/etc/workflow");
assertEquals(expected, actual);
}
@Test
public void testHashForCqPageContentNode1() throws IOException, RepositoryException {
Node page = setupPage1();
Map<String, String> actual = checksumGenerator.generateChecksums(session, "/content");
assertEquals("0362210a336ba79c6cab30bf09deaf2f1a749e6f",
actual.get("/content/test-page/jcr:content"));
}
@Test
public void testNodeTypeExcludes() throws RepositoryException, IOException {
Node page = setupPage1();
page.addNode("ignore", "nt:unstructured");
session.save();
CustomChecksumGeneratorOptions opts = new CustomChecksumGeneratorOptions();
opts.addIncludedNodeTypes(new String[]{ "cq:PageContent" });
opts.addExcludedNodeTypes(new String[]{ "nt:unstructured" });
Map<String, String> actual = checksumGenerator.generateChecksums(session, "/content", opts);
assertEquals("0362210a336ba79c6cab30bf09deaf2f1a749e6f",
actual.get("/content/test-page/jcr:content"));
}
@Test
public void testDamAsset() throws IOException, RepositoryException {
Node asset = setupAsset1();
CustomChecksumGeneratorOptions opts = new CustomChecksumGeneratorOptions();
opts.addIncludedNodeTypes(new String[]{ "dam:AssetContent" });
opts.addExcludedProperties(new String[]{ "jcr:created", "jcr:createdBy", "jcr:lastModified" });
// jcr:content/renditions/original/jcr:content
String propertiesChecksum =
"jcr:content/renditions/original/jcr:content/data="+ DigestUtils.shaHex("test binary string")
+ "jcr:content/renditions/original/jcr:content/jcr:primaryType="+ DigestUtils.shaHex("nt:resource");
String aggregatedPopertiesChecksum = DigestUtils.shaHex(propertiesChecksum);
String nodeChecksum = DigestUtils.shaHex("jcr:content/renditions/original/jcr:content="
+ aggregatedPopertiesChecksum);
final String originalJcrContentChecksum = nodeChecksum;
assertEquals(originalJcrContentChecksum, checksumGenerator.generatedNodeChecksum(asset.getPath(), asset.getNode
("renditions/original/jcr:content"), opts));
// jcr:content/renditions/original
propertiesChecksum =
"jcr:content/renditions/original/jcr:primaryType=" + DigestUtils.shaHex("nt:file");
aggregatedPopertiesChecksum = DigestUtils.shaHex(propertiesChecksum);
nodeChecksum = DigestUtils.shaHex("jcr:content/renditions/original="
+ aggregatedPopertiesChecksum
+ "jcr:content/renditions/original/jcr:content=" + originalJcrContentChecksum
);
final String originalChecksum = nodeChecksum;
assertEquals(originalChecksum, checksumGenerator.generatedNodeChecksum(asset.getPath(),
asset.getNode("renditions/original"), opts));
// jcr:content/renditions
propertiesChecksum = "jcr:content/renditions/jcr:primaryType=" + DigestUtils.shaHex("nt:folder");
aggregatedPopertiesChecksum = DigestUtils.shaHex(propertiesChecksum);
nodeChecksum = DigestUtils.shaHex("jcr:content/renditions=" + aggregatedPopertiesChecksum
+ "jcr:content/renditions/original=" + originalChecksum);
final String renditionsChecksum = nodeChecksum;
assertEquals(renditionsChecksum, checksumGenerator.generatedNodeChecksum(asset.getPath(),
asset.getNode("renditions"), opts));
// jcr:content/metadata
propertiesChecksum = "jcr:content/metadata/dc:title=" + DigestUtils.shaHex("Foo")
+ "jcr:content/metadata/jcr:primaryType=" + DigestUtils.shaHex("nt:unstructured");
aggregatedPopertiesChecksum = DigestUtils.shaHex(propertiesChecksum);
nodeChecksum = DigestUtils.shaHex("jcr:content/metadata=" + aggregatedPopertiesChecksum);
final String metadataChecksum = nodeChecksum;
assertEquals(metadataChecksum, checksumGenerator.generatedNodeChecksum(asset.getPath(),
asset.getNode("metadata"), opts));
// jcr:content
propertiesChecksum = "jcr:content/jcr:primaryType=" + DigestUtils.shaHex("dam:AssetContent")
+ "jcr:content/text=" + DigestUtils.shaHex("t");
aggregatedPopertiesChecksum = DigestUtils.shaHex(propertiesChecksum);
nodeChecksum = DigestUtils.shaHex("jcr:content=" + aggregatedPopertiesChecksum
+ "jcr:content/metadata=" + metadataChecksum
+ "jcr:content/renditions=" + renditionsChecksum);
String jcrContentChecksum = nodeChecksum;
assertEquals(jcrContentChecksum, checksumGenerator.generatedNodeChecksum(asset.getPath(),
asset, opts));
Map<String, String> actual = checksumGenerator.generateChecksums(session, "/content", opts);
// df5fa249ada79b02d435fe75d28afb6811d54edb
assertEquals(jcrContentChecksum, actual.get("/content/dam/foo.jpg/jcr:content"));
}
@Test
public void testMultiple() throws IOException, RepositoryException {
Node page = setupPage1();
Node asset = setupAsset1();
ChecksumGeneratorOptions defaultOptions = new DefaultChecksumGeneratorOptions();
CustomChecksumGeneratorOptions options = new CustomChecksumGeneratorOptions();
options.addExcludedProperties(defaultOptions.getExcludedProperties());
options.addExcludedProperties(new String[]{ "jcr:created", "jcr:createdBy", "jcr:lastModified" });
options.addIncludedNodeTypes(defaultOptions.getIncludedNodeTypes());
options.addExcludedNodeTypes(defaultOptions.getExcludedNodeTypes());
options.addSortedProperties(defaultOptions.getSortedProperties());
Map<String, String> actual = checksumGenerator.generateChecksums(session, "/content", options);
// Checksums proven by above tests
assertEquals("0362210a336ba79c6cab30bf09deaf2f1a749e6f", actual.get(page.getPath()));
assertEquals("df5fa249ada79b02d435fe75d28afb6811d54edb", actual.get(asset.getPath()));
}
@Test
public void testGeneratedNodeChecksum() throws RepositoryException, IOException {
Node node = session.getRootNode().addNode("page/jcr:content");
node.setProperty("jcr:title", "My Title");
node.setProperty("jcr:description", "This is my test node");
node.setProperty("long", new Long(100));
node.setProperty("double", new Double(99.99));
node.setProperty("boolean", true);
session.save();
String raw =
"jcr:content/boolean=" + DigestUtils.shaHex("true") +
"jcr:content/double=" + DigestUtils.shaHex("99.99") +
"jcr:content/jcr:description=" + DigestUtils.shaHex("This is my test node") +
"jcr:content/jcr:primaryType=" + DigestUtils.shaHex("nt:unstructured") +
"jcr:content/jcr:title=" + DigestUtils.shaHex("My Title") +
"jcr:content/long=" + DigestUtils.shaHex("100");
String propertiesChecksum = DigestUtils.shaHex(raw);
String expected = DigestUtils.shaHex("jcr:content=" + propertiesChecksum);
CustomChecksumGeneratorOptions opts = new CustomChecksumGeneratorOptions();
opts.addSortedProperties(new String[]{ "sorted" });
opts.addIncludedNodeTypes(new String[]{ "nt:unstructured" });
Map<String, String> actual = checksumGenerator.generateChecksums(session, node.getPath(), opts);
assertEquals(expected, actual.get("/page/jcr:content"));
}
@Test
public void testNestedNodesCheckusm() throws RepositoryException, IOException {
Node node = session.getRootNode().addNode("page/jcr:content");
Node a = node.addNode("a");
a.addNode("b");
a.addNode("c");
session.save();
// Test
String bChecksum = DigestUtils.shaHex("jcr:content/a/b/jcr:primaryType="
+ DigestUtils.shaHex("nt:unstructured"));
bChecksum = DigestUtils.shaHex("jcr:content/a/b=" + bChecksum);
//System.out.println("jcr:content/a/b Checksum: " + bChecksum);
String cChecksum = DigestUtils.shaHex("jcr:content/a/c/jcr:primaryType="
+ DigestUtils.shaHex("nt:unstructured"));
cChecksum = DigestUtils.shaHex("jcr:content/a/c=" + cChecksum);
//System.out.println("jcr:content/a/c Checksum: " + cChecksum);
String aChecksum = DigestUtils.shaHex("jcr:content/a/jcr:primaryType=" + DigestUtils.shaHex("nt:unstructured"))
+ "jcr:content/a/b=" + bChecksum
+ "jcr:content/a/c=" + cChecksum;
aChecksum = DigestUtils.shaHex("jcr:content/a=" + aChecksum);
//System.out.println("jcr:content/a Checksum: " + DigestUtils.shaHex("jcr:content/=" + aChecksum));
String jcrContentChecksum = DigestUtils.shaHex("jcr:content/jcr:primaryType=" + DigestUtils.shaHex("nt:unstructured"))
+ "jcr:content/a=" + aChecksum;
//System.out.println("jcrContentChecksum: " + jcrContentChecksum);
jcrContentChecksum = DigestUtils.shaHex("jcr:content=" + jcrContentChecksum);
String expected = jcrContentChecksum;
CustomChecksumGeneratorOptions opts = new CustomChecksumGeneratorOptions();
opts.addIncludedNodeTypes(new String[]{ "nt:unstructured" });
// A checksum
assertEquals(bChecksum, checksumGenerator.generatedNodeChecksum(node.getPath(), a.getNode("b"), opts));
assertEquals(cChecksum, checksumGenerator.generatedNodeChecksum(node.getPath(), a.getNode("c"), opts));
assertEquals(aChecksum, checksumGenerator.generatedNodeChecksum(node.getPath(), a, opts));
Map<String, String> actual = checksumGenerator.generateChecksums(node.getSession(), node.getPath(), opts);
assertEquals(expected, actual.get(node.getPath()));
}
@Test
public void testGeneratePropertyChecksums() throws RepositoryException, IOException {
Node node = session.getRootNode().addNode("page/jcr:content");
node.setProperty("jcr:title", "My Title");
node.setProperty("jcr:description", "This is my test node");
node.setProperty("long", new Long(100));
node.setProperty("double", new Double(99.99));
node.setProperty("boolean", true);
node.setProperty("unsorted", new String[]{ "woof", "bark", "howl" });
node.setProperty("sorted", new String[]{ "yelp", "arf" });
session.save();
// Expected to be sorted alphabetically
String raw =
"jcr:content/boolean=" + DigestUtils.shaHex("true")
+ "jcr:content/double=" + DigestUtils.shaHex("99.99")
+ "jcr:content/jcr:description=" + DigestUtils.shaHex("This is my test node")
+ "jcr:content/jcr:primaryType=" + DigestUtils.shaHex("nt:unstructured")
+ "jcr:content/jcr:title=" + DigestUtils.shaHex("My Title")
+ "jcr:content/long=" + DigestUtils.shaHex("100")
+ "jcr:content/sorted=" + DigestUtils.shaHex("yelp") + "," + DigestUtils.shaHex("arf")
// This order is dictated by the sorted values of the corresponding hashes
+ "jcr:content/unsorted=" + DigestUtils.shaHex("howl") + "," + DigestUtils.shaHex("bark") + ","
+ DigestUtils.shaHex("woof");
String expected = DigestUtils.shaHex(raw);
CustomChecksumGeneratorOptions opts = new CustomChecksumGeneratorOptions();
opts.addSortedProperties(new String[]{ "sorted" });
String actual = checksumGenerator.generatePropertyChecksums(node.getPath(), node, opts);
assertEquals(expected, actual);
}
@Test
public void testGeneratePropertyChecksums_IgnoreProperties() throws RepositoryException, IOException {
Node node = session.getRootNode().addNode("page/jcr:content");
node.setProperty("jcr:title", "My Title");
node.setProperty("jcr:description", "This is my test node");
node.setProperty("long", new Long(100));
node.setProperty("double", new Double(99.99));
node.setProperty("boolean", true);
node.setProperty("unsorted", new String[]{ "woof", "bark", "howl" });
node.setProperty("sorted", new String[]{ "yelp", "arf" });
session.save();
// Expected to be sorted alphabetically
String raw =
"jcr:content/boolean=" + DigestUtils.shaHex("true")
+ "jcr:content/double=" + DigestUtils.shaHex("99.99")
+ "jcr:content/jcr:primaryType=" + DigestUtils.shaHex("nt:unstructured")
+ "jcr:content/jcr:title=" + DigestUtils.shaHex("My Title")
+ "jcr:content/sorted=" + DigestUtils.shaHex("yelp") + "," + DigestUtils.shaHex("arf")
// This order is dictated by the sorted values of the corresponding hashes
+ "jcr:content/unsorted=" + DigestUtils.shaHex("howl") + "," + DigestUtils.shaHex("bark") + ","
+ DigestUtils.shaHex("woof");
String expected = DigestUtils.shaHex(raw);
CustomChecksumGeneratorOptions opts = new CustomChecksumGeneratorOptions();
opts.addSortedProperties(new String[]{ "sorted" });
opts.addExcludedProperties(new String[]{ "jcr:description", "long" });
String actual = checksumGenerator.generatePropertyChecksums(node.getPath(), node, opts);
assertEquals(expected, actual);
}
@Test
public void testAggregateChecksums() {
Map<String, String> checksums = new LinkedHashMap<String, String>();
checksums.put("jcr:content/foo", "1234");
checksums.put("jcr:content/bar", "5678,9012");
String expected = DigestUtils.shaHex("jcr:content/foo=1234jcr:content/bar=5678,9012");
String actual = checksumGenerator.aggregateChecksums(checksums);
assertEquals(expected, actual);
}
}