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.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; 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.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter; 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.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.junit.rules.ExternalResource; import org.junit.rules.ExternalResource;
import org.springframework.context.ApplicationContext;
/** /**
* A JUnit rule designed to help with the automatic cleanup of temporary test nodes. * A JUnit rule designed to help with the automatic cleanup of temporary test nodes.
* *
* @author Neil Mc Erlean * @author Neil Mc Erlean
* @since Odin * @since 4.1
*/ */
public class TemporaryNodes extends ExternalResource public class TemporaryNodes extends ExternalResource
{ {
@@ -73,8 +75,11 @@ public class TemporaryNodes extends ExternalResource
@Override protected void after() @Override protected void after()
{ {
final RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) appContextRule.getApplicationContext().getBean("retryingTransactionHelper"); final ApplicationContext springContext = appContextRule.getApplicationContext();
final NodeService nodeService = (NodeService) appContextRule.getApplicationContext().getBean("nodeService");
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. // Run as admin to ensure all non-system nodes can be deleted irrespecive of which user created them.
AuthenticationUtil.runAs(new RunAsWork<Void>() 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. // 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) for (NodeRef node : temporaryNodeRefs)
{ {
// If it's already been deleted, don't worry about it.
if (nodeService.exists(node)) 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)); log.debug("Deleting temporary node " + nodeService.getProperty(node, ContentModel.PROP_NAME));
nodeService.deleteNode(node); nodeService.deleteNode(node);
} }
@@ -100,7 +113,7 @@ public class TemporaryNodes extends ExternalResource
}); });
return null; 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.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.model.ContentModel; 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.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; 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.ContentReader;
import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
@@ -46,8 +50,8 @@ import org.junit.rules.RuleChain;
/** /**
* Test class for {@link TemporaryNodes}. * Test class for {@link TemporaryNodes}.
* *
* @author Neil McErlean * @author Neil Mc Erlean
* @since Odin * @since 4.1
*/ */
public class TemporaryNodesTest public class TemporaryNodesTest
{ {
@@ -74,6 +78,7 @@ public class TemporaryNodesTest
@Rule public RunAsFullyAuthenticatedRule runAsRule = new RunAsFullyAuthenticatedRule(AuthenticationUtil.getAdminUserName()); @Rule public RunAsFullyAuthenticatedRule runAsRule = new RunAsFullyAuthenticatedRule(AuthenticationUtil.getAdminUserName());
// Various services // Various services
private static CheckOutCheckInService COCI_SERVICE;
private static ContentService CONTENT_SERVICE; private static ContentService CONTENT_SERVICE;
private static NodeService NODE_SERVICE; private static NodeService NODE_SERVICE;
private static RetryingTransactionHelper TRANSACTION_HELPER; private static RetryingTransactionHelper TRANSACTION_HELPER;
@@ -85,6 +90,7 @@ public class TemporaryNodesTest
@BeforeClass public static void initStaticData() throws Exception @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); CONTENT_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("contentService", ContentService.class);
NODE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("nodeService", NodeService.class); NODE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("nodeService", NodeService.class);
TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.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;
}
});
}
} }