/** * */ package org.alfresco.util; import java.util.HashMap; import java.util.Map; import junit.framework.TestCase; import org.alfresco.error.AlfrescoRuntimeException; 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.repository.NodeRef; /** * Test case base class with helper methods for transactional tests. * * @author Roy Wetherall */ public abstract class RetryingTransactionHelperTestCase extends TestCase { /** * @return retrying transaction helper */ public abstract RetryingTransactionHelper getRetryingTransactionHelper(); /** * Executes a test in a retrying transaction as the admin user. * * @param type of the object resulting from the test, can be set to {@link Void} if none. * @param test test object to be executed within a retrying transaction * @return A the result of the test */ protected A doTestInTransaction(final Test test) { return doTestInTransaction(test, AuthenticationUtil.getAdminUserName()); } /** * Executes a test in a retrying transaction as the admin user. * * @param type of the object resulting from the test, can be set to {@link Void} if none. * @param test test object to be executed within a retrying transaction * @param asUser user to execute the test as * @return A the result of the test */ protected A doTestInTransaction(final Test test, final String asUser) { String origUser = AuthenticationUtil.getFullyAuthenticatedUser(); AuthenticationUtil.setFullyAuthenticatedUser(asUser); try { // Execute the run() method within a retrying transaction RetryingTransactionCallback doRun = new RetryingTransactionCallback() { @Override public A execute() throws Throwable { // Run test as user return test.run(); } }; final A result = getRetryingTransactionHelper().doInTransaction(doRun); // Execute the test() method within a retrying transaction RetryingTransactionCallback doTest = new RetryingTransactionCallback() { @Override public Void execute() throws Throwable { // pass the result of the run into the test test.test(result); return null; } }; getRetryingTransactionHelper().doInTransaction(doTest); return result; } finally { if (origUser != null) { AuthenticationUtil.setFullyAuthenticatedUser(origUser); } else { AuthenticationUtil.clearCurrentSecurityContext(); } } } /** * Executes a test in a retrying transaction. Run as admin user. * * @param test failure test object */ protected void doTestInTransaction(final FailureTest test) { doTestInTransaction(test, AuthenticationUtil.getAdminUserName()); } /** * Executes a test in a retrying transaction. * * @param test failure test object * @param asUser user to run test as */ protected void doTestInTransaction(final FailureTest test, final String asUser) { String origUser = AuthenticationUtil.getFullyAuthenticatedUser(); AuthenticationUtil.setFullyAuthenticatedUser(asUser); try { RetryingTransactionCallback doRun = new RetryingTransactionCallback() { @Override public Void execute() throws Throwable { Class eType = test.getExpectedExceptionClass(); try { test.run(); } catch (Throwable exception) { if (eType.isInstance(exception) == false) { // Genuine error so re-throw throw exception; } // Otherwise, it's an expected failure return null; } // Fail since not expected to succeed fail(test.getMessage()); return null; } }; getRetryingTransactionHelper().doInTransaction(doRun); } finally { if (origUser != null) { AuthenticationUtil.setFullyAuthenticatedUser(origUser); } else { AuthenticationUtil.clearCurrentSecurityContext(); } } } /** * Class containing the code to run as test and the optional code to check the results of the * test in a separate transaction if required. */ protected abstract class Test { /** Map containing model values. Used to pass data between run and test methods. */ protected Map model = new HashMap(5); /** * Helper method to set a string vaule in the model * @param key model key * @param value model value * @return String model value */ protected String setString(String key, String value) { if (value != null) { model.put(key, value); } return value; } /** * Helper method to get a string value from the model * @param key model key * @return String model value, null if none */ protected String getString(String key) { return (String)model.get(key); } /** * Helper method to set node reference value in model * @param key model key * @param nodeRef node reference * @return {@link NodeRef} node reference */ protected NodeRef setNodeRef(String key, NodeRef nodeRef) { if (nodeRef != null) { model.put(key, nodeRef); } return nodeRef; } /** * Helper method to get node reference value from model * @param key mode key * @return {@link NodeRef} node reference */ protected NodeRef getNodeRef(String key) { return (NodeRef)model.get(key); } /** * Body of the test is implemented here. * @return A result of the test * @throws Exception */ public abstract A run() throws Exception; /** * If you wish to test the results of the above method within a new and separate * transaction then implement the tests here. * @param result result of the above method * @throws Exception */ public void test(A result) throws Exception { // Default implementation does nothing. // Override this if you want to test the results of the // run method in a new transaction. } } /** * Class containing test code that is expected to fail. */ protected abstract class FailureTest { /** Failure message */ private String message = "This test was expected to fail."; /** Expected failure exception */ private Class expectedExceptionClass = AlfrescoRuntimeException.class; /** * @param message failure message */ public FailureTest(String message) { this.message = message; } /** * @param expectedExceptionClass expected exception class */ public FailureTest(Class expectedExceptionClass) { this.expectedExceptionClass = expectedExceptionClass; } /** * @param message failure message * @param expectedExceptionClass expected exception class */ public FailureTest(String message, Class expectedExceptionClass) { this.message = message; this.expectedExceptionClass = expectedExceptionClass; } /** * @return expected exception class */ public Class getExpectedExceptionClass() { return expectedExceptionClass; } /** * @return failure message */ public String getMessage() { return message; } /** * Default constructor */ public FailureTest() { super(); } /** * Code to test for failure * * @throws Exception */ public abstract void run() throws Exception; } }