/** * 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.policy; import static org.hamcrest.Matchers.hasItems; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.AdditionalAnswers.returnsFirstArg; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; import org.candlepin.auth.UserPrincipal; import org.candlepin.common.config.Configuration; import org.candlepin.config.ConfigProperties; import org.candlepin.controller.PoolManager; import org.candlepin.model.CandlepinQuery; import org.candlepin.model.Consumer; import org.candlepin.model.ConsumerType; import org.candlepin.model.ConsumerType.ConsumerTypeEnum; import org.candlepin.model.Entitlement; import org.candlepin.model.EntitlementCurator; import org.candlepin.model.Owner; import org.candlepin.model.OwnerProductCurator; import org.candlepin.model.Pool; import org.candlepin.model.Product; import org.candlepin.model.ProductCurator; import org.candlepin.model.Rules; import org.candlepin.model.RulesCurator; import org.candlepin.model.dto.Subscription; import org.candlepin.policy.js.pool.PoolHelper; import org.candlepin.policy.js.pool.PoolRules; import org.candlepin.policy.js.pool.PoolUpdate; import org.candlepin.test.TestUtil; import org.candlepin.util.Util; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; /** * JsPoolRulesTest: Tests for the default rules. */ @RunWith(MockitoJUnitRunner.class) public class PoolRulesStackDerivedTest { private PoolRules poolRules; private Consumer consumer; @Mock private RulesCurator rulesCuratorMock; @Mock private OwnerProductCurator ownerProductCuratorMock; @Mock private PoolManager poolManagerMock; @Mock private Configuration configMock; @Mock private EntitlementCurator entCurMock; @Mock private ProductCurator productCurator; private UserPrincipal principal; private Owner owner; private Subscription sub1; private Subscription sub2; private Subscription sub3; private Subscription sub4; private Pool pool1; private Pool pool2; private Pool pool3; private Pool pool4; // Marketing products: private Product prod1; private Product prod2; private Product prod3; // Engineering (provided) products: private Product provided1; private Product provided2; private Product provided3; private Product provided4; private List<Entitlement> stackedEnts = new LinkedList<Entitlement>(); private Pool stackDerivedPool; private Pool stackDerivedPool2; private static final String STACK = "a-stack"; @Before public void setUp() { // Load the default production rules: InputStream is = this.getClass().getResourceAsStream( RulesCurator.DEFAULT_RULES_FILE); Rules rules = new Rules(Util.readFile(is)); when(rulesCuratorMock.getUpdated()).thenReturn(new Date()); when(rulesCuratorMock.getRules()).thenReturn(rules); when(configMock.getInt(eq(ConfigProperties.PRODUCT_CACHE_MAX))).thenReturn(100); poolRules = new PoolRules(poolManagerMock, configMock, entCurMock, ownerProductCuratorMock, productCurator); principal = TestUtil.createOwnerPrincipal(); owner = principal.getOwners().get(0); consumer = new Consumer("consumer", "registeredbybob", owner, new ConsumerType(ConsumerTypeEnum.SYSTEM)); // Two subtly different products stacked together: prod1 = TestUtil.createProduct("prod1", "prod1"); prod1.setAttribute(Product.Attributes.VIRT_LIMIT, "2"); prod1.setAttribute(Product.Attributes.STACKING_ID, STACK); prod1.setAttribute("testattr1", "1"); when(ownerProductCuratorMock.getProductById(owner, prod1.getId())).thenReturn(prod1); prod2 = TestUtil.createProduct("prod2", "prod2"); prod2.setAttribute(Product.Attributes.VIRT_LIMIT, "unlimited"); prod2.setAttribute(Product.Attributes.STACKING_ID, STACK); prod2.setAttribute("testattr2", "2"); when(ownerProductCuratorMock.getProductById(owner, prod2.getId())).thenReturn(prod2); prod3 = TestUtil.createProduct("prod3", "prod3"); prod3.setAttribute(Product.Attributes.VIRT_LIMIT, "9"); prod3.setAttribute(Product.Attributes.STACKING_ID, STACK + "3"); prod3.setAttribute("testattr2", "2"); when(ownerProductCuratorMock.getProductById(owner, prod3.getId())).thenReturn(prod3); provided1 = TestUtil.createProduct(); provided2 = TestUtil.createProduct(); provided3 = TestUtil.createProduct(); provided4 = TestUtil.createProduct(); // Create three subscriptions with various start/end dates: sub1 = createStackedVirtSub(owner, prod1, TestUtil.createDate(2010, 1, 1), TestUtil.createDate(2015, 1, 1)); sub1.getProvidedProducts().add(provided1.toDTO()); pool1 = copyFromSub(sub1); sub2 = createStackedVirtSub(owner, prod2, TestUtil.createDate(2011, 1, 1), TestUtil.createDate(2017, 1, 1)); sub2.getProvidedProducts().add(provided2.toDTO()); pool2 = copyFromSub(sub2); sub3 = createStackedVirtSub(owner, prod2, TestUtil.createDate(2012, 1, 1), TestUtil.createDate(2020, 1, 1)); sub3.getProvidedProducts().add(provided3.toDTO()); pool3 = copyFromSub(sub3); sub4 = createStackedVirtSub(owner, prod3, TestUtil.createDate(2012, 1, 1), TestUtil.createDate(2020, 1, 1)); sub4.getProvidedProducts().add(provided4.toDTO()); pool4 = copyFromSub(sub4); // Initial entitlement from one of the pools: stackedEnts.add(createEntFromPool(pool2)); CandlepinQuery cqmock = mock(CandlepinQuery.class); when(cqmock.list()).thenReturn(stackedEnts); when(entCurMock.findByStackId(consumer, STACK)).thenReturn(cqmock); pool2.setAttribute(Product.Attributes.VIRT_LIMIT, "60"); pool4.setAttribute(Product.Attributes.VIRT_LIMIT, "80"); List<Pool> reqPools = new ArrayList<Pool>(); reqPools.add(pool2); Map<String, Entitlement> entitlements = new HashMap<String, Entitlement>(); entitlements.put(pool2.getId(), stackedEnts.get(0)); Map<String, Map<String, String>> attributes = new HashMap<String, Map<String, String>>(); attributes.put(pool2.getId(), PoolHelper.getFlattenedAttributes(pool2)); when(poolManagerMock.createPools(Matchers.anyListOf(Pool.class))).then(returnsFirstArg()); List<Pool> resPools = PoolHelper.createHostRestrictedPools(poolManagerMock, consumer, reqPools, entitlements, attributes, productCurator); stackDerivedPool = resPools.get(0); reqPools.clear(); reqPools.add(pool4); entitlements.clear(); entitlements.put(pool4.getId(), createEntFromPool(pool4)); attributes.clear(); attributes.put(pool4.getId(), PoolHelper.getFlattenedAttributes(pool4)); stackDerivedPool2 = PoolHelper.createHostRestrictedPools(poolManagerMock, consumer, reqPools, entitlements, attributes, productCurator).get(0); } private static int lastPoolId = 1; /** * Creates a Pool and caches stuff * @param sub * @return */ private Pool copyFromSub(Subscription sub) { Pool pool = TestUtil.copyFromSub(sub); pool.setId("" + lastPoolId++); when(productCurator.getPoolProvidedProductsCached(pool)) .thenReturn(pool.getProvidedProducts()); when(productCurator.getPoolDerivedProvidedProductsCached(pool)) .thenReturn(pool.getDerivedProvidedProducts()); return pool; } private Subscription createStackedVirtSub(Owner owner, Product product, Date start, Date end) { Subscription s = TestUtil.createSubscription(owner, TestUtil.createProduct()); s.setStartDate(start); s.setEndDate(end); s.setProduct(product.toDTO()); s.setContractNumber(Integer.toString(TestUtil.randomInt())); s.setOrderNumber(Integer.toString(TestUtil.randomInt())); s.setAccountNumber(Integer.toString(TestUtil.randomInt())); return s; } private Entitlement createEntFromPool(Pool pool) { Entitlement e = new Entitlement(pool, consumer, 2); e.setCreated(new Date()); try { Thread.sleep(1); } catch (InterruptedException e1) { e1.printStackTrace(); } return e; } @Test public void initialDates() { assertEquals(pool2.getStartDate(), stackDerivedPool.getStartDate()); assertEquals(pool2.getEndDate(), stackDerivedPool.getEndDate()); } @Test public void initialOrderInfo() { assertEquals(pool2.getAccountNumber(), stackDerivedPool.getAccountNumber()); assertEquals(pool2.getAccountNumber(), stackDerivedPool.getAccountNumber()); assertEquals(pool2.getOrderNumber(), stackDerivedPool.getOrderNumber()); } @Test public void initialProduct() { assertEquals(pool2.getProductId(), stackDerivedPool.getProductId()); assertEquals(pool2.getProductName(), stackDerivedPool.getProductName()); } @Test public void initialProvidedProducts() { assertEquals(1, stackDerivedPool.getProvidedProducts().size()); assertEquals(provided2.getUuid(), stackDerivedPool.getProvidedProducts().iterator().next().getUuid()); } @Test public void initialAttributes() { assertEquals(3, stackDerivedPool.getProduct().getAttributes().size()); assertEquals("2", stackDerivedPool.getProduct().getAttributeValue("testattr2")); } @Test public void addEarlierStartDate() { stackedEnts.add(createEntFromPool(pool1)); PoolUpdate update = poolRules.updatePoolFromStack(stackDerivedPool, null); assertTrue(update.changed()); assertTrue(update.getDatesChanged()); assertEquals(pool1.getStartDate(), stackDerivedPool.getStartDate()); assertEquals(pool2.getEndDate(), stackDerivedPool.getEndDate()); } @Test public void addLaterEndDate() { stackedEnts.add(createEntFromPool(pool1)); stackedEnts.add(createEntFromPool(pool3)); PoolUpdate update = poolRules.updatePoolFromStack(stackDerivedPool, null); assertTrue(update.changed()); assertTrue(update.getDatesChanged()); assertEquals(pool1.getStartDate(), stackDerivedPool.getStartDate()); assertEquals(pool3.getEndDate(), stackDerivedPool.getEndDate()); } @Test public void mergedProductAttributes() { Entitlement ent1 = createEntFromPool(pool1); ent1.setCreated(new Date(System.currentTimeMillis() - 86400000)); stackedEnts.add(ent1); stackedEnts.add(createEntFromPool(pool3)); PoolUpdate update = poolRules.updatePoolFromStack(stackDerivedPool, null); assertTrue(update.changed()); assertTrue(update.getProductAttributesChanged()); assertEquals(pool1.getProductAttributes(), stackDerivedPool.getProductAttributes()); } @Test public void mergedProvidedProducts() { stackedEnts.add(createEntFromPool(pool1)); stackedEnts.add(createEntFromPool(pool3)); PoolUpdate update = poolRules.updatePoolFromStack(stackDerivedPool, null); assertTrue(update.getProductsChanged()); assertEquals(3, stackDerivedPool.getProvidedProducts().size()); assertTrue(stackDerivedPool.getProvidedProducts().contains(provided1)); assertTrue(stackDerivedPool.getProvidedProducts().contains(provided2)); assertTrue(stackDerivedPool.getProvidedProducts().contains(provided3)); } @Test public void removeEldestEntitlement() { stackedEnts.add(createEntFromPool(pool1)); stackedEnts.add(createEntFromPool(pool3)); poolRules.updatePoolFromStack(stackDerivedPool, null); // Should change a variety of settings on the pool. stackedEnts.remove(0); PoolUpdate update = poolRules.updatePoolFromStack(stackDerivedPool, null); assertTrue(update.changed()); assertFalse(update.getDatesChanged()); // Should have changed from pool2 to pool1's info: assertEquals(pool1.getProductId(), stackDerivedPool.getProductId()); assertEquals(pool1.getProductName(), stackDerivedPool.getProductName()); assertEquals(pool1.getAccountNumber(), stackDerivedPool.getAccountNumber()); assertEquals(pool1.getContractNumber(), stackDerivedPool.getContractNumber()); assertEquals(pool1.getOrderNumber(), stackDerivedPool.getOrderNumber()); } @Test public void removeEarliestStartingEntitlement() { stackedEnts.add(createEntFromPool(pool1)); stackedEnts.add(createEntFromPool(pool3)); poolRules.updatePoolFromStack(stackDerivedPool, null); // Should change a variety of settings on the pool. stackedEnts.remove(1); PoolUpdate update = poolRules.updatePoolFromStack(stackDerivedPool, null); assertTrue(update.changed()); assertTrue(update.getDatesChanged()); assertEquals(pool2.getStartDate(), stackDerivedPool.getStartDate()); assertEquals(pool3.getEndDate(), stackDerivedPool.getEndDate()); } @Test public void virtLimitFromFirstVirtLimitEnt() { stackedEnts.clear(); stackedEnts.add(createEntFromPool(pool1)); stackedEnts.add(createEntFromPool(pool3)); PoolUpdate update = poolRules.updatePoolFromStack(stackDerivedPool, null); assertTrue(update.changed()); assertTrue(update.getQuantityChanged()); assertEquals((Long) 2L, stackDerivedPool.getQuantity()); } @Test public void virtLimitFromFirstVirtLimitEntBatch() { CandlepinQuery cqmock = mock(CandlepinQuery.class); stackedEnts.clear(); Entitlement e1 = createEntFromPool(pool1); e1.setQuantity(4); stackedEnts.add(e1); Entitlement e2 = createEntFromPool(pool4); e2.setQuantity(16); stackedEnts.add(e2); Class<Set<String>> listClass = (Class<Set<String>>) (Class) HashSet.class; ArgumentCaptor<Set<String>> arg = ArgumentCaptor.forClass(listClass); when(cqmock.iterator()).thenReturn(stackedEnts.iterator()); when(entCurMock.findByStackIds(eq(consumer), arg.capture())).thenReturn(cqmock); List<PoolUpdate> updates = poolRules.updatePoolsFromStack(consumer, Arrays.asList(stackDerivedPool, stackDerivedPool2), false); Set<String> stackIds = arg.getValue(); assertEquals(2, stackIds.size()); assertThat(stackIds, hasItems(STACK, STACK + "3")); for (PoolUpdate update : updates) { assertTrue(update.changed()); assertTrue(update.getQuantityChanged()); } assertEquals((Long) 2L, stackDerivedPool.getQuantity()); assertEquals((Long) 9L, stackDerivedPool2.getQuantity()); } @Test public void virtLimitFromLastVirtLimitEntWhenFirstIsRemoved() { stackedEnts.clear(); stackedEnts.add(createEntFromPool(pool1)); stackedEnts.add(createEntFromPool(pool2)); PoolUpdate update = poolRules.updatePoolFromStack(stackDerivedPool, null); assertEquals((Long) 2L, stackDerivedPool.getQuantity()); stackedEnts.remove(0); update = poolRules.updatePoolFromStack(stackDerivedPool, null); assertTrue(update.changed()); assertTrue(update.getQuantityChanged()); assertEquals(new Long("-1"), stackDerivedPool.getQuantity()); } @Test public void virtLimitNotChangedWhenLastVirtEntIsRemovedFromStack() { // Remove virt_limit from pool1 so that it is not considered // as virt limiting. Product product = pool1.getProduct(); product.clearAttributes(); product.setAttribute(Product.Attributes.STACKING_ID, STACK); product.setAttribute("testattr2", "2"); stackedEnts.clear(); stackedEnts.add(createEntFromPool(pool1)); stackedEnts.add(createEntFromPool(pool2)); PoolUpdate update = poolRules.updatePoolFromStack(stackDerivedPool, null); assertEquals(new Long("-1"), stackDerivedPool.getQuantity()); stackedEnts.remove(0); update = poolRules.updatePoolFromStack(stackDerivedPool, null); assertTrue(update.changed()); assertTrue(update.getDatesChanged()); assertFalse(update.getQuantityChanged()); assertEquals(new Long("-1"), stackDerivedPool.getQuantity()); } }