package in.partake.daemon.impl;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import in.partake.app.PartakeApp;
import in.partake.app.PartakeTestApp;
import in.partake.base.DateTime;
import in.partake.base.PartakeException;
import in.partake.base.TimeUtil;
import in.partake.controller.AbstractPartakeControllerTest;
import in.partake.model.IPartakeDAOs;
import in.partake.model.UserEx;
import in.partake.model.access.Transaction;
import in.partake.model.dao.DAOException;
import in.partake.model.dao.PartakeConnection;
import in.partake.model.dto.Event;
import in.partake.model.dto.EventTicket;
import in.partake.model.dto.MessageEnvelope;
import in.partake.model.dto.TwitterMessage;
import in.partake.model.dto.UserNotification;
import in.partake.model.dto.auxiliary.MessageDelivery;
import in.partake.model.dto.auxiliary.NotificationType;
import in.partake.model.fixture.TestDataProviderConstants;
import in.partake.view.util.Helper;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import org.junit.Before;
import org.junit.Test;
import twitter4j.TwitterException;
import twitter4j.internal.http.HttpResponse;
public class SendMessageEnvelopeTaskTest extends AbstractPartakeControllerTest implements TestDataProviderConstants {
private static final String TWITTER_MESSAGE_WILLFAIL_MESSAGE = "Sending this message should fail";
@Before
public void setUp() throws Exception {
super.setUp();
PartakeTestApp.getTestService().setDefaultFixtures();
// Removes MessageEnvelope.
new Transaction<Void>() {
@Override
protected Void doExecute(PartakeConnection con, IPartakeDAOs daos) throws DAOException, PartakeException {
daos.getMessageEnvelopeAccess().truncate(con);
return null;
}
}.execute();
PartakeTestApp.instance().reinitializeTwitterService();
}
@Test
public void sendEmpty() throws Exception {
new SendMessageEnvelopeTask().run();
}
@Test
public void sendTwitterMessage() throws Exception {
UUID uuid = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForTwitterMessage(uuid.toString(), TWITTER_MESSAGE_INQUEUE_ID, null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
List<MessageEnvelope> rest = loadEnvelopes();
assertThat(rest.isEmpty(), is(true));
verify(PartakeApp.getTwitterService()).updateStatus(anyString(), anyString(), eq("message"));
assertThat(loadTwitterMessage(TWITTER_MESSAGE_INQUEUE_ID).getDelivery(), is(MessageDelivery.SUCCESS));
}
@Test
public void sendTwitterMessageBeforeTryAfter() throws Exception {
DateTime now = TimeUtil.getCurrentDateTime();
TimeUtil.setCurrentDateTime(now);
String twitterMessageId = UUID.randomUUID().toString();
TwitterMessage message = new TwitterMessage(twitterMessageId, DEFAULT_USER_ID, TWITTER_MESSAGE_WILLFAIL_MESSAGE, MessageDelivery.INQUEUE, new DateTime(0), null);
storeTwitterMessage(message);
UUID envelopeId = UUID.randomUUID();
MessageEnvelope envelope = new MessageEnvelope(envelopeId.toString(), null, twitterMessageId, null, 0, null, null, now.nHourAfter(1), now, null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
// |modified| should not be changed.
MessageEnvelope modified = loadEnvelope(envelopeId.toString());
assertThat(modified.getNumTried(), is(0));
assertThat(modified.getLastTriedAt(), is(nullValue()));
}
@Test
public void sendTwitterMessageWithTwitterException() throws Exception {
DateTime now = TimeUtil.getCurrentDateTime();
TimeUtil.setCurrentDateTime(now);
HttpResponse res = mock(HttpResponse.class);
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Limit"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Remaining"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Reset"));
doThrow(new TwitterException("message", res)).when(PartakeApp.getTwitterService()).updateStatus(anyString(), anyString(), eq(TWITTER_MESSAGE_WILLFAIL_MESSAGE));
String twitterMessageId = UUID.randomUUID().toString();
TwitterMessage message = new TwitterMessage(twitterMessageId, DEFAULT_USER_ID, TWITTER_MESSAGE_WILLFAIL_MESSAGE, MessageDelivery.INQUEUE, new DateTime(0), null);
storeTwitterMessage(message);
UUID envelopeId = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForTwitterMessage(envelopeId.toString(), twitterMessageId, null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
MessageEnvelope modified = loadEnvelope(envelopeId.toString());
assertThat(modified.getNumTried(), is(1));
assertThat(modified.getLastTriedAt(), is(now));
assertThat(modified.getTryAfter(), is(now.nSecAfter(600)));
}
@Test
public void sendTwitterMessageWithTwitterExceptionCausedByNetworkError() throws Exception {
DateTime now = TimeUtil.getCurrentDateTime();
TimeUtil.setCurrentDateTime(now);
// When |cause| is IOException, TwitterException thinks it's a network error.
doThrow(new TwitterException("message", new IOException(""))).when(PartakeApp.getTwitterService()).updateStatus(anyString(), anyString(), eq(TWITTER_MESSAGE_WILLFAIL_MESSAGE));
String twitterMessageId = UUID.randomUUID().toString();
TwitterMessage message = new TwitterMessage(twitterMessageId, DEFAULT_USER_ID, TWITTER_MESSAGE_WILLFAIL_MESSAGE, MessageDelivery.INQUEUE, new DateTime(0), null);
storeTwitterMessage(message);
UUID envelopeId = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForTwitterMessage(envelopeId.toString(), twitterMessageId, null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
MessageEnvelope modified = loadEnvelope(envelopeId.toString());
assertThat(modified.getNumTried(), is(1));
assertThat(modified.getLastTriedAt(), is(now));
assertThat(modified.getTryAfter(), is(now.nSecAfter(600))); // 5 min later
}
@Test
public void sendTwitterMessageWithTwitterExceptionExceededLimit() throws Exception {
DateTime now = TimeUtil.getCurrentDateTime();
TimeUtil.setCurrentDateTime(now);
HttpResponse res = mock(HttpResponse.class);
doReturn(400).when(res).getStatusCode();
doReturn("100").when(res).getResponseHeader(eq("X-RateLimit-Limit"));
doReturn("100").when(res).getResponseHeader(eq("X-RateLimit-Remaining"));
doReturn("1").when(res).getResponseHeader(eq("X-RateLimit-Reset"));
doThrow(new TwitterException("message", res)).when(PartakeApp.getTwitterService()).updateStatus(anyString(), anyString(), eq(TWITTER_MESSAGE_WILLFAIL_MESSAGE));
String twitterMessageId = UUID.randomUUID().toString();
TwitterMessage message = new TwitterMessage(twitterMessageId, DEFAULT_USER_ID, TWITTER_MESSAGE_WILLFAIL_MESSAGE, MessageDelivery.INQUEUE, new DateTime(0), null);
storeTwitterMessage(message);
UUID envelopeId = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForTwitterMessage(envelopeId.toString(), twitterMessageId, null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
MessageEnvelope modified = loadEnvelope(envelopeId.toString());
assertThat(modified.getNumTried(), is(1));
assertThat(modified.getLastTriedAt(), is(now));
// TODO: We should think how to test this.
// assertThat(modified.getTryAfter(), is(now.nSecAfter(1))); // 1 sec after.
}
@Test
public void sendTwitterMessageWithTwitterExceptionCausedByUnauthorized() throws Exception {
DateTime now = TimeUtil.getCurrentDateTime();
TimeUtil.setCurrentDateTime(now);
HttpResponse res = mock(HttpResponse.class);
doReturn(401).when(res).getStatusCode();
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Limit"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Remaining"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Reset"));
doThrow(new TwitterException("message", res)).when(PartakeApp.getTwitterService()).updateStatus(anyString(), anyString(), eq(TWITTER_MESSAGE_WILLFAIL_MESSAGE));
String twitterMessageId = UUID.randomUUID().toString();
TwitterMessage message = new TwitterMessage(twitterMessageId, DEFAULT_USER_ID, TWITTER_MESSAGE_WILLFAIL_MESSAGE, MessageDelivery.INQUEUE, new DateTime(0), null);
storeTwitterMessage(message);
UUID envelopeId = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForTwitterMessage(envelopeId.toString(), twitterMessageId, null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
// Message Envelop should be removed.
MessageEnvelope modified = loadEnvelope(envelopeId.toString());
assertThat(modified, is(nullValue()));
// User should be unauthorized.
UserEx user = loadUserEx(DEFAULT_USER_ID);
assertThat(user.getTwitterLinkage().getAccessToken(), is(nullValue()));
assertThat(user.getTwitterLinkage().getAccessTokenSecret(), is(nullValue()));
}
@Test
public void sendTwitterMessageWithInvalidId() throws Exception {
DateTime now = TimeUtil.getCurrentDateTime();
TimeUtil.setCurrentDateTime(now);
HttpResponse res = mock(HttpResponse.class);
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Limit"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Remaining"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Reset"));
doThrow(new TwitterException("message", res)).when(PartakeApp.getTwitterService()).updateStatus(anyString(), anyString(), eq(TWITTER_MESSAGE_WILLFAIL_MESSAGE));
UUID envelopeId = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForTwitterMessage(envelopeId.toString(), UUID.randomUUID().toString(), null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
// The message should be removed from the queue.
MessageEnvelope modified = loadEnvelope(envelopeId.toString());
assertThat(modified, is(nullValue()));
}
@Test
public void sendTwitterMessageWithInvalidUserId() throws Exception {
DateTime now = TimeUtil.getCurrentDateTime();
TimeUtil.setCurrentDateTime(now);
HttpResponse res = mock(HttpResponse.class);
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Limit"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Remaining"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Reset"));
doThrow(new TwitterException("message", res)).when(PartakeApp.getTwitterService()).updateStatus(anyString(), anyString(), eq(TWITTER_MESSAGE_WILLFAIL_MESSAGE));
String twitterMessageId = UUID.randomUUID().toString();
TwitterMessage message = new TwitterMessage(twitterMessageId, INVALID_USER_ID, TWITTER_MESSAGE_WILLFAIL_MESSAGE, MessageDelivery.INQUEUE, new DateTime(0), null);
storeTwitterMessage(message);
UUID envelopeId = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForTwitterMessage(envelopeId.toString(), twitterMessageId, null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
// The message should be removed from the queue.
MessageEnvelope modified = loadEnvelope(envelopeId.toString());
assertThat(modified, is(nullValue()));
}
@Test
public void sendTwitterMessageWithNoTwitterLinkUser() throws Exception {
DateTime now = TimeUtil.getCurrentDateTime();
TimeUtil.setCurrentDateTime(now);
HttpResponse res = mock(HttpResponse.class);
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Limit"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Remaining"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Reset"));
doThrow(new TwitterException("message", res)).when(PartakeApp.getTwitterService()).updateStatus(anyString(), anyString(), eq(TWITTER_MESSAGE_WILLFAIL_MESSAGE));
String twitterMessageId = UUID.randomUUID().toString();
TwitterMessage message = new TwitterMessage(twitterMessageId, USER_NO_TWITTER_LINK_ID, TWITTER_MESSAGE_WILLFAIL_MESSAGE, MessageDelivery.INQUEUE, new DateTime(0), null);
storeTwitterMessage(message);
UUID envelopeId = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForTwitterMessage(envelopeId.toString(), twitterMessageId, null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
// The message should be removed from the queue.
MessageEnvelope modified = loadEnvelope(envelopeId.toString());
assertThat(modified, is(nullValue()));
}
@Test
public void sendTwitterMessageWithNoAuthorizedTwitterLinkUser() throws Exception {
DateTime now = TimeUtil.getCurrentDateTime();
TimeUtil.setCurrentDateTime(now);
HttpResponse res = mock(HttpResponse.class);
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Limit"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Remaining"));
doReturn(null).when(res).getResponseHeader(eq("X-RateLimit-Reset"));
doThrow(new TwitterException("message", res)).when(PartakeApp.getTwitterService()).updateStatus(anyString(), anyString(), eq(TWITTER_MESSAGE_WILLFAIL_MESSAGE));
String twitterMessageId = UUID.randomUUID().toString();
TwitterMessage message = new TwitterMessage(twitterMessageId, USER_TWITTER_NOAUTH_ID, TWITTER_MESSAGE_WILLFAIL_MESSAGE, MessageDelivery.INQUEUE, new DateTime(0), null);
storeTwitterMessage(message);
UUID envelopeId = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForTwitterMessage(envelopeId.toString(), twitterMessageId, null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
// The message should be removed from the queue.
MessageEnvelope modified = loadEnvelope(envelopeId.toString());
assertThat(modified, is(nullValue()));
}
// ----------------------------------------------------------------------
@Test
public void sendUserNotification() throws Exception {
UUID uuid = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForUserNotification(uuid.toString(), USER_NOTIFICATION_INQUEUE_ID, null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
List<MessageEnvelope> rest = loadEnvelopes();
assertThat(rest.isEmpty(), is(true));
verify(PartakeApp.getTwitterService()).sendDirectMesage(anyString(), anyString(), eq(DEFAULT_TWITTER_ID), anyString());
assertThat(loadUserNotification(USER_NOTIFICATION_INQUEUE_ID).getDelivery(), is(MessageDelivery.SUCCESS));
}
@Test
public void sendUserMessage() throws Exception {
UUID uuid = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForUserMessage(uuid.toString(), USER_RECEIVED_MESSAGE_INQUEUE_ID.toString(), null);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
List<MessageEnvelope> rest = loadEnvelopes();
assertThat(rest.isEmpty(), is(true));
verify(PartakeApp.getTwitterService()).sendDirectMesage(anyString(), anyString(), eq(DEFAULT_RECEIVER_TWITTER_ID), anyString());
assertThat(loadUserReceivedMessage(USER_RECEIVED_MESSAGE_INQUEUE_ID).getDelivery(), is(MessageDelivery.SUCCESS));
}
@Test
public void sendInvalidatedMessage() throws Exception {
DateTime now = TimeUtil.getCurrentDateTime();
DateTime before = now.nDayBefore(1);
UUID uuid = UUID.randomUUID();
MessageEnvelope envelope = MessageEnvelope.createForTwitterMessage(uuid.toString(), TWITTER_MESSAGE_INQUEUE_ID, before);
queueEnvelope(envelope);
new SendMessageEnvelopeTask().run();
List<MessageEnvelope> rest = loadEnvelopes();
assertThat(rest.isEmpty(), is(true));
verify(PartakeApp.getTwitterService(), never()).updateStatus(anyString(), anyString(), anyString());
assertThat(loadTwitterMessage(TWITTER_MESSAGE_INQUEUE_ID).getDelivery(), is(MessageDelivery.FAIL));
}
// ----------------------------------------------------------------------
@Test
public void testToBuildUserNotificationMessageBodyForOnedayBeforeReminder() throws Exception{
UserNotification notification = loadUserNotification(USER_NOTIFICATION_INQUEUE_ID);
Event event = loadEvent(DEFAULT_EVENT_ID);
EventTicket ticket = loadEventTicket(DEFAULT_EVENT_TICKET_ID);
UserNotification userNotification = new UserNotification(notification);
userNotification.setNotificationType(NotificationType.EVENT_ONEDAY_BEFORE_REMINDER);
String messageBody = SendMessageEnvelopeTask.buildUserNotificationMessageBody(userNotification, event, ticket);
String expected = String.format("[PRTK] 「title」は%sに開始です。あなたの参加は確定しています。 http://127.0.0.1:9000/events/00000000-0000-0002-0000-000000000000", Helper.readableDate(event.getBeginDate()));
assertThat(messageBody, is(expected));
}
@Test
public void testToBuildUserNotificationMessageBodyForOnedayBeforeReservationReminder() throws Exception{
UserNotification notification = loadUserNotification(USER_NOTIFICATION_INQUEUE_ID);
Event event = loadEvent(DEFAULT_EVENT_ID);
EventTicket ticket = loadEventTicket(DEFAULT_EVENT_TICKET_ID);
UserNotification userNotification = new UserNotification(notification);
userNotification.setNotificationType(NotificationType.ONE_DAY_BEFORE_REMINDER_FOR_RESERVATION);
String messageBody = SendMessageEnvelopeTask.buildUserNotificationMessageBody(userNotification, event, ticket);
String expected = String.format("[PRTK] 「title」の締め切りは%sです。参加・不参加を確定してください。 http://127.0.0.1:9000/events/00000000-0000-0002-0000-000000000000", Helper.readableDate(event.getBeginDate()));
assertThat(messageBody, is(expected));
}
@Test
public void testToBuildUserNotificationMessageBodyForHalfdayBeforeReservationReminder() throws Exception{
UserNotification notification = loadUserNotification(USER_NOTIFICATION_INQUEUE_ID);
Event event = loadEvent(DEFAULT_EVENT_ID);
EventTicket ticket = loadEventTicket(DEFAULT_EVENT_TICKET_ID);
UserNotification userNotification = new UserNotification(notification);
userNotification.setNotificationType(NotificationType.HALF_DAY_BEFORE_REMINDER_FOR_RESERVATION);
String messageBody = SendMessageEnvelopeTask.buildUserNotificationMessageBody(userNotification, event, ticket);
String expected = String.format("[PRTK] 「title」の締め切りは%sです。参加・不参加を確定してください。 http://127.0.0.1:9000/events/00000000-0000-0002-0000-000000000000", Helper.readableDate(event.getBeginDate()));
assertThat(messageBody, is(expected));
}
@Test
public void testToBuildUserNotificationMessageBodyForEnrolledReminder() throws Exception{
UserNotification notification = loadUserNotification(USER_NOTIFICATION_INQUEUE_ID);
Event event = loadEvent(DEFAULT_EVENT_ID);
EventTicket ticket = loadEventTicket(DEFAULT_EVENT_TICKET_ID);
UserNotification userNotification = new UserNotification(notification);
userNotification.setNotificationType(NotificationType.BECAME_TO_BE_ENROLLED);
String messageBody = SendMessageEnvelopeTask.buildUserNotificationMessageBody(userNotification, event, ticket);
String expected = "[PRTK] 「title」で補欠から参加者へ繰り上がりました。 http://127.0.0.1:9000/events/00000000-0000-0002-0000-000000000000";
assertThat(messageBody, is(expected));
}
@Test
public void testToBuildUserNotificationMessageBodyForCancelledReminder() throws Exception{
UserNotification notification = loadUserNotification(USER_NOTIFICATION_INQUEUE_ID);
Event event = loadEvent(DEFAULT_EVENT_ID);
EventTicket ticket = loadEventTicket(DEFAULT_EVENT_TICKET_ID);
UserNotification userNotification = new UserNotification(notification);
userNotification.setNotificationType(NotificationType.BECAME_TO_BE_CANCELLED);
String messageBody = SendMessageEnvelopeTask.buildUserNotificationMessageBody(userNotification, event, ticket);
String expected = "[PRTK] 「title」で参加者から補欠へ繰り下がりました。 http://127.0.0.1:9000/events/00000000-0000-0002-0000-000000000000";
assertThat(messageBody, is(expected));
}
// ----------------------------------------------------------------------
private void storeTwitterMessage(final TwitterMessage message) throws Exception {
new Transaction<Void>() {
@Override
protected Void doExecute(PartakeConnection con, IPartakeDAOs daos) throws DAOException, PartakeException {
daos.getTwitterMessageAccess().put(con, message);
return null;
}
}.execute();
}
private void queueEnvelope(final MessageEnvelope envelope) throws Exception {
new Transaction<Void>() {
@Override
protected Void doExecute(PartakeConnection con, IPartakeDAOs daos) throws DAOException, PartakeException {
daos.getMessageEnvelopeAccess().put(con, envelope);
return null;
}
}.execute();
}
}