/** * */ package net.frontlinesms.data.repository.hibernate; import java.util.Collection; import java.util.List; import net.frontlinesms.junit.HibernateTestCase; import net.frontlinesms.data.DuplicateKeyException; import net.frontlinesms.data.domain.Contact; import net.frontlinesms.data.domain.Group; import net.frontlinesms.data.domain.Keyword; import net.frontlinesms.data.domain.KeywordAction; import net.frontlinesms.data.repository.ContactDao; import net.frontlinesms.data.repository.GroupDao; import net.frontlinesms.data.repository.GroupMembershipDao; import net.frontlinesms.data.repository.KeywordActionDao; import net.frontlinesms.data.repository.KeywordDao; import net.frontlinesms.events.EventBus; import static org.mockito.Mockito.*; import org.springframework.beans.factory.annotation.Required; /** * Test class for {@link HibernateGroupDao} * @author Alex Anderson <alex@frontlinesms.com> * @author Morgan Belkadi <morgan@frontlinesms.com> */ public class HibernateGroupDaoTest extends HibernateTestCase { //> PROPERTIES // /** Embedded shared test code from InMemoryDownloadDaoTest - Removes need to CopyAndPaste shared test code */ // private final ReusableGroupDaoTest test = new ReusableGroupDaoTest() { /* nothing needs to be added */ }; private ContactDao contactDao; //@Autowired() private GroupDao groupDao; private GroupMembershipDao groupMembershipDao; private KeywordDao keywordDao; private KeywordActionDao keywordActionDao; //> TEST METHODS public void testDelete() throws DuplicateKeyException { Group myGroup = createGroup("My Group"), myGroup2 = createGroup("My Group 2"); Contact george = createContact("George", myGroup), abitbol = createContact("Abitbol", myGroup2); // Delete final int BEFORE_DELETE_GROUP_COUNT = 2; groupDao.deleteGroup(myGroup, false); assertTrue("Group has not been deleted. Expected: <1> group left, but was: <" + groupDao.getGroupCount() + ">", groupDao.getGroupCount() < BEFORE_DELETE_GROUP_COUNT); assertNull("Wrong group has been deleted", groupDao.getGroupByPath(myGroup.getPath())); assertTrue("Contact has been deleted and wasn't supposed to", contactDao.getFromMsisdn(george.getPhoneNumber()).equals(george)); final int AFTER_FIRST_DELETE_GROUP_COUNT = 1; groupDao.deleteGroup(myGroup2, true); assertTrue("Group has not been deleted. Expected: <0> groups left, but was: <" + groupDao.getGroupCount() + ">", groupDao.getGroupCount() < AFTER_FIRST_DELETE_GROUP_COUNT); assertNull("Wrong group has been deleted", groupDao.getGroupByPath(myGroup.getPath())); assertNull("Contact has not been deleted and was supposed to", contactDao.getFromMsisdn(abitbol.getPhoneNumber())); } /** Check that deleting a group's child is successful, and does not affect the group itself. * @throws DuplicateKeyException */ public void testChildDelete() throws DuplicateKeyException { Group parent = new Group(getRootGroup(), "parent"); groupDao.saveGroup(parent); Group child = new Group(parent, "child"); groupDao.saveGroup(child); Collection<Group> fetchedChildren = groupDao.getChildGroups(parent); assertEquals(1, fetchedChildren.size()); assertEquals(child, fetchedChildren.toArray(new Group[0])[0]); // Delete the child groupDao.deleteGroup(child, false); // Confirm that the child has been removed fetchedChildren = groupDao.getChildGroups(parent); assertEquals(0, fetchedChildren.size()); Group fetchedChild = groupDao.getGroupByPath("/parent/child"); assertNull(fetchedChild); // Confirm that the original group is still present assertEquals(parent, groupDao.getGroupByPath("/parent")); } /** Check that deleting a group's parent has the expected effect on the child groups * @throws DuplicateKeyException */ public void testParentDelete() throws DuplicateKeyException { Group parent = new Group(getRootGroup(), "parent"); groupDao.saveGroup(parent); Group child = new Group(parent, "child"); groupDao.saveGroup(child); // Confirm that the parent and child are both present in the database Group fetchedParent = groupDao.getGroupByPath("/parent"); assertEquals(parent, fetchedParent); Group fetchedChild = groupDao.getGroupByPath("/parent/child"); assertEquals(child, fetchedChild); mock(EventBus.class); // Delete the parent group groupDao.deleteGroup(parent, false); //verify(mockEventBus, new Times(2)).notifyObservers(any(FrontlineEventNotification.class)); // Confirm that the parent and child are both deleted from the database assertNull(groupDao.getGroupByPath("/parent")); assertNull(groupDao.getGroupByPath("/parent/child")); } /** Check that 2 lists cannot be created with the same path * @throws DuplicateKeyException */ public void testDuplicateKeys() throws DuplicateKeyException { // Test 2 groups at root level Group group1 = new Group(getRootGroup(), "name"); groupDao.saveGroup(group1); Group group2 = new Group(getRootGroup(), "name"); try { groupDao.saveGroup(group2); fail("Second attempt to create identical group should have failed."); } catch(DuplicateKeyException ex) {/* expected */} // Test 2 child groups Group child1 = new Group(group1, "child"); groupDao.saveGroup(child1); Group child2 = new Group(group1, "child"); try { groupDao.saveGroup(child2); fail("Second attempt to create identical group should have failed."); } catch(DuplicateKeyException ex) {/* expected */} } /** * Check that saving and deleting a group with special SQL characters in its name * functions correctly. This should verify that our HQL is not open to SQL Injection * issues. * @throws DuplicateKeyException */ public void testSqlInjection() throws DuplicateKeyException { Group myGroup = new Group(getRootGroup(), "Dan's Team"); groupDao.saveGroup(myGroup); groupDao.deleteGroup(myGroup, false); } /** * Test that deleting a group that has an associated {@link KeywordAction} is successful. * @throws DuplicateKeyException */ public void testDeleteWithKeywordAction() throws DuplicateKeyException { Group myGroup = new Group(getRootGroup(), "My Group"); groupDao.saveGroup(myGroup); Keyword keyword = new Keyword("key", "A test keyword"); keywordDao.saveKeyword(keyword); KeywordAction action = KeywordAction.createGroupJoinAction(keyword, myGroup, 0, Long.MAX_VALUE); keywordActionDao.saveKeywordAction(action); groupDao.deleteGroup(myGroup, false); } /** * Test deleting a group which has members. * @throws DuplicateKeyException */ public void testDeleteWithMembers() throws DuplicateKeyException { Group myGroup = new Group(getRootGroup(), "My Group"); groupDao.saveGroup(myGroup); Contact contact = new Contact("Alice", "123465789", null, null, null, true); contactDao.saveContact(contact); groupMembershipDao.addMember(myGroup, contact); groupDao.deleteGroup(myGroup, false); } public void testGroupOrder() throws DuplicateKeyException { Group barcelona = createGroup(getRootGroup(), "FC Barcelona"); Group lyon = createGroup(getRootGroup(), "Olympique Lyonnais"); Group munchen = createGroup(getRootGroup(), "Bayern Munchen"); Group milan = createGroup(getRootGroup(), "Inter Milan"); this.groupDao.saveGroup(barcelona); this.groupDao.saveGroup(lyon); this.groupDao.saveGroup(munchen); this.groupDao.saveGroup(milan); Group[] expectedResult = new Group[] { munchen, barcelona, milan, lyon }; // We test the order of the "All Groups" request this.assertEquals(expectedResult, this.groupDao.getAllGroups()); // We test the order of the child groups of Root this.assertEquals(expectedResult, this.groupDao.getChildGroups(getRootGroup())); } /** * Check if both arrays passed in parameter are identical, order included * @param expectedResult * @param actualResult */ private void assertEquals(Group[] expectedResult, List<Group> actualResult) { assertEquals(expectedResult.length, actualResult.size()); for (int i = 0 ; i < expectedResult.length ; ++i) { assertEquals(expectedResult[i], actualResult.get(i)); } } private Contact createContact(String name, Group... groups) throws DuplicateKeyException { // Make a phone number up that should be unique String phoneNumber = Integer.toString(name.hashCode()); return createContact(name, phoneNumber, groups); } /** Creates a contact with specified name and phone number. * @throws DuplicateKeyException */ private Contact createContact(String name, String phoneNumber, Group... groups) throws DuplicateKeyException { // Make a phone number up that should be unique Contact contact = new Contact(name, phoneNumber, null, null, null, true); this.contactDao.saveContact(contact); for(Group group : groups) { this.groupMembershipDao.addMember(group, contact); } return contact; } /** Create a top-level group. */ private Group createGroup(String name, Contact... contacts) throws DuplicateKeyException { return createGroup(getRootGroup(), name, contacts); } /** Create a group with specified parent. */ private Group createGroup(Group parent, String name, Contact... contacts) throws DuplicateKeyException { Group group = new Group(parent, name); this.groupDao.saveGroup(group); for(Contact contact : contacts) { this.groupMembershipDao.addMember(group, contact); } return group; } //> TEST SETUP/TEARDOWN //> ACCESSORS /** @param groupDao The DAO to use for the test. */ @Required public void setGroupDao(GroupDao groupDao) { this.groupDao = groupDao; } @Required public void setContactDao(ContactDao contactDao) { this.contactDao = contactDao; } @Required public void setGroupMembershipDao(GroupMembershipDao groupMembershipDao) { this.groupMembershipDao = groupMembershipDao; } @Required public void setKeywordActionDao(KeywordActionDao keywordActionDao) { this.keywordActionDao = keywordActionDao; } @Required public void setKeywordDao(KeywordDao keywordDao) { this.keywordDao = keywordDao; } private Group getRootGroup() { return new Group(null, null); } }