package com.fsck.k9.helper;
import java.lang.reflect.Array;
import java.util.ArrayList;
import com.fsck.k9.Account;
import com.fsck.k9.K9RobolectricTestRunner;
import com.fsck.k9.helper.ReplyToParser.ReplyToAddresses;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mail.internet.ListHeaders;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(K9RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class ReplyToParserTest {
private static final Address[] REPLY_TO_ADDRESSES = Address.parse("replyTo1@example.com, replyTo2@example.com");
private static final Address[] LIST_POST_ADDRESSES = Address.parse("listPost@example.com");
private static final Address[] FROM_ADDRESSES = Address.parse("from@example.com");
private static final Address[] TO_ADDRESSES = Address.parse("to1@example.com, to2@example.com");
private static final Address[] CC_ADDRESSES = Address.parse("cc1@example.com, cc2@example.com");
private static final String[] LIST_POST_HEADER_VALUES = new String[] { "<mailto:listPost@example.com>" };
public static final Address[] EMPTY_ADDRESSES = new Address[0];
private ReplyToParser replyToParser;
private Message message;
private Account account;
@Before
public void setUp() throws Exception {
message = mock(Message.class);
account = mock(Account.class);
replyToParser = new ReplyToParser();
}
@Test
public void getRecipientsToReplyTo_should_prefer_replyTo_over_any_other_field() throws Exception {
when(message.getReplyTo()).thenReturn(REPLY_TO_ADDRESSES);
when(message.getHeader(ListHeaders.LIST_POST_HEADER)).thenReturn(LIST_POST_HEADER_VALUES);
when(message.getFrom()).thenReturn(FROM_ADDRESSES);
ReplyToAddresses result = replyToParser.getRecipientsToReplyTo(message, account);
assertArrayEquals(REPLY_TO_ADDRESSES, result.to);
assertArrayEquals(EMPTY_ADDRESSES, result.cc);
verify(account).isAnIdentity(result.to);
}
@Test
public void getRecipientsToReplyTo_should_prefer_from_ifOtherIsIdentity() throws Exception {
when(message.getReplyTo()).thenReturn(REPLY_TO_ADDRESSES);
when(message.getHeader(ListHeaders.LIST_POST_HEADER)).thenReturn(LIST_POST_HEADER_VALUES);
when(message.getFrom()).thenReturn(FROM_ADDRESSES);
when(message.getRecipients(RecipientType.TO)).thenReturn(TO_ADDRESSES);
when(account.isAnIdentity(any(Address[].class))).thenReturn(true);
ReplyToAddresses result = replyToParser.getRecipientsToReplyTo(message, account);
assertArrayEquals(TO_ADDRESSES, result.to);
assertArrayEquals(EMPTY_ADDRESSES, result.cc);
}
@Test
public void getRecipientsToReplyTo_should_prefer_listPost_over_from_field() throws Exception {
when(message.getReplyTo()).thenReturn(EMPTY_ADDRESSES);
when(message.getHeader(ListHeaders.LIST_POST_HEADER)).thenReturn(LIST_POST_HEADER_VALUES);
when(message.getFrom()).thenReturn(FROM_ADDRESSES);
ReplyToAddresses result = replyToParser.getRecipientsToReplyTo(message, account);
assertArrayEquals(LIST_POST_ADDRESSES, result.to);
assertArrayEquals(EMPTY_ADDRESSES, result.cc);
verify(account).isAnIdentity(result.to);
}
@Test
public void getRecipientsToReplyTo_should_return_from_otherwise() throws Exception {
when(message.getReplyTo()).thenReturn(EMPTY_ADDRESSES);
when(message.getHeader(ListHeaders.LIST_POST_HEADER)).thenReturn(new String[0]);
when(message.getFrom()).thenReturn(FROM_ADDRESSES);
ReplyToAddresses result = replyToParser.getRecipientsToReplyTo(message, account);
assertArrayEquals(FROM_ADDRESSES, result.to);
assertArrayEquals(EMPTY_ADDRESSES, result.cc);
verify(account).isAnIdentity(result.to);
}
@Test
public void getRecipientsToReplyAllTo_should_returnFromAndToAndCcRecipients() throws Exception {
when(message.getReplyTo()).thenReturn(EMPTY_ADDRESSES);
when(message.getHeader(ListHeaders.LIST_POST_HEADER)).thenReturn(new String[0]);
when(message.getFrom()).thenReturn(FROM_ADDRESSES);
when(message.getRecipients(RecipientType.TO)).thenReturn(TO_ADDRESSES);
when(message.getRecipients(RecipientType.CC)).thenReturn(CC_ADDRESSES);
ReplyToAddresses recipientsToReplyAllTo = replyToParser.getRecipientsToReplyAllTo(message, account);
assertArrayEquals(arrayConcatenate(FROM_ADDRESSES, TO_ADDRESSES, Address.class), recipientsToReplyAllTo.to);
assertArrayEquals(CC_ADDRESSES, recipientsToReplyAllTo.cc);
}
@Test
public void getRecipientsToReplyAllTo_should_excludeIdentityAddresses() throws Exception {
when(message.getReplyTo()).thenReturn(EMPTY_ADDRESSES);
when(message.getHeader(ListHeaders.LIST_POST_HEADER)).thenReturn(new String[0]);
when(message.getFrom()).thenReturn(EMPTY_ADDRESSES);
when(message.getRecipients(RecipientType.TO)).thenReturn(TO_ADDRESSES);
when(message.getRecipients(RecipientType.CC)).thenReturn(CC_ADDRESSES);
Address excludedCcAddress = CC_ADDRESSES[1];
Address excludedToAddress = TO_ADDRESSES[0];
when(account.isAnIdentity(eq(excludedToAddress))).thenReturn(true);
when(account.isAnIdentity(eq(excludedCcAddress))).thenReturn(true);
ReplyToAddresses recipientsToReplyAllTo = replyToParser.getRecipientsToReplyAllTo(message, account);
assertArrayEquals(arrayExcept(TO_ADDRESSES, excludedToAddress), recipientsToReplyAllTo.to);
assertArrayEquals(arrayExcept(CC_ADDRESSES, excludedCcAddress), recipientsToReplyAllTo.cc);
}
@Test
public void getRecipientsToReplyAllTo_should_excludeDuplicates() throws Exception {
when(message.getReplyTo()).thenReturn(REPLY_TO_ADDRESSES);
when(message.getFrom()).thenReturn(arrayConcatenate(FROM_ADDRESSES, REPLY_TO_ADDRESSES, Address.class));
when(message.getRecipients(RecipientType.TO)).thenReturn(arrayConcatenate(FROM_ADDRESSES, TO_ADDRESSES, Address.class));
when(message.getRecipients(RecipientType.CC)).thenReturn(arrayConcatenate(CC_ADDRESSES, TO_ADDRESSES, Address.class));
when(message.getHeader(ListHeaders.LIST_POST_HEADER)).thenReturn(new String[0]);
ReplyToAddresses recipientsToReplyAllTo = replyToParser.getRecipientsToReplyAllTo(message, account);
assertArrayContainsAll(REPLY_TO_ADDRESSES, recipientsToReplyAllTo.to);
assertArrayContainsAll(FROM_ADDRESSES, recipientsToReplyAllTo.to);
assertArrayContainsAll(TO_ADDRESSES, recipientsToReplyAllTo.to);
int totalExpectedAddresses = REPLY_TO_ADDRESSES.length + FROM_ADDRESSES.length + TO_ADDRESSES.length;
assertEquals(totalExpectedAddresses, recipientsToReplyAllTo.to.length);
assertArrayEquals(CC_ADDRESSES, recipientsToReplyAllTo.cc);
}
public <T> void assertArrayContainsAll(T[] expecteds, T[] actual) {
for (T expected : expecteds) {
assertTrue("Element must be in array (" + expected + ")", Utility.arrayContains(actual, expected));
}
}
public <T> T[] arrayConcatenate(T[] first, T[] second, Class<T> cls) {
// noinspection unchecked
T[] result = (T[]) Array.newInstance(cls, first.length + second.length);
System.arraycopy(first, 0, result, 0, first.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
public <T> T[] arrayExcept(T[] in, T except) {
ArrayList<T> result = new ArrayList<>();
for (T element : in) {
if (!element.equals(except)) {
result.add(element);
}
}
// noinspection unchecked, it's a hack but it works ♪
return result.toArray((T[]) new Object[result.size()]);
}
}