diff options
author | Kevin McKiou <km097d@att.com> | 2017-08-22 16:08:06 -0500 |
---|---|---|
committer | Jorge Hernandez <jh1730@att.com> | 2017-08-22 22:04:57 +0000 |
commit | 36cf73f8313cbd1baac4bc41565bee23690fc152 (patch) | |
tree | a2fc708564a866403163d084eb781bbe9bc5cbf6 /feature-session-persistence/src/test/java | |
parent | 9356ffd28172c6cd0ea3ce80c4a49f4d8525bb30 (diff) |
Add feature-session-persistence
This commit adds the feature-session-persistence module
which will persist drools session data to allow stateful
transactions which can persist across node restarts and
failovers. It also picks up recent changes to the master
branch to avoid merge conflicts.
Issue-ID: POLICY-133
Change-Id: Ifdcd8280ea6df07db79562f1b01fa90296a8b878
Signed-off-by: Kevin McKiou <km097d@att.com>
Diffstat (limited to 'feature-session-persistence/src/test/java')
6 files changed, 2039 insertions, 0 deletions
diff --git a/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/DroolsSessionEntityTest.java b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/DroolsSessionEntityTest.java new file mode 100644 index 00000000..c7fa8486 --- /dev/null +++ b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/DroolsSessionEntityTest.java @@ -0,0 +1,198 @@ +/*- + * ============LICENSE_START======================================================= + * feature-session-persistence + * ================================================================================ + * Copyright (C) 2017 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.drools.persistence; + +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.drools.persistence.DroolsSessionEntity; + +public class DroolsSessionEntityTest { + + @Test + public void testHashCode() { + DroolsSessionEntity e = makeEnt("mynameA", 1); + + DroolsSessionEntity e2 = makeEnt("mynameA", 2); + + // session id is not part of hash code + assertTrue(e.hashCode() == e2.hashCode()); + + // diff sess name + e2 = makeEnt("mynameB", 1); + assertTrue(e.hashCode() != e2.hashCode()); + } + + /** + * Ensures that hashCode() functions as expected when the getXxx methods + * are overridden. + */ + @Test + public void testHashCode_Subclass() { + DroolsSessionEntity e = makeEnt2("mynameA", 1); + + DroolsSessionEntity e2 = makeEnt("mynameA", 2); + + // session id is not part of hash code + assertTrue(e.hashCode() == e2.hashCode()); + + // diff sess name + e2 = makeEnt("mynameB", 1); + assertTrue(e.hashCode() != e2.hashCode()); + } + + @Test + public void testGetSessionName_testSetSessionName() { + DroolsSessionEntity e = makeEnt("mynameZ", 1); + + assertEquals("mynameZ", e.getSessionName()); + + e.setSessionName("another"); + assertEquals("another", e.getSessionName()); + + // others unchanged + assertEquals(1, e.getSessionId()); + } + + @Test + public void testGetSessionId_testSetSessionId() { + DroolsSessionEntity e = makeEnt("mynameA", 1); + + assertEquals(1, e.getSessionId()); + + e.setSessionId(20); + assertEquals(20, e.getSessionId()); + + // others unchanged + assertEquals("mynameA", e.getSessionName()); + } + + @Test + public void testGetCreatedDate_testSetCreatedDate_testGetUpdatedDate_testSetUpdatedDate() { + DroolsSessionEntity e = new DroolsSessionEntity(); + + Date crtdt = new Date(System.currentTimeMillis() - 100); + e.setCreatedDate(crtdt); + + Date updt = new Date(System.currentTimeMillis() - 200); + e.setUpdatedDate(updt); + + assertEquals(crtdt, e.getCreatedDate()); + assertEquals(updt, e.getUpdatedDate()); + } + + @Test + public void testEqualsObject() { + DroolsSessionEntity e = makeEnt("mynameA", 1); + + // reflexive + assertTrue(e.equals(e)); + + DroolsSessionEntity e2 = makeEnt("mynameA", 2); + + // session id is not part of hash code + assertTrue(e.equals(e2)); + assertTrue(e.equals(e2)); + + // diff sess name + e2 = makeEnt("mynameB", 1); + assertFalse(e.equals(e2)); + assertFalse(e.equals(e2)); + } + + /** + * Ensures that equals() functions as expected when the getXxx methods + * are overridden. + */ + @Test + public void testEqualsObject_Subclass() { + DroolsSessionEntity e = makeEnt2("mynameA", 1); + + // reflexive + assertTrue(e.equals(e)); + + DroolsSessionEntity e2 = makeEnt("mynameA", 2); + + // session id is not part of hash code + assertTrue(e.equals(e2)); + assertTrue(e.equals(e2)); + + // diff sess name + e2 = makeEnt("mynameB", 1); + assertFalse(e.equals(e2)); + assertFalse(e.equals(e2)); + } + + @Test + public void testToString() { + DroolsSessionEntity e = makeEnt("mynameA", 23); + + assertEquals("{name=mynameA, id=23}", e.toString()); + } + + /** + * Makes a session Entity. The parameters are stored into the Entity + * object via the setXxx methods. + * @param sessnm session name + * @param sessid session id + * @return a new session Entity + */ + private DroolsSessionEntity makeEnt(String sessnm, long sessid) { + + DroolsSessionEntity e = new DroolsSessionEntity(); + + e.setSessionName(sessnm); + e.setSessionId(sessid); + + return e; + } + + /** + * Makes a session Entity that overrides the getXxx methods. The + * parameters that are provided are returned by the overridden methods, + * but they are <i>not</i> stored into the Entity object via the setXxx + * methods. + * @param sessnm session name + * @param sessid session id + * @return a new session Entity + */ + @SuppressWarnings("serial") + private DroolsSessionEntity makeEnt2(String sessnm, long sessid) { + + return new DroolsSessionEntity() { + + @Override + public String getSessionName() { + return sessnm; + } + + @Override + public long getSessionId() { + return sessid; + } + }; + } + +} diff --git a/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/EntityMgrCloserTest.java b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/EntityMgrCloserTest.java new file mode 100644 index 00000000..7350a7f7 --- /dev/null +++ b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/EntityMgrCloserTest.java @@ -0,0 +1,100 @@ +/*- + * ============LICENSE_START======================================================= + * feature-session-persistence + * ================================================================================ + * Copyright (C) 2017 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.drools.persistence; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import javax.persistence.EntityManager; + +import org.junit.Before; +import org.junit.Test; +import org.onap.policy.drools.persistence.EntityMgrCloser; + +public class EntityMgrCloserTest { + + private EntityManager mgr; + + + @Before + public void setUp() throws Exception { + mgr = mock(EntityManager.class); + } + + + /** + * Verifies that the constructor does not do anything extra before + * being closed. + */ + @Test + public void testEntityMgrCloser() { + EntityMgrCloser c = new EntityMgrCloser(mgr); + + // verify not closed yet + verify(mgr, never()).close(); + + c.close(); + } + + /** + * Verifies that the manager gets closed when close() is invoked. + */ + @Test + public void testClose() { + EntityMgrCloser c = new EntityMgrCloser(mgr); + + c.close(); + + // should be closed + verify(mgr).close(); + } + + /** + * Ensures that the manager gets closed when "try" block exits normally. + */ + @Test + public void testClose_TryWithoutExcept() { + try(EntityMgrCloser c = new EntityMgrCloser(mgr)) { + + } + + verify(mgr).close(); + } + + /** + * Ensures that the manager gets closed when "try" block throws an + * exception. + */ + @Test + public void testClose_TryWithExcept() { + try { + try(EntityMgrCloser c = new EntityMgrCloser(mgr)) { + throw new Exception("expected exception"); + } + + } catch (Exception e) { + } + + verify(mgr).close(); + } + +} diff --git a/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/EntityMgrTransTest.java b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/EntityMgrTransTest.java new file mode 100644 index 00000000..0165b1e4 --- /dev/null +++ b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/EntityMgrTransTest.java @@ -0,0 +1,232 @@ +/*- + * ============LICENSE_START======================================================= + * feature-session-persistence + * ================================================================================ + * Copyright (C) 2017 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.drools.persistence; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; + +import org.junit.Before; +import org.junit.Test; +import org.onap.policy.drools.persistence.EntityMgrTrans; + +public class EntityMgrTransTest { + + private EntityTransaction trans; + private EntityManager mgr; + + @Before + public void setUp() throws Exception { + trans = mock(EntityTransaction.class); + mgr = mock(EntityManager.class); + + when(mgr.getTransaction()).thenReturn(trans); + } + + + + /** + * Verifies that the constructor starts a transaction, but does not do + * anything extra before being closed. + */ + @Test + public void testEntityMgrTrans() { + EntityMgrTrans t = new EntityMgrTrans(mgr); + + // verify that transaction was started + verify(trans).begin(); + + // verify not closed, committed, or rolled back yet + verify(trans, never()).commit(); + verify(trans, never()).rollback(); + verify(mgr, never()).close(); + + t.close(); + } + + /** + * Verifies that the transaction is rolled back and the manager is + * closed when and a transaction is active. + */ + @Test + public void testClose_Active() { + EntityMgrTrans t = new EntityMgrTrans(mgr); + + when(trans.isActive()).thenReturn(true); + + t.close(); + + // closed and rolled back, but not committed + verify(trans, never()).commit(); + verify(trans).rollback(); + verify(mgr).close(); + } + + /** + * Verifies that the manager is closed, but that the transaction is + * <i>not</i> rolled back and when and no transaction is active. + */ + @Test + public void testClose_Inactive() { + EntityMgrTrans t = new EntityMgrTrans(mgr); + + when(trans.isActive()).thenReturn(false); + + t.close(); + + // closed, but not committed or rolled back + verify(mgr).close(); + verify(trans, never()).commit(); + verify(trans, never()).rollback(); + } + + /** + * Verifies that the manager is closed and the transaction rolled back + * when "try" block exits normally and a transaction is active. + */ + @Test + public void testClose_TryWithoutExcept_Active() { + when(trans.isActive()).thenReturn(true); + + try(EntityMgrTrans t = new EntityMgrTrans(mgr)) { + + } + + // closed and rolled back, but not committed + verify(trans, never()).commit(); + verify(trans).rollback(); + verify(mgr).close(); + } + + /** + * Verifies that the manager is closed, but that the transaction is + * <i>not</i> rolled back when "try" block exits normally and no + * transaction is active. + */ + @Test + public void testClose_TryWithoutExcept_Inactive() { + when(trans.isActive()).thenReturn(false); + + try(EntityMgrTrans t = new EntityMgrTrans(mgr)) { + + } + + // closed, but not rolled back or committed + verify(trans, never()).commit(); + verify(trans, never()).rollback(); + verify(mgr).close(); + } + + /** + * Verifies that the manager is closed and the transaction rolled back + * when "try" block throws an exception and a transaction is active. + */ + @Test + public void testClose_TryWithExcept_Active() { + when(trans.isActive()).thenReturn(true); + + try { + try(EntityMgrTrans t = new EntityMgrTrans(mgr)) { + throw new Exception("expected exception"); + } + + } catch (Exception e) { + } + + // closed and rolled back, but not committed + verify(trans, never()).commit(); + verify(trans).rollback(); + verify(mgr).close(); + } + + /** + * Verifies that the manager is closed, but that the transaction is + * <i>not</i> rolled back when "try" block throws an exception and no + * transaction is active. + */ + @Test + public void testClose_TryWithExcept_Inactive() { + when(trans.isActive()).thenReturn(false); + + try { + try(EntityMgrTrans t = new EntityMgrTrans(mgr)) { + throw new Exception("expected exception"); + } + + } catch (Exception e) { + } + + // closed, but not rolled back or committed + verify(trans, never()).commit(); + verify(trans, never()).rollback(); + verify(mgr).close(); + } + + /** + * Verifies that commit() only commits, and that the subsequent close() + * does not re-commit. + */ + @Test + public void testCommit() { + EntityMgrTrans t = new EntityMgrTrans(mgr); + + t.commit(); + + // committed, but not closed or rolled back + verify(trans).commit(); + verify(trans, never()).rollback(); + verify(mgr, never()).close(); + + // closed, but not re-committed + t.close(); + + verify(trans, times(1)).commit(); + verify(mgr).close(); + } + + /** + * Verifies that rollback() only rolls back, and that the subsequent + * close() does not re-roll back. + */ + @Test + public void testRollback() { + EntityMgrTrans t = new EntityMgrTrans(mgr); + + t.rollback(); + + // rolled back, but not closed or committed + verify(trans, never()).commit(); + verify(trans).rollback(); + verify(mgr, never()).close(); + + // closed, but not re-rolled back + t.close(); + + verify(trans, times(1)).rollback(); + verify(mgr).close(); + } + +} diff --git a/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/GenSchemaTest.java b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/GenSchemaTest.java new file mode 100644 index 00000000..b58c22c6 --- /dev/null +++ b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/GenSchemaTest.java @@ -0,0 +1,58 @@ +/*- + * ============LICENSE_START======================================================= + * feature-session-persistence + * ================================================================================ + * Copyright (C) 2017 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.drools.persistence; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; + +import org.junit.Test; + +/** + * Generates the schema DDL files. + */ +public class GenSchemaTest { + + private EntityManagerFactory emf; + + + /* + * This is a JUnit which is provided as a utility for producing a basic + * ddl schema file in the sql directory. + * + * To run this simple add @Test ahead of the method and then run this + * as a JUnit. + */ + public void generate() throws Exception { + Map<String, Object> propMap = new HashMap<>(); + + propMap.put("javax.persistence.jdbc.driver", "org.h2.Driver"); + propMap.put("javax.persistence.jdbc.url", + "jdbc:h2:mem:JpaDroolsSessionConnectorTest"); + + emf = Persistence.createEntityManagerFactory( + "schemaDroolsPU", propMap); + + emf.close(); + } +} diff --git a/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/JpaDroolsSessionConnectorTest.java b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/JpaDroolsSessionConnectorTest.java new file mode 100644 index 00000000..c16a1bbd --- /dev/null +++ b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/JpaDroolsSessionConnectorTest.java @@ -0,0 +1,160 @@ +/*- + * ============LICENSE_START======================================================= + * feature-session-persistence + * ================================================================================ + * Copyright (C) 2017 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.drools.persistence; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onap.policy.drools.persistence.DroolsSessionEntity; +import org.onap.policy.drools.persistence.EntityMgrTrans; +import org.onap.policy.drools.persistence.JpaDroolsSessionConnector; + +public class JpaDroolsSessionConnectorTest { + + private EntityManagerFactory emf; + private JpaDroolsSessionConnector conn; + + + @Before + public void setUp() throws Exception { + Map<String, Object> propMap = new HashMap<>(); + + propMap.put("javax.persistence.jdbc.driver", "org.h2.Driver"); + propMap.put("javax.persistence.jdbc.url", + "jdbc:h2:mem:JpaDroolsSessionConnectorTest"); + + emf = Persistence.createEntityManagerFactory( + "junitDroolsSessionEntityPU", propMap); + + conn = new JpaDroolsSessionConnector(emf); + } + + @After + public void tearDown() { + // this will cause the memory db to be dropped + emf.close(); + } + + @Test + public void testGet() { + /* + * Load up the DB with some data. + */ + + addSession("nameA", 10); + addSession("nameY", 20); + + + /* + * Now test the functionality. + */ + + // not found + assertNull( conn.get("unknown")); + + assertEquals("{name=nameA, id=10}", + conn.get("nameA").toString()); + + assertEquals("{name=nameY, id=20}", + conn.get("nameY").toString()); + } + + @Test + public void testReplace_Existing() { + addSession("nameA", 10); + + DroolsSessionEntity sess = + new DroolsSessionEntity("nameA", 30); + + conn.replace(sess); + + // id should be changed + assertEquals(sess.toString(), + conn.get("nameA").toString()); + } + + @Test + public void testReplace_New() { + DroolsSessionEntity sess = + new DroolsSessionEntity("nameA", 30); + + conn.replace(sess); + + assertEquals(sess.toString(), + conn.get("nameA").toString()); + } + + @Test + public void testAdd() { + DroolsSessionEntity sess = + new DroolsSessionEntity("nameA", 30); + + conn.replace(sess); + + assertEquals(sess.toString(), + conn.get("nameA").toString()); + } + + @Test + public void testUpdate() { + addSession("nameA", 10); + + DroolsSessionEntity sess = + new DroolsSessionEntity("nameA", 30); + + conn.replace(sess); + + // id should be changed + assertEquals("{name=nameA, id=30}", + conn.get("nameA").toString()); + } + + + /** + * Adds a session to the DB. + * @param sessnm session name + * @param sessid session id + */ + private void addSession(String sessnm, int sessid) { + EntityManager em = emf.createEntityManager(); + + try(EntityMgrTrans trans = new EntityMgrTrans(em)) { + DroolsSessionEntity ent = new DroolsSessionEntity(); + + ent.setSessionName(sessnm); + ent.setSessionId(sessid); + + em.persist(ent); + + trans.commit(); + } + } +} diff --git a/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/PersistenceFeatureTest.java b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/PersistenceFeatureTest.java new file mode 100644 index 00000000..e73031dd --- /dev/null +++ b/feature-session-persistence/src/test/java/org/onap/policy/drools/persistence/PersistenceFeatureTest.java @@ -0,0 +1,1291 @@ +/*- + * ============LICENSE_START======================================================= + * feature-session-persistence + * ================================================================================ + * Copyright (C) 2017 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.drools.persistence; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.net.UnknownHostException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import javax.persistence.EntityManagerFactory; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.kie.api.KieBase; +import org.kie.api.KieServices; +import org.kie.api.persistence.jpa.KieStoreServices; +import org.kie.api.runtime.Environment; +import org.kie.api.runtime.EnvironmentName; +import org.kie.api.runtime.KieContainer; +import org.kie.api.runtime.KieSession; +import org.kie.api.runtime.KieSessionConfiguration; +import org.mockito.ArgumentCaptor; +import org.onap.policy.drools.persistence.DroolsPersistenceProperties; +import org.onap.policy.drools.persistence.DroolsSession; +import org.onap.policy.drools.persistence.DroolsSessionConnector; +import org.onap.policy.drools.persistence.PersistenceFeature; +import org.onap.policy.drools.core.PolicyContainer; +import org.onap.policy.drools.core.PolicySession; +import org.onap.policy.drools.system.PolicyController; + +import bitronix.tm.BitronixTransactionManager; +import bitronix.tm.Configuration; +import bitronix.tm.resource.jdbc.PoolingDataSource; + +public class PersistenceFeatureTest { + + private static final String JDBC_DATASRC = "fake.datasource"; + private static final String JDBC_DRIVER = "fake.driver"; + private static final String JDBC_URL = "fake.url"; + private static final String JDBC_USER = "fake.user"; + private static final String JDBC_PASSWD = "fake.password"; + private static final String SRC_TEST_RESOURCES = "src/test/resources"; + + private static Properties stdprops; + + private DroolsSessionConnector jpa; + private DroolsSession sess; + private PoolingDataSource pds; + private KieSession kiesess; + private Properties dsprops; + private EntityManagerFactory emf; + private Connection conn; + private Properties props; + private KieServices kiesvc; + private Environment kieenv; + private KieSessionConfiguration kiecfg; + private KieBase kiebase; + private KieStoreServices kiestore; + private KieContainer kiecont; + private Configuration bitcfg; + private BitronixTransactionManager bittrans; + private PolicyController polctlr; + private PolicyContainer polcont; + private PolicySession polsess; + private PersistenceFeature.Factory fact; + + private PersistenceFeature feat; + + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + stdprops = new Properties(); + + stdprops.put(DroolsPersistenceProperties.DB_DATA_SOURCE, JDBC_DATASRC); + stdprops.put(DroolsPersistenceProperties.DB_DRIVER, JDBC_DRIVER); + stdprops.put(DroolsPersistenceProperties.DB_URL, JDBC_URL); + stdprops.put(DroolsPersistenceProperties.DB_USER, JDBC_USER); + stdprops.put(DroolsPersistenceProperties.DB_PWD, JDBC_PASSWD); + stdprops.put(DroolsPersistenceProperties.DB_SESSIONINFO_TIMEOUT, "50"); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + @Before + public void setUp() throws Exception { + jpa = mock(DroolsSessionConnector.class); + sess = mock(DroolsSession.class); + pds = mock(PoolingDataSource.class); + kiesess = mock(KieSession.class); + dsprops = new Properties(); + emf = null; + conn = null; + props = new Properties(); + kiesvc = mock(KieServices.class); + kieenv = mock(Environment.class); + kiecfg = mock(KieSessionConfiguration.class); + kiebase = mock(KieBase.class); + kiestore = mock(KieStoreServices.class); + kiecont = mock(KieContainer.class); + bitcfg = mock(Configuration.class); + bittrans = mock(BitronixTransactionManager.class); + polcont = mock(PolicyContainer.class); + polctlr = mock(PolicyController.class); + polsess = mock(PolicySession.class); + fact = mock(PersistenceFeature.Factory.class); + + feat = new PersistenceFeature(); + feat.setFactory(fact); + + props.putAll(stdprops); + + when(pds.getUniqueName()).thenReturn("myds"); + + when(fact.getKieServices()).thenReturn(kiesvc); + when(fact.getTransMgrConfig()).thenReturn(bitcfg); + when(fact.getTransMgr()).thenReturn(bittrans); + when(fact.loadProperties(anyString())).thenReturn(props); + + when(kiesvc.newEnvironment()).thenReturn(kieenv); + when(kiesvc.getStoreServices()).thenReturn(kiestore); + when(kiesvc.newKieSessionConfiguration()).thenReturn(kiecfg); + + when(polcont.getKieContainer()).thenReturn(kiecont); + + when(polsess.getPolicyContainer()).thenReturn(polcont); + + when(kiecont.getKieBase(anyString())).thenReturn(kiebase); + } + + @After + public void tearDown() { + // this will cause the in-memory test DB to be dropped + if(conn != null) { + try { conn.close(); } catch (SQLException e) { } + } + + if(emf != null) { + try { emf.close(); } catch (Exception e) { } + } + } + + @Test + public void testGetContainerAdjunct_New() throws Exception { + + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + // force getContainerAdjunct() to be invoked + feat.activatePolicySession(polcont, "myname", "mybase"); + + ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap = + ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class); + + verify(polcont, times(1)).setAdjunct(any(), adjcap.capture()); + + assertNotNull( adjcap.getValue()); + } + + @Test + public void testGetContainerAdjunct_Existing() throws Exception { + + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + // force getContainerAdjunct() to be invoked + feat.activatePolicySession(polcont, "myname", "mybase"); + + ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap = + ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class); + + verify(polcont, times(1)).setAdjunct(any(), adjcap.capture()); + + // return adjunct on next call + when(polcont.getAdjunct(any())).thenReturn( adjcap.getValue()); + + // force getContainerAdjunct() to be invoked again + setUpKie("myname2", 999L, true); + feat.activatePolicySession(polcont, "myname2", "mybase"); + + // ensure it isn't invoked again + verify(polcont, times(1)).setAdjunct(any(), any()); + } + + @Test + public void testGetSequenceNumber() { + assertEquals(1, feat.getSequenceNumber()); + } + + @Test + public void testGlobalInit() throws Exception { + when(fact.getHostName()).thenReturn("myhost"); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + // verify that various factory methods were invoked + verify(fact).getHostName(); + verify(fact).getKieServices(); + verify(fact).getTransMgrConfig(); + verify(fact).loadProperties("src/test/resources/feature-session-persistence.properties"); + + verify(bitcfg).setJournal(null); + verify(bitcfg).setServerId("myhost"); + } + + @Test + public void testActivatePolicySession() throws Exception { + PreparedStatement ps = mockDbConn(5); + setUpKie("myname", 999L, true); + + feat.globalInit(null, SRC_TEST_RESOURCES); + feat.beforeActivate(null); + + KieSession s = + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(kiestore).loadKieSession(anyLong(), any(), any(), any()); + verify(kiestore, never()).newKieSession(any(), any(), any()); + + assertEquals(s, kiesess); + + verify(ps).executeUpdate(); + + verify(kieenv, times(2)).set(anyString(), any()); + verify(pds).init(); + assertFalse( dsprops.isEmpty()); + + verify(jpa).get("myname"); + verify(jpa).replace(any()); + } + + @Test + public void testActivatePolicySession_NoPersistence() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + PreparedStatement ps = mockDbConn(5); + setUpKie("myname", 999L, true); + + props.remove("persistence.type"); + + feat.beforeStart(null); + + assertNull( feat.activatePolicySession(polcont, "myname", "mybase")); + + verify(ps, never()).executeUpdate(); + verify(kiestore, never()).loadKieSession(anyLong(), any(), any(), any()); + verify(kiestore, never()).newKieSession(any(), any(), any()); + } + + /** + * Verifies that a new KIE session is created when there is no existing + * session entity. + */ + @Test + public void testActivatePolicySession_New() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("noName", 999L, true); + + + KieSession s = + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(kiestore, never()).loadKieSession(anyLong(), any(), any(), any()); + verify(kiestore).newKieSession(any(), any(), any()); + + assertEquals(s, kiesess); + + verify(kieenv, times(2)).set(anyString(), any()); + verify(pds).init(); + assertFalse( dsprops.isEmpty()); + + verify(jpa).get("myname"); + verify(jpa).replace(any()); + } + + /** + * Verifies that a new KIE session is created when there KIE fails + * to load an existing session. + */ + @Test + public void testActivatePolicySession_LoadFailed() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, false); + + + KieSession s = + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(kiestore).loadKieSession(anyLong(), any(), any(), any()); + verify(kiestore).newKieSession(any(), any(), any()); + + assertEquals(s, kiesess); + + verify(kieenv, times(2)).set(anyString(), any()); + verify(pds).init(); + assertFalse( dsprops.isEmpty()); + + verify(jpa).get("myname"); + + ArgumentCaptor<DroolsSession> d = + ArgumentCaptor.forClass(DroolsSession.class); + verify(jpa).replace( d.capture()); + + assertEquals("myname", d.getValue().getSessionName()); + assertEquals(100L, d.getValue().getSessionId()); + } + + @Test + public void testConfigureKieEnv() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, false); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(kieenv, times(2)).set(any(), any()); + + verify(kieenv).set(EnvironmentName.ENTITY_MANAGER_FACTORY, emf); + verify(kieenv).set(EnvironmentName.TRANSACTION_MANAGER, bittrans); + } + + @Test + public void testInitDataSource() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, false); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + assertEquals(JDBC_URL, dsprops.getProperty("URL")); + assertEquals(JDBC_USER, dsprops.getProperty("user")); + assertEquals(JDBC_PASSWD, dsprops.getProperty("password")); + + verify(pds).setUniqueName("jdbc/BitronixJTADataSource/myname"); + verify(pds).setClassName(JDBC_DATASRC); + verify(pds).setMaxPoolSize(anyInt()); + verify(pds).setIsolationLevel("SERIALIZABLE"); + verify(pds).setAllowLocalTransactions(true); + verify(pds).init(); + } + + @Test + public void testLoadKieSession() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + + KieSession s = + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(kiestore).loadKieSession(999L, kiebase, kiecfg, kieenv); + verify(kiestore, never()).newKieSession(any(), any(), any()); + + assertEquals(s, kiesess); + } + + /* + * Verifies that loadKieSession() returns null (thus causing newKieSession() + * to be called) when an Exception occurs. + */ + @Test + public void testLoadKieSession_Ex() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, false); + + when(kiestore.loadKieSession(anyLong(), any(), any(), any())) + .thenThrow( new RuntimeException("expected exception")); + + + KieSession s = + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(kiestore).loadKieSession(anyLong(), any(), any(), any()); + verify(kiestore).newKieSession(any(), any(), any()); + + assertEquals(s, kiesess); + } + + @Test + public void testNewKieSession() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, false); + + + KieSession s = + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(kiestore).newKieSession(kiebase, null, kieenv); + + assertEquals(s, kiesess); + } + + @Test + public void testLoadDataSource_RepeatSameSession() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, false); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap = + ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class); + + verify(polcont).setAdjunct(any(), adjcap.capture()); + + when(polcont.getAdjunct(any())).thenReturn( adjcap.getValue()); + + // invoke it again + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(fact, times(1)).makePoolingDataSource(); + } + + @Test + public void testLoadDataSource_DiffSession() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, false); + feat.activatePolicySession(polcont, "myname", "mybase"); + + ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap = + ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class); + + verify(polcont).setAdjunct(any(), adjcap.capture()); + + when(polcont.getAdjunct(any())).thenReturn( adjcap.getValue()); + + setUpKie("myname2", 999L, false); + + // invoke it again + feat.activatePolicySession(polcont, "myname2", "mybase"); + + verify(fact, times(2)).makePoolingDataSource(); + } + + @Test + public void testDisposeKieSession() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap = + ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class); + + mockDbConn(5); + setUpKie("myname", 999L, false); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(pds, never()).close(); + verify(polcont).setAdjunct(any(), adjcap.capture()); + + when(polcont.getAdjunct(any())).thenReturn( adjcap.getValue()); + + feat.disposeKieSession(polsess); + + // call twice to ensure it isn't re-closed + feat.disposeKieSession(polsess); + + verify(pds, times(1)).close(); + } + + @Test + public void testDisposeKieSession_NoAdjunct() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + feat.disposeKieSession(polsess); + } + + @Test + public void testDisposeKieSession_NoPersistence() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap = + ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class); + + mockDbConn(5); + setUpKie("myname", 999L, false); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(pds, never()).close(); + verify(polcont).setAdjunct(any(), adjcap.capture()); + + when(polcont.getAdjunct(any())).thenReturn( adjcap.getValue()); + + // specify a session that was never loaded + when(polsess.getName()).thenReturn("anotherName"); + + feat.disposeKieSession(polsess); + + verify(pds, never()).close(); + } + + @Test + public void testDestroyKieSession() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap = + ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class); + + mockDbConn(5); + setUpKie("myname", 999L, false); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(pds, never()).close(); + verify(polcont).setAdjunct(any(), adjcap.capture()); + + when(polcont.getAdjunct(any())).thenReturn( adjcap.getValue()); + + feat.destroyKieSession(polsess); + + // call twice to ensure it isn't re-closed + feat.destroyKieSession(polsess); + + verify(pds, times(1)).close(); + } + + @Test + public void testDestroyKieSession_NoAdjunct() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + feat.destroyKieSession(polsess); + } + + @Test + public void testDestroyKieSession_NoPersistence() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap = + ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class); + + mockDbConn(5); + setUpKie("myname", 999L, false); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(pds, never()).close(); + verify(polcont).setAdjunct(any(), adjcap.capture()); + + when(polcont.getAdjunct(any())).thenReturn( adjcap.getValue()); + + // specify a session that was never loaded + when(polsess.getName()).thenReturn("anotherName"); + + feat.destroyKieSession(polsess); + + verify(pds, never()).close(); + } + + @Test + public void testAfterStart() { + assertFalse( feat.afterStart(null)); + } + + @Test + public void testBeforeStart() { + assertFalse( feat.beforeStart(null)); + } + + @Test + public void testBeforeShutdown() { + assertFalse( feat.beforeShutdown(null)); + } + + @Test + public void testAfterShutdown() { + assertFalse( feat.afterShutdown(null)); + } + + @Test + public void testBeforeConfigure() { + assertFalse( feat.beforeConfigure(null, null)); + } + + @Test + public void testAfterConfigure() { + assertFalse( feat.afterConfigure(null)); + } + + @Test + public void testBeforeActivate() { + assertFalse( feat.beforeActivate(null)); + } + + @Test + public void testAfterActivate() { + assertFalse( feat.afterActivate(null)); + } + + @Test + public void testBeforeDeactivate() { + assertFalse( feat.beforeDeactivate(null)); + } + + @Test + public void testAfterDeactivate() { + assertFalse( feat.afterDeactivate(null)); + } + + @Test + public void testBeforeStop() { + assertFalse( feat.beforeStop(null)); + } + + @Test + public void testAfterStop() { + assertFalse( feat.afterStop(null)); + } + + @Test + public void testBeforeLock() { + assertFalse( feat.beforeLock(null)); + } + + @Test + public void testAfterLock() { + assertFalse( feat.afterLock(null)); + } + + @Test + public void testBeforeUnlock() { + assertFalse( feat.beforeUnlock(null)); + } + + @Test + public void testAfterUnlock() { + assertFalse( feat.afterUnlock(null)); + } + + @Test + public void testGetPersistenceTimeout_Valid() throws Exception { + PreparedStatement s = mockDbConn(5); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + setUpKie("myname", 999L, true); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(s).executeUpdate(); + } + + @Test + public void testGetPersistenceTimeout_Missing() throws Exception { + + props.remove(DroolsPersistenceProperties.DB_SESSIONINFO_TIMEOUT); + + PreparedStatement s = mockDbConn(0); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + setUpKie("myname", 999L, true); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(s, never()).executeUpdate(); + } + + @Test + public void testGetPersistenceTimeout_Invalid() throws Exception { + props.setProperty(DroolsPersistenceProperties.DB_SESSIONINFO_TIMEOUT, "abc"); + PreparedStatement s = mockDbConn(0); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + setUpKie("myname", 999L, true); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(s, never()).executeUpdate(); + } + + @Test + public void testInitHostName() throws Exception { + when(fact.getHostName()).thenReturn("myhost"); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + verify(bitcfg).setServerId("myhost"); + } + + @Test(expected = RuntimeException.class) + public void testInitHostName_Ex() throws Exception { + when(fact.getHostName()) + .thenThrow( + new UnknownHostException("expected exception")); + + feat.globalInit(null, SRC_TEST_RESOURCES); + } + + @Test + public void testCleanUpSessionInfo() throws Exception { + setUpKie("myname", 999L, true); + + // use a real DB so we can verify that the "delete" works correctly + fact = new PartialFactory(); + feat.setFactory(fact); + + makeSessionInfoTbl(20000); + + + feat.globalInit(null, SRC_TEST_RESOURCES); + + feat.beforeStart(null); + feat.activatePolicySession(polcont, "myname", "mybase"); + + assertEquals("[1, 4, 5]", getSessions().toString()); + } + + @Test + public void testCleanUpSessionInfo_WithBeforeStart() throws Exception { + PreparedStatement s = mockDbConn(0); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + setUpKie("myname", 999L, true); + + // reset + feat.beforeStart(null); + + feat.activatePolicySession(polcont, "myname", "mybase"); + verify(s, times(1)).executeUpdate(); + + // should not clean-up again + feat.activatePolicySession(polcont, "myname", "mybase"); + feat.activatePolicySession(polcont, "myname", "mybase"); + verify(s, times(1)).executeUpdate(); + + + // reset + feat.beforeStart(null); + + feat.activatePolicySession(polcont, "myname", "mybase"); + verify(s, times(2)).executeUpdate(); + + // should not clean-up again + feat.activatePolicySession(polcont, "myname", "mybase"); + feat.activatePolicySession(polcont, "myname", "mybase"); + verify(s, times(2)).executeUpdate(); + } + + @Test + public void testCleanUpSessionInfo_WithBeforeActivate() throws Exception { + PreparedStatement s = mockDbConn(0); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + setUpKie("myname", 999L, true); + + // reset + feat.beforeActivate(null); + + feat.activatePolicySession(polcont, "myname", "mybase"); + verify(s, times(1)).executeUpdate(); + + // should not clean-up again + feat.activatePolicySession(polcont, "myname", "mybase"); + feat.activatePolicySession(polcont, "myname", "mybase"); + verify(s, times(1)).executeUpdate(); + + + // reset + feat.beforeActivate(null); + + feat.activatePolicySession(polcont, "myname", "mybase"); + verify(s, times(2)).executeUpdate(); + + // should not clean-up again + feat.activatePolicySession(polcont, "myname", "mybase"); + feat.activatePolicySession(polcont, "myname", "mybase"); + verify(s, times(2)).executeUpdate(); + } + + @Test + public void testCleanUpSessionInfo_NoTimeout() throws Exception { + + props.remove(DroolsPersistenceProperties.DB_SESSIONINFO_TIMEOUT); + + PreparedStatement s = mockDbConn(0); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + setUpKie("myname", 999L, true); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(s, never()).executeUpdate(); + } + + @Test + public void testCleanUpSessionInfo_NoUrl() throws Exception { + PreparedStatement s = mockDbConn(0); + + props.remove(DroolsPersistenceProperties.DB_URL); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + setUpKie("myname", 999L, true); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(s, never()).executeUpdate(); + } + + @Test + public void testCleanUpSessionInfo_NoUser() throws Exception { + PreparedStatement s = mockDbConn(0); + + props.remove(DroolsPersistenceProperties.DB_USER); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + setUpKie("myname", 999L, true); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(s, never()).executeUpdate(); + } + + @Test + public void testCleanUpSessionInfo_NoPassword() throws Exception { + PreparedStatement s = mockDbConn(0); + + props.remove(DroolsPersistenceProperties.DB_PWD); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + setUpKie("myname", 999L, true); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(s, never()).executeUpdate(); + } + + @Test + public void testCleanUpSessionInfo_SqlEx() throws Exception { + PreparedStatement s = mockDbConn(-1); + + feat.globalInit(null, SRC_TEST_RESOURCES); + + setUpKie("myname", 999L, true); + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(s).executeUpdate(); + } + + @Test + public void testGetDroolsSessionConnector() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + + feat.activatePolicySession(polcont, "myname", "mybase"); + + + ArgumentCaptor<Properties> propcap = + ArgumentCaptor.forClass(Properties.class); + + verify(fact).makeJpaConnector(anyString(), propcap.capture()); + + Properties p = propcap.getValue(); + assertNotNull(p); + + assertEquals(JDBC_DRIVER, + p.getProperty("javax.persistence.jdbc.driver")); + + assertEquals(JDBC_URL, + p.getProperty("javax.persistence.jdbc.url")); + + assertEquals(JDBC_USER, + p.getProperty("javax.persistence.jdbc.user")); + + assertEquals(JDBC_PASSWD, + p.getProperty("javax.persistence.jdbc.password")); + } + + @Test + public void testReplaceSession() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + ArgumentCaptor<DroolsSession> sesscap = + ArgumentCaptor.forClass(DroolsSession.class); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + + feat.activatePolicySession(polcont, "myname", "mybase"); + + verify(jpa).replace( sesscap.capture()); + + assertEquals("myname", sesscap.getValue().getSessionName()); + assertEquals(999L, sesscap.getValue().getSessionId()); + } + + @Test + public void testIsPersistenceEnabled_Auto() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + props.setProperty("persistence.type", "auto"); + + assertNotNull( feat.activatePolicySession(polcont, "myname", "mybase")); + } + + @Test + public void testIsPersistenceEnabled_Native() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + props.setProperty("persistence.type", "native"); + + assertNotNull( feat.activatePolicySession(polcont, "myname", "mybase")); + } + + @Test + public void testIsPersistenceEnabled_None() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + props.remove("persistence.type"); + + assertNull( feat.activatePolicySession(polcont, "myname", "mybase")); + } + + @Test + public void testGetProperties_Ex() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + when(fact.getPolicyContainer(polcont)) + .thenThrow( new IllegalArgumentException("expected exception")); + + assertNull( feat.activatePolicySession(polcont, "myname", "mybase")); + } + + @Test + public void testGetProperty_Specific() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + props.remove("persistence.type"); + props.setProperty("persistence.myname.type", "auto"); + + assertNotNull( feat.activatePolicySession(polcont, "myname", "mybase")); + } + + @Test + public void testGetProperty_Specific_None() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + props.remove("persistence.type"); + props.setProperty("persistence.xxx.type", "auto"); + + assertNull( feat.activatePolicySession(polcont, "myname", "mybase")); + } + + @Test + public void testGetProperty_Both_SpecificOn() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + props.setProperty("persistence.type", "other"); + props.setProperty("persistence.myname.type", "auto"); + + assertNotNull( feat.activatePolicySession(polcont, "myname", "mybase")); + } + + @Test + public void testGetProperty_Both_SpecificDisabledOff() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + props.setProperty("persistence.type", "auto"); + props.setProperty("persistence.myname.type", "other"); + + assertNull( feat.activatePolicySession(polcont, "myname", "mybase")); + } + + @Test + public void testGetProperty_None() throws Exception { + feat.globalInit(null, SRC_TEST_RESOURCES); + + mockDbConn(5); + setUpKie("myname", 999L, true); + + props.remove("persistence.type"); + + assertNull( feat.activatePolicySession(polcont, "myname", "mybase")); + } + + + /** + * Gets an ordered list of ids of the current SessionInfo records. + * @return ordered list of SessInfo IDs + * @throws SQLException + * @throws IOException + */ + private List<Integer> getSessions() throws SQLException, IOException { + attachDb(); + + ArrayList<Integer> lst = new ArrayList<>(5); + + try( + PreparedStatement stmt = conn.prepareStatement("SELECT id from sessioninfo order by id"); + ResultSet rs = stmt.executeQuery()) { + + while(rs.next()) { + lst.add( rs.getInt(1)); + } + } + + return lst; + } + + /** + * Sets up for doing invoking the newKieSession() method. + * @param sessnm name to which JPA should respond with a session + * @param sessid session id to be returned by the session + * @param loadOk {@code true} if loadKieSession() should return a + * value, {@code false} to return null + */ + private void setUpKie(String sessnm, long sessid, boolean loadOk) { + + when(fact.makeJpaConnector(any(), any())).thenReturn(jpa); + when(fact.makePoolingDataSource()).thenReturn(pds); + when(fact.getPolicyContainer(polcont)).thenReturn(polctlr); + + props.setProperty("persistence.type", "auto"); + + when(polctlr.getProperties()).thenReturn(props); + + when(jpa.get(sessnm)).thenReturn(sess); + + when(pds.getDriverProperties()).thenReturn(dsprops); + + when(sess.getSessionId()).thenReturn(sessid); + + when(polsess.getPolicyContainer()).thenReturn(polcont); + when(polsess.getName()).thenReturn(sessnm); + + if(loadOk) { + when(kiesess.getIdentifier()).thenReturn(sessid); + when(kiestore.loadKieSession(anyLong(), any(), any(), any())) + .thenReturn(kiesess); + + } else { + // use an alternate id for the new session + when(kiesess.getIdentifier()).thenReturn(100L); + when(kiestore.loadKieSession(anyLong(), any(), any(), any())) + .thenReturn(null); + } + + when(kiestore.newKieSession(any(), any(), any())).thenReturn(kiesess); + } + + /** + * Creates the SessionInfo DB table and populates it with some data. + * @param expMs number of milli-seconds for expired sessioninfo records + * @throws SQLException + * @throws IOException + */ + private void makeSessionInfoTbl(int expMs) + throws SQLException, IOException { + + attachDb(); + + try( + PreparedStatement stmt = conn.prepareStatement( + "CREATE TABLE sessioninfo(id int, lastmodificationdate timestamp)")) { + + stmt.executeUpdate(); + } + + try( + PreparedStatement stmt = conn.prepareStatement( + "INSERT into sessioninfo(id, lastmodificationdate) values(?, ?)")) { + + Timestamp ts; + + // current data + ts = new Timestamp( System.currentTimeMillis()); + stmt.setTimestamp(2, ts); + + stmt.setInt(1, 1); + stmt.executeUpdate(); + + stmt.setInt(1, 4); + stmt.executeUpdate(); + + stmt.setInt(1, 5); + stmt.executeUpdate(); + + // expired data + ts = new Timestamp( System.currentTimeMillis() - expMs); + stmt.setTimestamp(2, ts); + + stmt.setInt(1, 2); + stmt.executeUpdate(); + + stmt.setInt(1, 3); + stmt.executeUpdate(); + } + } + + /** + * Attaches {@link #conn} to the DB, if it isn't already attached. + * @throws SQLException + * @throws IOException if the property file cannot be read + */ + private void attachDb() throws SQLException, IOException { + if(conn == null) { + Properties p = loadDbProps(); + + conn = DriverManager.getConnection( + p.getProperty(DroolsPersistenceProperties.DB_URL), + p.getProperty(DroolsPersistenceProperties.DB_USER), + p.getProperty(DroolsPersistenceProperties.DB_PWD)); + conn.setAutoCommit(true); + } + } + + /** + * Loads the DB properties from the file, + * <i>feature-session-persistence.properties</i>. + * @return the properties that were loaded + * @throws IOException if the property file cannot be read + * @throws FileNotFoundException if the property file does not exist + */ + private Properties loadDbProps() + throws IOException, FileNotFoundException { + + Properties p = new Properties(); + + try(FileReader rdr = new FileReader( + "src/test/resources/feature-session-persistence.properties")) { + p.load(rdr); + } + + return p; + } + + /** + * Create a mock DB connection and statement. + * @param retval value to be returned when the statement is executed, + * or negative to throw an exception + * @return the statement that will be returned by the connection + * @throws SQLException + */ + private PreparedStatement mockDbConn(int retval) throws SQLException { + Connection c = mock(Connection.class); + PreparedStatement s = mock(PreparedStatement.class); + + when(fact.makeDbConnection(anyString(), anyString(), anyString())) + .thenReturn(c); + when(c.prepareStatement(anyString())).thenReturn(s); + + if(retval < 0) { + // should throw an exception + when(s.executeUpdate()) + .thenThrow( new SQLException("expected exception")); + + } else { + // should return the value + when(s.executeUpdate()).thenReturn(retval); + } + + return s; + } + + /** + * A partial factory, which exports a few of the real methods, but + * overrides the rest. + */ + private class PartialFactory extends PersistenceFeature.Factory { + + @Override + public PoolingDataSource makePoolingDataSource() { + return pds; + } + + @Override + public KieServices getKieServices() { + return kiesvc; + } + + @Override + public BitronixTransactionManager getTransMgr() { + return null; + } + + @Override + public EntityManagerFactory makeEntMgrFact(String pu, + Properties propMap) { + if(pu.equals("onapsessionsPU")) { + return null; + } + + return super.makeEntMgrFact("junitPersistenceFeaturePU", propMap); + } + + @Override + public PolicyController getPolicyContainer(PolicyContainer container) { + return polctlr; + } + + } +} |