/*
* Copyright (c) 2009-2010 Lockheed Martin Corporation
*
* 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.
*/
package org.eurekastreams.server.persistence;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import org.apache.commons.logging.Log;
import org.eurekastreams.commons.logging.LogFactory;
import org.eurekastreams.server.domain.Layout;
import org.eurekastreams.server.domain.Tab;
import org.eurekastreams.server.domain.TabGroup;
import org.eurekastreams.server.persistence.exceptions.TabDeletionException;
import org.eurekastreams.server.persistence.exceptions.TabUndeletionException;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* This class is responsible for testing the JPA Implementation of the TabGroup Mapper interface. The tests contained in
* here ensure proper interaction with the database.
*/
public class TabGroupMapperTest extends DomainEntityMapperTest
{
/**
* Log.
*/
Log log = LogFactory.make();
/**
* JpaTabGroupMapper - the system under test.
*/
@Autowired
private TabGroupMapper jpaTabGroupMapper;
/**
* JpaTabMapper - a mapper we need to help assert tab existence.
*/
@Autowired
private TabMapper jpaTabMapper;
/**
* Reset the deleted tab expiration before each test.
*/
@Before
public void setup()
{
jpaTabGroupMapper.setUndeleteTabWindowInMinutes(undeleteWindowInMinutes);
}
/**
* Test the DBUnit XML Dataset - Tabs.
*/
@Test
public void testDBUnitDatasetTabs()
{
final int expectedTabCount = 3;
TabGroup tabGroup = jpaTabGroupMapper.findById(fordsStartPageId);
List<Tab> tabs = tabGroup.getTabs();
assertEquals("Expected the DBUnit-loaded TabGroup with ID=" + fordsStartPageId + " to have " + expectedTabCount
+ "(non-deleted) tabs", expectedTabCount, tabs.size());
// Assert the order is 1,2,3
assertEquals("Expected Ford's first tab in his first TabGroup to be called 'Ford Tab 1' from DBUnit setup.",
"Ford Tab 1", tabs.get(0).getTabName());
assertEquals("Expected Ford's first tab in his first TabGroup to be called 'Ford Tab 2' from DBUnit setup.",
"Ford Tab 2", tabs.get(1).getTabName());
assertEquals("Expected Ford's first tab in his first TabGroup to be called 'Ford Tab 3' from DBUnit setup.",
"Ford Tab 3", tabs.get(2).getTabName());
}
/**
* Test the domain entity name of the mapper - used for parent class generic operations.
*/
@Test
public void testGetDomainEntityName()
{
assertEquals("Domain entity name should be 'TabGroup'", "TabGroup", jpaTabGroupMapper.getDomainEntityName());
}
/**
* Test persisting a start tabGroup.
*/
@Test
public void testInsert()
{
TabGroup p = new TabGroup();
jpaTabGroupMapper.insert(p);
assertTrue(p.getId() > 0);
}
/**
* Test inserting a tab group, then finding the user by ID when the object is still in object cache.
*/
@Test
public void testFindByIdWhenCached()
{
assertSame("When finding a Person by ID and that object exists in object cache, "
+ "expected the cached instance to be returned.", jpaTabGroupMapper.findById(fordsStartPageId),
jpaTabGroupMapper.findById(fordsStartPageId));
}
/**
* Test adding a tab to a tab group persists when we update the tab group.
*/
@Test
public void testUpdateAddNewTab()
{
TabGroup tabGroup = jpaTabGroupMapper.findById(fordsStartPageId);
tabGroup.getTabs().add(new Tab("Foo", Layout.THREECOLUMN));
jpaTabGroupMapper.insert(tabGroup);
getEntityManager().flush();
getEntityManager().clear();
tabGroup = jpaTabGroupMapper.findById(fordsStartPageId);
assertEquals("Attemped updating a Person after adding a Tab to his first "
+ "TabGroup, then flushing and clearing the EntityManager. "
+ "Expected to see the new tab after re-loading the Person.", "Foo", tabGroup.getTabs().get(
tabGroup.getTabs().size() - 1).getTabName());
}
/**
* This test deletes a record and then ensures that the record still remains in the database so that it can be
* undeleted.
*
* @throws TabDeletionException
* thrown on error during tab deletion.
*/
@Test
public void testDeletedRecordRemainsAvailableinDBForUndelete() throws TabDeletionException
{
TabGroup fordsTabGroup = jpaTabGroupMapper.findById(fordsStartPageId);
Tab fordTab1 = jpaTabMapper.findById(fordsFirstTabId);
long pageId = fordsTabGroup.getId();
long tabId = fordTab1.getId();
int tabIndex = fordTab1.getTabIndex();
// delete the first tab
jpaTabGroupMapper.deleteTab(fordTab1);
jpaTabGroupMapper.flush();
Boolean deletedFlag = (Boolean) getEntityManager().createQuery(
"select deleted from Tab where id = :tabId and "
+ "tabGroupId = :tabGroupId and deleted = true and tabIndex = :tabIndex").setParameter(
"tabGroupId", pageId).setParameter("tabId", tabId).setParameter("tabIndex", tabIndex).getSingleResult();
assertEquals("Expected that after deleting a tab, it's still tied to the "
+ "tabGroup, the tabIndex is null, and the tab is marked as deleted", true, deletedFlag.booleanValue());
}
/**
* Test deleting the first tab in a collection leaves the collection with the other tabs intact.
*
* @throws TabDeletionException
* thrown on error during tab deletion.
*/
@Test
public void testDeletingTabRemovesFromCollection() throws TabDeletionException
{
TabGroup tabGroup = jpaTabGroupMapper.findById(fordsStartPageId);
Tab fordTab1 = jpaTabMapper.findById(fordsFirstTabId);
// delete the first tab
jpaTabGroupMapper.deleteTab(fordTab1);
jpaTabGroupMapper.flush();
// clear the entityManager so we can re-query the collection
getEntityManager().clear();
// re-get and assert
tabGroup = jpaTabGroupMapper.findById(fordsStartPageId);
assertEquals("Expected 2 tabs after deleting the first one.", 2, tabGroup.getTabs().size());
assertEquals("Expected the previously second tab to be the first now after deleting the first one.",
fordsSecondTabId, tabGroup.getTabs().get(0).getId());
}
/**
* Test deleting a tab, then undeleting it.
*/
@Test
public void testDeleteThenUndelete()
{
TabGroup tabGroup = jpaTabGroupMapper.findById(fordsStartPageId);
Tab fordTab2 = jpaTabMapper.findById(fordsSecondTabId);
final int expectedTabCountAfterDeletingAndUndeleting = 3;
// delete the first tab
try
{
jpaTabGroupMapper.deleteTab(fordTab2);
}
catch (TabDeletionException e)
{
throw new RuntimeException(e);
}
jpaTabGroupMapper.flush();
// clear the entityManager so we can re-query the collection
getEntityManager().clear();
try
{
jpaTabGroupMapper.undeleteTab(fordsSecondTabId);
}
catch (TabUndeletionException e)
{
log.error(e);
}
jpaTabGroupMapper.flush();
// clear the entityManager so we can re-query the collection
getEntityManager().clear();
// re-get and assert
tabGroup = jpaTabGroupMapper.findById(fordsStartPageId);
assertEquals("Expected 3 tabs in Ford's startPage after deleting and undeleting.",
expectedTabCountAfterDeletingAndUndeleting, tabGroup.getTabs().size());
assertEquals(
"Expected the previously 1st tab to still be 1st after deleting and undeleting the original second tab",
fordsFirstTabId, tabGroup.getTabs().get(0).getId());
assertEquals("Expected the original 2nd tab to 2nd again after deleting and undeleting it", fordsSecondTabId,
tabGroup.getTabs().get(1).getId());
assertEquals("Expected the previously 2nd tab to be 3rd after deleting and undeleting the original second tab",
fordsThirdTabId, tabGroup.getTabs().get(2).getId());
}
/**
* Test that undeleting a tab that's not deleted throws a TabUndeleteException.
*
* @throws TabUndeletionException
* thrown on error during tab undeletion.
*/
@Test
public void testUndeleteThrowsExceptionWhenNotDeleted() throws TabUndeletionException
{
// try the deletion of the tab that's not deleted - should throw a
// TabUndeletionException
boolean exceptionOccurred = false;
try
{
jpaTabGroupMapper.undeleteTab(fordsFirstTabId);
}
catch (TabUndeletionException ex)
{
exceptionOccurred = true;
assertEquals("TabUndeletionException was thrown, but didn't contain the "
+ " tab id that the caller was trying to delete from.", fordsFirstTabId, ex.getTabId());
}
assertTrue("TabUndeletionException didn't occur when trying to delete a Tab that wasn't deleted.",
exceptionOccurred);
}
/**
* This test is trying to undelete the Tab that is designated as delete in the DBUnit Test Database.
*
* @throws TabUndeletionException
* thrown during tab undeletion.
*/
@Test
public void testUndeleteTab() throws TabUndeletionException
{
Boolean deletedFlag;
jpaTabGroupMapper.undeleteTab(fordsDeletedTabId);
jpaTabGroupMapper.flush();
deletedFlag = (Boolean) getEntityManager().createQuery(
"select de.deleted from Tab de " + "where de.id = :tabId").setParameter("tabId",
new Long(fordsDeletedTabId)).getSingleResult();
assertEquals(false, deletedFlag.booleanValue());
assertEquals("The tab name of the undeleted tab was not as expected.", "Ford Tab 4", jpaTabMapper.findById(
fordsDeletedTabId).getTabName());
}
/**
* Test that undeleting a tab restores the tab to its original position and bumps the other tabs back one.
*
* @throws TabUndeletionException
* thrown on error during tab undeletion.
*/
@Test
public void testUndeleteTabRestoresTabToItsOriginalPosition() throws TabUndeletionException
{
final int expectedTabCountBeforeUndelete = 3;
// undelete the previously deleted tab
TabGroup tabGroup = jpaTabGroupMapper.findById(fordsStartPageId);
assertEquals("Expected 3 tabs in Ford's first tabGroup before undeleting the previously-deleted tab.",
expectedTabCountBeforeUndelete, tabGroup.getTabs().size());
jpaTabGroupMapper.undeleteTab(fordsDeletedTabId);
jpaTabGroupMapper.flush();
// clear the getEntityManager()'s cache so we're going back to the
// databGroupase.
tabGroup = jpaTabGroupMapper.findById(fordsStartPageId);
assertEquals("Expected 4 tabs in Ford's third tabGroup after undeleting the previously-deleted tab.",
expectedTabCountBeforeUndelete + 1, tabGroup.getTabs().size());
assertEquals(
"Expected the previously 1st tab to still be 1st after deleting and undeleting the original 2nd tab",
fordsFirstTabId, tabGroup.getTabs().get(0).getId());
assertEquals("Expected the undeleted original 2nd tab to be 2nd again.", fordsDeletedTabId, tabGroup.getTabs()
.get(1).getId());
assertEquals("Expected the previously 2nd tab to be 3rd after deleting and undeleting the original 2nd tab",
fordsSecondTabId, tabGroup.getTabs().get(2).getId());
assertEquals("Expected the previously 3rd tab to be 4th after deleting and undeleting the original 2nd tab",
fordsThirdTabId, tabGroup.getTabs().get(3).getId());
}
/**
* This test verifies that deleted items expire and are purged from the database after a period of time.
*
* @throws TabDeletionException
* thrown on error during tab deletion.
*/
@Test
public void testUndeleteExpiration() throws TabDeletionException
{
long fordsActiveTabId = fordsFirstTabId;
int resultCount = getEntityManager()
.createQuery("select version from Tab where id = :tabId and deleted = true").setParameter("tabId",
fordsDeletedTabId).getResultList().size();
assertEquals("Could not find the already-deleted Tab", 1, resultCount);
// delete an item other than the one that is already deleted.
jpaTabGroupMapper.deleteTab(jpaTabMapper.findById(fordsActiveTabId));
// now make sure that the already-deleted tab is gone - it should have
// expired away
resultCount = getEntityManager().createQuery("select version from Tab where id = :tabId and deleted = true")
.setParameter("tabId", fordsDeletedTabId).getResultList().size();
assertEquals(
"After deleting a Tab, the Tab that was deleted a long time ago should have been permanently deleted.",
0, resultCount);
// TODO: make sure none of the gadgets from that tab exist
}
/**
* Verify the configurable time window for permanent deletion of a Tab - for a tab that's still within the window.
*
* @throws TabDeletionException
* thrown on exception deleting the tab.
*/
@Test
public void testDeletedTabWithinWindowShouldRemainAfterAnotherDelete() throws TabDeletionException
{
final int undeleteTabWindowInMinutes = 110;
final int minutesSinceTestTabWasDeleted = 100;
// set the undelete tab window
jpaTabGroupMapper.setUndeleteTabWindowInMinutes(undeleteTabWindowInMinutes);
// set the date the tab was deleted
GregorianCalendar tabDeletionDate = new GregorianCalendar();
tabDeletionDate.add(Calendar.MINUTE, -minutesSinceTestTabWasDeleted);
getEntityManager().createQuery(
"update versioned Tab set " + "dateDeleted = :deletedTimestamp " + "where id = :tabId").setParameter(
"deletedTimestamp", tabDeletionDate.getTime()).setParameter("tabId", fordsDeletedTabId).executeUpdate();
// delete the active tab
jpaTabGroupMapper.deleteTab(jpaTabMapper.findById(fordsFirstTabId));
// make sure that the deleted tab is still available
int resultCount = getEntityManager()
.createQuery("select version from Tab where id = :tabId and deleted = true").setParameter("tabId",
fordsDeletedTabId).getResultList().size();
assertEquals("After deleting a Tab, the Tab that was deleted within the undo window should still be present.",
1, resultCount);
}
/**
* Verify the configurable time window for permanent deletion of a Tab - for a tab that's outside the window.
*
* @throws TabDeletionException
* thrown on exception deleting the tab.
*/
@Test
public void testDeletedTabOutsideWindowShouldBePermanentlyDeletedAfterAnotherDelete() throws TabDeletionException
{
final int undeleteTabWindowInMinutes = 90;
final int minutesSinceTestTabWasDeleted = 100;
// set the undelete tab window
jpaTabGroupMapper.setUndeleteTabWindowInMinutes(undeleteTabWindowInMinutes);
// set the date the tab was deleted
GregorianCalendar tabDeletionDate = new GregorianCalendar();
tabDeletionDate.add(Calendar.MINUTE, -minutesSinceTestTabWasDeleted);
getEntityManager().createQuery(
"update versioned Tab set " + "dateDeleted = :deletedTimestamp " + "where id = :tabId").setParameter(
"deletedTimestamp", tabDeletionDate.getTime()).setParameter("tabId", fordsDeletedTabId).executeUpdate();
// delete the active tab
jpaTabGroupMapper.deleteTab(jpaTabMapper.findById(fordsFirstTabId));
// make sure that the deleted tab is still available
int resultCount = getEntityManager()
.createQuery("select version from Tab where id = :tabId and deleted = true").setParameter("tabId",
fordsDeletedTabId).getResultList().size();
assertEquals("After deleting a Tab, the Tab that was deleted outside the undo "
+ "window should still be permanently deleted.", 0, resultCount);
}
/**
* Test that when we add a tab to the end of a Page's Tabs collection, then flush, clear, and re-get, the Tab's
* tabIndex is set to the last index.
*/
@Test
public void testAddTabSetsTabIndexToLast()
{
TabGroup tabGroup = jpaTabGroupMapper.findById(fordsStartPageId);
Tab tab = new Tab("FooBar", Layout.THREECOLUMN);
tabGroup.getTabs().add(tab);
jpaTabGroupMapper.flush();
jpaTabGroupMapper.clear();
int expectedIndex = tabGroup.getTabs().size() - 1;
long tabId = tab.getId();
tab = jpaTabMapper.findById(tabId);
assertEquals("Expected tabIndex to be tabs.size()-1 when adding a tab to a tabGroup, after flush()",
expectedIndex, tab.getTabIndex());
}
}