/**
* 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.pinsetter.tasks;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import org.candlepin.auth.Principal;
import org.candlepin.auth.UserPrincipal;
import org.candlepin.controller.CandlepinPoolManager;
import org.candlepin.controller.Refresher;
import org.candlepin.model.Consumer;
import org.candlepin.model.ConsumerType;
import org.candlepin.model.ExporterMetadata;
import org.candlepin.model.ExporterMetadataCurator;
import org.candlepin.model.Entitlement;
import org.candlepin.model.ImportRecord;
import org.candlepin.model.ImportRecordCurator;
import org.candlepin.model.Owner;
import org.candlepin.model.OwnerCurator;
import org.candlepin.model.Pool;
import org.candlepin.model.Pool.PoolType;
import org.candlepin.model.Product;
import org.candlepin.model.UeberCertificate;
import org.candlepin.model.UeberCertificateGenerator;
import org.candlepin.model.UpstreamConsumer;
import org.candlepin.pinsetter.core.PinsetterJobListener;
import org.candlepin.pinsetter.core.model.JobStatus;
import org.candlepin.service.SubscriptionServiceAdapter;
import org.candlepin.sync.ImporterException;
import org.candlepin.test.DatabaseTestFixture;
import org.candlepin.test.TestUtil;
import com.google.inject.Inject;
import com.google.inject.persist.UnitOfWork;
import org.junit.Before;
import org.junit.Test;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.xnap.commons.i18n.I18n;
import org.xnap.commons.i18n.I18nFactory;
import java.sql.SQLException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* UndoImportsJobTest
*/
public class UndoImportsJobTest extends DatabaseTestFixture {
@Inject protected I18n i18n;
@Inject protected CandlepinPoolManager poolManagerBase;
@Inject protected ImportRecordCurator importRecordCurator;
@Inject protected ExporterMetadataCurator exportCuratorBase;
@Inject protected UeberCertificateGenerator ueberCertGenerator;
protected CandlepinPoolManager poolManager;
protected OwnerCurator ownerCurator;
protected SubscriptionServiceAdapter subAdapter;
protected Refresher refresher;
protected ExporterMetadataCurator exportCurator;
protected JobExecutionContext jobContext;
protected JobDataMap jobDataMap;
protected UndoImportsJob undoImportsJob;
@Before
public void setUp() {
this.i18n = I18nFactory.getI18n(this.getClass(), Locale.US, I18nFactory.FALLBACK);
// Reset mocks/spys/objects
this.poolManager = mock(CandlepinPoolManager.class);
this.ownerCurator = mock(OwnerCurator.class);
this.subAdapter = mock(SubscriptionServiceAdapter.class);
this.refresher = mock(Refresher.class);
this.exportCurator = mock(ExporterMetadataCurator.class);
this.jobContext = mock(JobExecutionContext.class);
this.jobDataMap = new JobDataMap();
// Setup common behavior
when(this.jobContext.getMergedJobDataMap()).thenReturn(this.jobDataMap);
when(this.poolManager.getRefresher(eq(this.subAdapter), anyBoolean())).thenReturn(this.refresher);
when(this.refresher.setUnitOfWork(any(UnitOfWork.class))).thenReturn(this.refresher);
when(this.refresher.add(any(Owner.class))).thenReturn(this.refresher);
this.undoImportsJob = new UndoImportsJob(
this.i18n, this.ownerCurator, this.poolManager, this.subAdapter,
this.exportCurator, this.importRecordCurator
);
}
@Test
public void testUndoImport() throws JobExecutionException, IOException, ImporterException {
// We need proper curators for this test
this.poolManager = this.poolManagerBase;
this.ownerCurator = super.ownerCurator;
this.exportCurator = this.exportCuratorBase;
this.undoImportsJob = new UndoImportsJob(
this.i18n, this.ownerCurator, this.poolManager, this.subAdapter,
this.exportCurator, this.importRecordCurator
);
// Create owner w/upstream consumer
Owner owner1 = TestUtil.createOwner();
Owner owner2 = TestUtil.createOwner();
ConsumerType type = TestUtil.createConsumerType();
UpstreamConsumer uc1 = new UpstreamConsumer("uc1", null, type, "uc1");
UpstreamConsumer uc2 = new UpstreamConsumer("uc2", null, type, "uc2");
this.consumerTypeCurator.create(type);
this.ownerCurator.create(owner1);
this.ownerCurator.create(owner2);
owner1.setUpstreamConsumer(uc1);
owner1.setUpstreamConsumer(uc2);
this.ownerCurator.merge(owner1);
this.ownerCurator.merge(owner2);
// Create metadata
ExporterMetadata metadata1 = new ExporterMetadata(ExporterMetadata.TYPE_PER_USER, new Date(), owner1);
ExporterMetadata metadata2 = new ExporterMetadata(ExporterMetadata.TYPE_PER_USER, new Date(), owner2);
this.exportCurator.create(metadata1);
this.exportCurator.create(metadata2);
// Create pools w/upstream pool IDs
Pool pool1 = this.createPool("pool1", owner1, true, PoolType.NORMAL);
Pool pool2 = this.createPool("pool2", owner1, true, PoolType.BONUS);
Pool pool3 = this.createPool("pool3", owner1, false, PoolType.NORMAL);
Pool pool4 = this.createPool("pool4", owner1, false, PoolType.BONUS);
Pool pool5 = this.createPool("pool5", owner1, true, PoolType.ENTITLEMENT_DERIVED);
Pool pool6 = this.createPool("pool6", owner1, false, PoolType.ENTITLEMENT_DERIVED);
Pool pool7 = this.createPool("pool7", owner2, true, PoolType.NORMAL);
Pool pool8 = this.createPool("pool8", owner2, true, PoolType.BONUS);
Pool pool9 = this.createPool("pool9", owner2, true, PoolType.ENTITLEMENT_DERIVED);
// Create an ueber certificate for the owner.
UeberCertificate uebercert = ueberCertGenerator.generate(owner1.getKey(),
this.setupAdminPrincipal("test_admin"));
assertNotNull(uebercert);
// Verify initial state
assertEquals(
Arrays.asList(pool1, pool2, pool3, pool4, pool5, pool6),
this.poolManager.listPoolsByOwner(owner1).list()
);
assertEquals(Arrays.asList(pool7, pool8, pool9), this.poolManager.listPoolsByOwner(owner2).list());
assertEquals(metadata1, exportCurator.lookupByTypeAndOwner(ExporterMetadata.TYPE_PER_USER, owner1));
assertEquals(metadata2, exportCurator.lookupByTypeAndOwner(ExporterMetadata.TYPE_PER_USER, owner2));
assertEquals(0, this.importRecordCurator.findRecords(owner1).list().size());
assertEquals(0, this.importRecordCurator.findRecords(owner2).list().size());
// Execute job
Principal principal = new UserPrincipal("JarJarBinks", null, true);
this.jobDataMap.put(JobStatus.TARGET_TYPE, JobStatus.TargetType.OWNER);
this.jobDataMap.put(JobStatus.TARGET_ID, owner1.getId());
this.jobDataMap.put(UndoImportsJob.OWNER_KEY, owner1.getKey());
this.jobDataMap.put(PinsetterJobListener.PRINCIPAL_KEY, principal);
beginTransaction(); //since we locking owner we need start transaction
this.undoImportsJob.toExecute(this.jobContext);
commitTransaction();
// Verify deletions -- Ueber pools should not get deleted.
assertEquals(Arrays.asList(pool3, pool4, pool5, pool6),
this.poolManager.listPoolsByOwner(owner1).list());
assertEquals(Arrays.asList(pool7, pool8, pool9), this.poolManager.listPoolsByOwner(owner2).list());
assertNull(exportCurator.lookupByTypeAndOwner(ExporterMetadata.TYPE_PER_USER, owner1));
assertEquals(metadata2, exportCurator.lookupByTypeAndOwner(ExporterMetadata.TYPE_PER_USER, owner2));
assertNull(owner1.getUpstreamConsumer());
List<ImportRecord> records = this.importRecordCurator.findRecords(owner1).list();
assertEquals(1, records.size());
assertEquals(ImportRecord.Status.DELETE, records.get(0).getStatus());
assertEquals(0, this.importRecordCurator.findRecords(owner2).list().size());
}
protected Pool createPool(String name, Owner owner, boolean keepSourceSub, PoolType type) {
Product product = this.createProduct(name, name, owner);
Pool pool = TestUtil.createPool(owner, product);
if (!keepSourceSub) {
pool.setSourceSubscription(null);
}
this.poolCurator.create(pool);
Consumer consumer;
Entitlement entitlement;
switch (type) {
// TODO: Others as necessary
case ENTITLEMENT_DERIVED:
pool.setAttribute(Pool.Attributes.DERIVED_POOL, "true");
consumer = TestUtil.createConsumer(owner);
entitlement = TestUtil.createEntitlement(owner, consumer, pool, null);
this.consumerTypeCurator.create(consumer.getType());
this.consumerCurator.create(consumer);
this.entitlementCurator.create(entitlement);
pool.setSourceEntitlement(entitlement);
break;
case BONUS:
pool.setAttribute(Pool.Attributes.DERIVED_POOL, "true");
break;
default:
// Required by checkstyle.
}
this.poolCurator.merge(pool);
return pool;
}
@Test
public void handleException() throws JobExecutionException {
// the real thing we want to handle
doThrow(new NullPointerException()).when(this.ownerCurator).lockAndLoadById(anyString());
try {
this.undoImportsJob.execute(this.jobContext);
fail("Expected exception not thrown");
}
catch (JobExecutionException ex) {
assertFalse(ex.refireImmediately());
}
}
// If we encounter a runtime job exception, wrapping a SQLException, we should see
// a refire job exception thrown:
@Test
public void refireOnWrappedSQLException() throws JobExecutionException {
RuntimeException e = new RuntimeException("uh oh", new SQLException("not good"));
doThrow(e).when(this.ownerCurator).lockAndLoadById(anyString());
try {
this.undoImportsJob.execute(this.jobContext);
fail("Expected exception not thrown");
}
catch (JobExecutionException ex) {
assertTrue(ex.refireImmediately());
}
}
// If we encounter a runtime job exception, wrapping a SQLException, we should see
// a refire job exception thrown:
@Test
public void refireOnMultiLayerWrappedSQLException() throws JobExecutionException {
RuntimeException e = new RuntimeException("uh oh", new SQLException("not good"));
RuntimeException e2 = new RuntimeException("trouble!", e);
doThrow(e2).when(this.ownerCurator).lockAndLoadById(anyString());
try {
this.undoImportsJob.execute(this.jobContext);
fail("Expected exception not thrown");
}
catch (JobExecutionException ex) {
assertTrue(ex.refireImmediately());
}
}
@Test
public void noRefireOnRegularRuntimeException() throws JobExecutionException {
RuntimeException e = new RuntimeException("uh oh", new NullPointerException());
doThrow(e).when(this.ownerCurator).lockAndLoadById(anyString());
try {
this.undoImportsJob.execute(this.jobContext);
fail("Expected exception not thrown");
}
catch (JobExecutionException ex) {
assertFalse(ex.refireImmediately());
}
}
}