You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by en...@apache.org on 2023/05/15 16:02:31 UTC

[sling-org-apache-sling-testing-jcr-mock] branch master updated: SLING-11874 auto-set created/lastModified prop values when appropriate (#23)

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

enorman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-jcr-mock.git


The following commit(s) were added to refs/heads/master by this push:
     new dbfcc6b  SLING-11874 auto-set created/lastModified prop values when appropriate (#23)
dbfcc6b is described below

commit dbfcc6bc20a857d5397d0d91de80601c1beee05f
Author: Eric Norman <en...@apache.org>
AuthorDate: Mon May 15 09:02:25 2023 -0700

    SLING-11874 auto-set created/lastModified prop values when appropriate (#23)
---
 .../apache/sling/testing/mock/jcr/MockNode.java    |  95 ++++++++++++-------
 .../mock/jcr/MockNodeOnlyRegisteredTypesTest.java  |   3 +
 .../sling/testing/mock/jcr/MockNodeTest.java       |  69 ++++++++++++++
 .../sling/testing/mock/jcr/MockNodeTypeTest.java   |  13 ++-
 .../sling/testing/mock/jcr/test_nodetypes.cnd      | 104 +++++++++++++++++++++
 5 files changed, 249 insertions(+), 35 deletions(-)

diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockNode.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockNode.java
index 56a88e7..485f56e 100644
--- a/src/main/java/org/apache/sling/testing/mock/jcr/MockNode.java
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockNode.java
@@ -59,6 +59,12 @@ import org.apache.sling.testing.mock.jcr.MockNodeTypeManager.ResolveMode;
  * Mock {@link Node} implementation
  */
 class MockNode extends AbstractItem implements Node {
+    // constants for some well-known types and properties
+    private static final String MIX_CREATED = "mix:created";
+    private static final String JCR_CREATEDBY = "jcr:createdBy";
+
+    private static final String MIX_LASTMODIFIED = "mix:lastModified";
+    private static final String JCR_LASTMODIFIEDBY = "jcr:lastModifiedBy";
 
     public MockNode(final ItemData itemData, final Session session) {
         super(itemData, session);
@@ -81,26 +87,25 @@ class MockNode extends AbstractItem implements Node {
         if (((MockNodeTypeManager)getSession().getWorkspace().getNodeTypeManager()).isMode(ResolveMode.ONLY_REGISTERED)) {
             // auto-add any autocreated children
             NodeDefinition[] childNodeDefinitions = nodeType.getChildNodeDefinitions();
-            if (childNodeDefinitions != null) {
-                for (NodeDefinition nodeDefinition : childNodeDefinitions) {
-                    if (nodeDefinition.isAutoCreated()) {
-                        String defaultPrimaryTypeName = nodeDefinition.getDefaultPrimaryTypeName();
-                        node.addNode(nodeDefinition.getName(), defaultPrimaryTypeName);
-                    }
+            for (NodeDefinition nodeDefinition : childNodeDefinitions) {
+                if (nodeDefinition.isAutoCreated()) {
+                    String defaultPrimaryTypeName = nodeDefinition.getDefaultPrimaryTypeName();
+                    node.addNode(nodeDefinition.getName(), defaultPrimaryTypeName);
                 }
             }
 
             // auto-add any autocreated properties
             PropertyDefinition[] propDefinitions = nodeType.getPropertyDefinitions();
-            if (propDefinitions != null) {
-                for (PropertyDefinition propDefinition : propDefinitions) {
-                    if (propDefinition.isAutoCreated()) {
-                        Value[] defaultValues = propDefinition.getDefaultValues();
-                        if (propDefinition.isMultiple()) {
-                            node.setProperty(propDefinition.getName(), defaultValues);
-                        } else if (defaultValues.length > 0) {
-                            node.setProperty(propDefinition.getName(), defaultValues[0]);
-                        }
+            for (PropertyDefinition propDefinition : propDefinitions) {
+                if (propDefinition.isAutoCreated()) {
+                    Value[] defaultValues = propDefinition.getDefaultValues();
+                    if (propDefinition.isMultiple()) {
+                        node.setProperty(propDefinition.getName(), defaultValues);
+                    } else if (defaultValues.length > 0) {
+                        node.setProperty(propDefinition.getName(), defaultValues[0]);
+                    } else {
+                        // compute system generated property values
+                        maybeSetGeneratedPropertyValue(node, propDefinition);
                     }
                 }
             }
@@ -109,12 +114,39 @@ class MockNode extends AbstractItem implements Node {
         // special handling for some node types
         if (StringUtils.equals(primaryNodeTypeName, JcrConstants.NT_FILE)) {
             node.setProperty(JcrConstants.JCR_CREATED, Calendar.getInstance());
-            node.setProperty("jcr:createdBy", getMockedSession().getUserID());
+            node.setProperty(JCR_CREATEDBY, getMockedSession().getUserID());
         }
 
         return node;
     }
 
+    /**
+     * Sets a system generated value if the specified property definition is known
+     * to need it.
+     * 
+     * @param node the node to set property on
+     * @param propDefinition the property definition to consider
+     */
+    private void maybeSetGeneratedPropertyValue(Node node, PropertyDefinition propDefinition)
+            throws RepositoryException {
+        String name = propDefinition.getName();
+        String declaringNT = propDefinition.getDeclaringNodeType().getName();
+        if (JcrConstants.JCR_CREATED.equals(name) &&
+                (MIX_CREATED.equals(declaringNT) || JcrConstants.NT_VERSION.equals(declaringNT))) {
+            // jcr:created property of a version or a mix:created
+            node.setProperty(name, Calendar.getInstance());
+        } else if (JCR_CREATEDBY.equals(name) && MIX_CREATED.equals(declaringNT)) {
+            // jcr:createdBy property of a mix:created
+            node.setProperty(name, getSession().getUserID());
+        } else if (JcrConstants.JCR_LASTMODIFIED.equals(name) && MIX_LASTMODIFIED.equals(declaringNT)) {
+            // jcr:lastModified property of a mix:lastModified
+            node.setProperty(name, Calendar.getInstance());
+        } else if (JCR_LASTMODIFIEDBY.equals(name) && MIX_LASTMODIFIED.equals(declaringNT)) {
+            // jcr:lastModifiedBy property of a mix:lastModified
+            node.setProperty(name, getSession().getUserID());
+        }
+    }
+
     @Override
     public Node getNode(final String relPath) throws RepositoryException {
         String path = makeAbsolutePath(relPath);
@@ -511,31 +543,30 @@ class MockNode extends AbstractItem implements Node {
                 ).toArray(Value[]::new);
                 this.setProperty(JcrConstants.JCR_MIXINTYPES, newValues);
             }
-            
+
             if (((MockNodeTypeManager)getSession().getWorkspace().getNodeTypeManager()).isMode(ResolveMode.ONLY_REGISTERED)) {
                 NodeType mixinNodeType = getSession().getWorkspace().getNodeTypeManager().getNodeType(mixinName);
                 // auto-add any autocreated children
                 NodeDefinition[] childNodeDefinitions = mixinNodeType.getChildNodeDefinitions();
-                if (childNodeDefinitions != null) {
-                    for (NodeDefinition nodeDefinition : childNodeDefinitions) {
-                        if (nodeDefinition.isAutoCreated()) {
-                            String defaultPrimaryTypeName = nodeDefinition.getDefaultPrimaryTypeName();
-                            addNode(nodeDefinition.getName(), defaultPrimaryTypeName);
-                        }
+                for (NodeDefinition nodeDefinition : childNodeDefinitions) {
+                    if (nodeDefinition.isAutoCreated()) {
+                        String defaultPrimaryTypeName = nodeDefinition.getDefaultPrimaryTypeName();
+                        addNode(nodeDefinition.getName(), defaultPrimaryTypeName);
                     }
                 }
 
                 // auto-add any autocreated properties
                 PropertyDefinition[] propDefinitions = mixinNodeType.getPropertyDefinitions();
-                if (propDefinitions != null) {
-                    for (PropertyDefinition propDefinition : propDefinitions) {
-                        if (propDefinition.isAutoCreated()) {
-                            Value[] defaultValues = propDefinition.getDefaultValues();
-                            if (propDefinition.isMultiple()) {
-                                setProperty(propDefinition.getName(), defaultValues);
-                            } else if (defaultValues.length > 0) {
-                                setProperty(propDefinition.getName(), defaultValues[0]);
-                            }
+                for (PropertyDefinition propDefinition : propDefinitions) {
+                    if (propDefinition.isAutoCreated()) {
+                        Value[] defaultValues = propDefinition.getDefaultValues();
+                        if (propDefinition.isMultiple()) {
+                            setProperty(propDefinition.getName(), defaultValues);
+                        } else if (defaultValues.length > 0) {
+                            setProperty(propDefinition.getName(), defaultValues[0]);
+                        } else {
+                            // compute system generated property values
+                            maybeSetGeneratedPropertyValue(this, propDefinition);
                         }
                     }
                 }
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeOnlyRegisteredTypesTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeOnlyRegisteredTypesTest.java
index 63f2408..b56e456 100644
--- a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeOnlyRegisteredTypesTest.java
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeOnlyRegisteredTypesTest.java
@@ -19,6 +19,7 @@
 package org.apache.sling.testing.mock.jcr;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -67,7 +68,9 @@ public class MockNodeOnlyRegisteredTypesTest {
         auto1.addMixin("mix:autocreatedChildAndProp");
         assertTrue(auto1.hasProperty("prop1"));
         assertTrue(auto1.hasProperty("prop2"));
+        assertFalse(auto1.hasProperty("prop3"));
         assertTrue(auto1.hasNode("child1"));
+        assertFalse(auto1.hasNode("child2"));
     }
 
     @Test
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTest.java
index 6fc055d..e9976f9 100644
--- a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTest.java
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTest.java
@@ -26,6 +26,9 @@ import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -44,6 +47,7 @@ import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.util.TraversingItemVisitor;
 
 import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.commons.cnd.ParseException;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -407,4 +411,69 @@ public class MockNodeTest extends AbstractItemTest {
         assertEquals(4, leaveProperties.size());
     }
 
+    /**
+     * load the node types and switch the nodetypemanager to ONLY_REGISTERED mode
+     */
+    protected void loadNodeTypes() throws ParseException, RepositoryException, IOException {
+        // load the node types and switch the nodetypemanager to ONLY_REGISTERED mode
+        try (Reader reader = new InputStreamReader(getClass().getResourceAsStream("test_nodetypes.cnd"))) {
+            MockJcr.loadNodeTypeDefs(this.session, reader);
+        }
+    }
+
+    /**
+     * SLING-11874 auto-set created/lastModified prop values when appropriate
+     */
+    @Test
+    public void testAddNodeCreatesSystemGeneratedAutoPropertyValues() throws RepositoryException, IOException, ParseException {
+        loadNodeTypes();
+        Node foo = this.session.getRootNode().addNode("foo", JcrConstants.NT_HIERARCHYNODE);
+        Node versionFoo = this.session.getRootNode().addNode("versionFoo", JcrConstants.NT_VERSION);
+
+        // verify that the autocreated props exist
+        assertTrue(foo.hasProperty(JcrConstants.JCR_CREATED));
+        assertTrue(foo.hasProperty("jcr:createdBy"));
+        assertTrue(versionFoo.hasProperty(JcrConstants.JCR_CREATED));
+    }
+
+    /**
+     * SLING-11874 auto-set created/lastModified prop values when appropriate
+     */
+    @Test
+    public void testAddMixinCreatesSystemGeneratedAutoPropertyValues() throws RepositoryException, IOException, ParseException {
+        loadNodeTypes();
+
+        Node foo = this.session.getRootNode().addNode("foo", JcrConstants.NT_UNSTRUCTURED);
+        // verify that the autocreated props do not exist
+        assertFalse(foo.hasProperty(JcrConstants.JCR_CREATED));
+        assertFalse(foo.hasProperty("jcr:createdBy"));
+        assertFalse(foo.hasProperty(JcrConstants.JCR_LASTMODIFIED));
+        assertFalse(foo.hasProperty("jcr:lastModifiedBy"));
+
+        // add mixins
+        foo.addMixin("mix:created");
+        foo.addMixin("mix:lastModified");
+
+        // verify that the autocreated props do exist
+        assertTrue(foo.hasProperty(JcrConstants.JCR_CREATED));
+        assertTrue(foo.hasProperty("jcr:createdBy"));
+        assertTrue(foo.hasProperty(JcrConstants.JCR_LASTMODIFIED));
+        assertTrue(foo.hasProperty("jcr:lastModifiedBy"));
+    }
+
+    /**
+     * SLING-11874 To test created and lastModified props are not autocreated when not declared in their mixins
+     */
+    @Test
+    public void testAddNodeDoesNotCreateNonMixinCreatedAndLastModified() throws RepositoryException, IOException, ParseException {
+        loadNodeTypes();
+
+        Node foo = this.session.getRootNode().addNode("foo", "nt:nonMixinCreatedAndLastModified");
+        // verify that the autocreated props do not exist
+        assertFalse(foo.hasProperty(JcrConstants.JCR_CREATED));
+        assertFalse(foo.hasProperty("jcr:createdBy"));
+        assertFalse(foo.hasProperty(JcrConstants.JCR_LASTMODIFIED));
+        assertFalse(foo.hasProperty("jcr:lastModifiedBy"));
+    }
+
 }
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTypeTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTypeTest.java
index b11b9fe..94f3096 100644
--- a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTypeTest.java
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTypeTest.java
@@ -24,6 +24,8 @@ import static org.junit.Assert.assertTrue;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.stream.Stream;
 
 import javax.jcr.RepositoryException;
@@ -212,9 +214,14 @@ public class MockNodeTypeTest extends AbstractMockNodeTypeTest {
         NodeType mixCreated = nodeTypeManager.getNodeType("mix:created");
         NodeTypeIterator subtypes = mixCreated.getSubtypes();
         assertEquals(3, subtypes.getSize());
-        assertEquals(JcrConstants.NT_FILE, subtypes.nextNodeType().getName());
-        assertEquals(JcrConstants.NT_HIERARCHYNODE, subtypes.nextNodeType().getName());
-        assertEquals(JcrConstants.NT_FOLDER, subtypes.nextNodeType().getName());
+        // convert to a set to avoid iteration order troubles
+        Set<String> subtypesSet = new HashSet<>();
+        while (subtypes.hasNext()) {
+            subtypesSet.add(subtypes.nextNodeType().getName());
+        }
+        assertTrue(subtypesSet.contains(JcrConstants.NT_FILE));
+        assertTrue(subtypesSet.contains(JcrConstants.NT_HIERARCHYNODE));
+        assertTrue(subtypesSet.contains(JcrConstants.NT_FOLDER));
 
         NodeType ntFolder = nodeTypeManager.getNodeType(JcrConstants.NT_FOLDER);
         subtypes = ntFolder.getSubtypes();
diff --git a/src/test/resources/org/apache/sling/testing/mock/jcr/test_nodetypes.cnd b/src/test/resources/org/apache/sling/testing/mock/jcr/test_nodetypes.cnd
index 35985d0..689b837 100644
--- a/src/test/resources/org/apache/sling/testing/mock/jcr/test_nodetypes.cnd
+++ b/src/test/resources/org/apache/sling/testing/mock/jcr/test_nodetypes.cnd
@@ -152,6 +152,100 @@
   mixin
   - jcr:uuid (STRING) mandatory autocreated protected INITIALIZE 
 
+//------------------------------------------------------------------------------
+// V E R S I O N I N G
+//------------------------------------------------------------------------------
+
+/**
+ * @since 2.0
+ */
+[mix:simpleVersionable]
+  mixin
+  - jcr:isCheckedOut (BOOLEAN) = 'true' mandatory autocreated protected IGNORE
+
+/**
+ * @since 1.0
+ */
+[mix:versionable] > mix:simpleVersionable, mix:referenceable
+  mixin
+  - jcr:versionHistory (REFERENCE) mandatory protected IGNORE < 'nt:versionHistory'
+  - jcr:baseVersion (REFERENCE) mandatory protected IGNORE < 'nt:version'
+  - jcr:predecessors (REFERENCE) mandatory protected multiple IGNORE < 'nt:version'
+  - jcr:mergeFailed (REFERENCE) protected multiple ABORT < 'nt:version'
+    /** @since 2.0 */
+  - jcr:activity (REFERENCE) protected < 'nt:activity'
+    /** @since 2.0 */
+  - jcr:configuration (REFERENCE) protected IGNORE < 'nt:configuration'
+
+/**
+ * @since 1.0
+ */
+[nt:versionHistory] > mix:referenceable
+  - jcr:versionableUuid (STRING) mandatory autocreated protected ABORT
+    /** @since 2.0 */
+  - jcr:copiedFrom (WEAKREFERENCE) protected ABORT < 'nt:version'
+  + jcr:rootVersion (nt:version) = nt:version mandatory autocreated protected ABORT
+  + jcr:versionLabels (nt:versionLabels) = nt:versionLabels mandatory autocreated protected ABORT
+  + * (nt:version) = nt:version protected ABORT
+
+/**
+ * @since 1.0
+ */
+[nt:versionLabels]
+  - * (REFERENCE) protected ABORT < 'nt:version'
+
+/**
+ * @since 1.0
+ */
+[nt:version] > mix:referenceable
+  - jcr:created (DATE) mandatory autocreated protected ABORT
+  - jcr:predecessors (REFERENCE) protected multiple ABORT < 'nt:version'
+  - jcr:successors (REFERENCE) protected multiple ABORT < 'nt:version'
+    /** @since 2.0 */
+  - jcr:activity (REFERENCE) protected ABORT < 'nt:activity'
+  + jcr:frozenNode (nt:frozenNode) protected ABORT
+
+/**
+ * @since 1.0
+ */
+[nt:frozenNode]
+  orderable
+  - jcr:frozenPrimaryType (NAME) mandatory autocreated protected ABORT
+  - jcr:frozenMixinTypes (NAME) protected multiple ABORT
+  - jcr:frozenUuid (STRING) mandatory autocreated protected ABORT
+  - * (UNDEFINED) protected ABORT
+  - * (UNDEFINED) protected multiple ABORT
+  + * (nt:base) protected sns ABORT
+
+/**
+ * @since 1.0
+ */
+[nt:versionedChild]
+  - jcr:childVersionHistory (REFERENCE) mandatory autocreated protected ABORT < 'nt:versionHistory'
+
+/**
+ * @since 2.0
+ */
+[nt:activity] > mix:referenceable
+  - jcr:activityTitle (STRING) mandatory autocreated protected
+
+/**
+ * @since 2.0
+ */
+[nt:configuration] > mix:versionable
+  - jcr:root (REFERENCE) mandatory autocreated protected
+
+/**
+ * @since oak 1.0
+ */
+ [rep:VersionablePaths]
+  mixin
+  - * (PATH) protected ABORT
+
+
+//------------------------------------------------------------------------------
+// O T H E R 
+//------------------------------------------------------------------------------
 
 /**
  * For code coverage of the cnd loading
@@ -173,6 +267,16 @@
 [mix:autocreatedChildAndProp]
   mixin
   + child1 (nt:base) = nt:folder autocreated
+  + child2 (nt:base) = nt:folder
   - prop1 (STRING) = 'value1' autocreated
   - prop2 (STRING) = 'value1', 'value2' multiple autocreated
+  - prop3 (STRING) = 'value1'
 
+/**
+ * To test created and lastModified props are not autocreated when not declared in their mixins
+ */
+[nt:nonMixinCreatedAndLastModified]
+  - jcr:created (DATE) autocreated
+  - jcr:createdBy (STRING) autocreated
+  - jcr:lastModified (DATE) autocreated
+  - jcr:lastModifiedBy (STRING) autocreated