diff options
Diffstat (limited to 'controlloop/common/database/src')
8 files changed, 775 insertions, 191 deletions
diff --git a/controlloop/common/database/src/main/java/org/onap/policy/database/operationshistory/CountRecentOperationsPip.java b/controlloop/common/database/src/main/java/org/onap/policy/database/operationshistory/CountRecentOperationsPip.java index 1f73ed3ce..7b6f13611 100644 --- a/controlloop/common/database/src/main/java/org/onap/policy/database/operationshistory/CountRecentOperationsPip.java +++ b/controlloop/common/database/src/main/java/org/onap/policy/database/operationshistory/CountRecentOperationsPip.java @@ -27,11 +27,12 @@ import com.att.research.xacml.api.pip.PIPRequest; import com.att.research.xacml.api.pip.PIPResponse; import com.att.research.xacml.std.pip.StdMutablePIPResponse; import com.att.research.xacml.std.pip.StdPIPResponse; -import com.google.common.base.Strings; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.Properties; -import javax.persistence.Persistence; +import java.util.Set; import org.onap.policy.database.ToscaDictionary; import org.onap.policy.database.std.StdOnapPip; import org.slf4j.Logger; @@ -42,6 +43,9 @@ public class CountRecentOperationsPip extends StdOnapPip { public static final String ISSUER_NAME = "count-recent-operations"; private static Logger logger = LoggerFactory.getLogger(CountRecentOperationsPip.class); + private static final Set<String> TIME_WINDOW_SCALES = Collections + .unmodifiableSet(new HashSet<>(Arrays.asList("minute", "hour", "day", "week", "month", "year"))); + public CountRecentOperationsPip() { super(); } @@ -53,25 +57,7 @@ public class CountRecentOperationsPip extends StdOnapPip { @Override public void configure(String id, Properties properties) throws PIPException { - super.configure(id, properties); - // - // Create our entity manager - // - em = null; - try { - // - // In case there are any overloaded properties for the JPA - // - Properties emProperties = new Properties(properties); - // - // Create the entity manager factory - // - em = Persistence.createEntityManagerFactory( - properties.getProperty(ISSUER_NAME + ".persistenceunit"), - emProperties).createEntityManager(); - } catch (Exception e) { - logger.error("Persistence failed {} operations history db {}", e.getLocalizedMessage(), e); - } + super.configure(id, properties, ISSUER_NAME); } /** @@ -85,23 +71,11 @@ public class CountRecentOperationsPip extends StdOnapPip { public PIPResponse getAttributes(PIPRequest pipRequest, PIPFinder pipFinder) throws PIPException { logger.debug("getAttributes requesting attribute {} of type {} for issuer {}", pipRequest.getAttributeId(), pipRequest.getDataTypeId(), pipRequest.getIssuer()); - // - // Determine if the issuer is correct - // - if (Strings.isNullOrEmpty(pipRequest.getIssuer())) { - logger.debug("issuer is null - returning empty response"); - // - // We only respond to ourself as the issuer - // - return StdPIPResponse.PIP_RESPONSE_EMPTY; - } - if (! pipRequest.getIssuer().startsWith(ToscaDictionary.GUARD_ISSUER_PREFIX)) { - logger.debug("Issuer does not start with guard"); - // - // We only respond to ourself as the issuer - // + + if (isRequestInvalid(pipRequest)) { return StdPIPResponse.PIP_RESPONSE_EMPTY; } + // // Parse out the issuer which denotes the time window // Eg: any-prefix:tw:10:minute @@ -156,12 +130,7 @@ public class CountRecentOperationsPip extends StdOnapPip { // // Compute the time window // - if (! "minute".equalsIgnoreCase(timeWindowScale) - && ! "hour".equalsIgnoreCase(timeWindowScale) - && ! "day".equalsIgnoreCase(timeWindowScale) - && ! "week".equalsIgnoreCase(timeWindowScale) - && ! "month".equalsIgnoreCase(timeWindowScale) - && ! "year".equalsIgnoreCase(timeWindowScale)) { + if (! TIME_WINDOW_SCALES.contains(timeWindowScale.toLowerCase())) { // // Unsupported // @@ -197,7 +166,7 @@ public class CountRecentOperationsPip extends StdOnapPip { .setParameter(4, timeWindowScale) .setParameter(5, timeWindowVal * -1) .getSingleResult(); - } catch (Exception e) { + } catch (RuntimeException e) { logger.error("Named query failed ", e); } // @@ -209,10 +178,10 @@ public class CountRecentOperationsPip extends StdOnapPip { // logger.info("operations query returned {}", result); // - // Should get back a long + // Should get back a number // - if (result instanceof Long) { - return ((Long) result).intValue(); + if (result instanceof Number) { + return ((Number) result).intValue(); } // // We shouldn't really get this result, but just diff --git a/controlloop/common/database/src/main/java/org/onap/policy/database/operationshistory/GetOperationOutcomePip.java b/controlloop/common/database/src/main/java/org/onap/policy/database/operationshistory/GetOperationOutcomePip.java index 20c8f028a..5a0db0501 100644 --- a/controlloop/common/database/src/main/java/org/onap/policy/database/operationshistory/GetOperationOutcomePip.java +++ b/controlloop/common/database/src/main/java/org/onap/policy/database/operationshistory/GetOperationOutcomePip.java @@ -25,12 +25,10 @@ import com.att.research.xacml.api.pip.PIPRequest; import com.att.research.xacml.api.pip.PIPResponse; import com.att.research.xacml.std.pip.StdMutablePIPResponse; import com.att.research.xacml.std.pip.StdPIPResponse; -import com.google.common.base.Strings; import java.util.Arrays; import java.util.Collection; import java.util.Properties; import javax.persistence.NoResultException; -import javax.persistence.Persistence; import org.onap.policy.database.ToscaDictionary; import org.onap.policy.database.std.StdOnapPip; import org.slf4j.Logger; @@ -52,25 +50,7 @@ public class GetOperationOutcomePip extends StdOnapPip { @Override public void configure(String id, Properties properties) throws PIPException { - super.configure(id, properties); - // - // Create our entity manager - // - em = null; - try { - // - // In case there are any overloaded properties for the JPA - // - Properties emProperties = new Properties(properties); - // - // Create the entity manager factory - // - em = Persistence.createEntityManagerFactory( - properties.getProperty(ISSUER_NAME + ".persistenceunit"), - emProperties).createEntityManager(); - } catch (Exception e) { - logger.error("Persistence failed {} operations history db {}", e.getLocalizedMessage(), e); - } + super.configure(id, properties, ISSUER_NAME); } /** @@ -84,31 +64,28 @@ public class GetOperationOutcomePip extends StdOnapPip { public PIPResponse getAttributes(PIPRequest pipRequest, PIPFinder pipFinder) throws PIPException { logger.debug("getAttributes requesting attribute {} of type {} for issuer {}", pipRequest.getAttributeId(), pipRequest.getDataTypeId(), pipRequest.getIssuer()); - // - // Determine if the issuer is correct - // - if (Strings.isNullOrEmpty(pipRequest.getIssuer())) { - logger.debug("issuer is null - returning empty response"); - // - // We only respond to ourself as the issuer - // - return StdPIPResponse.PIP_RESPONSE_EMPTY; - } - if (! pipRequest.getIssuer().startsWith(ToscaDictionary.GUARD_ISSUER_PREFIX)) { - logger.debug("Issuer does not start with guard"); - // - // We only respond to ourself as the issuer - // + + if (isRequestInvalid(pipRequest)) { return StdPIPResponse.PIP_RESPONSE_EMPTY; } + // // Parse out the issuer which denotes the time window // Eg: any-prefix:clname:some-controlloop-name // String[] s1 = pipRequest.getIssuer().split("clname:"); String clname = s1[1]; - String target = null; - target = getTarget(pipFinder); + String target = getTarget(pipFinder); + // + // Sanity check + // + if (target == null) { + // + // See if we have all the values + // + logger.error("missing attributes return empty"); + return StdPIPResponse.PIP_RESPONSE_EMPTY; + } logger.debug("Going to query DB about: clname={}, target={}", clname, target); String outcome = doDatabaseQuery(clname, target); diff --git a/controlloop/common/database/src/main/java/org/onap/policy/database/std/StdOnapPip.java b/controlloop/common/database/src/main/java/org/onap/policy/database/std/StdOnapPip.java index a94727371..1416b3ef9 100644 --- a/controlloop/common/database/src/main/java/org/onap/policy/database/std/StdOnapPip.java +++ b/controlloop/common/database/src/main/java/org/onap/policy/database/std/StdOnapPip.java @@ -39,6 +39,8 @@ import java.util.Collections; import java.util.Iterator; import java.util.Properties; import javax.persistence.EntityManager; +import javax.persistence.Persistence; +import org.apache.commons.lang3.StringUtils; import org.onap.policy.database.ToscaDictionary; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,11 +76,65 @@ public abstract class StdOnapPip extends StdConfigurableEngine { return Collections.emptyList(); } - @Override - public void configure(String id, Properties properties) throws PIPException { + /** + * Configures this object and initializes {@link #em}. + * + * @param id name of this engine + * @param properties configuration properties + * @param issuerName name of this issuer, used to identify the persistence unit + * @throws PIPException if an error occurs + */ + protected void configure(String id, Properties properties, String issuerName) throws PIPException { super.configure(id, properties); logger.debug("Configuring historyDb PIP {}", properties); this.properties = properties; + + // + // Create our entity manager + // + em = null; + try { + // + // In case there are any overloaded properties for the JPA + // + Properties emProperties = new Properties(properties); + // + // Create the entity manager factory + // + em = Persistence.createEntityManagerFactory( + properties.getProperty(issuerName + ".persistenceunit"), + emProperties).createEntityManager(); + } catch (Exception e) { + logger.error("Persistence failed {} operations history db {}", e.getLocalizedMessage(), e); + } + } + + /** + * Determines if a request is valid. + * + * @param pipRequest request to validate + * @return {@code true} if the request is <i>NOT</i> valid, {@code false} if it is + */ + protected boolean isRequestInvalid(PIPRequest pipRequest) { + // + // Determine if the issuer is correct + // + if (StringUtils.isBlank(pipRequest.getIssuer())) { + logger.debug("issuer is null - returning empty response"); + // + // We only respond to ourself as the issuer + // + return true; + } + if (! pipRequest.getIssuer().startsWith(ToscaDictionary.GUARD_ISSUER_PREFIX)) { + logger.debug("Issuer does not start with guard"); + // + // We only respond to ourself as the issuer + // + return true; + } + + return false; } protected String getActor(PIPFinder pipFinder) { @@ -150,14 +206,16 @@ public abstract class StdOnapPip extends StdConfigurableEngine { } protected String findFirstAttributeValue(PIPResponse pipResponse) { - for (Attribute attribute: pipResponse.getAttributes()) { - Iterator<AttributeValue<String>> iterAttributeValues = attribute.findValues(DataTypes.DT_STRING); - if (iterAttributeValues != null) { - while (iterAttributeValues.hasNext()) { - String value = iterAttributeValues.next().getValue(); - if (value != null) { - return value; - } + for (Attribute attribute : pipResponse.getAttributes()) { + Iterator<AttributeValue<String>> iterAttributeValues = attribute.findValues(DataTypes.DT_STRING); + if (iterAttributeValues == null) { + continue; + } + + while (iterAttributeValues.hasNext()) { + String value = iterAttributeValues.next().getValue(); + if (value != null) { + return value; } } } @@ -165,31 +223,25 @@ public abstract class StdOnapPip extends StdConfigurableEngine { } protected void addIntegerAttribute(StdMutablePIPResponse stdPipResponse, Identifier category, - Identifier attributeId, int value, PIPRequest pipRequest) { - AttributeValue<BigInteger> attributeValue = null; + Identifier attributeId, int value, PIPRequest pipRequest) { try { - attributeValue = DataTypes.DT_INTEGER.createAttributeValue(value); + AttributeValue<BigInteger> attributeValue = DataTypes.DT_INTEGER.createAttributeValue(value); + stdPipResponse.addAttribute(new StdMutableAttribute(category, attributeId, attributeValue, + pipRequest.getIssuer(), false)); } catch (Exception e) { logger.error("Failed to convert {} to integer {}", value, e); } - if (attributeValue != null) { - stdPipResponse.addAttribute(new StdMutableAttribute(category, attributeId, attributeValue, - pipRequest.getIssuer(), false)); - } } protected void addStringAttribute(StdMutablePIPResponse stdPipResponse, Identifier category, Identifier attributeId, - String value, PIPRequest pipRequest) { - AttributeValue<String> attributeValue = null; + String value, PIPRequest pipRequest) { try { - attributeValue = DataTypes.DT_STRING.createAttributeValue(value); + AttributeValue<String> attributeValue = DataTypes.DT_STRING.createAttributeValue(value); + stdPipResponse.addAttribute(new StdMutableAttribute(category, attributeId, attributeValue, + pipRequest.getIssuer(), false)); } catch (Exception ex) { logger.error("Failed to convert {} to an AttributeValue<String>", value, ex); } - if (attributeValue != null) { - stdPipResponse.addAttribute(new StdMutableAttribute(category, attributeId, attributeValue, - pipRequest.getIssuer(), false)); - } } } diff --git a/controlloop/common/database/src/test/java/org/onap/policy/database/operationshistory/CountRecentOperationsPipTest.java b/controlloop/common/database/src/test/java/org/onap/policy/database/operationshistory/CountRecentOperationsPipTest.java index e3cb17fd5..66b412028 100644 --- a/controlloop/common/database/src/test/java/org/onap/policy/database/operationshistory/CountRecentOperationsPipTest.java +++ b/controlloop/common/database/src/test/java/org/onap/policy/database/operationshistory/CountRecentOperationsPipTest.java @@ -19,31 +19,54 @@ package org.onap.policy.database.operationshistory; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import com.att.research.xacml.api.Attribute; +import com.att.research.xacml.api.AttributeValue; +import com.att.research.xacml.api.XACML3; +import com.att.research.xacml.api.pip.PIPException; +import com.att.research.xacml.api.pip.PIPFinder; +import com.att.research.xacml.api.pip.PIPRequest; +import com.att.research.xacml.api.pip.PIPResponse; +import com.att.research.xacml.std.pip.StdPIPResponse; import java.io.FileInputStream; -import java.lang.reflect.Method; import java.sql.Date; import java.time.Instant; - +import java.util.Collection; import java.util.Properties; import java.util.UUID; - import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; - import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.onap.policy.database.ToscaDictionary; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CountRecentOperationsPipTest { + private static final String ID = "issuer"; private static final Logger LOGGER = LoggerFactory.getLogger(CountRecentOperationsPipTest.class); - private static CountRecentOperationsPip pipEngine; + private static final String ISSUER = ToscaDictionary.GUARD_ISSUER_PREFIX + "-my-issuer:tw:1:HOUR"; + private static final String ACTOR = "Controller"; + private static final String RECIPE = "operationA"; + private static final String TARGET = "vnf-1"; + private static final String EXPECTED_EXCEPTION = "expected exception"; + + private static MyPip pipEngine; + private static Properties properties; + private static EntityManagerFactory emf; private static EntityManager em; + private PIPRequest req; + /** * Create an instance of our engine and also the persistence * factory. @@ -51,23 +74,23 @@ public class CountRecentOperationsPipTest { * @throws Exception connectivity issues */ @BeforeClass - public static void setup() throws Exception { + public static void setUpBeforeClass() throws Exception { LOGGER.info("Setting up PIP Testing"); // // Create instance // - pipEngine = new CountRecentOperationsPip(); + pipEngine = new MyPip(); // // Load our test properties to use // - Properties properties = new Properties(); + properties = new Properties(); try (FileInputStream is = new FileInputStream("src/test/resources/test.properties")) { properties.load(is); } // // Configure it using properties // - pipEngine.configure("issuer", properties); + pipEngine.configure(ID, properties); LOGGER.info("PIP configured now creating our entity manager"); LOGGER.info("properties {}", properties); // @@ -75,12 +98,18 @@ public class CountRecentOperationsPipTest { // String persistenceUnit = CountRecentOperationsPip.ISSUER_NAME + ".persistenceunit"; LOGGER.info("persistenceunit {}", persistenceUnit); - em = Persistence.createEntityManagerFactory(properties.getProperty(persistenceUnit), properties) - .createEntityManager(); + emf = Persistence.createEntityManagerFactory(properties.getProperty(persistenceUnit), properties); + em = emf.createEntityManager(); // // // - LOGGER.info("Configured own entity manager", em.toString()); + LOGGER.info("Configured own entity manager"); + } + + @Before + public void setUp() { + req = mock(PIPRequest.class); + when(req.getIssuer()).thenReturn(ISSUER); } private Dbao createEntry(String cl, String target, String outcome) { @@ -91,8 +120,8 @@ public class CountRecentOperationsPipTest { newEntry.setClosedLoopName(cl); newEntry.setTarget(target); newEntry.setOutcome(outcome); - newEntry.setActor("Controller"); - newEntry.setOperation("operationA"); + newEntry.setActor(ACTOR); + newEntry.setOperation(RECIPE); newEntry.setStarttime(Date.from(Instant.now().minusMillis(20000))); newEntry.setEndtime(Date.from(Instant.now())); newEntry.setRequestId(UUID.randomUUID().toString()); @@ -100,35 +129,64 @@ public class CountRecentOperationsPipTest { } @Test - public void testGetCountFromDb() throws Exception { - // - // Use reflection to run getCountFromDB - // - Method method = CountRecentOperationsPip.class.getDeclaredMethod("doDatabaseQuery", - String.class, - String.class, - String.class, - int.class, - String.class); - method.setAccessible(true); - // - // create entry - // - Dbao newEntry = createEntry("cl-foobar-1", "vnf-1", "SUCCESS"); - // - // Test pipEngine - // - int count = (int) method.invoke(pipEngine, newEntry.getActor(), newEntry.getOperation(), newEntry.getTarget(), - 1, "HOUR"); + public void testAttributesRequired() { + assertEquals(3, pipEngine.attributesRequired().size()); + } + + @Test + public void testGetAttributes_InvalidRequestInfo() throws PIPException { + // invalid request - null issuer + when(req.getIssuer()).thenReturn(null); + assertEquals(StdPIPResponse.PIP_RESPONSE_EMPTY, pipEngine.getAttributes(req, null)); + + /* + * Make a valid issuer in the request, for subsequent tests. + */ + when(req.getIssuer()).thenReturn(ISSUER); + + // null actor + MyPip pip = new MyPip() { + @Override + protected String getActor(PIPFinder pipFinder) { + return null; + } + }; + pip.configure(ID, properties); + assertEquals(StdPIPResponse.PIP_RESPONSE_EMPTY, pip.getAttributes(req, null)); + + // null recipe + pip = new MyPip() { + @Override + protected String getRecipe(PIPFinder pipFinder) { + return null; + } + }; + pip.configure(ID, properties); + assertEquals(StdPIPResponse.PIP_RESPONSE_EMPTY, pip.getAttributes(req, null)); + + // null target + pip = new MyPip() { + @Override + protected String getTarget(PIPFinder pipFinder) { + return null; + } + }; + pip.configure(ID, properties); + assertEquals(StdPIPResponse.PIP_RESPONSE_EMPTY, pip.getAttributes(req, null)); + } + + @Test + public void testDoDatabaseQuery() throws Exception { + // // No entries yet // - assertEquals(0, count); + assertEquals(0, getCount(pipEngine.getAttributes(req, null))); // // Add entry // em.getTransaction().begin(); - em.persist(newEntry); + em.persist(createEntry("cl-foobar-1", TARGET, "SUCCESS")); em.getTransaction().commit(); // // Directly check ground truth @@ -137,14 +195,67 @@ public class CountRecentOperationsPipTest { .setParameter(1, 1); LOGGER.info("{} entries", queryCount.getSingleResult()); // - // Test pipEngine - // - count = (int) method.invoke(pipEngine, newEntry.getActor(), newEntry.getOperation(), newEntry.getTarget(), - 1, "HOUR"); - // // Should count 1 entry now // - assertEquals(1, count); + assertEquals(1, getCount(pipEngine.getAttributes(req, null))); + } + + @Test + public void testDoDatabaseQuery_InvalidTimeWindow() throws Exception { + when(req.getIssuer()).thenReturn(ISSUER + "invalid time window"); + + assertEquals(-1, getCount(pipEngine.getAttributes(req, null))); + } + + @Test + public void testDoDatabaseQuery_NullEm() throws Exception { + assertEquals(-1, getCount(new MyPip().getAttributes(req, null))); + } + + @Test + public void testDoDatabaseQuery_EmException() throws Exception { + MyPip pip = new MyPip() { + @Override + public void configure(String id, Properties properties) throws PIPException { + em = mock(EntityManager.class); + when(em.createNativeQuery(any())).thenThrow(new RuntimeException(EXPECTED_EXCEPTION)); + } + }; + pip.configure(ID, properties); + + assertEquals(-1, getCount(pip.getAttributes(req, null))); + } + + @Test + public void testDoDatabaseQuery_NonNumeric() throws Exception { + MyPip pip = new MyPip() { + @Override + public void configure(String id, Properties properties) throws PIPException { + em = mock(EntityManager.class); + Query query = mock(Query.class); + when(em.createNativeQuery(any())).thenReturn(query); + when(query.setParameter(anyInt(), any())).thenReturn(query); + when(query.getSingleResult()).thenReturn("200"); + } + }; + pip.configure(ID, properties); + + assertEquals(200, getCount(pip.getAttributes(req, null))); + } + + private int getCount(PIPResponse resp) { + Collection<Attribute> attrs = resp.getAttributes(); + assertEquals(1, attrs.size()); + + Attribute attr = attrs.iterator().next(); + assertEquals(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, attr.getCategory()); + assertEquals(ToscaDictionary.ID_RESOURCE_GUARD_OPERATIONCOUNT, attr.getAttributeId()); + + Collection<AttributeValue<?>> values = attr.getValues(); + assertEquals(1, values.size()); + + AttributeValue<?> value = values.iterator().next(); + return ((Number) value.getValue()).intValue(); } /** @@ -152,9 +263,26 @@ public class CountRecentOperationsPipTest { */ @AfterClass public static void cleanup() { - if (em != null) { - em.close(); + if (emf != null) { + emf.close(); } } + private static class MyPip extends CountRecentOperationsPip { + + @Override + protected String getActor(PIPFinder pipFinder) { + return ACTOR; + } + + @Override + protected String getRecipe(PIPFinder pipFinder) { + return RECIPE; + } + + @Override + protected String getTarget(PIPFinder pipFinder) { + return TARGET; + } + } } diff --git a/controlloop/common/database/src/test/java/org/onap/policy/database/operationshistory/GetOperationOutcomePipTest.java b/controlloop/common/database/src/test/java/org/onap/policy/database/operationshistory/GetOperationOutcomePipTest.java index 7e7258c5f..5ab850722 100644 --- a/controlloop/common/database/src/test/java/org/onap/policy/database/operationshistory/GetOperationOutcomePipTest.java +++ b/controlloop/common/database/src/test/java/org/onap/policy/database/operationshistory/GetOperationOutcomePipTest.java @@ -19,27 +19,57 @@ package org.onap.policy.database.operationshistory; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import com.att.research.xacml.api.Attribute; +import com.att.research.xacml.api.AttributeValue; +import com.att.research.xacml.api.XACML3; +import com.att.research.xacml.api.pip.PIPException; +import com.att.research.xacml.api.pip.PIPFinder; +import com.att.research.xacml.api.pip.PIPRequest; +import com.att.research.xacml.api.pip.PIPResponse; +import com.att.research.xacml.std.pip.StdPIPResponse; import java.io.FileInputStream; -import java.lang.reflect.Method; import java.sql.Date; import java.time.Instant; +import java.util.Collection; import java.util.Properties; import java.util.UUID; import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.NoResultException; import javax.persistence.Persistence; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.onap.policy.database.ToscaDictionary; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GetOperationOutcomePipTest { + private static final String ID = "issuer"; + private static final String TEST_CL1 = "testcl1"; + private static final String TEST_TARGET1 = "testtarget1"; + private static final String TEST_TARGET2 = "testtarget2"; + private static final String ACTOR = "Controller"; + private static final String RECIPE = "operationA"; + private static final String EXPECTED_EXCEPTION = "expected exception"; + private static final String ISSUER_PREFIX = ToscaDictionary.GUARD_ISSUER_PREFIX + "-my-issuer:clname:"; + private static final String ISSUER = ISSUER_PREFIX + TEST_CL1; private static final Logger LOGGER = LoggerFactory.getLogger(GetOperationOutcomePipTest.class); - private static GetOperationOutcomePip pipEngine; + private static MyPip pipEngine; + private static Properties properties; + + private static EntityManagerFactory emf; private static EntityManager em; + private PIPRequest req; + /** * Create an instance of our engine and also the persistence * factory. @@ -47,23 +77,23 @@ public class GetOperationOutcomePipTest { * @throws Exception connectivity issues */ @BeforeClass - public static void setup() throws Exception { + public static void setUpBeforeClass() throws Exception { LOGGER.info("Setting up PIP Testing"); // // Create instance // - pipEngine = new GetOperationOutcomePip(); + pipEngine = new MyPip(TEST_TARGET1); // // Load our test properties to use // - Properties properties = new Properties(); + properties = new Properties(); try (FileInputStream is = new FileInputStream("src/test/resources/test.properties")) { properties.load(is); } // // Configure it using properties // - pipEngine.configure("issuer", properties); + pipEngine.configure(ID, properties); LOGGER.info("PIP configured now creating our entity manager"); LOGGER.info("properties {}", properties); // @@ -71,14 +101,20 @@ public class GetOperationOutcomePipTest { // String persistenceUnit = GetOperationOutcomePip.ISSUER_NAME + ".persistenceunit"; LOGGER.info("persistenceunit {}", persistenceUnit); - em = Persistence.createEntityManagerFactory(properties.getProperty(persistenceUnit), properties) - .createEntityManager(); + emf = Persistence.createEntityManagerFactory(properties.getProperty(persistenceUnit), properties); + em = emf.createEntityManager(); // // // LOGGER.info("Configured own entity manager", em.toString()); } + @Before + public void setUp() { + req = mock(PIPRequest.class); + when(req.getIssuer()).thenReturn(ISSUER); + } + private void insertEntry(String cl, String target, String outcome) { // // Create entry @@ -87,8 +123,8 @@ public class GetOperationOutcomePipTest { newEntry.setClosedLoopName(cl); newEntry.setTarget(target); newEntry.setOutcome(outcome); - newEntry.setActor("Controller"); - newEntry.setOperation("operationA"); + newEntry.setActor(ACTOR); + newEntry.setOperation(RECIPE); newEntry.setStarttime(Date.from(Instant.now().minusMillis(20000))); newEntry.setEndtime(Date.from(Instant.now())); newEntry.setRequestId(UUID.randomUUID().toString()); @@ -102,43 +138,104 @@ public class GetOperationOutcomePipTest { @Test - public void testGetOutcomeFromDb() throws Exception { - // - // Use reflection to run getCountFromDB - // - Method method = GetOperationOutcomePip.class.getDeclaredMethod("doDatabaseQuery", - String.class, - String.class); - method.setAccessible(true); + public void testAttributesRequired() { + assertEquals(1, pipEngine.attributesRequired().size()); + } + + @Test + public void testGetAttributes_InvalidRequestInfo() throws PIPException { + // invalid request - null issuer + when(req.getIssuer()).thenReturn(null); + assertEquals(StdPIPResponse.PIP_RESPONSE_EMPTY, pipEngine.getAttributes(req, null)); + + /* + * Make a valid issuer in the request, for subsequent tests. + */ + when(req.getIssuer()).thenReturn(ISSUER); + + // null target + MyPip pip = new MyPip(null); + pip.configure(ID, properties); + assertEquals(StdPIPResponse.PIP_RESPONSE_EMPTY, pip.getAttributes(req, null)); + } + + @Test + public void testDoDatabaseQuery() throws Exception { // // Insert entry // - insertEntry("testcl1", "testtarget1", "1"); - // - // Test pipEngine - // - String outcome = (String) method.invoke(pipEngine, "testcl1", "testtarget1"); + insertEntry(TEST_CL1, TEST_TARGET1, "1"); // // outcome should be "1" // - assertEquals("1", outcome); + assertEquals("1", getOutcome(pipEngine.getAttributes(req, null))); // // Insert more entries // - insertEntry("testcl1", "testtarget1", "2"); - insertEntry("testcl2", "testtarget2", "3"); - insertEntry("testcl1", "testtarget2", "4"); + insertEntry(TEST_CL1, TEST_TARGET1, "2"); + insertEntry("testcl2", TEST_TARGET2, "3"); + insertEntry(TEST_CL1, TEST_TARGET2, "4"); // // Test pipEngine // - outcome = (String) method.invoke(pipEngine, "testcl1", "testtarget1"); - assertEquals("2", outcome); + assertEquals("2", getOutcome(TEST_CL1, TEST_TARGET1)); - outcome = (String) method.invoke(pipEngine, "testcl2", "testtarget2"); - assertEquals("3", outcome); + assertEquals("3", getOutcome("testcl2", TEST_TARGET2)); - outcome = (String) method.invoke(pipEngine, "testcl1", "testtarget2"); - assertEquals("4", outcome); + assertEquals("4", getOutcome(TEST_CL1, TEST_TARGET2)); + } + + @Test + public void testDoDatabaseQuery_NoResult() throws Exception { + MyPip pip = new MyPip(TEST_TARGET1) { + @Override + public void configure(String id, Properties properties) throws PIPException { + em = mock(EntityManager.class); + when(em.createQuery(anyString())).thenThrow(new NoResultException()); + } + }; + pip.configure(ID, properties); + + assertNull(getOutcome(pip.getAttributes(req, null))); + } + + @Test + public void testDoDatabaseQuery_EmException() throws Exception { + MyPip pip = new MyPip(TEST_TARGET1) { + @Override + public void configure(String id, Properties properties) throws PIPException { + em = mock(EntityManager.class); + when(em.createQuery(anyString())).thenThrow(new RuntimeException(EXPECTED_EXCEPTION)); + } + }; + pip.configure(ID, properties); + + assertEquals(null, getOutcome(pip.getAttributes(req, null))); + } + + private String getOutcome(String clname, String target) throws PIPException { + req = mock(PIPRequest.class); + when(req.getIssuer()).thenReturn(ISSUER_PREFIX + clname); + + MyPip pip = new MyPip(target); + pip.configure(ID, properties); + + return getOutcome(pip.getAttributes(req, null)); + } + + private String getOutcome(PIPResponse resp) { + Collection<Attribute> attrs = resp.getAttributes(); + assertEquals(1, attrs.size()); + + Attribute attr = attrs.iterator().next(); + assertEquals(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, attr.getCategory()); + assertEquals(ToscaDictionary.ID_RESOURCE_GUARD_OPERATIONOUTCOME, attr.getAttributeId()); + + Collection<AttributeValue<?>> values = attr.getValues(); + assertEquals(1, values.size()); + + AttributeValue<?> value = values.iterator().next(); + return (String) value.getValue(); } /** @@ -146,9 +243,31 @@ public class GetOperationOutcomePipTest { */ @AfterClass public static void cleanup() { - if (em != null) { - em.close(); + if (emf != null) { + emf.close(); } } + private static class MyPip extends GetOperationOutcomePip { + private String target; + + public MyPip(String target) { + this.target = target; + } + + @Override + protected String getActor(PIPFinder pipFinder) { + return ACTOR; + } + + @Override + protected String getRecipe(PIPFinder pipFinder) { + return RECIPE; + } + + @Override + protected String getTarget(PIPFinder pipFinder) { + return target; + } + } } diff --git a/controlloop/common/database/src/test/java/org/onap/policy/database/std/DbaoTest.java b/controlloop/common/database/src/test/java/org/onap/policy/database/std/DbaoTest.java new file mode 100644 index 000000000..43c6157cf --- /dev/null +++ b/controlloop/common/database/src/test/java/org/onap/policy/database/std/DbaoTest.java @@ -0,0 +1,70 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.database.std; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Date; +import org.junit.Test; +import org.onap.policy.database.operationshistory.Dbao; + +public class DbaoTest { + + @Test + public void test() { + Dbao dao = new Dbao(); + + dao.setActor("my-actor"); + dao.setClosedLoopName("cl-name"); + Date endDate = new Date(); + dao.setEndtime(endDate); + dao.setId(100L); + dao.setMessage("my-message"); + dao.setOperation("my-operation"); + dao.setOutcome("my-outcome"); + dao.setRequestId("my-request"); + Date startDate = new Date(endDate.getTime() - 1); + dao.setStarttime(startDate); + dao.setSubrequestId("my-sub"); + dao.setTarget("my-target"); + + assertEquals("my-actor", dao.getActor()); + assertEquals("cl-name", dao.getClosedLoopName()); + assertEquals(endDate, dao.getEndtime()); + assertEquals(100L, dao.getId().longValue()); + assertEquals("my-message", dao.getMessage()); + assertEquals("my-operation", dao.getOperation()); + assertEquals("my-outcome", dao.getOutcome()); + assertEquals("my-request", dao.getRequestId()); + assertEquals(startDate, dao.getStarttime()); + assertEquals("my-sub", dao.getSubrequestId()); + assertEquals("my-target", dao.getTarget()); + + assertTrue(dao.toString().startsWith("Dbao")); + + int hc = dao.hashCode(); + dao.setId(101L); + assertTrue(hc != dao.hashCode()); + + assertTrue(dao.equals(dao)); + assertFalse(dao.equals(new Dbao())); + } +} diff --git a/controlloop/common/database/src/test/java/org/onap/policy/database/std/StdOnapPipTest.java b/controlloop/common/database/src/test/java/org/onap/policy/database/std/StdOnapPipTest.java new file mode 100644 index 000000000..de186c41d --- /dev/null +++ b/controlloop/common/database/src/test/java/org/onap/policy/database/std/StdOnapPipTest.java @@ -0,0 +1,268 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.database.std; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.att.research.xacml.api.Attribute; +import com.att.research.xacml.api.AttributeValue; +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.Status; +import com.att.research.xacml.api.pip.PIPException; +import com.att.research.xacml.api.pip.PIPFinder; +import com.att.research.xacml.api.pip.PIPRequest; +import com.att.research.xacml.api.pip.PIPResponse; +import com.att.research.xacml.std.datatypes.DataTypes; +import com.att.research.xacml.std.pip.StdMutablePIPResponse; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import org.junit.Before; +import org.junit.Test; +import org.onap.policy.database.ToscaDictionary; + +public class StdOnapPipTest { + private static final String EXPECTED_EXCEPTION = "expected exception"; + private static final String A_VALUE = "a-value"; + private static final String ISSUER = ToscaDictionary.GUARD_ISSUER_PREFIX + "-my-issuer"; + + private MyPip pip; + private PIPRequest req; + private PIPFinder finder; + private StdMutablePIPResponse resp; + + /** + * Initializes mocks and populates {@link #pip}. + */ + @Before + public void setUp() { + req = mock(PIPRequest.class); + finder = mock(PIPFinder.class); + resp = new StdMutablePIPResponse(); + + when(req.getIssuer()).thenReturn(ISSUER); + + pip = new MyPip(); + } + + @Test + public void testAttributesProvided() { + assertTrue(pip.attributesProvided().isEmpty()); + } + + @Test + public void testIsRequestInvalid() { + // valid issuer + when(req.getIssuer()).thenReturn(ISSUER); + assertFalse(pip.isRequestInvalid(req)); + + // invalid issuer + when(req.getIssuer()).thenReturn("bogus-issuer"); + assertTrue(pip.isRequestInvalid(req)); + + // null issuer + when(req.getIssuer()).thenReturn(null); + assertTrue(pip.isRequestInvalid(req)); + } + + @Test + public void testGetActor() { + testGetArbitraryAttribute(StdOnapPip.PIP_REQUEST_ACTOR, pip2 -> pip2.getActor(finder)); + } + + @Test + public void testGetRecipe() { + testGetArbitraryAttribute(StdOnapPip.PIP_REQUEST_RECIPE, pip2 -> pip2.getRecipe(finder)); + } + + @Test + public void testGetTarget() { + testGetArbitraryAttribute(StdOnapPip.PIP_REQUEST_TARGET, pip2 -> pip2.getTarget(finder)); + } + + private void testGetArbitraryAttribute(PIPRequest request, Function<StdOnapPip, String> getter) { + // target found + pip = new MyPip() { + @Override + protected PIPResponse getAttribute(PIPRequest pipRequest, PIPFinder pipFinder) { + return resp; + } + + @Override + protected String findFirstAttributeValue(PIPResponse pipResponse) { + return A_VALUE; + } + }; + + pip = spy(pip); + + assertEquals(A_VALUE, getter.apply(pip)); + verify(pip).getAttribute(request, finder); + verify(pip).findFirstAttributeValue(resp); + + + // not found + pip = new MyPip() { + @Override + protected PIPResponse getAttribute(PIPRequest pipRequest, PIPFinder pipFinder) { + return null; + } + + @Override + protected String findFirstAttributeValue(PIPResponse pipResponse) { + return A_VALUE; + } + }; + + pip = spy(pip); + + assertNull(getter.apply(pip)); + verify(pip).getAttribute(request, finder); + verify(pip, never()).findFirstAttributeValue(resp); + } + + @Test + public void testGetAttribute() throws PIPException { + when(finder.getMatchingAttributes(req, pip)).thenReturn(resp); + + Status status = mock(Status.class); + Identifier ident = mock(Identifier.class); + + when(ident.stringValue()).thenReturn("my-attr-id"); + when(req.getAttributeId()).thenReturn(ident); + + // status != OK + resp.setStatus(status); + when(status.isOk()).thenReturn(false); + assertNull(pip.getAttribute(req, finder)); + + // status OK, empty attributes + resp.setStatus(status); + when(status.isOk()).thenReturn(true); + assertNull(pip.getAttribute(req, finder)); + + // status OK, has attributes + resp.setStatus(status); + when(status.isOk()).thenReturn(true); + resp.setAttributes(Arrays.asList(mock(Attribute.class))); + assertSame(resp, pip.getAttribute(req, finder)); + + // null status, has attributes + resp.setStatus(null); + resp.setAttributes(Arrays.asList(mock(Attribute.class))); + assertSame(resp, pip.getAttribute(req, finder)); + + // with exception + when(finder.getMatchingAttributes(req, pip)).thenThrow(new PIPException()); + assertNull(pip.getAttribute(req, finder)); + } + + @Test + public void testFindFirstAttributeValue() { + + // no attributes + resp.setAttributes(Collections.emptyList()); + assertNull(pip.findFirstAttributeValue(resp)); + + // attribute that returns null + Attribute attr = mock(Attribute.class); + resp.setAttributes(Arrays.asList(attr, attr)); + assertNull(pip.findFirstAttributeValue(resp)); + + // attribute that returns a list of null values + Attribute attr2 = mock(Attribute.class); + resp.setAttributes(Arrays.asList(attr, attr2)); + List<AttributeValue<String>> lst = Arrays.asList(makeAttr(null), makeAttr(null)); + when(attr.findValues(DataTypes.DT_STRING)).thenReturn(lst.iterator()); + assertNull(pip.findFirstAttributeValue(resp)); + + // non-null value in the middle of the list + lst = Arrays.asList(makeAttr(null), makeAttr(A_VALUE), makeAttr(null)); + when(attr.findValues(DataTypes.DT_STRING)).thenReturn(lst.iterator()); + assertEquals(A_VALUE, pip.findFirstAttributeValue(resp)); + } + + private AttributeValue<String> makeAttr(String value) { + @SuppressWarnings("unchecked") + AttributeValue<String> attrval = mock(AttributeValue.class); + + when(attrval.getValue()).thenReturn(value); + + return attrval; + } + + @Test + public void testAddIntegerAttribute() { + resp = spy(resp); + + Identifier cat = mock(Identifier.class); + Identifier attrid = mock(Identifier.class); + + pip.addIntegerAttribute(resp, cat, attrid, 100, req); + + verify(resp).addAttribute(any()); + + // try with exception + doThrow(new RuntimeException(EXPECTED_EXCEPTION)).when(resp).addAttribute(any()); + pip.addIntegerAttribute(resp, cat, attrid, 100, req); + } + + @Test + public void testAddStringAttribute() { + resp = spy(resp); + + Identifier cat = mock(Identifier.class); + Identifier attrid = mock(Identifier.class); + + pip.addStringAttribute(resp, cat, attrid, A_VALUE, req); + + verify(resp).addAttribute(any()); + + // try with exception + doThrow(new RuntimeException(EXPECTED_EXCEPTION)).when(resp).addAttribute(any()); + pip.addStringAttribute(resp, cat, attrid, A_VALUE, req); + } + + private class MyPip extends StdOnapPip { + + @Override + public Collection<PIPRequest> attributesRequired() { + return Collections.emptyList(); + } + + @Override + public PIPResponse getAttributes(PIPRequest pipRequest, PIPFinder pipFinder) throws PIPException { + return null; + } + + } +} diff --git a/controlloop/common/database/src/test/resources/META-INF/persistence.xml b/controlloop/common/database/src/test/resources/META-INF/persistence.xml index 0c49d94e2..202850d96 100644 --- a/controlloop/common/database/src/test/resources/META-INF/persistence.xml +++ b/controlloop/common/database/src/test/resources/META-INF/persistence.xml @@ -19,7 +19,8 @@ ============LICENSE_END========================================================= --> -<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_1_0.xsd" version="1.0"> +<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_1_0.xsd" version="1.0"> <persistence-unit name="PipEngineTest" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> @@ -29,13 +30,13 @@ <properties> <property name="eclipselink.ddl-generation" value="create-tables" /> <property name="eclipselink.logging.level" value="FINE" /> - <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> + <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" /> <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" /> <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:testdb;DATABASE_TO_UPPER=FALSE" /> <property name="javax.persistence.jdbc.user" value="policy" /> <property name="javax.persistence.jdbc.password" value="P01icY" /> - <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/> - <property name="javax.persistence.schema-generation.create-source" value="metadata"/> + <property name="javax.persistence.schema-generation.database.action" value="drop-and-create" /> + <property name="javax.persistence.schema-generation.create-source" value="metadata" /> </properties> </persistence-unit> |