/**
* Copyright (c) 2009 - 2012 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package org.candlepin.test;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
import org.candlepin.TestingInterceptor;
import org.candlepin.TestingModules;
import org.candlepin.auth.Access;
import org.candlepin.auth.Principal;
import org.candlepin.auth.UserPrincipal;
import org.candlepin.auth.permissions.OwnerPermission;
import org.candlepin.auth.permissions.Permission;
import org.candlepin.auth.permissions.PermissionFactory.PermissionType;
import org.candlepin.common.config.Configuration;
import org.candlepin.config.CandlepinCommonTestConfig;
import org.candlepin.guice.CandlepinRequestScope;
import org.candlepin.guice.TestPrincipalProviderSetter;
import org.candlepin.junit.CandlepinLiquibaseResource;
import org.candlepin.model.Cdn;
import org.candlepin.model.CdnCurator;
import org.candlepin.model.CertificateSerial;
import org.candlepin.model.CertificateSerialCurator;
import org.candlepin.model.Consumer;
import org.candlepin.model.ConsumerCurator;
import org.candlepin.model.ConsumerType;
import org.candlepin.model.ConsumerTypeCurator;
import org.candlepin.model.Content;
import org.candlepin.model.ContentCurator;
import org.candlepin.model.Entitlement;
import org.candlepin.model.EntitlementCertificate;
import org.candlepin.model.EntitlementCertificateCurator;
import org.candlepin.model.EntitlementCurator;
import org.candlepin.model.Environment;
import org.candlepin.model.EnvironmentContent;
import org.candlepin.model.EnvironmentContentCurator;
import org.candlepin.model.EnvironmentCurator;
import org.candlepin.model.EventCurator;
import org.candlepin.model.IdentityCertificateCurator;
import org.candlepin.model.ImportRecordCurator;
import org.candlepin.model.JobCurator;
import org.candlepin.model.Owner;
import org.candlepin.model.OwnerContentCurator;
import org.candlepin.model.OwnerCurator;
import org.candlepin.model.OwnerInfoCurator;
import org.candlepin.model.OwnerProductCurator;
import org.candlepin.model.PermissionBlueprint;
import org.candlepin.model.Pool;
import org.candlepin.model.PoolCurator;
import org.candlepin.model.Product;
import org.candlepin.model.ProductCertificateCurator;
import org.candlepin.model.ProductCurator;
import org.candlepin.model.ProductShareCurator;
import org.candlepin.model.Role;
import org.candlepin.model.RoleCurator;
import org.candlepin.model.SourceSubscription;
import org.candlepin.model.UserCurator;
import org.candlepin.model.activationkeys.ActivationKey;
import org.candlepin.model.activationkeys.ActivationKeyCurator;
import org.candlepin.resteasy.ResourceLocatorMap;
import org.candlepin.util.DateSource;
import org.candlepin.util.Util;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.persist.PersistFilter;
import com.google.inject.util.Modules;
import org.hibernate.cfg.beanvalidation.BeanValidationEventListener;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.internal.SessionFactoryImpl;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xnap.commons.i18n.I18n;
import org.xnap.commons.i18n.I18nFactory;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Test fixture for test classes requiring access to the database.
*/
public class DatabaseTestFixture {
protected static Logger log = LoggerFactory.getLogger(DatabaseTestFixture.class);
private static final String DEFAULT_CONTRACT = "SUB349923";
private static final String DEFAULT_ACCOUNT = "ACC123";
private static final String DEFAULT_ORDER = "ORD222";
@SuppressWarnings("checkstyle:visibilitymodifier")
@ClassRule
@Rule
public static CandlepinLiquibaseResource liquibase = new CandlepinLiquibaseResource();
protected Configuration config;
@Inject protected ActivationKeyCurator activationKeyCurator;
@Inject protected CdnCurator cdnCurator;
@Inject protected ConsumerCurator consumerCurator;
@Inject protected ConsumerTypeCurator consumerTypeCurator;
@Inject protected CertificateSerialCurator certSerialCurator;
@Inject protected ContentCurator contentCurator;
@Inject protected EntitlementCurator entitlementCurator;
@Inject protected EntitlementCertificateCurator entitlementCertificateCurator;
@Inject protected EnvironmentCurator environmentCurator;
@Inject protected EnvironmentContentCurator environmentContentCurator;
@Inject protected EventCurator eventCurator;
@Inject protected IdentityCertificateCurator identityCertificateCurator;
@Inject protected ImportRecordCurator importRecordCurator;
@Inject protected JobCurator jobCurator;
@Inject protected OwnerContentCurator ownerContentCurator;
@Inject protected OwnerCurator ownerCurator;
@Inject protected OwnerInfoCurator ownerInfoCurator;
@Inject protected OwnerProductCurator ownerProductCurator;
@Inject protected ProductCertificateCurator productCertificateCurator;
@Inject protected ProductCurator productCurator;
@Inject protected PoolCurator poolCurator;
@Inject protected RoleCurator roleCurator;
@Inject protected UserCurator userCurator;
@Inject protected ProductShareCurator productShareCurator;
@Inject protected ResourceLocatorMap locatorMap;
private static Injector parentInjector;
protected Injector injector;
private CandlepinRequestScope cpRequestScope;
protected TestingInterceptor securityInterceptor;
protected DateSourceForTesting dateSource;
protected I18n i18n;
@BeforeClass
public static void initClass() {
parentInjector = Guice.createInjector(new TestingModules.JpaModule());
insertValidationEventListeners(parentInjector);
}
/**
* There's no way to really get Guice to perform injections on stuff that
* the JpaPersistModule is creating, so we resort to grabbing the EntityManagerFactory
* after the fact and adding the Validation EventListener ourselves.
* @param inj
*/
private static void insertValidationEventListeners(Injector inj) {
Provider<EntityManagerFactory> emfProvider = inj.getProvider(EntityManagerFactory.class);
HibernateEntityManagerFactory hibernateEntityManagerFactory =
(HibernateEntityManagerFactory) emfProvider.get();
SessionFactoryImpl sessionFactoryImpl =
(SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
EventListenerRegistry registry = sessionFactoryImpl
.getServiceRegistry()
.getService(EventListenerRegistry.class);
Provider<BeanValidationEventListener> listenerProvider =
inj.getProvider(BeanValidationEventListener.class);
registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(listenerProvider.get());
registry.getEventListenerGroup(EventType.PRE_UPDATE).appendListener(listenerProvider.get());
registry.getEventListenerGroup(EventType.PRE_DELETE).appendListener(listenerProvider.get());
}
@Before
public void init() throws Exception {
this.init(true);
}
public void init(boolean beginTransaction) throws Exception {
this.config = new CandlepinCommonTestConfig();
Module testingModule = new TestingModules.StandardTest(this.config);
this.injector = parentInjector.createChildInjector(
Modules.override(testingModule).with(getGuiceOverrideModule()));
locatorMap = this.injector.getInstance(ResourceLocatorMap.class);
locatorMap.init();
securityInterceptor = this.injector.getInstance(TestingInterceptor.class);
cpRequestScope = injector.getInstance(CandlepinRequestScope.class);
// Because all candlepin operations are running in the CandlepinRequestScope
// we'll force the instance creations to be done inside the scope.
// Exit the scope to make sure that it is clean before starting the test.
cpRequestScope.exit();
cpRequestScope.enter();
this.injector.injectMembers(this);
dateSource = (DateSourceForTesting) injector.getInstance(DateSource.class);
dateSource.currentDate(TestDateUtil.date(2010, 1, 1));
HttpServletRequest req = parentInjector.getInstance(HttpServletRequest.class);
when(req.getAttribute("username")).thenReturn("mock_user");
this.i18n = I18nFactory.getI18n(getClass(), Locale.US, I18nFactory.FALLBACK);
if (beginTransaction) {
this.beginTransaction();
}
}
@After
public void shutdown() {
cpRequestScope.exit();
// If we have any pending transactions, we should commit it before we move on
EntityManager manager = this.getEntityManager();
EntityTransaction transaction = manager.getTransaction();
if (transaction.isActive()) {
if (transaction.getRollbackOnly()) {
transaction.rollback();
}
else {
transaction.commit();
}
}
// We are using a singleton for the principal in tests. Make sure we clear it out
// after every test. TestPrincipalProvider controls the default behavior.
TestPrincipalProviderSetter.get().setPrincipal(null);
manager.clear();
reset(parentInjector.getInstance(HttpServletRequest.class));
reset(parentInjector.getInstance(HttpServletResponse.class));
}
@AfterClass
public static void destroy() {
parentInjector.getInstance(PersistFilter.class).destroy();
EntityManager manager = parentInjector.getInstance(EntityManager.class);
if (manager.isOpen()) {
manager.close();
}
EntityManagerFactory emf = parentInjector.getInstance(EntityManagerFactory.class);
if (emf.isOpen()) {
emf.close();
}
}
protected Module getGuiceOverrideModule() {
return new AbstractModule() {
@Override
protected void configure() {
// NO OP
}
};
}
/**
* Populates the given object by injecting dependencies for its members tagged with the @Inject
* annotation.
*
* @param object
* The object to populate
*/
protected void injectMembers(Object object) {
this.injector.injectMembers(object);
}
protected com.google.inject.Provider<EntityManager> getEntityManagerProvider() {
return this.injector.getProvider(EntityManager.class);
}
protected EntityManager getEntityManager() {
return this.getEntityManagerProvider().get();
}
/**
* Opens a new transaction if a transaction has not already been opened. If a transaction is
* already open, this method does nothing; repeated calls are safe within the context of a
* single thread.
*/
protected void beginTransaction() {
EntityTransaction transaction = this.getEntityManager().getTransaction();
if (!transaction.isActive()) {
transaction.begin();
}
else {
log.warn("beginTransaction called with an active transaction");
}
}
/**
* Commits the current transaction, flushing pending writes as necessary. If a transaction has
* not yet been opened, this method does nothing; repeated calls are safe within the context of
* a single thread.
*/
protected void commitTransaction() {
EntityTransaction transaction = this.getEntityManager().getTransaction();
if (transaction.isActive()) {
transaction.commit();
}
else {
log.warn("commitTransaction called without an active transaction");
}
}
/**
* Rolls back the current transaction, discarding any pending writes. If a transaction has not
* yet been opened, this method does nothing; repeated calls are safe within the context of a
* single thread.
*/
protected void rollbackTransaction() {
EntityTransaction transaction = this.getEntityManager().getTransaction();
if (transaction.isActive()) {
transaction.rollback();
}
else {
log.warn("rollbackTransaction called without an active transaction");
}
}
// Entity creation methods
protected ActivationKey createActivationKey(Owner owner) {
return TestUtil.createActivationKey(owner, null);
}
public Role createAdminRole(Owner owner) {
PermissionBlueprint p = new PermissionBlueprint(PermissionType.OWNER, owner, Access.ALL);
Role role = new Role("testrole" + TestUtil.randomInt());
role.addPermission(p);
return role;
}
protected Cdn createCdn() {
int rand = TestUtil.randomInt();
String name = "test-cdn-" + rand;
String url = "https://" + rand + ".cdn.com";
return this.createCdn(name, name, url);
}
protected Cdn createCdn(String name, String url) {
return this.createCdn(name, name, url);
}
protected Cdn createCdn(String name, String label, String url) {
Cdn cdn = new Cdn(name, label, url);
return this.cdnCurator.create(cdn);
}
protected Content createContent(Owner... owners) {
String contentId = "test-content-" + TestUtil.randomInt();
return this.createContent(contentId, contentId, owners);
}
protected Content createContent(String id, String name, Owner... owners) {
Content content = TestUtil.createContent(id, name);
content = this.contentCurator.create(content);
this.ownerContentCurator.mapContentToOwners(content, owners);
return content;
}
protected Consumer createConsumer(Owner owner) {
ConsumerType type = new ConsumerType("test-consumer-type-" + TestUtil.randomInt());
consumerTypeCurator.create(type);
Consumer c = new Consumer("test-consumer", "test-user", owner, type);
consumerCurator.create(c);
return c;
}
protected Entitlement createEntitlement(Owner owner, Consumer consumer, Pool pool,
EntitlementCertificate cert) {
Entitlement entitlement = new Entitlement();
entitlement.setOwner(owner);
entitlement.setPool(pool);
entitlement.setConsumer(consumer);
this.entitlementCurator.create(entitlement);
// Maintain runtime consistency
consumer.addEntitlement(entitlement);
pool.getEntitlements().add(entitlement);
if (cert != null) {
cert.setEntitlement(entitlement);
entitlement.getCertificates().add(cert);
this.entitlementCertificateCurator.merge(cert);
this.entitlementCurator.merge(entitlement);
}
return entitlement;
}
protected EntitlementCertificate createEntitlementCertificate(String key, String cert) {
EntitlementCertificate toReturn = new EntitlementCertificate();
CertificateSerial certSerial = new CertificateSerial(new Date());
certSerialCurator.create(certSerial);
toReturn.setKeyAsBytes(key.getBytes());
toReturn.setCertAsBytes(cert.getBytes());
toReturn.setSerial(certSerial);
return toReturn;
}
protected Environment createEnvironment(Owner owner, String id) {
String name = "test-env-" + TestUtil.randomInt();
return this.createEnvironment(owner, name, name, null, null, null);
}
protected Environment createEnvironment(Owner owner, String id, String name) {
return this.createEnvironment(owner, id, name, null, null, null);
}
protected Environment createEnvironment(Owner owner, String id, String name, String description,
Collection<Consumer> consumers, Collection<Content> content) {
Environment environment = new Environment(id, name, owner);
environment.setDescription(description);
if (consumers != null) {
// Ugly hack to deal with how environment currently encapsulates its collections
if (!(consumers instanceof List)) {
consumers = new LinkedList<Consumer>(consumers);
}
environment.setConsumers((List<Consumer>) consumers);
}
if (content != null) {
for (Content elem : content) {
EnvironmentContent envContent = new EnvironmentContent(environment, elem, true);
// Impl note:
// At the time of writing, this line is redundant. But if we ever fix environment,
// this will be good to have as a backup.
environment.getEnvironmentContent().add(envContent);
}
}
return this.environmentCurator.create(environment);
}
protected Owner createOwner() {
return this.createOwner("Test Owner " + TestUtil.randomInt());
}
protected Owner createOwner(String key) {
return this.createOwner(key, key);
}
protected Owner createOwner(String key, String name) {
Owner owner = TestUtil.createOwner(key, name);
this.ownerCurator.create(owner);
return owner;
}
protected Pool createPool(Owner owner, Product product) {
return this.createPool(
owner, product, 1L, TestUtil.createDate(2000, 1, 1), TestUtil.createDate(2100, 1, 1)
);
}
/**
* Create an entitlement pool.
*
* @return an entitlement pool
*/
protected Pool createPool(Owner owner, Product product, Long quantity, Date startDate, Date endDate) {
Pool pool = new Pool(
owner,
product,
new HashSet<Product>(),
quantity,
startDate,
endDate,
DEFAULT_CONTRACT,
DEFAULT_ACCOUNT,
DEFAULT_ORDER
);
pool.setSourceSubscription(new SourceSubscription(Util.generateDbUUID(), "master"));
return poolCurator.create(pool);
}
protected Pool createPool(Owner owner, Product product, Long quantity, String subscriptionId,
String subscriptionSubKey, Date startDate, Date endDate) {
Pool pool = new Pool(
owner,
product,
new HashSet<Product>(),
quantity,
startDate,
endDate,
DEFAULT_CONTRACT,
DEFAULT_ACCOUNT,
DEFAULT_ORDER
);
pool.setSourceSubscription(new SourceSubscription(subscriptionId, subscriptionSubKey));
return poolCurator.create(pool);
}
protected Pool createPool(Owner owner, Product product, Long quantity, Date startDate, Date endDate,
String contractNr) {
Pool pool = createPool(owner, product, quantity, startDate, endDate);
pool.setContractNumber(contractNr);
return poolCurator.merge(pool);
}
protected Pool createPool(Owner owner, Product product, Long quantity, Date startDate, Date endDate,
String contractNr, String subscriptionId) {
Pool pool = createPool(owner, product, quantity, subscriptionId, "master", startDate, endDate);
pool.setContractNumber(contractNr);
return poolCurator.merge(pool);
}
protected Product createProduct(Owner... owners) {
String productId = "test-product-" + TestUtil.randomInt();
return this.createProduct(productId, productId, owners);
}
protected Product createProduct(String id, String name, Owner... owners) {
Product product = TestUtil.createProduct(id, name);
return this.createProduct(product, owners);
}
protected Product createProduct(Product product, Owner... owners) {
product = this.productCurator.create(product);
this.ownerProductCurator.mapProductToOwners(product, owners);
return product;
}
protected Principal setupPrincipal(Owner owner, Access role) {
return setupPrincipal("someuser", owner, role);
}
protected Principal setupPrincipal(String username, Owner owner, Access verb) {
OwnerPermission p = new OwnerPermission(owner, verb);
// Only need a detached owner permission here:
Principal ownerAdmin = new UserPrincipal(username, Arrays.asList(new Permission[] {p}), false);
setupPrincipal(ownerAdmin);
return ownerAdmin;
}
protected Principal setupAdminPrincipal(String username) {
UserPrincipal principal = new UserPrincipal(username, null, true);
setupPrincipal(principal);
return principal;
}
protected Principal setupPrincipal(Principal p) {
TestPrincipalProviderSetter.get().setPrincipal(p);
return p;
}
/**
* For parameterized tests, the method called to provide the parameter values is called before the @Before
* methods are called. Our Guice injection occurs in the @Before methods and therefore injection isn't
* a possibility in parameter providers. Thus we need a special method to return a Configuration object
* when required by parameter providers.
* @return a Configuration object
*/
protected Configuration getConfigForParameters() {
return new CandlepinCommonTestConfig();
}
}