Merged HEAD-QA to HEAD (4.2) (including moving test classes into separate folders)

51903 to 54309 


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@54310 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Samuel Langlois
2013-08-20 17:17:31 +00:00
parent 0a36e2af67
commit ab4ca7177f
1576 changed files with 36419 additions and 8603 deletions

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
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.security.PersonService;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
/**
* @author Neil Mc Erlean
* @since 4.2
*/
public abstract class AbstractAlfrescoPersonTest
{
/** Gets the username of the test user to be created. */
protected abstract String createTestUserName();
/** A hookpoint to allow subclasses to add addition validation. */
protected void additionalValidations(String username, boolean userExists)
{
// Intentionally empty
}
// Rule to initialise the default Alfresco spring configuration
@ClassRule public static ApplicationContextInit APP_CONTEXT_INIT = new ApplicationContextInit();
// Rule to create a test user
protected final String testUsername = createTestUserName();
public final AlfrescoPerson testUserRule = new AlfrescoPerson(APP_CONTEXT_INIT, testUsername);
// A rule to allow individual test methods all to be run as "admin".
public RunAsFullyAuthenticatedRule runAsRule = new RunAsFullyAuthenticatedRule(AuthenticationUtil.getAdminUserName());
@Rule public final RuleChain ruleChain = RuleChain.outerRule(runAsRule).around(testUserRule);
protected static PersonService PERSON_SERVICE;
protected static RetryingTransactionHelper TRANSACTION_HELPER;
@BeforeClass public static void initStaticData() throws Exception
{
PERSON_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("PersonService", PersonService.class);
TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
}
@Test public void ensureTestUserWasCreated() throws Exception
{
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
validateCmPersonNode(testUsername, true);
additionalValidations(testUsername, true);
return null;
}
});
}
/** Validate that the person is correctly persisted (or not). */
protected abstract void validateCmPersonNode(String username, boolean exists);
@Test public void ensureUserIsCleanedUp() 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.
// First manually run the 'after' part of the rule on this class - so that it does not interfere.
this.testUserRule.after();
final String testUserForThisMethodOnly = createTestUserName();
AlfrescoPerson myTestUser = new AlfrescoPerson(APP_CONTEXT_INIT, testUserForThisMethodOnly);
// Manually trigger the execution of the 'before' part of the rule.
myTestUser.before();
// Now trigger the Rule's cleanup behaviour.
myTestUser.after();
// and ensure that the nodes are all gone.
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
validateCmPersonNode(testUserForThisMethodOnly, false);
additionalValidations(testUserForThisMethodOnly, false);
return null;
}
});
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.test.testusers.TestUserComponent;
import org.springframework.context.ApplicationContext;
/**
* This class is an abstract base class for JUnit rules which manage the lifecycle of <code>cm:person</code>
* nodes and authentication details for the transient users often required within test code.
*
* @author Neil Mc Erlean
* @since Odin
*/
public abstract class AbstractPersonRule extends AbstractRule
{
/**
* Constructs a person rule with the specified spring context, which will be necessary
* to actually create and delete the users.
*
* @param appContext the spring app context (needed to get at Alfresco services).
*/
public AbstractPersonRule(ApplicationContext appContext)
{
super(appContext);
}
/**
* Constructs a person rule with a reference to an {@link ApplicationContextInit rule}. This other rule will
* be used to access the application context and from there the necessary services for the creation and deletion of users.
*
* @param appContext a rule which can provide the spring application context.
*/
public AbstractPersonRule(ApplicationContextInit appContextRule)
{
super(appContextRule);
}
/**
* This method creates a user with the specified username.
* If an authentication for this username does not exist, it is created.
* If a cm:person for this username does not exist, it is created.
* This method does not handle transactions.
*
* @param userName the username of the new user.
* @return the NodeRef of the created cm:person node.
*/
protected NodeRef createPerson(final String userName)
{
// Get the spring context
final ApplicationContext ctxt = getApplicationContext();
// Extract required service beans
final TestUserComponent testUserComponent = (TestUserComponent) ctxt.getBean("testUserComponent");
return testUserComponent.createTestUser(userName);
}
/**
* This method deletes the specified user's person and authentication details if they are
* present in the system.
* This method does not handle transactions.
*
* @param userName the username of the user to be deleted.
*/
protected void deletePerson(final String userName)
{
// Get the spring context
final ApplicationContext ctxt = getApplicationContext();
// Extract required service beans
final TestUserComponent testUserComponent = (TestUserComponent) ctxt.getBean("testUserComponent");
testUserComponent.deleteTestUser(userName);
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2005-2012
* Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import org.alfresco.util.ParameterCheck;
import org.junit.rules.ExternalResource;
import org.springframework.context.ApplicationContext;
/**
* Abstract junit rule, which provides access to the Spring application context.
*
* An explicit ApplicationContext or an ApplicationContextInit rule can be passed at construction time.
* getApplicationContext will either return the instance passed in, or retrieve one from the rule.
*
* @author Alex Miller
*/
public abstract class AbstractRule extends ExternalResource
{
protected final ApplicationContext appContext;
protected final ApplicationContextInit appContextRule;
/**
* @param appContext for use by sub classes.
*/
protected AbstractRule(ApplicationContext appContext)
{
ParameterCheck.mandatory("appContext", appContext);
this.appContext = appContext;
this.appContextRule = null;
}
/**
* @param appContextRule {@link ApplicationContextInit} rule used to provide ApplicationContext to sub classes.
*/
protected AbstractRule(ApplicationContextInit appContextRule)
{
ParameterCheck.mandatory("appContextRule", appContextRule);
this.appContext = null;
this.appContextRule = appContextRule;
}
/**
* This method provides the spring application context to subclasses. It either provides the explicit ApplicationContext, passed in
* at construction time, or retrieves it from the {@link ApplicationContextInit} rule, passed in the alternative constructor.
*
* @return the spring application context
* @throws NullPointerException if the application context has not been initialised when requested.
*/
protected ApplicationContext getApplicationContext() {
ApplicationContext result = null;
// The app context is either provided explicitly:
if (appContext != null)
{
result = appContext;
}
// or is implicitly accessed via another rule:
else
{
ApplicationContext contextFromRule = appContextRule.getApplicationContext();
if (contextFromRule != null)
{
result = contextFromRule;
}
else
{
throw new NullPointerException("Cannot retrieve application context from provided rule.");
}
}
return result;
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright (C) 2005-2012
Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
/**
* This JUnit rule can be used to setup and teardown a set of Alfresco users for test purposes.
* <p/>
* Example usage:
* <pre>
* public class YourTestClass
* {
* // Normally we would initialise the spring application context in another rule.
* &#64;ClassRule public static final ApplicationContextInit APP_CONTEXT_RULE = new ApplicationContextInit();
*
* // This rule will give us 8 GUID-named users.
* &#64;Rule public final AlfrescoPeople testPeople = new AlfrescoPeople(APP_CONTEXT_RULE, 8);
*
* &#64;Test public void aTestMethod()
* {
* Set&#60;String&#62; userNames = testPeople.getUsernames();
* // etc
* }
* }
* </pre>
*
* @author Neil Mc Erlean
* @since Odin
*/
public class AlfrescoPeople extends AbstractPersonRule
{
private static final Log log = LogFactory.getLog(AlfrescoPeople.class);
private final int personCount;
/**
* A Map (username: person nodeRef) of created users.
*/
Map<String, NodeRef> usersPersons = new TreeMap<String, NodeRef>();
/**
* @param appContext the spring app context (needed to get at Alfresco services).
* @param personCount the number of users to be created
*/
public AlfrescoPeople(ApplicationContext appContext, int personCount)
{
super(appContext);
this.personCount = personCount;
}
/**
* @param appContextRule a rule which can be used to retrieve the spring app context.
* @param personCount the number of users to be created
*/
public AlfrescoPeople(ApplicationContextInit appContextRule, int personCount)
{
super(appContextRule);
this.personCount = personCount;
}
@Override protected void before() throws Throwable
{
// Set up required services
ApplicationContext ctxt = getApplicationContext();
final RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) ctxt.getBean("retryingTransactionHelper");
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override public Void execute() throws Throwable
{
if (log.isDebugEnabled())
{
log.debug("Creating " + personCount + " users for test purposes...");
}
for (int i = 0; i < personCount; i++)
{
final String userName = GUID.generate();
NodeRef personNode = createPerson(userName);
usersPersons.put(userName, personNode);
}
return null;
}
});
}
@Override protected void after()
{
// Set up required services
ApplicationContext ctxt = getApplicationContext();
final RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) ctxt.getBean("retryingTransactionHelper");
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override public Void execute() throws Throwable
{
for (Map.Entry<String, NodeRef> entry : usersPersons.entrySet())
{
deletePerson(entry.getKey());
}
return null;
}
});
}
/**
* @return the usernames of the people created by this rule.
*/
public Set<String> getUsernames()
{
return this.usersPersons.keySet();
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.GUID;
import org.springframework.context.ApplicationContext;
/**
* This JUnit rule can be used to setup and teardown a single Alfresco user for test purposes.
* <p/>
* Example usage:
* <pre>
* public class YourTestClass
* {
* // Normally we would initialise the spring application context in another rule.
* &#64;ClassRule public static final ApplicationContextInit APP_CONTEXT_RULE = new ApplicationContextInit();
*
* // We pass the rule that creates the spring application context.
* // This rule will give us a user with username 'NeilM'.
* &#64;Rule public final AlfrescoPerson namedPerson = new AlfrescoPerson(APP_CONTEXT_RULE, "NeilM");
* // This rule with give us a user with a GUID-generated name.
* &#64;Rule public final AlfrescoPerson guidPerson = new AlfrescoPerson(APP_CONTEXT_RULE);
*
* &#64;Test public void aTestMethod()
* {
* AuthenticationUtil.setFullyAuthenticatedUser(namedPerson.getUsername());
* // etc
* }
* }
* </pre>
*
* @author Neil Mc Erlean
* @since Odin
*/
public class AlfrescoPerson extends AbstractPersonRule
{
private final String userName;
private NodeRef personNodeRef;
/**
* Constructs the rule with a spring ApplicationContext.
* A GUID-generated username will be used for the test user.
*
* @param appContext the spring app context (needed to get at Alfresco services).
*/
public AlfrescoPerson(ApplicationContext appContext)
{
this(appContext, GUID.generate());
}
/**
* Constructs the rule with a reference to a {@link ApplicationContextInit rule} which can be used to retrieve the ApplicationContext.
* A GUID-generated username will be used for the test user.
*
* @param appContextRule a rule which can be used to retrieve the spring app context.
*/
public AlfrescoPerson(ApplicationContextInit appContextRule)
{
this(appContextRule, GUID.generate());
}
/**
* Constructs the rule with a spring ApplicationContext.
*
* @param appContext the spring app context (needed to get at Alfresco services).
* @param userName the username for the person to be created.
*/
public AlfrescoPerson(ApplicationContext appContext, String userName)
{
super(appContext);
this.userName = userName;
}
/**
* Constructs the rule with a reference to a {@link ApplicationContextInit rule} which can be used to retrieve the ApplicationContext.
*
* @param appContextRule a rule which can be used to retrieve the spring app context.
* @param userName the username for the person to be created.
*/
public AlfrescoPerson(ApplicationContextInit appContextRule, String userName)
{
super(appContextRule);
this.userName = userName;
}
@Override protected void before()
{
ApplicationContext ctxt = getApplicationContext();
RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) ctxt.getBean("retryingTransactionHelper");
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override public Void execute() throws Throwable
{
personNodeRef = createPerson(userName);
return null;
}
});
}
@Override protected void after()
{
ApplicationContext ctxt = getApplicationContext();
RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) ctxt.getBean("retryingTransactionHelper");
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override public Void execute() throws Throwable
{
deletePerson(userName);
return null;
}
});
}
/**
* @return the username of the person created by this rule.
*/
public String getUsername()
{
return this.userName;
}
/**
* Gets the {@link NodeRef person node}.
* @return the person node.
*/
public NodeRef getPersonNode()
{
return this.personNodeRef;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import static org.junit.Assert.assertEquals;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.util.GUID;
/**
* Test class for {@link AlfrescoPerson}.
*
* @author Neil Mc Erlean
* @since 4.2
*/
public class AlfrescoPersonTest extends AbstractAlfrescoPersonTest
{
@Override protected String createTestUserName()
{
// In Community/Enterprise Alfresco, usernames are "just Strings" - e.g. they need not be email addresses.
return GUID.generate();
}
@Override protected void validateCmPersonNode(final String username, final boolean exists)
{
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
assertEquals("Test person's existence was wrong", exists, PERSON_SERVICE.personExists(username));
return null;
}
});
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.util.GUID;
import org.springframework.context.ApplicationContext;
/**
* This JUnit rule can be used to setup and teardown a single Alfresco Tenant for test purposes.
* <p/>
* Example usage:
* <pre>
* public class YourTestClass
* {
* // Normally we would initialise the spring application context in another rule.
* &#64;ClassRule public static final ApplicationContextInit APP_CONTEXT_RULE = new ApplicationContextInit();
*
* // We pass the rule that creates the spring application context.
* // This rule will give us a tenant with the domain 'testtenant'.
* &#64;Rule public final AlfrescoTenant tenant = new AlfrescoTenant(APP_CONTEXT_RULE, "testtenant");
*
* &#64;Test public void aTestMethod()
* {
tenant.runAsSystem(new TenantRunAsWork<Void>() {
&#64;Override
public Void doWork() throws Exception {
// Do something as the tenant system user.
}
});
* }
* }
* </pre>
*
* @author Alex Miller
* @see AlfrescoPerson Consider using {@link AlfrescoPerson} instead, which will create tenants as needed when run in a Cloud build.
*/
public class AlfrescoTenant extends AbstractRule
{
public static final String ADMIN_PASSWORD = "password";
private final String tenantName;
/**
* Constructs the rule with a spring ApplicationContext.
* A GUID-generated tenant name will be used for the test tenant.
*
* @param appContext the spring app context (needed to get at Alfresco services).
*/
public AlfrescoTenant(ApplicationContext appContext)
{
this(appContext, GUID.generate());
}
/**
* Constructs the rule with a reference to a {@link ApplicationContextInit rule} which can be used to retrieve the ApplicationContext.
* A GUID-generated tenant name will be used for the test user.
*
* @param appContextRule a rule which can be used to retrieve the spring app context.
*/
public AlfrescoTenant(ApplicationContextInit appContextRule)
{
this(appContextRule, GUID.generate());
}
/**
* Constructs the rule with a spring ApplicationContext.
*
* @param appContext the spring app context (needed to get at Alfresco services).
* @param userName the username for the person to be created.
*/
public AlfrescoTenant(ApplicationContext appContext, String tenantName)
{
super(appContext);
this.tenantName = tenantName.toLowerCase();
}
/**
* Constructs the rule with a reference to a {@link ApplicationContextInit rule} which can be used to retrieve the ApplicationContext.
*
* @param appContextRule a rule which can be used to retrieve the spring app context.
* @param tenantName the name for the tenant to be created.
*/
public AlfrescoTenant(ApplicationContextInit appContextRule, String tenantName)
{
super(appContextRule);
this.tenantName = tenantName.toLowerCase();
}
/**
* Create the tenant.
*/
@Override protected void before() throws Throwable
{
final ApplicationContext appCtx = getApplicationContext();
RetryingTransactionHelper transactionHelper = appCtx.getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
final TenantAdminService tenantAdminService = appCtx.getBean("tenantAdminService", TenantAdminService.class);
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override public Void execute() throws Throwable
{
tenantAdminService.createTenant(tenantName, ADMIN_PASSWORD.toCharArray());
return null;
}
});
}
/**
* Remove the tenant
*/
@Override protected void after()
{
final ApplicationContext appCtx = getApplicationContext();
RetryingTransactionHelper transactionHelper = appCtx.getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
final TenantAdminService tenantAdminService = appCtx.getBean("tenantAdminService", TenantAdminService.class);
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override public Void execute() throws Throwable
{
tenantAdminService.deleteTenant(tenantName);
return null;
}
});
}
/**
* @return The tenant domain.
*/
public String getTenantDomain() {
return tenantName;
}
/**
* Do runAsWork as the system user for this tenant.
*
* @param runAsWork The work to be done as the system user of this tenant.
* @return The result of the work
*/
public <T> T runAsSystem(TenantRunAsWork<T> runAsWork) {
return TenantUtil.runAsSystemTenant(runAsWork, tenantName);
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright (C) 2005-2012
Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.alfresco.util.ApplicationContextHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.rules.ExternalResource;
import org.springframework.context.ApplicationContext;
/**
* This JUnit rule can be used to bring up a {@link ApplicationContext spring application context}.
* <p/>
* Example usage:
* <pre>
* public class YourTestClass
* {
* // Parameterless construction brings up the default Alfresco spring configuration.
* &#64;ClassRule public static final ApplicationContextInit APP_CONTEXT_RULE = new ApplicationContextInit();
* private static NodeService NODE_SERVICE;
*
* &#64;BeforeClass public static void initSpringServices() throws Exception
* {
* NODE_SERVICE = (NodeService) APP_CONTEXT_RULE.getApplicationContext().getBean("nodeService");
* }
* }
* </pre>
*
* @author Neil Mc Erlean
* @since Odin
*/
public class ApplicationContextInit extends ExternalResource
{
public static final String GLOBAL_INTEGRATION_TEST_CONFIG = "classpath:alfresco/test/global-integration-test-context.xml";
/**
* The locations for the application context configurations.
*/
private final String[] configLocations;
/**
* The initialised {@link ApplicationContext spring context}.
*/
private ApplicationContext appContext;
/**
* Construct a JUnit rule which will bring up a spring ApplicationContext based on the default Alfresco spring context.
*/
public ApplicationContextInit()
{
this(ApplicationContextHelper.CONFIG_LOCATIONS);
}
/**
* Construct a JUnit rule which will bring up an ApplicationContext based on the specified spring contexts.
*
* @param configLocations locations of spring contexts
*/
public ApplicationContextInit(String... configLocations)
{
List<String> requestedConfigs = new ArrayList<String>();
requestedConfigs.addAll(Arrays.asList(configLocations));
// No matter what spring contexts are provided in construction of this object, we always
// add the global test integration config to the end.
// Yes, this will mean that devs cannot override that context file, but it's almost empty anyway.
// We may have to change how this class handles this, but for now: keep it simple, s.
requestedConfigs.add(GLOBAL_INTEGRATION_TEST_CONFIG);
this.configLocations = requestedConfigs.toArray(new String[0]);
}
/**
* This factory method constructs a JUnit rule which will bring up an ApplicationContext consisting
* of the default Alfresco context with any additionConfigLocations appended. It is a convenient way to specify
* override contexts in test code.
*
* @param additionalConfigLocations addition config locations containing additional or overriding beans.
*/
public static ApplicationContextInit createStandardContextWithOverrides(String... additionalConfigLocations)
{
List<String> contexts = new ArrayList<String>();
// The defaults (currently only one)
for (String defaultConfigLocation: ApplicationContextHelper.CONFIG_LOCATIONS)
{
contexts.add(defaultConfigLocation);
}
// any user supplied extras
for (String additionalContext : additionalConfigLocations)
{
contexts.add(additionalContext);
}
String[] contextsAsArray = contexts.toArray(new String[0]);
return new ApplicationContextInit(contextsAsArray);
}
@Override protected void before()
{
log.debug("Initialising custom Spring Configuration: " + Arrays.asList(configLocations).toString());
appContext = ApplicationContextHelper.getApplicationContext(configLocations);
}
@Override protected void after()
{
// Intentionally empty. Do nothing.
}
/**
* Gets the configLocations as supplied to the code on construction.
*/
public List<String> getConfigLocations()
{
return Arrays.asList(configLocations);
}
/**
* Gets the ApplicationContext as initialised by the rule.
*/
public ApplicationContext getApplicationContext()
{
if (this.appContext == null)
{
// Chain order is wrong, try to help out by doing the @Before now
before();
}
return this.appContext;
}
private static final Log log = LogFactory.getLog(ApplicationContextInit.class);
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.alfresco.service.cmr.repository.ContentService;
import org.junit.ClassRule;
import org.junit.Test;
/**
* Test class for {@link ApplicationContextInit}.
*
* @author Neil Mc Erlean
* @since Odin
*/
public class ApplicationContextInitTest
{
// Some dummy contexts with test beans in them.
public static final String[] dummySpringContexts = new String[] {"classpath:org/alfresco/util/test/junitrules/dummy1-context.xml",
"classpath:org/alfresco/util/test/junitrules/dummy2-context.xml"};
// Rule to initialise the default Alfresco spring configuration
@ClassRule public static ApplicationContextInit APP_CONTEXT_INIT = ApplicationContextInit.createStandardContextWithOverrides(dummySpringContexts);
@Test public void ensureSpringContextWasInitedWithOverrides() throws Exception
{
// Bean from the standard Alfresco context
assertNotNull("Spring context did not contain expected bean.",
APP_CONTEXT_INIT.getApplicationContext().getBean("contentService", ContentService.class));
// Bean from the first override context
assertEquals("Value from dummy1-context.xml",
APP_CONTEXT_INIT.getApplicationContext().getBean("testBean1", String.class));
// Bean from the second override context
assertEquals("Value from dummy2-context.xml",
APP_CONTEXT_INIT.getApplicationContext().getBean("testBean2", String.class));
// Bean overridden in second context
assertEquals("Value from dummy2-context.xml",
APP_CONTEXT_INIT.getApplicationContext().getBean("testBean1and2", String.class));
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright (C) 2005-2012
Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.rules.ErrorCollector;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
* This JUnit rule can be used to turn existing test code into Load Tests.
* It does this in conjunction with the {@link AlfrescoPeople} JUnit rule.
* That rule is used to {@link AlfrescoPeople#AlfrescoPeople(ApplicationContextInit, int) create} a
* fixed number of Alfresco users. Then {@link LoadTestRule} will do the following for each of your JUnit 4 &#64;Test methods:
* <ul>
* <li>if they are annotated with the {@link LoadTest} marker annotation:
* <ol>
* <li>one Java thread for each of the users created by the {@link AlfrescoPeople} rule will be created and started.</li>
* <li>each of those threads will start by authenticating as a different user from the above set.</li>
* <li>each of those threads will concurrently execute the same JUnit &#64;Test method.</li>
* <li>if all the concurrent threads complete execution successfully, the &#64;Test method is passed.</li>
* <li>but if one or more of those concurrent threads fail, the error messages will be aggregated together into a single error for that method.</li>
* </ol>
* <li>else they will be executed as normal and will pass or fail as normal.</li>
* </ul>
* <p/>
* Example usage, where we have a 'normal' feature test and a load test for the same feature.:
* <pre>
* public class YourTestClass
* {
* // We need to ensure that JUnit Rules in the same 'group' (in this case the 'static' group) execute in the correct
* // order. To do this we do not annotate the JUnit Rule fields themselves, but instead wrap them up in a RuleChain.
*
* // Initialise the spring application context with a rule.
* public static final ApplicationContextInit APP_CONTEXT_RULE = new ApplicationContextInit();
* public static final AlfrescoPeople TEST_USERS = new AlfrescoPeople(APP_CONTEXT_RULE, 32);
*
* &#64;ClassRule public static RuleChain STATIC_RULE_CHAIN = RuleChain.outerRule(APP_CONTEXT_RULE)
* .around(TEST_USERS);
*
* &#64;Rule public LoadTestRule loadTestRule = new LoadTestRule(TEST_USERS);
*
* &#64;Test public void aNormalTestMethod()
* {
* ensureFeatureFooWorks()
* }
*
* &#64;LoadTest &#64;Test public void aLoadTestMethod()
* {
* ensureFeatureFooWorks()
* }
*
* public void ensureFeatureFooWorks() {}
* }
* </pre>
*
* @author Neil Mc Erlean
*/
public class LoadTestRule extends ErrorCollector
{
private static final Log log = LogFactory.getLog(LoadTestRule.class);
private final AlfrescoPeople people;
public LoadTestRule(AlfrescoPeople people)
{
this.people = people;
}
/**
* Gets the number of users/concurrent threads that this Rule has been configured to use.
* @return the number of users/threads.
*/
public int getCount()
{
return this.people.getUsernames().size();
}
@Override public Statement apply(final Statement base, final Description description)
{
boolean loadTestingRequestedForThisMethod = false;
Collection<Annotation> annotations = description.getAnnotations();
for (Annotation anno : annotations)
{
if (anno.annotationType().equals(LoadTest.class))
{
loadTestingRequestedForThisMethod = true;
}
}
if (loadTestingRequestedForThisMethod)
{
log.debug(LoadTest.class.getSimpleName() + "-based testing configured for method " + description.getMethodName());
return new Statement()
{
@Override public void evaluate() throws Throwable
{
int executionCount = getCount();
int currentIndex = 1;
final CountDownLatch latch = new CountDownLatch(executionCount);
for (String username: people.getUsernames())
{
log.debug("About to start " + description.getMethodName() + ". " + currentIndex + "/" + executionCount + " as " + username);
new Thread(new StatementEvaluatorRunnable(username, base, latch)).start();
currentIndex++;
}
latch.await();
verify();
}
};
}
else
{
log.debug(LoadTest.class.getSimpleName() + "-based testing NOT configured for this method.");
return base;
}
}
private class StatementEvaluatorRunnable implements Runnable
{
private final String username;
private final CountDownLatch latch;
private final Statement base;
public StatementEvaluatorRunnable(String username, Statement base, CountDownLatch latch)
{
this.username = username;
this.latch = latch;
this.base = base;
}
@Override public void run()
{
try
{
log.debug("Setting fully auth'd user to " + username);
AuthenticationUtil.setFullyAuthenticatedUser(username);
base.evaluate();
}
catch (Throwable t)
{
addError(t);
}
latch.countDown();
}
}
/**
* This annotation is a marker used to identify a JUnit &#64;Test method as a "load test".
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoadTest
{
// Intentionally empty
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright (C) 2005-2012
Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import junit.framework.Test;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.util.ParameterCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
* This JUnit rule can be used to make test methods run as a particular user.
* A username can be provided on construction to the rule and then all <code>@Test</code> methods will be
* run as that user.
* <p/>
* Furthermore, if an individual test method is annotated like this <code>@RunAsUser(userName="John")</code> than that
* method (and only that method) will be run as "John".
* <p/>
* Example usage:
* <pre>
* public class YourTestClass
* {
* &#64;ClassRule public static final ApplicationContextInit APP_CONTEXT_RULE = new ApplicationContextInit();
* &#64;Rule public RunAsFullyAuthenticatedRule runAsGuidPerson = new RunAsFullyAuthenticatedRule("NeilM");
*
* &#64;Test public void doSomething() throws Exception
* {
* // This will run as NeilM
* }
*
* &#64;Test &#64;RunAsUser(userName="DaveC") public void doSomething() throws Exception
* {
* // This will run as DaveC
* }
* }
* </pre>
*
* @author Neil Mc Erlean
* @since Odin
*/
public class RunAsFullyAuthenticatedRule implements TestRule
{
/**
* This annotation can be used to mark an individual {@link Test} method for running as a named user.
*
* @author Neil Mc Erlean
* @since Odin
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RunAsUser
{
String userName() default "";
}
private static final Log log = LogFactory.getLog(RunAsFullyAuthenticatedRule.class);
/**
* A fixed username to run as.
*/
private final String fixedUserName;
/**
* A rule which will provide a username to run as
*/
private final AlfrescoPerson personRule;
/**
* This constructs a rule where there is no specified user to run as.
* For this to be useful (or legal) a user must be specified on every test method using {@link RunAsUser}.
*/
public RunAsFullyAuthenticatedRule()
{
this.fixedUserName = null;
this.personRule = null;
}
/**
* @param userName the username which all test methods should run as.
*/
public RunAsFullyAuthenticatedRule(String userName)
{
ParameterCheck.mandatory("userName", userName);
this.fixedUserName = userName;
this.personRule = null;
}
/**
* @param personRule the rule which will provide the username which all test methods should run as.
*/
public RunAsFullyAuthenticatedRule(AlfrescoPerson personRule)
{
ParameterCheck.mandatory("personRule", personRule);
this.fixedUserName = null;
this.personRule = personRule;
}
/**
* Get the username which test methods will run as.
*/
public String getUsername()
{
return this.fixedUserName;
}
@Override public Statement apply(final Statement base, final Description description)
{
return new Statement()
{
@Override public void evaluate() throws Throwable
{
// Store the current authentication
AuthenticationUtil.pushAuthentication();
// First, try for a username provided on the @Test method itself.
String runAsUser = getMethodAnnotatedUserName(description);
if (runAsUser != null)
{
// There is a @Test method username.
log.debug("Running as method annotation-provided user: " + runAsUser);
log.debug(" See " + description.getClassName() + "." + description.getMethodName());
}
else
{
// There is no @Test method username, so fall back to rule-provided person.
if (fixedUserName != null)
{
runAsUser = fixedUserName;
log.debug("Running as username defined in this rule: " + runAsUser);
}
else if (personRule != null)
{
runAsUser = personRule.getUsername();
log.debug("Running as username provided by another rule: " + runAsUser);
}
else
{
throw new Exception("Illegal rule: must provide username or " +
AlfrescoPerson.class.getSimpleName() + " at rule construction or else a " +
RunAsUser.class.getSimpleName() + " annotation.");
}
}
AuthenticationUtil.setFullyAuthenticatedUser(runAsUser);
try
{
// Execute the test method or whatever other rules are configured further down the stack.
base.evaluate();
}
finally
{
// After - ensure that pass or fail, the authentication is restored.
AuthenticationUtil.popAuthentication();
}
}
};
}
/**
*
* @param description the description object from JUnit
* @return the username specified in the {@link RunAsUser} annotation, if there was one, else <code>null</code>.
*/
private String getMethodAnnotatedUserName(Description description) throws IllegalArgumentException,
SecurityException, IllegalAccessException,
InvocationTargetException, NoSuchMethodException
{
String result = null;
Collection<Annotation> annotations = description.getAnnotations();
for (Annotation anno : annotations)
{
if (anno.annotationType().equals(RunAsUser.class))
{
result = (String) anno.annotationType().getMethod("userName").invoke(anno);
}
}
return result;
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright (C) 2005-2012
Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.rules.ExternalResource;
import org.springframework.util.ReflectionUtils;
/**
* A JUnit rule designed to help with the automatic revert of test objects with mocked fields.
* This is intended to be used when writing test code and you wish to set a mock object on a spring singleton bean.
* By mocking out fields on spring singletons beans, if you don't remember to revert them to their original values, then
* any subsequent tests that expect to see 'proper' services plugged in, may fail when they are instead given a mock.
*
* <p/>
* Example usage:
* <pre>
* public class YourTestClass
* {
* // Declare the rule.
* &#64;Rule public final TemporaryMockOverride mockOverride = new TemporaryMockOverride();
*
* &#64;Test public void aTestMethod()
* {
* // Get a singleton bean from the spring context
* FooService fooService = appContext.getBean("fooService", FooService.class);
*
* // Create a mocked service (this uses Mockito, but that's not required for this rule to work)
* BarService mockedBarService = mock(BarService.class);
*
* // Don't do this as you're just replacing the original barService: fooService.setBarService(mockedBarService);
* // Instead do this:
* mockOverride.setTemporaryField(fooService, barService, mockedBarService);
*
* // Go ahead and use the FooService in test code, whilst relying on a mocked BarService behind it.
* // After the rule has completed, the original BarService which spring injected into the FooService will be reset.
* }
* }
* </pre>
*
* @author Neil Mc Erlean
* @since Odin
*/
public class TemporaryMockOverride extends ExternalResource
{
private static final Log log = LogFactory.getLog(TemporaryMockOverride.class);
private List<FieldValueOverride> pristineFieldValues = new ArrayList<FieldValueOverride>();
@Override protected void before() throws Throwable
{
// Intentionally empty
}
@Override protected void after()
{
// For all objects that have been tampered with, we'll revert them to their original state.
for (int i = pristineFieldValues.size() - 1; i >= 0; i-- )
{
FieldValueOverride override = pristineFieldValues.get(i);
if (log.isDebugEnabled())
{
log.debug("Reverting mocked field '" + override.fieldName + "' on object " + override.objectContainingField + " to original value '" + override.fieldPristineValue + "'");
}
// Hack into the Java field object
Field f = ReflectionUtils.findField(override.objectContainingField.getClass(), override.fieldName);
ReflectionUtils.makeAccessible(f);
// and revert its value.
ReflectionUtils.setField(f, override.objectContainingField, override.fieldPristineValue);
}
}
public void setTemporaryField(Object objectContainingField, String fieldName, Object fieldValue)
{
if (log.isDebugEnabled())
{
log.debug("Overriding field '" + fieldName + "' on object " + objectContainingField + " to new value '" + fieldValue + "'");
}
// Extract the pristine value of the field we're going to mock.
Field f = ReflectionUtils.findField(objectContainingField.getClass(), fieldName);
if (f == null)
{
final String msg = "Object of type '" + objectContainingField.getClass().getSimpleName() + "' has no field named '" + fieldName + "'";
if (log.isDebugEnabled())
{
log.debug(msg);
}
throw new IllegalArgumentException(msg);
}
ReflectionUtils.makeAccessible(f);
Object pristineValue = ReflectionUtils.getField(f, objectContainingField);
// and add it to the list.
pristineFieldValues.add(new FieldValueOverride(objectContainingField, fieldName, pristineValue));
// and set it on the object
ReflectionUtils.setField(f, objectContainingField, fieldValue);
}
private static class FieldValueOverride
{
public FieldValueOverride(Object objectContainingField, String fieldName, Object pristineValue)
{
this.objectContainingField = objectContainingField;
this.fieldName = fieldName;
this.fieldPristineValue = pristineValue;
}
public final Object objectContainingField;
public final String fieldName;
public final Object fieldPristineValue;
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
/**
* Test class for {@link TemporaryMockOverride}.
*
* @author Neil McErlean
* @since Odin
*/
public class TemporaryMockOverrideTest
{
private static final String REAL_DATA = "Hello";
private static final String MOCKED_DATA = "--";
private final FooService realFooService = new FooServiceImpl();
private final BarService realBarService = new BarServiceImpl();
private final AbcService realAbcService = new AbcServiceImpl();
@Before public void init()
{
FooServiceImpl fooServiceImpl = (FooServiceImpl)realFooService;
fooServiceImpl.setBarService(realBarService);
fooServiceImpl.setAbcService(realAbcService);
}
@Test public void mockFieldsWithinServiceAndThenEnsureTheProperFieldValuesAreRestoredAfterCleanup() throws Throwable
{
TemporaryMockOverride mockRule = new TemporaryMockOverride();
mockRule.before();
assertEquals("Original BarService giving wrong data.", REAL_DATA, realFooService.getBarService().getString());
assertEquals("Original AbcService giving wrong data.", REAL_DATA, realFooService.getAbcService().getString());
BarService mockedBarService = mock(BarService.class);
when(mockedBarService.getString()).thenReturn(MOCKED_DATA);
AbcService mockedAbcService = mock(AbcService.class);
when(mockedAbcService.getString()).thenReturn(MOCKED_DATA);
FooServiceImpl fooServiceWithMockedServices = new FooServiceImpl();
// We'll start it off with the 'correct' values
fooServiceWithMockedServices.setBarService(realBarService);
fooServiceWithMockedServices.setAbcService(realAbcService);
// ...and then set the mocked values via the rule, which will remember the old values and revert them for us automatically.
mockRule.setTemporaryField(fooServiceWithMockedServices, "barService", mockedBarService);
mockRule.setTemporaryField(fooServiceWithMockedServices, "abcService", mockedAbcService);
assertEquals("Mocked BarService giving wrong data.", MOCKED_DATA, fooServiceWithMockedServices.getBarService().getString());
assertEquals("Mocked AbcService giving wrong data.", MOCKED_DATA, fooServiceWithMockedServices.getAbcService().getString());
mockRule.after();
// Now it should all be magically reverted.
assertEquals("BarService giving wrong data.", REAL_DATA, fooServiceWithMockedServices.getBarService().getString());
assertEquals("AbcService giving wrong data.", REAL_DATA, fooServiceWithMockedServices.getAbcService().getString());
}
@Test(expected=IllegalArgumentException.class) public void mockNonExistentFieldsWithinService() throws Throwable
{
TemporaryMockOverride mockRule = new TemporaryMockOverride();
mockRule.before();
assertEquals("Original BarService giving wrong data.", REAL_DATA, realFooService.getBarService().getString());
BarService mockedBarService = mock(BarService.class);
when(mockedBarService.getString()).thenReturn(MOCKED_DATA);
FooServiceImpl fooServiceWithMockedServices = new FooServiceImpl();
// We'll start it off with the 'correct' values
fooServiceWithMockedServices.setBarService(realBarService);
// ...and then set an illegal mocked value via the rule.
mockRule.setTemporaryField(fooServiceWithMockedServices, "noSuchService", mockedBarService);
assertEquals("Mocked BarService giving wrong data.", MOCKED_DATA, fooServiceWithMockedServices.getBarService().getString());
mockRule.after();
// Now it should all be magically reverted.
assertEquals("BarService giving wrong data.", REAL_DATA, fooServiceWithMockedServices.getBarService().getString());
}
public interface FooService { public BarService getBarService(); public AbcService getAbcService(); }
public class FooServiceImpl implements FooService
{
private BarService barService;
private AbcService abcService;
public BarService getBarService() { return this.barService; }
public void setBarService(BarService barService) { this.barService = barService; }
public AbcService getAbcService() { return this.abcService; }
public void setAbcService(AbcService abcService) { this.abcService = abcService; }
}
public interface BarService { public String getString(); }
public class BarServiceImpl implements BarService { public String getString() { return REAL_DATA; } }
public interface AbcService { public String getString(); }
public class AbcServiceImpl implements AbcService { public String getString() { return REAL_DATA; } }
}

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) 2005-2012
Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.dictionary.M2Model;
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.dictionary.DictionaryException;
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 models and to make it easier to
* create common test models with JUnit code.
*
* @author Alex Miller
* @since 4.2
*/
public class TemporaryModels extends ExternalResource
{
private static final Log logger = LogFactory.getLog(TemporaryModels.class);
private final ApplicationContextInit appContextRule;
private final Set<QName> loadedModels = new HashSet<QName>();
/**
* Constructs the rule with a reference to a {@link ApplicationContextInit rule} which can be used to retrieve the ApplicationContext.
*
* @param appContextRule a rule which can be used to retrieve the spring app context.
*/
public TemporaryModels(ApplicationContextInit appContextRule)
{
this.appContextRule = appContextRule;
}
@Override protected void before() throws Throwable
{
// Intentionally empty
}
@Override protected void after()
{
final RetryingTransactionHelper transactionHelper = getTransactionHelper();
final DictionaryDAO dictionaryDAO = getDictionaryDAO();
// Run as system to ensure all non-system nodes can be deleted irrespective of which user created them.
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
@Override public Void doWork() throws Exception
{
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override public Void execute() throws Throwable
{
for (QName model : loadedModels)
{
dictionaryDAO.removeModel(model);
}
return null;
}
});
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
private RetryingTransactionHelper getTransactionHelper() {
final ApplicationContext springContext = appContextRule.getApplicationContext();
final RetryingTransactionHelper transactionHelper = springContext.getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
return transactionHelper;
}
public QName loadModel(String modelPath, ClassLoader classLoader)
{
InputStream modelStream = classLoader.getResourceAsStream(modelPath);
if (modelStream == null)
{
throw new DictionaryException("Could not find bootstrap model " + modelPath);
}
try
{
return loadModel(modelStream);
}
finally
{
try
{
modelStream.close();
}
catch (IOException ioe)
{
logger.warn("Failed to close model input stream for '"+modelPath+"': "+ioe);
}
}
}
public QName loadModel(InputStream modelStream)
{
try
{
final M2Model model = M2Model.createModel(modelStream);
return loadModel(model);
}
catch(DictionaryException e)
{
throw new DictionaryException("Could not import model", e);
}
}
private QName loadModel(final M2Model model) {
if (logger.isDebugEnabled())
{
logger.debug("Loading model: "+model.getName());
}
final DictionaryDAO dictionaryDAO = getDictionaryDAO();
QName modelQName = dictionaryDAO.putModel(model);
loadedModels.add(modelQName);
return modelQName;
}
private DictionaryDAO getDictionaryDAO() {
final ApplicationContext springContext = appContextRule.getApplicationContext();
DictionaryDAO dictionaryDAO = springContext.getBean("dictionaryDAO", DictionaryDAO.class);
return dictionaryDAO;
}
}

View File

@@ -0,0 +1,417 @@
/*
* Copyright (C) 2005-2012
Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import java.io.File;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.site.SiteModel;
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.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.namespace.NamespaceService;
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 and to ake it easier to
* create common test content with JUnit code.
*
* @author Neil Mc Erlean
* @since 4.1
*/
public class TemporaryNodes extends ExternalResource
{
private static final Log log = LogFactory.getLog(TemporaryNodes.class);
private final ApplicationContextInit appContextRule;
private List<NodeRef> temporaryNodeRefs = new ArrayList<NodeRef>();
/**
* Constructs the rule with a reference to a {@link ApplicationContextInit rule} which can be used to retrieve the ApplicationContext.
*
* @param appContextRule a rule which can be used to retrieve the spring app context.
*/
public TemporaryNodes(ApplicationContextInit appContextRule)
{
this.appContextRule = appContextRule;
}
@Override protected void before() throws Throwable
{
// Intentionally empty
}
@Override protected void after()
{
final ApplicationContext springContext = appContextRule.getApplicationContext();
final RetryingTransactionHelper transactionHelper = springContext.getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
final CheckOutCheckInService cociService = springContext.getBean("CheckOutCheckInService", CheckOutCheckInService.class);
final DictionaryService dictionaryService = springContext.getBean("DictionaryService", DictionaryService.class);
final NodeService nodeService = springContext.getBean("NodeService", NodeService.class);
final SiteService siteService = springContext.getBean("SiteService", SiteService.class);
// Run as system to ensure all non-system nodes can be deleted irrespective of which user created them.
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
@Override public Void doWork() throws Exception
{
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override public Void execute() throws Throwable
{
// 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));
// Site nodes are a special case which must be deleted through the SiteService.
final QName nodeType = nodeService.getType(node);
if (nodeType.equals(SiteModel.TYPE_SITE) || dictionaryService.isSubClass(nodeType, SiteModel.TYPE_SITE))
{
SiteInfo siteInfo = siteService.getSite(node);
siteService.deleteSite(siteInfo.getShortName());
}
else
{
nodeService.deleteNode(node);
}
}
}
return null;
}
});
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
/**
* Add a specified NodeRef to the list of NodeRefs to be deleted by this rule.
*
* @param temporaryNodeRef a NodeRef
*/
public void addNodeRef(NodeRef temporaryNodeRef)
{
this.temporaryNodeRefs.add(temporaryNodeRef);
}
/**
* This method creates a NodeRef and adds it to the internal list of NodeRefs to be tidied up by the rule.
* This method will be run in its own transaction and will be run with the specified user as the fully authenticated user,
* thus ensuring the named user is the cm:creator of the new node.
*
* @param parentNode the parent node
* @param nodeCmName the cm:name of the new node
* @param nodeType the type of the new node
* @param nodeCreator the username of the person who will create the node
* @return the newly created NodeRef.
*/
public NodeRef createNode(final NodeRef parentNode, final String nodeCmName, final QName nodeType, final String nodeCreator)
{
return this.createNodeWithTextContent(parentNode, nodeCmName, nodeType, nodeCreator, null);
}
/**
* This method creates a NodeRef with some text/plain, UTF-8 content and adds it to the internal list of NodeRefs to be tidied up by the rule.
* This method will be run in its own transaction and will be run with the specified user as the fully authenticated user,
* thus ensuring the named user is the cm:creator of the new node.
*
* @param parentNode the parent node
* @param nodeCmName the cm:name of the new node
* @param nodeType the type of the new node
* @param nodeCreator the username of the person who will create the node
* @param textContent the text/plain, UTF-8 content that will be stored in the node's content. <code>null</code> content will not be written.
* @return the newly created NodeRef.
*/
public NodeRef createNodeWithTextContent(final NodeRef parentNode, final String nodeCmName, final QName nodeType, final String nodeCreator, final String textContent)
{
QName childName = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, nodeCmName);
return createNodeWithTextContent(parentNode, childName, nodeCmName, nodeType, nodeCreator, textContent);
}
/**
* This method creates a NodeRef with some text/plain, UTF-8 content and adds it to the internal list of NodeRefs to be tidied up by the rule.
* This method will be run in its own transaction and will be run with the specified user as the fully authenticated user,
* thus ensuring the named user is the cm:creator of the new node.
*
* @param parentNode the parent node
* @param nodeCmName the cm:name of the new node
* @param nodeType the type of the new node
* @param nodeCreator the username of the person who will create the node
* @param textContent the text/plain, UTF-8 content that will be stored in the node's content. <code>null</code> content will not be written.
* @return the newly created NodeRef.
*/
public NodeRef createNodeWithTextContent(final NodeRef parentNode, final QName childName, final String nodeCmName, final QName nodeType, final String nodeCreator, final String textContent)
{
final RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) appContextRule.getApplicationContext().getBean("retryingTransactionHelper");
AuthenticationUtil.pushAuthentication();
AuthenticationUtil.setFullyAuthenticatedUser(nodeCreator);
NodeRef newNodeRef = transactionHelper.doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
final NodeService nodeService = (NodeService) appContextRule.getApplicationContext().getBean("nodeService");
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_NAME, nodeCmName);
ChildAssociationRef childAssoc = nodeService.createNode(parentNode,
ContentModel.ASSOC_CONTAINS,
childName,
nodeType,
props);
// If there is any content, add it.
if (textContent != null)
{
ContentService contentService = appContextRule.getApplicationContext().getBean("contentService", ContentService.class);
ContentWriter writer = contentService.getWriter(childAssoc.getChildRef(), ContentModel.PROP_CONTENT, true);
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
writer.setEncoding("UTF-8");
writer.putContent(textContent);
}
return childAssoc.getChildRef();
}
});
AuthenticationUtil.popAuthentication();
this.temporaryNodeRefs.add(newNodeRef);
return newNodeRef;
}
/**
* This method creates a cm:folder NodeRef and adds it to the internal list of NodeRefs to be tidied up by the rule.
* This method will be run in its own transaction and will be run with the specified user as the fully authenticated user,
* thus ensuring the named user is the cm:creator of the new node.
*
* @param parentNode the parent node
* @param nodeCmName the cm:name of the new node
* @param nodeCreator the username of the person who will create the node
* @return the newly created NodeRef.
*/
public NodeRef createFolder(final NodeRef parentNode, final String nodeCmName, final String nodeCreator)
{
final RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) appContextRule.getApplicationContext().getBean("retryingTransactionHelper");
AuthenticationUtil.pushAuthentication();
AuthenticationUtil.setFullyAuthenticatedUser(nodeCreator);
NodeRef newNodeRef = transactionHelper.doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
final NodeRef result = createNode(nodeCmName, parentNode, ContentModel.TYPE_FOLDER);
return result;
}
});
AuthenticationUtil.popAuthentication();
this.temporaryNodeRefs.add(newNodeRef);
return newNodeRef;
}
/**
* This method creates a cm:content NodeRef whose content is taken from an Alfresco 'quick' file and adds it to the internal
* list of NodeRefs to be tidied up by the rule.
* This method will be run in its own transaction and will be run with the specified user as the fully authenticated user,
* thus ensuring the named user is the cm:creator of the new node.
*
* @param mimetype the MimeType of the content to put in the new node.
* @param parentNode the parent node
* @param nodeCmName the cm:name of the new node
* @param nodeCreator the username of the person who will create the node
* @return the newly created NodeRef.
*/
public NodeRef createQuickFile(final String mimetype, final NodeRef parentNode, final String nodeCmName, final String nodeCreator)
{
final RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) appContextRule.getApplicationContext().getBean("retryingTransactionHelper");
AuthenticationUtil.pushAuthentication();
AuthenticationUtil.setFullyAuthenticatedUser(nodeCreator);
NodeRef newNodeRef = transactionHelper.doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
final NodeRef result = createNode(nodeCmName, parentNode, ContentModel.TYPE_CONTENT);
File quickFile = loadQuickFile(getQuickResource(mimetype));
ContentService contentService = appContextRule.getApplicationContext().getBean("contentService", ContentService.class);
ContentWriter writer = contentService.getWriter(result, ContentModel.PROP_CONTENT, true);
writer.setMimetype(mimetype);
writer.setEncoding("UTF-8");
writer.putContent(quickFile);
return result;
}
});
AuthenticationUtil.popAuthentication();
this.temporaryNodeRefs.add(newNodeRef);
return newNodeRef;
}
/**
* This method creates a cm:content NodeRef whose content is taken from the named Alfresco 'quick' file and adds it to the internal
* list of NodeRefs to be tidied up by the rule.
* This method will be run in its own transaction and will be run with the specified user as the fully authenticated user,
* thus ensuring the named user is the cm:creator of the new node.
*
* @param parentNode the parent node
* @param nodeCmName the file name of the quick file - will also be the cm:name of the new node.
* @param nodeCreator the username of the person who will create the node
* @return the newly created NodeRef.
* @since 4.1.4
*/
public NodeRef createQuickFileByName(final String quickFileName, final NodeRef parentNode, final String nodeCreator)
{
final MimetypeMap mimetypeService = (MimetypeMap) appContextRule.getApplicationContext().getBean("mimetypeService");
final RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) appContextRule.getApplicationContext().getBean("retryingTransactionHelper");
AuthenticationUtil.pushAuthentication();
AuthenticationUtil.setFullyAuthenticatedUser(nodeCreator);
NodeRef newNodeRef = transactionHelper.doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
final NodeRef result = createNode(quickFileName, parentNode, ContentModel.TYPE_CONTENT);
File quickFile = loadQuickFile(quickFileName);
ContentService contentService = appContextRule.getApplicationContext().getBean("contentService", ContentService.class);
ContentWriter writer = contentService.getWriter(result, ContentModel.PROP_CONTENT, true);
writer.setMimetype(mimetypeService.guessMimetype(quickFileName));
writer.setEncoding("UTF-8");
writer.putContent(quickFile);
return result;
}
});
AuthenticationUtil.popAuthentication();
this.temporaryNodeRefs.add(newNodeRef);
return newNodeRef;
}
private NodeRef createNode(String cmName, NodeRef parentNode, QName nodeType)
{
final NodeService nodeService = (NodeService) appContextRule.getApplicationContext().getBean("nodeService");
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_NAME, cmName);
ChildAssociationRef childAssoc = nodeService.createNode(parentNode,
ContentModel.ASSOC_CONTAINS,
ContentModel.ASSOC_CONTAINS,
nodeType,
props);
return childAssoc.getChildRef();
}
/**
* Gets the resource name for the Alfresco 'quick file' associated with the given mime type.
* @param mimetype the MIME type e.g. {@link MimetypeMap#MIMETYPE_IMAGE_JPEG}
* @return the resource path e.g. "quick/quick.jpg"
*/
private String getQuickResource(String mimetype)
{
final MimetypeMap mimetypeService = (MimetypeMap) appContextRule.getApplicationContext().getBean("mimetypeService");
final String extension = mimetypeService.getExtension(mimetype);
if (extension == null)
{
throw new UnsupportedOperationException("No 'quick' file for unrecognised mimetype: " + mimetype);
}
return "quick." + extension;
}
/**
* Gets the resource name for the named Alfresco 'quick file'.
*
* @param quickFileName e.g. "quickGEO.jpg"
* @return the resource path e.g. "quick/quickGEO.jpg"
*/
private String getNamedQuickResource(String quickFileName)
{
return "quick/" + quickFileName;
}
private File loadQuickFile(String quickfile)
{
final String quickResource = getNamedQuickResource(quickfile);
URL url = AbstractContentTransformerTest.class.getClassLoader().getResource(quickResource);
if (url == null)
{
throw new UnsupportedOperationException("No 'quick' file for file: " + quickResource);
}
File file = new File(url.getFile());
if (!file.exists())
{
throw new UnsupportedOperationException("No 'quick' file for file: " + quickResource);
}
return file;
}
}

View File

@@ -0,0 +1,315 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
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;
import org.alfresco.repo.content.MimetypeMap;
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;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.site.SiteVisibility;
import org.alfresco.service.namespace.QName;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
/**
* Test class for {@link TemporaryNodes}.
*
* @author Neil Mc Erlean
* @since 4.1
*/
public class TemporaryNodesTest
{
// Rule to initialise the default Alfresco spring configuration
public static ApplicationContextInit APP_CONTEXT_INIT = new ApplicationContextInit();
// Rules to create test users.
public static AlfrescoPerson TEST_USER1 = new AlfrescoPerson(APP_CONTEXT_INIT, "UserOne");
public static AlfrescoPerson TEST_USER2 = new AlfrescoPerson(APP_CONTEXT_INIT, "UserTwo");
// A rule to manage test nodes reused across all the test methods
public static TemporaryNodes STATIC_TEST_NODES = new TemporaryNodes(APP_CONTEXT_INIT);
// Tie them together in a static Rule Chain
@ClassRule public static RuleChain ruleChain = RuleChain.outerRule(APP_CONTEXT_INIT)
.around(TEST_USER1)
.around(TEST_USER2)
.around(STATIC_TEST_NODES);
// A rule to manage test nodes use in each test method
@Rule public TemporaryNodes testNodes = new TemporaryNodes(APP_CONTEXT_INIT);
// A rule to allow individual test methods all to be run as "admin".
@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 SiteService SITE_SERVICE;
private static RetryingTransactionHelper TRANSACTION_HELPER;
private static NodeRef COMPANY_HOME;
// These NodeRefs are used by the test methods.
private NodeRef testNode1, testNode2;
@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);
SITE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("siteService", SiteService.class);
TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
Repository repositoryHelper = APP_CONTEXT_INIT.getApplicationContext().getBean("repositoryHelper", Repository.class);
COMPANY_HOME = repositoryHelper.getCompanyHome();
}
@Before public void createTestContent()
{
// Create some test content
testNode1 = testNodes.createNode(COMPANY_HOME, "doc 1", ContentModel.TYPE_CONTENT, TEST_USER1.getUsername());
testNode2 = testNodes.createNodeWithTextContent(COMPANY_HOME, "doc 2", ContentModel.TYPE_CONTENT, TEST_USER2.getUsername(), "Hello world");
}
@Test public void ensureTestNodesWereCreatedOk() throws Exception
{
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
assertTrue("Test node does not exist", NODE_SERVICE.exists(testNode1));
assertTrue("Test node does not exist", NODE_SERVICE.exists(testNode2));
Map<QName, Serializable> node1Props = NODE_SERVICE.getProperties(testNode1);
Map<QName, Serializable> node2Props = NODE_SERVICE.getProperties(testNode2);
// name
assertEquals("cm:name was wrong", "doc 1", node1Props.get(ContentModel.PROP_NAME));
assertEquals("cm:name was wrong", "doc 2", node2Props.get(ContentModel.PROP_NAME));
// creator
assertEquals("cm:creator was wrong", TEST_USER1.getUsername(), node1Props.get(ContentModel.PROP_CREATOR));
assertEquals("cm:creator was wrong", TEST_USER2.getUsername(), node2Props.get(ContentModel.PROP_CREATOR));
// content
ContentReader reader = CONTENT_SERVICE.getReader(testNode1, ContentModel.PROP_CONTENT);
assertNull("Content was unexpectedly present", reader);
reader = CONTENT_SERVICE.getReader(testNode2, ContentModel.PROP_CONTENT);
assertEquals("Content was wrong", "Hello world", reader.getContentString("Hello world".length()));
return null;
}
});
}
@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;
}
});
}
/** Site nodes are a special case as they can only be deleted through the SiteService. */
@Test public void ensureSiteNodesAreCleanedUp() 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();
// and ensure that the site node is gone.
SiteInfo createdSite = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<SiteInfo>()
{
public SiteInfo execute() throws Throwable
{
return SITE_SERVICE.createSite("sitePreset", "siteShortName", "site title", "site description", SiteVisibility.PUBLIC);
}
});
final NodeRef siteNodeRef = createdSite.getNodeRef();
myTemporaryNodes.addNodeRef(siteNodeRef);
// Now trigger the Rule's cleanup behaviour.
myTemporaryNodes.after();
// and ensure that the site node is gone.
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
if (NODE_SERVICE.exists(siteNodeRef))
{
fail("Node '" + NODE_SERVICE.getProperty(siteNodeRef, ContentModel.PROP_NAME) + "' still exists.");
}
return null;
}
});
}
@Test public void testCreateFolderAndQuickFiles() 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 the temporary nodes relevant for this test.
//
// Create a target folder
final NodeRef folder = myTemporaryNodes.createFolder(COMPANY_HOME, "testFolder", AuthenticationUtil.getAdminUserName());
// create a normal 'quick' node under it.
final NodeRef quickTxt = myTemporaryNodes.createQuickFile(MimetypeMap.MIMETYPE_TEXT_PLAIN, folder, "quickFile", AuthenticationUtil.getAdminUserName());
// create a named 'quick' node under it.
final NodeRef namedQuickTxt = myTemporaryNodes.createQuickFileByName("quickCorrupt.jpg", folder, AuthenticationUtil.getAdminUserName());
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
// Check the nodes were created ok
assertTrue(NODE_SERVICE.exists(folder));
assertTrue(NODE_SERVICE.exists(quickTxt));
assertTrue(NODE_SERVICE.exists(namedQuickTxt));
assertEquals(ContentModel.TYPE_FOLDER, NODE_SERVICE.getType(folder));
assertEquals(ContentModel.TYPE_CONTENT, NODE_SERVICE.getType(quickTxt));
assertEquals(ContentModel.TYPE_CONTENT, NODE_SERVICE.getType(namedQuickTxt));
assertEquals(AuthenticationUtil.getAdminUserName(), NODE_SERVICE.getProperty(folder, ContentModel.PROP_CREATOR));
assertEquals(AuthenticationUtil.getAdminUserName(), NODE_SERVICE.getProperty(quickTxt, ContentModel.PROP_CREATOR));
assertEquals(AuthenticationUtil.getAdminUserName(), NODE_SERVICE.getProperty(namedQuickTxt, ContentModel.PROP_CREATOR));
ContentReader reader = CONTENT_SERVICE.getReader(quickTxt, ContentModel.PROP_CONTENT);
assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, reader.getMimetype());
assertEquals(235, reader.getSize()); // 235 chars in the quick.txt file
final String content = reader.getContentString();
assertTrue(content.contains("quick brown fox"));
ContentReader reader2 = CONTENT_SERVICE.getReader(namedQuickTxt, ContentModel.PROP_CONTENT);
assertEquals(MimetypeMap.MIMETYPE_IMAGE_JPEG, reader2.getMimetype());
// No checks on the actual content.
return null;
}
});
// Now trigger the Rule's cleanup behaviour.
myTemporaryNodes.after();
// and ensure that the temporary nodes are gone.
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
assertFalse(NODE_SERVICE.exists(folder));
assertFalse(NODE_SERVICE.exists(quickTxt));
assertFalse(NODE_SERVICE.exists(namedQuickTxt));
return null;
}
});
}
}

View File

@@ -0,0 +1,273 @@
/*
* Copyright (C) 2005-2012
Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.site.SiteModel;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.site.SiteVisibility;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.webscripts.GUID;
/**
* A JUnit rule designed to help with the automatic cleanup of temporary st:site nodes.
*
* @author Neil Mc Erlean
* @since 4.0.3
*/
public class TemporarySites extends AbstractPersonRule
{
private static final Log log = LogFactory.getLog(TemporarySites.class);
private List<SiteInfo> temporarySites = new ArrayList<SiteInfo>();
private List<String> temporarySiteUsers = new ArrayList<String>();
/**
* Constructs the rule with a reference to a {@link ApplicationContextInit rule} which can be used to retrieve the ApplicationContext.
*
* @param appContextRule a rule which can be used to retrieve the spring app context.
*/
public TemporarySites(ApplicationContextInit appContextRule)
{
super(appContextRule);
}
@Override protected void before() throws Throwable
{
// Intentionally empty
}
@Override protected void after()
{
final RetryingTransactionHelper transactionHelper = (RetryingTransactionHelper) appContextRule.getApplicationContext().getBean("retryingTransactionHelper");
final SiteService siteService = appContextRule.getApplicationContext().getBean("siteService", SiteService.class);
// Run as admin to ensure all sites can be deleted irrespective of which user created them.
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
@Override public Void doWork() throws Exception
{
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override public Void execute() throws Throwable
{
for (SiteInfo site : temporarySites)
{
final String shortName = site.getShortName();
if (siteService.getSite(shortName) != null)
{
log.debug("Deleting temporary site " + shortName);
siteService.deleteSite(shortName);
}
}
for (String username : temporarySiteUsers)
{
log.debug("Deleting temporary site user " + username);
deletePerson(username);
}
return null;
}
});
return null;
}
}, AuthenticationUtil.getAdminUserName());
}
/**
* Add a specified site to the list of SiteInfos to be deleted by this rule.
*
* @param temporarySite a SiteInfo
*/
public void addSite(SiteInfo temporarySite)
{
this.temporarySites.add(temporarySite);
}
/**
* This method creates a Share Site and adds it to the internal list of NodeRefs to be tidied up by the rule.
* This method will be run in its own transaction and will be run with the specified user as the fully authenticated user,
* thus ensuring the named user is the creator of the new site.
*
* @param sitePreset the site preset
* @param siteShortName the short name of the new site
* @param siteTitle the title of the new site
* @param siteDescription the description of the new site
* @param visibility the visibility
* @param siteCreator the username of the person who will create the site
* @return the newly created SiteInfo (will be of type st:site).
*/
public SiteInfo createSite(final String sitePreset, final String siteShortName, final String siteTitle, final String siteDescription,
final SiteVisibility visibility, final String siteCreator)
{
return this.createSite(sitePreset, siteShortName, siteTitle, siteDescription, visibility, SiteModel.TYPE_SITE, siteCreator);
}
/**
* This method creates a Share Site (<b>or subtype</b>) and adds it to the internal list of NodeRefs to be tidied up by the rule.
* This method will be run in its own transaction and will be run with the specified user as the fully authenticated user,
* thus ensuring the named user is the creator of the new site.
*
* @param sitePreset the site preset
* @param siteShortName the short name of the new site
* @param siteTitle the title of the new site
* @param siteDescription the description of the new site
* @param visibility the visibility
* @param node type the node type of the site (must be st:site or subtype)
* @param siteCreator the username of the person who will create the site
* @return the newly created SiteInfo.
*/
public SiteInfo createSite(final String sitePreset, final String siteShortName, final String siteTitle, final String siteDescription,
final SiteVisibility visibility, final QName siteType, final String siteCreator)
{
final RetryingTransactionHelper transactionHelper = appContextRule.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
AuthenticationUtil.pushAuthentication();
AuthenticationUtil.setFullyAuthenticatedUser(siteCreator);
SiteInfo newSite = transactionHelper.doInTransaction(new RetryingTransactionCallback<SiteInfo>()
{
public SiteInfo execute() throws Throwable
{
final SiteService siteService = appContextRule.getApplicationContext().getBean("siteService", SiteService.class);
SiteInfo newSite = siteService.createSite(sitePreset, siteShortName, siteTitle, siteDescription, visibility, siteType);
// ensure that the Document Library folder is pre-created so that test code can start creating content straight away.
// At the time of writing HEAD does not create this folder automatically, but Thor does.
// So to be safe, I'll pre-check if the node is there.
NodeRef docLibFolder = siteService.getContainer(siteShortName, SiteService.DOCUMENT_LIBRARY);
if (docLibFolder == null)
{
docLibFolder = siteService.createContainer(siteShortName, SiteService.DOCUMENT_LIBRARY, ContentModel.TYPE_FOLDER, null);
}
return newSite;
}
});
AuthenticationUtil.popAuthentication();
this.temporarySites.add(newSite);
return newSite;
}
/**
* This method creates a test site (of Alfresco type <code>st:site</code>) and one user for each of the Share Site Roles.
* This method will be run in its own transaction and will be run with the specified user as the fully authenticated user,
* thus ensuring the named user is the creator of the new site.
* The site and its users will be deleted automatically by the rule.
*
* @param sitePreset the site preset.
* @param visibility the Site visibility.
* @param siteCreator the username of a user who will be used to create the site (user must exist of course).
* @return the {@link SiteInfo} object for the newly created site.
*/
public TestSiteAndMemberInfo createTestSiteWithUserPerRole(final String siteShortName, String sitePreset, SiteVisibility visibility, String siteCreator)
{
// create the site
SiteInfo result = this.createSite(sitePreset, siteShortName, null, null, visibility, siteCreator);
// create the users
final RetryingTransactionHelper transactionHelper = appContextRule.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
final SiteService siteService = appContextRule.getApplicationContext().getBean("siteService", SiteService.class);
AuthenticationUtil.pushAuthentication();
AuthenticationUtil.setFullyAuthenticatedUser(siteCreator);
// Create users for this test site that cover the various roles.
List<String> userNames = transactionHelper.doInTransaction(new RetryingTransactionCallback<List<String>>()
{
public List<String> execute() throws Throwable
{
List<String> users = new ArrayList<String>(4);
for (String shareRole : SiteModel.STANDARD_PERMISSIONS)
{
final String userName = siteShortName + "_" + shareRole + "_" + GUID.generate();
log.debug("Creating temporary site user " + userName);
createPerson(userName);
siteService.setMembership(siteShortName, userName, shareRole);
users.add(userName);
temporarySiteUsers.add(userName);
}
return users;
}
});
NodeRef doclibFolder = transactionHelper.doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
return siteService.getContainer(siteShortName, SiteService.DOCUMENT_LIBRARY);
}
});
AuthenticationUtil.popAuthentication();
return new TestSiteAndMemberInfo(result, doclibFolder, userNames.get(0),
userNames.get(1),
userNames.get(2),
userNames.get(3));
}
/**
* A simple POJO class to store the {@link SiteInfo} for this site and its initial, automatically created members' usernames.
*
* @author Neil Mc Erlean
*/
public static class TestSiteAndMemberInfo
{
public final SiteInfo siteInfo;
public final NodeRef doclib;
public final String siteManager;
public final String siteCollaborator;
public final String siteContributor;
public final String siteConsumer;
public TestSiteAndMemberInfo(SiteInfo siteInfo, NodeRef siteDocLib, String siteManager, String siteCollaborator, String siteContributor, String siteConsumer)
{
this.siteInfo = siteInfo;
this.doclib = siteDocLib;
this.siteManager = siteManager;
this.siteCollaborator = siteCollaborator;
this.siteContributor = siteContributor;
this.siteConsumer = siteConsumer;
}
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.site.SiteModel;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.site.SiteVisibility;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.alfresco.util.test.junitrules.TemporarySites.TestSiteAndMemberInfo;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
/**
* Test class for {@link TemporarySites}.
*
* @author Neil McErlean
* @since 4.0.3
*/
public class TemporarySitesTest
{
// Rule to initialise the default Alfresco spring configuration
public static ApplicationContextInit APP_CONTEXT_INIT =
ApplicationContextInit.createStandardContextWithOverrides("classpath:sites/test-"
+ TemporarySitesTest.class.getSimpleName() + "-context.xml");
// A rule to manage test sites reused across all the test methods
public static TemporaryNodes STATIC_TEST_SITES = new TemporaryNodes(APP_CONTEXT_INIT);
// Tie them together in a static Rule Chain
@ClassRule public static RuleChain ruleChain = RuleChain.outerRule(APP_CONTEXT_INIT)
.around(STATIC_TEST_SITES);
// A rule to manage test sites use in each test method
public TemporarySites testSites = new TemporarySites(APP_CONTEXT_INIT);
// A rule to allow individual test methods all to be run as "admin".
public RunAsFullyAuthenticatedRule runAsRule = new RunAsFullyAuthenticatedRule(AuthenticationUtil.getAdminUserName());
// A non-static rule chain to ensure execution order is correct.
@Rule public RuleChain nonStaticRules = RuleChain.outerRule(runAsRule)
.around(testSites);
// Various services
private static NamespaceService NAMESPACE_SERVICE;
private static NodeService NODE_SERVICE;
private static SiteService SITE_SERVICE;
private static RetryingTransactionHelper TRANSACTION_HELPER;
// These SiteInfos are used by the test methods.
private SiteInfo testSite1, testSite2;
private TestSiteAndMemberInfo testSiteWithMembers;
@BeforeClass public static void initStaticData() throws Exception
{
NAMESPACE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("namespaceService", NamespaceService.class);
NODE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("nodeService", NodeService.class);
SITE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("siteService", SiteService.class);
TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class);
}
@Before public void createTestContent()
{
// Create some test content
final String guid = GUID.generate();
testSite1 = testSites.createSite("sitePreset", "testSite1_" + guid, "t", "d", SiteVisibility.PUBLIC, AuthenticationUtil.getAdminUserName());
final QName subSiteType = QName.createQName("testsite", "testSubsite", NAMESPACE_SERVICE);
testSite2 = testSites.createSite("sitePreset", "testSite2_" + guid, "T", "D", SiteVisibility.PUBLIC, subSiteType, AuthenticationUtil.getAdminUserName());
testSiteWithMembers = testSites.createTestSiteWithUserPerRole(GUID.generate(), "sitePreset", SiteVisibility.PUBLIC, AuthenticationUtil.getAdminUserName());
}
@Test public void ensureTestSitesWereCreatedOk() throws Exception
{
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
final SiteInfo recoveredSite1 = SITE_SERVICE.getSite(testSite1.getShortName());
final SiteInfo recoveredSite2 = SITE_SERVICE.getSite(testSite2.getShortName());
assertNotNull("Test site does not exist", recoveredSite1);
assertNotNull("Test site does not exist", recoveredSite2);
assertEquals("cm:title was wrong", "t", recoveredSite1.getTitle());
assertEquals("cm:description was wrong", "d", recoveredSite1.getDescription());
assertEquals("preset was wrong", "sitePreset", recoveredSite1.getSitePreset());
assertEquals("site visibility was wrong", SiteVisibility.PUBLIC, recoveredSite1.getVisibility());
for (String siteShortName : new String[] { testSite1.getShortName(), testSite2.getShortName() })
{
assertNotNull("site had no doclib container node", SITE_SERVICE.getContainer(siteShortName, SiteService.DOCUMENT_LIBRARY));
}
return null;
}
});
}
@Test public void ensureUsersWithShareRolesArePresentAndCorrect() throws Exception
{
TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
final String shortName = testSiteWithMembers.siteInfo.getShortName();
final SiteInfo recoveredSite = SITE_SERVICE.getSite(shortName);
assertNotNull("Test site does not exist", recoveredSite);
assertEquals(SiteModel.SITE_MANAGER, SITE_SERVICE.getMembersRole(shortName, testSiteWithMembers.siteManager));
assertEquals(SiteModel.SITE_COLLABORATOR, SITE_SERVICE.getMembersRole(shortName, testSiteWithMembers.siteCollaborator));
assertEquals(SiteModel.SITE_CONTRIBUTOR, SITE_SERVICE.getMembersRole(shortName, testSiteWithMembers.siteContributor));
assertEquals(SiteModel.SITE_CONSUMER, SITE_SERVICE.getMembersRole(shortName, testSiteWithMembers.siteConsumer));
assertNotNull(testSiteWithMembers.doclib);
assertTrue("Site doclib was not pre-created.", NODE_SERVICE.exists(testSiteWithMembers.doclib));
assertEquals("Site doclib was in wrong place.", testSiteWithMembers.siteInfo.getNodeRef(),
NODE_SERVICE.getPrimaryParent(testSiteWithMembers.doclib).getParentRef());
return null;
}
});
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2005-2012
* Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork;
import org.alfresco.util.ParameterCheck;
/**
* This JUnit rule can be used to setup and teardown a single Alfresco user, within a tenant, for test purposes.
* <p/>
* Example usage:
* <pre>
* public class YourTestClass
* {
* // Initialise the spring application context and tenant in a rule chain
* public static final ApplicationContextInit APP_CONTEXT_RULE = new ApplicationContextInit();
* public static final AlfrescoTenant TENANT_RULE = new AlfrescoTenant(APP_CONTEXT_RULE, "testtenant");
*
* &#64;ClassRule public static RuleChain ruleChain = RuleChain.outerRule(APP_CONTEXT_INIT)
* .around(TENANT);
*
* // We pass the rule that creates the spring application context.
* // This rule will give us a user with username 'AlexM@testtenant'.
* &#64;Rule public final TenantPerson namedPerson = new AlfrescoPerson(APP_CONTEXT_RULE, "AlexM@testTenant", TENANT);
*
* &#64;Test public void aTestMethod()
* {
* AUSTRALIAN_USER.runAsFullyAuthenticated(new TenantRunAsWork<Void>()
* {
* @Override
* public Void doWork() throws Exception
* {
* // Do something as the tenant user
* }
* });
* }
* }
* </pre>
*
* @author Alex Miller
*/
public class TenantPerson extends AlfrescoPerson
{
private AlfrescoTenant tenant;
/**
* Constructs the rule with a reference to a {@link ApplicationContextInit rule} which can be used to retrieve the ApplicationContext.
*
* @param appContextRule a rule which can be used to retrieve the spring app context.
* @param userName the username for the person to be created.
* @param tenant the tenant the person should be created under.
*/
public TenantPerson(ApplicationContextInit appContextInit, String userName, AlfrescoTenant tenant) {
super(appContextInit, userName + "@" + tenant.getTenantDomain());
ParameterCheck.mandatory("tenant", tenant);
this.tenant = tenant;
}
/**
* Create the user, in the given tenant, using the tenant system user.
*/
@Override protected void before()
{
tenant.runAsSystem(new TenantRunAsWork<Void>() {
@Override
public Void doWork() throws Exception {
TenantPerson.super.before();
return null;
}
});
}
/**
* Remove the user, using the system user for the tenant.
*/
@Override protected void after()
{
tenant.runAsSystem(new TenantRunAsWork<Void>() {
@Override
public Void doWork() throws Exception {
TenantPerson.super.after();
return null;
}
});
}
/**
* Do runAsWork as the fully authenticated user managed by this class,
*
* @param runAsWork
* @return The result of runAsWork
*/
public <T> T runAsFullyAuthenticated(TenantRunAsWork<T> runAsWork)
{
AuthenticationUtil.pushAuthentication();
AuthenticationUtil.setFullyAuthenticatedUser(getUsername());
try
{
return TenantUtil.runAsUserTenant(runAsWork, getUsername(), tenant.getTenantDomain());
}
finally
{
AuthenticationUtil.popAuthentication();
}
}
/**
* Get the tenant name of the users tenant.
*/
public String getTenantName() {
return tenant.getTenantDomain();
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2005-2012
Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.test.junitrules;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.model.Repository;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.rules.ExternalResource;
/**
* A JUnit rule designed to help with finding well known nodes
* in the system. It provides functionality similar to
* {@link Repository}, but with extra nodes needed for testing
*
* @author Nick Burch
* @since Odin
*/
public class WellKnownNodes extends ExternalResource
{
private static final Log log = LogFactory.getLog(WellKnownNodes.class);
private final ApplicationContextInit appContextRule;
private final Repository repositoryHelper;
private final NodeService nodeService;
private static QName SYSTEM_FOLDER_QNAME =
QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "system");
/**
* Constructs the rule with a reference to a {@link ApplicationContextInit rule} which can be used to retrieve the ApplicationContext.
*
* @param appContextRule a rule which can be used to retrieve the spring app context.
*/
public WellKnownNodes(ApplicationContextInit appContextRule)
{
this.appContextRule = appContextRule;
this.repositoryHelper = (Repository)appContextRule.getApplicationContext().getBean("repositoryHelper");
this.nodeService = (NodeService)appContextRule.getApplicationContext().getBean("NodeService");
}
@Override protected void before() throws Throwable
{
// Intentionally empty
}
@Override protected void after()
{
// Intentionally empty
}
/**
* Returns the root of the workspace store
*/
public NodeRef getWorkspaceRoot()
{
return repositoryHelper.getRootHome();
}
/**
* Returns company home
*/
public NodeRef getCompanyHome()
{
return repositoryHelper.getCompanyHome();
}
/**
* Returns the system root
*/
public NodeRef getSystemRoot()
{
NodeRef root = getWorkspaceRoot();
List<ChildAssociationRef> sysRefs = nodeService.getChildAssocs(
root, ContentModel.ASSOC_CHILDREN, SYSTEM_FOLDER_QNAME);
if (sysRefs.size() != 1)
{
throw new IllegalStateException("System folder missing / duplicated! Found " + sysRefs);
}
final NodeRef system = sysRefs.get(0).getChildRef();
return system;
}
/**
* Returns the given System Container
*/
public NodeRef getSystemContainer(QName containerName)
{
NodeRef system = getSystemRoot();
List<ChildAssociationRef> containerRefs = nodeService.getChildAssocs(
system, ContentModel.ASSOC_CHILDREN, containerName);
if (containerRefs.size() != 1)
{
throw new IllegalStateException("System Container " + containerName + " missing / duplicated! Found " + containerRefs);
}
final NodeRef container = containerRefs.get(0).getChildRef();
return container;
}
}

View File

@@ -0,0 +1,7 @@
/**
* This package contains <a href="http://junit.org/node/580">JUnit rules</a> intended for use in testing Alfresco.
*/
@PackageMarker
package org.alfresco.util.test.junitrules;
import org.alfresco.util.PackageMarker;