Merged BRANCHES/V4.1 to HEAD:

40010: Enhancement to TemporaryNodes JUnit @Rule so that it gracefully handles checked-out nodes during test code.



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@40012 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Neil McErlean
2012-08-02 10:16:54 +00:00
parent f6b0569d6c
commit bb42c798a0
2 changed files with 78 additions and 6 deletions

View File

@@ -31,6 +31,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
@@ -41,12 +42,13 @@ import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.rules.ExternalResource;
import org.springframework.context.ApplicationContext;
/**
* A JUnit rule designed to help with the automatic cleanup of temporary test nodes.
*
* @author Neil Mc Erlean
* @since Odin
* @since 4.1
*/
public class TemporaryNodes extends ExternalResource
{
@@ -73,8 +75,11 @@ public class TemporaryNodes extends ExternalResource
@Override protected void after()
{
final RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) appContextRule.getApplicationContext().getBean("retryingTransactionHelper");
final NodeService nodeService = (NodeService) appContextRule.getApplicationContext().getBean("nodeService");
final ApplicationContext springContext = appContextRule.getApplicationContext();
final RetryingTransactionHelper transactionHelper = springContext.getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
final CheckOutCheckInService cociService = springContext.getBean("CheckOutCheckInService", CheckOutCheckInService.class);
final NodeService nodeService = springContext.getBean("NodeService", NodeService.class);
// Run as admin to ensure all non-system nodes can be deleted irrespecive of which user created them.
AuthenticationUtil.runAs(new RunAsWork<Void>()
@@ -88,8 +93,16 @@ public class TemporaryNodes extends ExternalResource
// Although we loop through all nodes, this is a cascade-delete and so we may only need to delete the first node.
for (NodeRef node : temporaryNodeRefs)
{
// If it's already been deleted, don't worry about it.
if (nodeService.exists(node))
{
// If it has been checked out, cancel the checkout before deletion.
if (cociService.isCheckedOut(node))
{
log.debug("Cancelling checkout of temporary node " + nodeService.getProperty(node, ContentModel.PROP_NAME));
NodeRef workingCopy = cociService.getWorkingCopy(node);
cociService.cancelCheckout(workingCopy);
}
log.debug("Deleting temporary node " + nodeService.getProperty(node, ContentModel.PROP_NAME));
nodeService.deleteNode(node);
}
@@ -100,7 +113,7 @@ public class TemporaryNodes extends ExternalResource
});
return null;
}
}, AuthenticationUtil.getAdminUserName());
}, AuthenticationUtil.getSystemUserName());
}
/**

View File

@@ -22,8 +22,11 @@ package org.alfresco.util.test.junitrules;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
@@ -31,6 +34,7 @@ import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -46,8 +50,8 @@ import org.junit.rules.RuleChain;
/**
* Test class for {@link TemporaryNodes}.
*
* @author Neil McErlean
* @since Odin
* @author Neil Mc Erlean
* @since 4.1
*/
public class TemporaryNodesTest
{
@@ -74,6 +78,7 @@ public class TemporaryNodesTest
@Rule public RunAsFullyAuthenticatedRule runAsRule = new RunAsFullyAuthenticatedRule(AuthenticationUtil.getAdminUserName());
// Various services
private static CheckOutCheckInService COCI_SERVICE;
private static ContentService CONTENT_SERVICE;
private static NodeService NODE_SERVICE;
private static RetryingTransactionHelper TRANSACTION_HELPER;
@@ -85,6 +90,7 @@ public class TemporaryNodesTest
@BeforeClass public static void initStaticData() throws Exception
{
COCI_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("checkOutCheckInService", CheckOutCheckInService.class);
CONTENT_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("contentService", ContentService.class);
NODE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("nodeService", NodeService.class);
TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
@@ -131,4 +137,57 @@ public class TemporaryNodesTest
}
});
}
@Test public void ensureCheckedOutNodesAreCleanedUp() throws Throwable
{
// Note that because we need to test that the Rule's 'after' behaviour has worked correctly, we cannot
// use the Rule that has been declared in the normal way - otherwise nothing would be cleaned up until
// after our test method.
// Therefore we have to manually poke the Rule to get it to cleanup during test execution.
// NOTE! This is *not* how a JUnit Rule would normally be used.
TemporaryNodes myTemporaryNodes = new TemporaryNodes(APP_CONTEXT_INIT);
// Currently this is a no-op, but just in case that changes.
myTemporaryNodes.before();
// Create some test nodes.
final List<NodeRef> nodesThatShouldBeDeletedByRule = new ArrayList<NodeRef>();
nodesThatShouldBeDeletedByRule.add(myTemporaryNodes.createNode(COMPANY_HOME, "normal node", ContentModel.TYPE_CONTENT, TEST_USER1.getUsername()));
final NodeRef checkedoutNode = myTemporaryNodes.createNode(COMPANY_HOME, "checkedout node", ContentModel.TYPE_CONTENT, TEST_USER1.getUsername());
nodesThatShouldBeDeletedByRule.add(checkedoutNode);
// and check one of them out.
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
NodeRef workingCopy = COCI_SERVICE.checkout(checkedoutNode);
// Ensure that the working copy is cleaned up too.
nodesThatShouldBeDeletedByRule.add(workingCopy);
return null;
}
});
// Now trigger the Rule's cleanup behaviour.
myTemporaryNodes.after();
// and ensure that the nodes are all gone.
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
for (NodeRef node : nodesThatShouldBeDeletedByRule)
{
if (NODE_SERVICE.exists(node))
{
fail("Node '" + NODE_SERVICE.getProperty(node, ContentModel.PROP_NAME) + "' still exists.");
}
}
return null;
}
});
}
}