/**
*
*/
package net.frontlinesms.data.repository.hibernate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;
import net.frontlinesms.junit.HibernateTestCase;
import net.frontlinesms.data.DuplicateKeyException;
import net.frontlinesms.data.Order;
import net.frontlinesms.data.domain.FrontlineMultimediaMessage;
import net.frontlinesms.data.domain.FrontlineMultimediaMessagePart;
import net.frontlinesms.data.domain.Keyword;
import net.frontlinesms.data.domain.FrontlineMessage;
import net.frontlinesms.data.domain.FrontlineMessage.Type;
import net.frontlinesms.data.repository.KeywordDao;
import net.frontlinesms.data.repository.MessageDao;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;
/**
* Test class for {@link HibernateMessageDao}
* @author Alex
*/
public class HibernateMessageDaoTest extends HibernateTestCase {
//> STATIC CONSTANTS
private static final String ARTHUR = "+44123456789";
private static final String BERNADETTE = "+447890123456";
private static final long DATE_1970 = createDate(1970);
private static final long DATE_1980 = createDate(1980);
private static final long DATE_1990 = createDate(1990);
private static final long DATE_2000 = createDate(2000);
private static final long DATE_2010 = createDate(2010);
//> INSTANCE PROPERTIES
/** Logging object */
private final Log log = LogFactory.getLog(getClass());
/** Instance of this DAO implementation we are testing. */
private MessageDao dao;
/** Keyword DAO */
private KeywordDao keywordDao;
//> TEST METHODS
/**
* Test everything all at once!
*/
public void testSaveDeleteSimple() {
checkSanity();
long startTime = System.currentTimeMillis();
FrontlineMessage m = FrontlineMessage.createIncomingMessage(startTime + 1000, ARTHUR, BERNADETTE, "Hello mate.");
dao.saveMessage(m);
checkSanity();
assertEquals(1, dao.getSMSCount(0l, Long.MAX_VALUE));
assertEquals(1, dao.getSMSCountForMsisdn(ARTHUR, 0l, Long.MAX_VALUE));
assertEquals(1, dao.getSMSCountForMsisdn(BERNADETTE, 0l, Long.MAX_VALUE));
assertEquals(0, dao.getSMSCountForMsisdn("whatever i am invented", 0l, Long.MAX_VALUE));
assertEquals(0, dao.getMessageCount(Type.OUTBOUND, 0l, Long.MAX_VALUE));
assertEquals(1, dao.getMessageCount(Type.RECEIVED, 0l, Long.MAX_VALUE));
dao.deleteMessage(m);
checkSanity();
assertEquals(0, dao.getSMSCount(startTime, Long.MAX_VALUE));
}
public void testGetSimilarKeywords() throws DuplicateKeyException {
// Create a number of keywords and messages, and perform queries over them
createKeywords("", "te", "test", "test complex", "test other complex", "test complex again", "distraction", "another distraction");
testGetSimilarKeywords("", "te", "test", "test complex", "test other complex", "test complex again", "distraction", "another distraction");
testGetSimilarKeywords("te");
testGetSimilarKeywords("test", "test complex", "test other complex", "test complex again");
testGetSimilarKeywords("test complex", "test complex again");
testGetSimilarKeywords("test other complex");
testGetSimilarKeywords("test complex again");
testGetSimilarKeywords("distraction");
testGetSimilarKeywords("another distraction");
}
private void testGetSimilarKeywords(String keyword, String... expectedMatches) {
HibernateMessageDao dao = (HibernateMessageDao) this.dao;
// Convert expectedMathches to upper case
for (int i = 0; i < expectedMatches.length; i++) {
expectedMatches[i] = expectedMatches[i].toUpperCase();
}
List<String> actualMatches = dao.getSimilarKeywords(new Keyword(keyword, "test keyword: trying to get similar."));
assertEquals("Unexpected results for keyword '" + keyword + "'",
expectedMatches.length, actualMatches.size());
assertEqualsIgnoreOrder(keyword, expectedMatches, actualMatches);
}
public void testGetMessagesForKeywordWithParameters() throws DuplicateKeyException {
createKeywords("", "test", "test complex", "distraction");
createMessagesWithParameters("", "test", "test complex", "distraction");
testGetMessagesForKeywordWithParameters("", "test", "test complex", "distraction");
}
private void testGetMessagesForKeywordWithParameters(String... keywords) {
for(String keyword : keywords) {
// Test sorting messages by date ASCENDING
// 1. get all messages
testGetMessagesForKeywordWithParameters(keyword, 20, Long.MIN_VALUE, Long.MAX_VALUE);
testGetMessagesForKeywordWithParameters(keyword, 20, null, null);
testGetMessagesForKeywordWithParameters(keyword, 20, Long.MIN_VALUE, null);
testGetMessagesForKeywordWithParameters(keyword, 20, null, Long.MAX_VALUE);
testGetMessagesForKeywordWithParameters(keyword, 20, DATE_1970, DATE_2010);
// 2. get ranges of messages
testGetMessagesForKeywordWithParameters(keyword, 12, Long.MIN_VALUE, DATE_1990);
testGetMessagesForKeywordWithParameters(keyword, 12, null, DATE_1990);
testGetMessagesForKeywordWithParameters(keyword, 8, DATE_2000, Long.MAX_VALUE);
testGetMessagesForKeywordWithParameters(keyword, 8, DATE_2000, null);
testGetMessagesForKeywordWithParameters(keyword, 8, DATE_1980, DATE_1990);
}
}
private void testGetMessagesForKeywordWithParameters(String keyword, int totalMessageCount, Long startDate, Long endDate) {
testGetMessagesForKeywordWithParameters(keyword, totalMessageCount, startDate, endDate, 0, 100);
testGetMessagesForKeywordWithParameters(keyword, totalMessageCount, startDate, endDate, 0, totalMessageCount);
testGetMessagesForKeywordWithParameters(keyword, totalMessageCount, startDate, endDate, 0, totalMessageCount/2);
testGetMessagesForKeywordWithParameters(keyword, totalMessageCount, startDate, endDate, 1, 2);
testGetMessagesForKeywordWithParameters(keyword, totalMessageCount, startDate, endDate, totalMessageCount, 100);
}
private void testGetMessagesForKeywordWithParameters(String keyword, int totalMessageCount, Long startDate, Long endDate, int startIndex, int limit) {
int actualMessageCount = this.dao.getMessageCount(FrontlineMessage.Type.ALL, new Keyword(keyword, ""), startDate, endDate);
assertTrue("Wrong message count. Expected <" + totalMessageCount + ">, but was <" + actualMessageCount + ">", totalMessageCount == actualMessageCount);
// Adjust the expected message count to take into account the paging
int expectedMessageCount = Math.min(totalMessageCount-startIndex, limit);
// Test ascending
{
List<FrontlineMessage> dateAscMessages = this.dao.getMessagesForKeyword(FrontlineMessage.Type.ALL, new Keyword(keyword, ""),
FrontlineMessage.Field.DATE, Order.ASCENDING, startDate, endDate, startIndex, limit);
assertEquals("Messages for keyword '" + keyword + "' " +
"start=" + startIndex + ";limit=" + limit,
expectedMessageCount, dateAscMessages.size());
Long lastDate = null;
if (startDate != null) {
lastDate = startDate;
}
for(FrontlineMessage m : dateAscMessages) {
if (lastDate == null) {
lastDate = m.getDate();
}
assertTrue(m.getTextContent().startsWith(keyword));
assertTrue(lastDate + " is supposed to be lower than " + m.getDate(), lastDate <= m.getDate());
lastDate = m.getDate();
}
}
// Test descending
{
List<FrontlineMessage> dateDescMessages = this.dao.getMessagesForKeyword(FrontlineMessage.Type.ALL, new Keyword(keyword, ""),
FrontlineMessage.Field.DATE, Order.DESCENDING, startDate, endDate, startIndex, limit);
assertEquals("Messages for keyword '" + keyword + "' " +
"start=" + startIndex + ";limit=" + limit,
expectedMessageCount, dateDescMessages.size());
Long lastDate = null;
if (endDate != null) {
lastDate = endDate;
}
for(FrontlineMessage m : dateDescMessages) {
if (lastDate == null) {
lastDate = m.getDate();
}
assertTrue(m.getTextContent().startsWith(keyword));
assertTrue(lastDate + " is supposed to be greater than " + m.getDate(), lastDate >= m.getDate());
lastDate = m.getDate();
}
}
}
private void createMessagesWithParameters(String... keywords) {
for(String keyword : keywords) {
String prefix = keyword.length() > 0 ? keyword + " " : "";
createMessages(prefix + "A 1970", DATE_1970);
createMessages(prefix + "B 1970", DATE_1970);
createMessages(prefix + "A 1980", DATE_1980);
createMessages(prefix + "B 1980", DATE_1980);
createMessages(prefix + "A 1990", DATE_1990);
createMessages(prefix + "B 1990", DATE_1990);
createMessages(prefix + "A 2000", DATE_2000);
createMessages(prefix + "B 2000", DATE_2000);
createMessages(prefix + "A 2010", DATE_2010);
createMessages(prefix + "B 2010", DATE_2010);
}
}
/**
* Create an incoming and outgoing message sent in a particular year.
* @param textContent
* @param date
*/
private void createMessages(String messageContent, long date) {
final String senderMsisdn = "test sender";
final String recipientMsisdn = "test recipient";
FrontlineMessage incomingMessage = FrontlineMessage.createIncomingMessage(date, senderMsisdn, recipientMsisdn, messageContent);
this.dao.saveMessage(incomingMessage);
FrontlineMessage outgoingMessage = FrontlineMessage.createOutgoingMessage(date, senderMsisdn, recipientMsisdn, messageContent);
this.dao.saveMessage(outgoingMessage);
}
/**
* Test {@link MessageDao#getMessagesForKeyword(int, net.frontlinesms.data.domain.Keyword)}.
* @throws DuplicateKeyException
*/
public void testGetMessagesForKeyword() throws DuplicateKeyException {
// Create a number of keywords and messages, and perform queries over them
createKeywords("", "te", "test", "test complex", "test other complex", "test complex again", "distraction", "another distraction");
createMessages(
"", // -> ""
"te", // -> "te"
"test", // -> "test"
"test complex", // -> "test complex"
"Here is a message that should show as blank.", // -> ""
"Test the test keyword with this message", // -> "test"
"Test Complex keyword behaviour with this message", // -> "test complex"
"test test test", // -> "test"
"don't test me" // -> ""
);
testGetMessagesForKeyword("", 3);
testGetMessagesForKeyword("te", 1);
testGetMessagesForKeyword("test", 3);
testGetMessagesForKeyword("test complex", 2);
testGetMessagesForKeyword("test other complex", 0);
testGetMessagesForKeyword("distraction", 0);
testGetMessagesForKeyword("another distraction", 0);
}
/**
* Test individual values for {@link #testGetMessagesForKeyword()}
* @param keywordString the keyword string to match
* @param expectedMessageCount the expected number of incoming and outgoing messages. Total messages should be twice this.
*/
private void testGetMessagesForKeyword(String keywordString, int expectedMessageCount) {
Keyword keyword = new Keyword(keywordString, "Test keyword.");
List<FrontlineMessage> allMessagesForBlankKeyword = dao.getMessagesForKeyword(FrontlineMessage.Type.ALL, keyword);
List<FrontlineMessage> incomingMessagesForBlankKeyword = dao.getMessagesForKeyword(FrontlineMessage.Type.RECEIVED, keyword);
List<FrontlineMessage> outgoingMessagesForBlankKeyword = dao.getMessagesForKeyword(FrontlineMessage.Type.OUTBOUND, keyword);
int allMessageCount = allMessagesForBlankKeyword.size();
int incomingMessageCount = incomingMessagesForBlankKeyword.size();
int outgoingMessageCount = outgoingMessagesForBlankKeyword.size();
assertTrue("Message count mismatch for keyword: '" + keywordString + "'", incomingMessageCount == outgoingMessageCount);
assertTrue("Message count mismatch for keyword: '" + keywordString + "'", allMessageCount == 2 * incomingMessageCount);
assertEquals("Unexpected message count for keyword: '" + keywordString + "'", expectedMessageCount, incomingMessageCount);
}
/** TODO may not be necessary to create keywords. */
private void createKeywords(String... keywordStrings) throws DuplicateKeyException {
for(String keywordString : keywordStrings) {
createKeyword(keywordString);
}
}
private void createKeyword(String keywordString) throws DuplicateKeyException {
Keyword k = new Keyword(keywordString, "generated for test in " + this.getClass().getName());
this.keywordDao.saveKeyword(k);
}
private void createMessages(String... messageContents) {
for(String messageContent : messageContents) {
createIncomingMessage(messageContent);
createOutgoingMessage(messageContent);
}
}
private void createOutgoingMessage(String messageContent) {
FrontlineMessage m = FrontlineMessage.createOutgoingMessage(0, "testSender", "testRecipient", messageContent);
this.dao.saveMessage(m);
}
private void createIncomingMessage(String messageContent) {
FrontlineMessage m = FrontlineMessage.createIncomingMessage(0, "testSender", "testRecipient", messageContent);
this.dao.saveMessage(m);
}
public void testMultimediaMessageRetrieval() {
// Text message
{
FrontlineMultimediaMessage mms = new FrontlineMultimediaMessage(
FrontlineMessage.Type.RECEIVED, "Subject 1", "summary here", Arrays.asList(new FrontlineMultimediaMessagePart[]{
FrontlineMultimediaMessagePart.createTextPart("Hullo")
}));
this.dao.saveMessage(mms);
List<FrontlineMessage> messages = this.dao.getAllMessages();
assertEquals(1, messages.size());
FrontlineMessage actualMessage = messages.get(0);
assertEquals(FrontlineMultimediaMessage.class, actualMessage.getClass());
assertEquals(1, (((FrontlineMultimediaMessage) actualMessage).getMultimediaParts().size()));
}
// Binary message
{
FrontlineMultimediaMessage mms = new FrontlineMultimediaMessage(
FrontlineMessage.Type.RECEIVED, "Subject 1", "summary here", Arrays.asList(new FrontlineMultimediaMessagePart[]{
FrontlineMultimediaMessagePart.createBinaryPart("/somewhere/something.wot")
}));
this.dao.saveMessage(mms);
List<FrontlineMessage> messages = this.dao.getAllMessages();
assertEquals(2, messages.size());
FrontlineMessage actualMessage = messages.get(1);
assertEquals(FrontlineMultimediaMessage.class, actualMessage.getClass());
assertEquals(1, (((FrontlineMultimediaMessage) actualMessage).getMultimediaParts().size()));
}
// Mixed message
{
FrontlineMultimediaMessage mms = new FrontlineMultimediaMessage(
FrontlineMessage.Type.RECEIVED, "Subject 1", "summary here", Arrays.asList(new FrontlineMultimediaMessagePart[]{
FrontlineMultimediaMessagePart.createTextPart("another message"),
FrontlineMultimediaMessagePart.createBinaryPart("/somewhereElse/somethingElse.who"),
FrontlineMultimediaMessagePart.createTextPart("The End."),
}));
this.dao.saveMessage(mms);
List<FrontlineMessage> messages = this.dao.getAllMessages();
assertEquals(3, messages.size());
FrontlineMessage actualMessage = messages.get(2);
assertEquals(FrontlineMultimediaMessage.class, actualMessage.getClass());
assertEquals(3, (((FrontlineMultimediaMessage) actualMessage).getMultimediaParts().size()));
}
// Delete messages
}
//> INSTANCE HELPER METHODS
/**
* Check that various methods agree with each other.
*/
private void checkSanity() {
assertEquals(dao.getSMSCount(0l, Long.MAX_VALUE), dao.getAllMessages().size());
}
//> ACCESSORS
/** @param d The DAO to use for the test. */
@Required
public void setMessageDao(MessageDao d) {
this.dao = d;
}
@Required
public void setKeywordDao(KeywordDao keywordDao) {
this.keywordDao = keywordDao;
}
//> STATIC HELPER METHODS
/** Create a date in millis */
private static long createDate(int year) {
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
c.setTimeInMillis(0);
c.set(Calendar.YEAR, year);
return c.getTimeInMillis();
}
static final <T> void assertEqualsIgnoreOrder(String keyword, T[] expected, List<T> actual) {
List<T> tempActual = new ArrayList<T>(actual);
assertEquals("Incorrect object count for " + keyword, expected.length, actual.size());
for (int i = 0; i < expected.length; i++) {
assertTrue("Unexpected keyword match for '" + keyword + "' at index: " + i, tempActual.remove(expected[i]));
}
}
}