blob: 77e7ddd3671c089b0016a11bafe079d30406c658 [file] [log] [blame]
/*
* 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.resourcebuilder.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
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.ResourceUtil;
import org.apache.sling.commons.mime.MimeTypeService;
import org.apache.sling.resourcebuilder.api.ResourceBuilder;
import org.apache.sling.resourcebuilder.test.ResourceAssertions;
import org.apache.sling.testing.resourceresolver.MockResourceResolverFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
@SuppressWarnings("null")
public class ResourceBuilderImplTest {
private String testRootPath;
private long lastModified;
private Random random = new Random(System.currentTimeMillis());
private ResourceAssertions A;
@Mock
private MimeTypeService mimeTypeService;
private ResourceResolver resourceResolver;
private Resource getTestRoot(String path) throws PersistenceException {
final Resource root = resourceResolver.resolve("/");
assertNotNull("Expecting non-null root", root);
return resourceResolver.create(root, ResourceUtil.getName(path), null);
}
private ResourceBuilderImpl getBuilder(String path) throws Exception {
lastModified = random.nextLong();
final Resource parent = getTestRoot(path);
final ResourceBuilderImpl result = new ResourceBuilderImpl(parent, mimeTypeService) {
@Override
protected long getLastModified(long userSuppliedValue) {
final long now = System.currentTimeMillis();
final long superValue = super.getLastModified(-1);
final long maxDelta = 60 * 1000L;
if(superValue < now || superValue - now > maxDelta) {
fail("getLastModified does not seem to use current time as its default value");
}
if(userSuppliedValue >= 0) {
return super.getLastModified(userSuppliedValue);
}
return lastModified;
}
};
return result;
}
@Before
public void setup() throws Exception {
when(mimeTypeService.getMimeType(anyString())).thenReturn("application/javascript");
resourceResolver = new MockResourceResolverFactory().getResourceResolver(null);
testRootPath = "/" + UUID.randomUUID().toString();
A = new ResourceAssertions(testRootPath, resourceResolver);
}
@After
public void cleanup() throws PersistenceException {
final Resource r = resourceResolver.getResource(testRootPath);
if(r != null) {
resourceResolver.delete(r);
resourceResolver.commit();
}
}
@Test
public void basicResource() throws Exception {
getBuilder(testRootPath)
.resource("child", "title", "foo")
.commit();
A.assertProperties("child", "title", "foo");
assertEquals(A.fullPath("child"), A.assertResource("child").getPath());
}
@Test
public void basicResourceWithMap() throws Exception {
Map<String,Object> props = new HashMap<String,Object>();
props.put("title", "foo");
getBuilder(testRootPath)
.resource("child", props)
.commit();
A.assertProperties("child", "title", "foo");
assertEquals(A.fullPath("child"), A.assertResource("child").getPath());
}
@Test
public void ensureResourceExists() throws Exception {
class MyResourceBuilder extends ResourceBuilderImpl {
MyResourceBuilder() {
super(resourceResolver.getResource("/"), null);
}
Resource r(String path) {
return ensureResourceExists(path);
}
};
final MyResourceBuilder b = new MyResourceBuilder();
assertEquals("/", b.r(null).getPath());
assertEquals("/", b.r("").getPath());
assertEquals("/", b.r("/").getPath());
}
@Test
public void deepResource() throws Exception {
getBuilder(testRootPath)
.resource("a/b/c", "title", "foo")
.commit();
A.assertProperties("a/b/c", "title", "foo");
assertEquals(A.fullPath("a/b/c"), A.assertResource("a/b/c").getPath());
A.assertResource("a/b");
A.assertResource("a");
}
@Test
public void intermediatePrimaryTypes() throws Exception {
getBuilder(testRootPath)
.resource("a/b/c")
.withIntermediatePrimaryType("foo")
.resource("d/e")
.withIntermediatePrimaryType(null)
.resource("f/g")
.commit();
A.assertProperties("a/b", ResourceBuilderImpl.JCR_PRIMARYTYPE, "nt:unstructured");
A.assertProperties("a/b/c/d", ResourceBuilderImpl.JCR_PRIMARYTYPE, "foo");
A.assertProperties("a/b/c/d/e/f", ResourceBuilderImpl.JCR_PRIMARYTYPE, "nt:unstructured");
}
@Test
public void resetParent() throws Exception {
getBuilder(testRootPath)
.resource("a/b/c")
.siblingsMode()
.resource("one")
.resource("two")
.atParent() // also sets hierarchyMode
.resource("d/e")
.resource("f/g")
.siblingsMode()
.resource("three")
.resource("four")
.commit();
A.assertResource("a/b/c");
A.assertResource("a/b/c/one");
A.assertResource("a/b/c/two");
A.assertResource("d/e");
A.assertResource("d/e/f/g");
A.assertResource("d/e/f/g/three");
A.assertResource("d/e/f/g/four");
}
@Test
public void noResetParent() throws Exception {
getBuilder(testRootPath)
.resource("a/b/c")
.resource("d/e")
.commit();
A.assertResource("a/b/c");
A.assertResource("a/b/c/d/e");
}
@Test
public void getParent() throws Exception {
final Resource parent = getBuilder(testRootPath).getCurrentParent();
assertNotNull(parent);
assertEquals(testRootPath, parent.getPath());
}
@Test(expected=RuntimeException.class)
public void missingParentFails() throws Exception {
new ResourceBuilderImpl(null, null).resource("foo");
}
@Test(expected=IllegalArgumentException.class)
public void aboveParentFails() throws Exception {
getBuilder(testRootPath).resource("../foo");
}
@Test(expected=IllegalArgumentException.class)
public void aboveParentFailsFile() throws Exception {
getBuilder(testRootPath).file("../foo.js", null);
}
@Test
public void simpleTree() throws Exception {
getBuilder(testRootPath)
.resource("a/b/c", "title", "foo", "count", 21)
.siblingsMode()
.resource("1")
.resource("2")
.resource("3")
.hierarchyMode()
.resource("with")
.resource("more/here", "it", "worked")
.resource("deepest", "it", "worked")
.commit();
A.assertProperties("a/b/c", "count", 21, "title", "foo");
A.assertProperties("a/b/c/with/more/here", "it", "worked");
A.assertResource("a/b/c/with/more/here/deepest");
A.assertResource("a/b/c/1");
A.assertResource("a/b/c/2");
A.assertResource("a/b/c/3");
}
@Test
public void treeWithFiles() throws Exception {
getBuilder(testRootPath)
.resource("apps/myapp/components/resource")
.siblingsMode()
.file("models.js", getClass().getResourceAsStream("/files/models.js"), "MT1", 42)
.file("text.html", getClass().getResourceAsStream("/files/text.html"), "MT2", 43)
.atParent()
.file("apps/myapp.json", getClass().getResourceAsStream("/files/myapp.json"), "MT3", 44)
.atParent()
.resource("apps/content/myapp/resource")
.atParent()
.resource("apps/content", "title", "foo")
.file("myapp.json", getClass().getResourceAsStream("/files/myapp.json"), "MT4", 45)
.commit()
;
A.assertResource("apps/content/myapp/resource");
A.assertResource("apps/myapp/components/resource");
A.assertProperties("apps/content", "title", "foo");
A.assertFile("apps/myapp/components/resource/models.js",
"MT1", "function someJavascriptFunction()", 42L);
A.assertFile("apps/myapp/components/resource/text.html",
"MT2", "This is an html file", 43L);
A.assertFile("apps/myapp.json",
"MT3", "\"sling:resourceType\":\"its/resource/type\"", 44L);
A.assertFile("apps/content/myapp.json",
"MT4", "\"sling:resourceType\":\"its/resource/type\"", 45L);
}
@Test
public void autoMimetype() throws Exception {
getBuilder(testRootPath)
.file("models.js", getClass().getResourceAsStream("/files/models.js"), null, 42)
.commit()
;
A.assertFile("models.js",
"application/javascript", "function someJavascriptFunction()", 42L);
}
@Test
public void autoLastModified() throws Exception {
getBuilder(testRootPath)
.file("models.js", getClass().getResourceAsStream("/files/models.js"), "MT1", -1)
.commit()
;
A.assertFile("models.js",
"MT1", "function someJavascriptFunction()", lastModified);
}
@Test
public void autoEverything() throws Exception {
getBuilder(testRootPath)
.file("a/b/c/models.js", getClass().getResourceAsStream("/files/models.js"))
.commit()
;
A.assertFile("a/b/c/models.js",
"application/javascript", "function someJavascriptFunction()", lastModified);
}
@Test(expected=IllegalStateException.class)
public void duplicatedFileFails() throws Exception {
getBuilder(testRootPath)
.siblingsMode()
.file("models.js", getClass().getResourceAsStream("/files/models.js"), null, 42)
.file("models.js", getClass().getResourceAsStream("/files/models.js"), null, 42)
;
}
@Test(expected=IllegalArgumentException.class)
public void nullDataFails() throws Exception {
getBuilder(testRootPath)
.file("models.js", null, null, 42)
;
}
@Test
public void forParent() throws PersistenceException {
new ResourceBuilderFactoryService()
.forParent(getTestRoot(testRootPath))
.resource("a/b/c")
.commit();
A.assertResource("a/b/c");
}
@Test
public void forResolver() throws PersistenceException {
new ResourceBuilderFactoryService()
.forResolver(resourceResolver)
.resource("d/e/f")
.commit();
// Resource is created at root in this case
A.assertResource("/d/e/f");
}
@Test
public void absolutePath() throws Exception {
new ResourceBuilderFactoryService()
.forResolver(resourceResolver)
.resource("/a/b/c")
.resource("/a/b/f")
.resource("/g/h/i")
.resource("j/l/m")
.resource("/o/p/q");
// absolute paths are supported and can be mixed with relative paths
A.assertResource("/a/b/c");
A.assertResource("/a/b/f");
A.assertResource("/g/h/i");
A.assertResource("/g/h/i/j/l/m");
A.assertResource("/o/p/q");
}
@Test
public void absolutePathSwitchBackToSiblingMode() throws Exception {
new ResourceBuilderFactoryService()
.forResolver(resourceResolver)
.resource("/a").siblingsMode().resource("a1").resource("a2")
.resource("/b").siblingsMode().resource("b1").resource("b2")
.resource("/c").resource("c1").resource("c11");
// make sure sibling mode is switched back to hierarchy mode when absolute resources is used
A.assertResource("/a");
A.assertResource("/a/a1");
A.assertResource("/a/a2");
A.assertResource("/b");
A.assertResource("/b/b1");
A.assertResource("/b/b2");
A.assertResource("/c");
A.assertResource("/c/c1");
A.assertResource("/c/c1/c11");
}
@Test
public void reuseInstance() throws Exception {
ResourceBuilder content = new ResourceBuilderFactoryService()
.forResolver(resourceResolver)
.resource("/content");
content.resource("a");
content.resource("b/c");
content.resource("/test")
.siblingsMode()
.resource("1")
.resource("2");
A.assertResource("/content/a");
A.assertResource("/content/b/c");
A.assertResource("/test");
A.assertResource("/test/1");
A.assertResource("/test/2");
}
}