Merge pull request #1316 from philipwhiuk/webDavFixes

WebDAV fixes
This commit is contained in:
cketti 2016-04-29 03:16:57 +02:00
commit 9240e25921
4 changed files with 282 additions and 159 deletions

View file

@ -161,7 +161,7 @@ class WebDavFolder extends Folder<WebDavMessage> {
messageCount = dataset.getMessageCount();
}
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_WEBDAV) {
Log.v(LOG_TAG, "Counted messages and webdav returned: "+messageCount);
Log.v(LOG_TAG, "Counted messages and webdav returned: " + messageCount);
}
return messageCount;
@ -257,7 +257,6 @@ class WebDavFolder extends Folder<WebDavMessage> {
headers.put("Brief", "t");
headers.put("Range", "rows=" + start + "-" + end);
DataSet dataset = store.processRequest(this.mFolderUrl, "SEARCH", messageBody, headers);
uids = dataset.getUids();
Map<String, String> uidToUrl = dataset.getUidToUrl();
uidsLength = uids.length;
@ -354,7 +353,7 @@ class WebDavFolder extends Folder<WebDavMessage> {
* associated. Verify and fix that
*/
if (wdMessage.getUrl().equals("")) {
wdMessage.setUrl(getMessageUrls(new String[] { wdMessage.getUid() }).get(wdMessage.getUid()));
wdMessage.setUrl(getMessageUrls(new String[]{wdMessage.getUid()}).get(wdMessage.getUid()));
Log.i(LOG_TAG, "Fetching messages with UID = '" + wdMessage.getUid() + "', URL = '"
+ wdMessage.getUrl() + "'");
if (wdMessage.getUrl().equals("")) {
@ -505,7 +504,7 @@ class WebDavFolder extends Folder<WebDavMessage> {
try {
wdMessage.setFlagInternal(Flag.SEEN, uidToReadStatus.get(wdMessage.getUid()));
} catch (NullPointerException e) {
Log.v(LOG_TAG,"Under some weird circumstances, setting the read status when syncing from webdav threw an NPE. Skipping.");
Log.v(LOG_TAG, "Under some weird circumstances, setting the read status when syncing from webdav threw an NPE. Skipping.");
}
if (listener != null) {
@ -532,12 +531,12 @@ class WebDavFolder extends Folder<WebDavMessage> {
}
if (startMessages.size() > 10) {
List<WebDavMessage> newMessages = new ArrayList<WebDavMessage>(startMessages.size() - 10);
List<WebDavMessage> newMessages = new ArrayList<WebDavMessage>(startMessages.size() - 10);
for (int i = 0, count = startMessages.size(); i < count; i++) {
if (i < 10) {
messages.add(i, startMessages.get(i));
} else {
newMessages.add(i - 10,startMessages.get(i));
newMessages.add(i - 10, startMessages.get(i));
}
}

View file

@ -55,7 +55,7 @@ public class WebDavStore extends RemoteStore {
/**
* Decodes a WebDavStore URI.
*
* <p/>
* <p>Possible forms:</p>
* <pre>
* webdav://user:password@server:port ConnectionSecurity.NONE
@ -156,11 +156,8 @@ public class WebDavStore extends RemoteStore {
/**
* Creates a WebDavStore URI with the supplied settings.
*
* @param server
* The {@link ServerSettings} object that holds the server settings.
*
* @param server The {@link ServerSettings} object that holds the server settings.
* @return A WebDavStore URI that holds the same information as the {@code server} parameter.
*
* @see StoreConfig#getStoreUri()
* @see WebDavStore#decodeUri(String)
*/
@ -198,7 +195,7 @@ public class WebDavStore extends RemoteStore {
try {
return new URI(scheme, userInfo, server.host, server.port, uriPath,
null, null).toString();
null, null).toString();
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Can't create WebDavStore URI", e);
}
@ -227,7 +224,7 @@ public class WebDavStore extends RemoteStore {
private Map<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
public WebDavStore(StoreConfig storeConfig, WebDavHttpClient.WebDavHttpClientFactory clientFactory)
throws MessagingException {
throws MessagingException {
super(storeConfig, null);
mHttpClientFactory = clientFactory;
@ -306,7 +303,7 @@ public class WebDavStore extends RemoteStore {
}
@Override
public List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException {
public List<? extends Folder> getPersonalNamespaces(boolean forceListAll) throws MessagingException {
List<Folder> folderList = new LinkedList<Folder>();
/**
* We have to check authentication here so we have the proper URL stored
@ -383,10 +380,9 @@ public class WebDavStore extends RemoteStore {
WebDavFolder wdFolder = null;
String folderName = getFolderName(folderUrl);
if (folderName != null) {
if (!this.mFolderList.containsKey(folderName)) {
wdFolder = new WebDavFolder(this, folderName);
wdFolder = getFolder(folderName);
if (wdFolder != null) {
wdFolder.setUrl(folderUrl);
mFolderList.put(folderName, wdFolder);
}
}
// else: Unknown URL format => NO Folder created
@ -426,11 +422,12 @@ public class WebDavStore extends RemoteStore {
}
@Override
public Folder getFolder(String name) {
public WebDavFolder getFolder(String name) {
WebDavFolder folder;
if ((folder = this.mFolderList.get(name)) == null) {
folder = new WebDavFolder(this, name);
mFolderList.put(name, folder);
}
return folder;
@ -494,7 +491,7 @@ public class WebDavStore extends RemoteStore {
builder.append("SELECT \"DAV:visiblecount\"\r\n");
builder.append(" FROM \"\"\r\n");
builder.append(" WHERE \"DAV:ishidden\"=False AND \"DAV:isfolder\"=False AND \"urn:schemas:httpmail:read\"=")
.append(messageState).append("\r\n");
.append(messageState).append("\r\n");
builder.append(" GROUP BY \"DAV:ishidden\"\r\n");
builder.append("</a:sql></a:searchrequest>\r\n");
return builder.toString();
@ -632,7 +629,7 @@ public class WebDavStore extends RemoteStore {
* @throws MessagingException
*/
public boolean authenticate()
throws MessagingException {
throws MessagingException {
try {
if (mAuthentication == WebDavConstants.AUTH_TYPE_NONE) {
ConnectionInfo info = doInitialConnection();
@ -652,7 +649,7 @@ public class WebDavStore extends RemoteStore {
throw new MessagingException("Invalid username or password for authentication.");
} else {
throw new MessagingException("Error with code " + response.getStatusLine().getStatusCode() +
" during request processing: " + response.getStatusLine().toString());
" during request processing: " + response.getStatusLine().toString());
}
} else if (info.requiredAuthType == WebDavConstants.AUTH_TYPE_FORM_BASED) {
doFBA(info);
@ -679,7 +676,7 @@ public class WebDavStore extends RemoteStore {
* @throws MessagingException
*/
private ConnectionInfo doInitialConnection()
throws MessagingException {
throws MessagingException {
// For our initial connection we are sending an empty GET request to
// the configured URL, which should be in the following form:
// https://mail.server.com/Exchange/alias
@ -706,8 +703,8 @@ public class WebDavStore extends RemoteStore {
// an authentication header for basic authentication.
info.requiredAuthType = WebDavConstants.AUTH_TYPE_BASIC;
} else if ((info.statusCode >= 200 && info.statusCode < 300) || // Success
(info.statusCode >= 300 && info.statusCode < 400) || // Redirect
(info.statusCode == 440)) { // Unauthorized
(info.statusCode >= 300 && info.statusCode < 400) || // Redirect
(info.statusCode == 440)) { // Unauthorized
// We will handle all 3 situations the same. First we take an educated
// guess at where the authorization DLL is located. If this is this
// doesn't work, then we'll use the redirection URL for OWA login given
@ -730,7 +727,7 @@ public class WebDavStore extends RemoteStore {
}
} else {
throw new IOException("Error with code " + info.statusCode + " during request processing: " +
response.getStatusLine().toString());
response.getStatusLine().toString());
}
} catch (SSLException e) {
throw new CertificateValidationException(e.getMessage(), e);
@ -748,9 +745,9 @@ public class WebDavStore extends RemoteStore {
* @throws MessagingException
*/
public void doFBA(ConnectionInfo info)
throws IOException, MessagingException {
throws IOException, MessagingException {
// Clear out cookies from any previous authentication.
if(mAuthCookies != null) mAuthCookies.clear();
if (mAuthCookies != null) mAuthCookies.clear();
WebDavHttpClient httpClient = getHttpClient();
@ -820,12 +817,12 @@ public class WebDavStore extends RemoteStore {
// Reconstruct the login URL based on the original login URL and the form action.
URI finalUri = new URI(loginUri.getScheme(),
loginUri.getUserInfo(),
loginUri.getHost(),
loginUri.getPort(),
urlPath,
null,
null);
loginUri.getUserInfo(),
loginUri.getHost(),
loginUri.getPort(),
urlPath,
null,
null);
loginUrl = finalUri.toString();
}
@ -859,7 +856,7 @@ public class WebDavStore extends RemoteStore {
* @throws IOException
*/
private String findFormAction(InputStream istream)
throws IOException {
throws IOException {
String formAction = null;
BufferedReader reader = new BufferedReader(new InputStreamReader(istream), 4096);
@ -889,7 +886,7 @@ public class WebDavStore extends RemoteStore {
}
private boolean testAuthenticationResponse(HttpResponse response)
throws MessagingException {
throws MessagingException {
boolean authenticated = false;
int statusCode = response.getStatusLine().getStatusCode();
// Exchange 2007 will return a 302 status code no matter what.
@ -979,7 +976,7 @@ public class WebDavStore extends RemoteStore {
private InputStream sendRequest(String url, String method, StringEntity messageBody,
Map<String, String> headers, boolean tryAuth)
throws MessagingException {
throws MessagingException {
if (url == null || method == null) {
return null;
}
@ -1028,7 +1025,7 @@ public class WebDavStore extends RemoteStore {
}
} else if (statusCode < 200 || statusCode >= 300) {
throw new IOException("Error with code " + statusCode + " during request processing: " +
response.getStatusLine().toString());
response.getStatusLine().toString());
}
if (entity != null) {
@ -1055,17 +1052,17 @@ public class WebDavStore extends RemoteStore {
* response.
*/
DataSet processRequest(String url, String method, String messageBody, Map<String, String> headers)
throws MessagingException {
throws MessagingException {
return processRequest(url, method, messageBody, headers, true);
}
DataSet processRequest(String url, String method, String messageBody, Map<String, String> headers,
boolean needsParsing)
throws MessagingException {
throws MessagingException {
DataSet dataset = new DataSet();
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_WEBDAV) {
Log.v(LOG_TAG, "processRequest url = '" + url + "', method = '" + method + "', messageBody = '"
+ messageBody + "'");
+ messageBody + "'");
}
if (url == null ||
@ -1101,7 +1098,7 @@ public class WebDavStore extends RemoteStore {
throw new MessagingException("SAXException in processRequest() ", se);
} catch (ParserConfigurationException pce) {
Log.e(LOG_TAG, "ParserConfigurationException in processRequest() " + pce + "\nTrace: "
+ WebDavUtils.processException(pce));
+ WebDavUtils.processException(pce));
throw new MessagingException("ParserConfigurationException in processRequest() ", pce);
}

View file

@ -19,6 +19,9 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
@ -36,12 +39,14 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyMap;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -55,6 +60,7 @@ import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -76,9 +82,15 @@ public class WebDavFolderTest {
private HttpResponse mockHttpResponse;
@Mock
private StatusLine mockStatusLine;
@Captor
private ArgumentCaptor<Map<String, String>> headerCaptor;
private WebDavFolder folder;
private File tempDirectory;
private WebDavFolder destinationFolder;
private String moveOrCopyXml = "<xml>MoveOrCopyXml</xml>";
private HashMap<String, String> moveOrCopyHeaders;
private List<WebDavMessage> messages;
@Before
public void before() throws MessagingException, IOException {
@ -88,13 +100,16 @@ public class WebDavFolderTest {
when(mockStore.getStoreConfig()).thenReturn(mockStoreConfig);
folder = new WebDavFolder(mockStore, "testFolder");
setupTempDirectory();
}
tempDirectory = new File("temp");
private void setupTempDirectory() {
File tempDirectory = new File("temp");
if (!tempDirectory.exists()) {
assertTrue(tempDirectory.mkdir());
tempDirectory.deleteOnExit();
}
BinaryTempFileBody.setTempDirectory(new File("temp"));
BinaryTempFileBody.setTempDirectory(tempDirectory);
}
private WebDavFolder setupDestinationFolder() {
@ -103,14 +118,31 @@ public class WebDavFolderTest {
return destinationFolder;
}
private void verifyUrlMappingRequest(String uid, String url) throws MessagingException {
private void setupFolderWithMessages(int count) throws MessagingException {
HashMap<String, String> headers = new HashMap<>();
headers.put("Brief", "t");
String messageCountXml = "<xml>MessageCountXml</xml>";
when(mockStore.getMessageCountXml("True")).thenReturn(messageCountXml);
when(mockStore.processRequest("https://localhost/webDavStoreUrl/testFolder",
"SEARCH", messageCountXml, headers)).thenReturn(mockDataSet);
when(mockDataSet.getMessageCount()).thenReturn(count);
folder.getMessageCount();
}
private WebDavMessage createWebDavMessage(String uid) {
WebDavMessage webDavMessage = mock(WebDavMessage.class);
when(webDavMessage.getUid()).thenReturn(uid);
return webDavMessage;
}
private void setupGetUrlsRequestResponse(String uid, String url) throws MessagingException {
String getUrlsXml = "<xml>GetUrls</xml>";
when(mockStore.getMessageUrlsXml(new String[]{uid})).thenReturn(getUrlsXml);
HashMap<String, String> headers = new HashMap<>();
headers.put("Brief", "t");
when(mockStore.processRequest("https://localhost/webDavStoreUrl/testFolder", "SEARCH", getUrlsXml, headers))
.thenReturn(mockDataSet);
Map<String, String> urlUids = new HashMap<String,String>();
Map<String, String> urlUids = new HashMap<String, String>();
urlUids.put(uid, url);
when(mockDataSet.getUidToUrl()).thenReturn(urlUids);
}
@ -121,7 +153,7 @@ public class WebDavFolderTest {
.thenReturn(mockDataSet);
List<WebDavMessage> messages = new ArrayList<>();
for(int i = 0; i < 5; i++) {
for (int i = 0; i < 5; i++) {
WebDavMessage mockMessage = mock(WebDavMessage.class);
messages.add(mockMessage);
}
@ -137,7 +169,7 @@ public class WebDavFolderTest {
.thenReturn(mockDataSet);
List<WebDavMessage> messages = new ArrayList<>();
for(int i = 0; i < 15; i++) {
for (int i = 0; i < 15; i++) {
WebDavMessage mockMessage = mock(WebDavMessage.class);
messages.add(mockMessage);
}
@ -152,7 +184,7 @@ public class WebDavFolderTest {
.thenReturn(mockDataSet);
List<WebDavMessage> messages = new ArrayList<>();
for(int i = 0; i < 5; i++) {
for (int i = 0; i < 5; i++) {
WebDavMessage mockMessage = mock(WebDavMessage.class);
messages.add(mockMessage);
}
@ -167,7 +199,7 @@ public class WebDavFolderTest {
.thenReturn(mockDataSet);
List<WebDavMessage> messages = new ArrayList<>();
for(int i = 0; i < 25; i++) {
for (int i = 0; i < 25; i++) {
WebDavMessage mockMessage = mock(WebDavMessage.class);
messages.add(mockMessage);
}
@ -179,26 +211,26 @@ public class WebDavFolderTest {
@Test
public void folder_can_fetch_sensible_body_data_and_notifies_listener()
throws MessagingException, IOException, URISyntaxException {
throws MessagingException, IOException, URISyntaxException {
setupStoreForMessageFetching();
List<WebDavMessage> messages = setup25MessagesToFetch();
when(mockHttpClient.executeOverride(any(HttpUriRequest.class), any(HttpContext.class))).thenAnswer(
new Answer<HttpResponse>() {
@Override
public HttpResponse answer(InvocationOnMock invocation) throws Throwable {
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
when(statusLine.getStatusCode()).thenReturn(200);
new Answer<HttpResponse>() {
@Override
public HttpResponse answer(InvocationOnMock invocation) throws Throwable {
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
when(statusLine.getStatusCode()).thenReturn(200);
BasicHttpEntity httpEntity = new BasicHttpEntity();
String body = "";
httpEntity.setContent(new ByteArrayInputStream(body.getBytes("UTF-8")));
when(httpResponse.getEntity()).thenReturn(httpEntity);
return httpResponse;
}
});
BasicHttpEntity httpEntity = new BasicHttpEntity();
String body = "";
httpEntity.setContent(new ByteArrayInputStream(body.getBytes("UTF-8")));
when(httpResponse.getEntity()).thenReturn(httpEntity);
return httpResponse;
}
});
FetchProfile profile = new FetchProfile();
profile.add(FetchProfile.Item.BODY_SANE);
@ -217,9 +249,9 @@ public class WebDavFolderTest {
private List<WebDavMessage> setup25MessagesToFetch() {
List<WebDavMessage> messages = new ArrayList<>();
for(int i = 0; i < 25; i++) {
WebDavMessage message = new WebDavMessage("message"+i, folder);
message.setUrl("http://example.org/Exchange/user/Inbox/message"+i+".EML");
for (int i = 0; i < 25; i++) {
WebDavMessage message = new WebDavMessage("message" + i, folder);
message.setUrl("http://example.org/Exchange/user/Inbox/message" + i + ".EML");
messages.add(message);
}
return messages;
@ -231,16 +263,16 @@ public class WebDavFolderTest {
List<WebDavMessage> messages = setup25MessagesToFetch();
when(mockHttpClient.executeOverride(any(HttpUriRequest.class), any(HttpContext.class))).thenAnswer(
new Answer<HttpResponse>() {
@Override
public HttpResponse answer(InvocationOnMock invocation) throws Throwable {
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
when(statusLine.getStatusCode()).thenReturn(200);
return httpResponse;
}
});
new Answer<HttpResponse>() {
@Override
public HttpResponse answer(InvocationOnMock invocation) throws Throwable {
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
when(statusLine.getStatusCode()).thenReturn(200);
return httpResponse;
}
});
FetchProfile profile = new FetchProfile();
profile.add(FetchProfile.Item.BODY_SANE);
@ -255,23 +287,23 @@ public class WebDavFolderTest {
List<WebDavMessage> messages = setup25MessagesToFetch();
when(mockHttpClient.executeOverride(any(HttpUriRequest.class), any(HttpContext.class))).thenAnswer(
new Answer<HttpResponse>() {
@Override
public HttpResponse answer(InvocationOnMock invocation) throws Throwable {
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
when(statusLine.getStatusCode()).thenReturn(200);
new Answer<HttpResponse>() {
@Override
public HttpResponse answer(InvocationOnMock invocation) throws Throwable {
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
when(statusLine.getStatusCode()).thenReturn(200);
BasicHttpEntity httpEntity = new BasicHttpEntity();
InputStream mockInputStream = mock(InputStream.class);
when(mockInputStream.read(any(byte[].class), anyInt(), anyInt())).thenReturn(1).thenReturn(-1);
doThrow(new IOException("Test")).when(mockInputStream).close();
httpEntity.setContent(mockInputStream);
when(httpResponse.getEntity()).thenReturn(httpEntity);
return httpResponse;
}
});
BasicHttpEntity httpEntity = new BasicHttpEntity();
InputStream mockInputStream = mock(InputStream.class);
when(mockInputStream.read(any(byte[].class), anyInt(), anyInt())).thenReturn(1).thenReturn(-1);
doThrow(new IOException("Test")).when(mockInputStream).close();
httpEntity.setContent(mockInputStream);
when(httpResponse.getEntity()).thenReturn(httpEntity);
return httpResponse;
}
});
FetchProfile profile = new FetchProfile();
profile.add(FetchProfile.Item.BODY_SANE);
@ -311,74 +343,146 @@ public class WebDavFolderTest {
@Test
public void can_fetch_message_count() throws Exception {
int messageCount = 23;
HashMap<String, String> headers = new HashMap<>();
headers.put("Brief", "t");
String messageCountXml = "<xml>MessageCountXml</xml>";
when(mockStore.getMessageCountXml("True")).thenReturn(messageCountXml);
when(mockStore.processRequest("https://localhost/webDavStoreUrl/testFolder",
"SEARCH", messageCountXml, headers)).thenReturn(mockDataSet);
when(mockDataSet.getMessageCount()).thenReturn(23);
assertEquals(23, folder.getMessageCount());
when(mockDataSet.getMessageCount()).thenReturn(messageCount);
int result = folder.getMessageCount();
assertEquals(messageCount, result);
}
@Test
public void can_fetch_unread_message_count() throws Exception {
int unreadMessageCount = 13;
HashMap<String, String> headers = new HashMap<>();
headers.put("Brief", "t");
String messageCountXml = "<xml>MessageCountXml</xml>";
when(mockStore.getMessageCountXml("False")).thenReturn(messageCountXml);
when(mockStore.processRequest("https://localhost/webDavStoreUrl/testFolder",
"SEARCH", messageCountXml, headers)).thenReturn(mockDataSet);
when(mockDataSet.getMessageCount()).thenReturn(13);
assertEquals(13, folder.getUnreadMessageCount());
when(mockDataSet.getMessageCount()).thenReturn(unreadMessageCount);
int result = folder.getUnreadMessageCount();
assertEquals(unreadMessageCount, result);
}
@Test
public void getMessages_should_request_message_search() throws MessagingException {
int totalMessages = 23;
int messageStart = 1;
int messageEnd = 11;
setupFolderWithMessages(totalMessages);
String messagesXml = "<xml>MessagesXml</xml>";
buildSearchResponse(mockDataSet);
when(mockStore.getMessagesXml()).thenReturn(messagesXml);
when(mockStore.processRequest(eq("https://localhost/webDavStoreUrl/testFolder"), eq("SEARCH"),
eq(messagesXml), Matchers.<Map<String, String>>any())).thenReturn(mockDataSet);
folder.getMessages(1, 11, new Date(), listener);
verify(listener, times(5)).messageStarted(anyString(), anyInt(), eq(5));
verify(listener, times(5)).messageFinished(any(WebDavMessage.class), anyInt(), eq(5));
}
@Test
public void getMessages_shouldProvideCorrectHeadersInRequest() throws MessagingException {
int totalMessages = 23;
int messageStart = 1;
int messageEnd = 11;
setupFolderWithMessages(totalMessages);
String messagesXml = "<xml>MessagesXml</xml>";
buildSearchResponse(mockDataSet);
when(mockStore.getMessagesXml()).thenReturn(messagesXml);
when(mockStore.processRequest(eq("https://localhost/webDavStoreUrl/testFolder"), eq("SEARCH"),
eq(messagesXml), Matchers.<Map<String, String>>any())).thenReturn(mockDataSet);
folder.getMessages(messageStart, messageEnd, new Date(), listener);
verify(mockStore, times(2)).processRequest(anyString(), anyString(), anyString(),
headerCaptor.capture());
assertEquals(2, headerCaptor.getValue().size());
assertEquals("t", headerCaptor.getValue().get("Brief"));
assertEquals("rows=" + (totalMessages - (messageEnd)) + "-" + (totalMessages - messageStart)
, headerCaptor.getValue().get("Range"));
}
private void buildSearchResponse(DataSet mockDataSet) {
String[] uids = new String[]{"uid1", "uid2", "uid3", "uid4", "uid5"};
HashMap<String, String> uidToUrls = new HashMap<>();
uidToUrls.put("uid1", "url1");
uidToUrls.put("uid2", "url2");
uidToUrls.put("uid3", "url3");
uidToUrls.put("uid4", "url4");
uidToUrls.put("uid5", "url5");
when(mockDataSet.getUids()).thenReturn(uids);
when(mockDataSet.getUidToUrl()).thenReturn(uidToUrls);
}
@Test(expected = MessagingException.class)
public void getMessages_should_throw_message_exception_if_requesting_messages_from_empty_folder()
throws MessagingException {
folder.getMessages(0, 10, new Date(), listener);
}
private void setupMoveOrCopy() throws MessagingException {
destinationFolder = setupDestinationFolder();
String uid = "uid1";
String url = "url1";
messages = singletonList(createWebDavMessage(uid));
setupGetUrlsRequestResponse(uid, url);
when(mockStore.getMoveOrCopyMessagesReadXml(eq(new String[]{url}), anyBoolean())).thenReturn(moveOrCopyXml);
moveOrCopyHeaders = new HashMap<>();
moveOrCopyHeaders.put("Destination", "https://localhost/webDavStoreUrl/destFolder");
moveOrCopyHeaders.put("Brief", "t");
moveOrCopyHeaders.put("If-Match", "*");
}
@Test
public void moveMessages_should_requestMoveXml() throws Exception {
setupMoveOrCopy();
folder.moveMessages(messages, destinationFolder);
verify(mockStore).getMoveOrCopyMessagesReadXml(eq(new String[]{"url1"}),
eq(true));
}
@Test
public void moveMessages_should_send_move_command() throws Exception {
WebDavFolder destinationFolder = setupDestinationFolder();
String uid = "uid1";
String url = "url1";
List<WebDavMessage> messages = singletonList(createWebDavMessage(uid));
verifyUrlMappingRequest(uid, url);
setupMoveOrCopy();
String moveOrCopyXml = "<xml>MoveOrCopyXml</xml>";
when(mockStore.getMoveOrCopyMessagesReadXml(new String[]{url}, true)).thenReturn(moveOrCopyXml);
HashMap<String, String> headers = new HashMap<>();
headers.put("Destination", "https://localhost/webDavStoreUrl/destFolder");
headers.put("Brief", "t");
headers.put("If-Match", "*");
when(mockStore.processRequest("https://localhost/webDavStoreUrl/testFolder", "BMOVE", moveOrCopyXml, headers, false))
.thenReturn(mockDataSet);
folder.moveMessages(messages, destinationFolder);
Map<String, String> result = folder.moveMessages(messages, destinationFolder);
assertNull(result);
verify(mockStore).processRequest("https://localhost/webDavStoreUrl/testFolder", "BMOVE",
moveOrCopyXml, moveOrCopyHeaders, false);
}
@Test
public void copyMessages_should_requestCopyXml() throws Exception {
setupMoveOrCopy();
folder.copyMessages(messages, destinationFolder);
verify(mockStore).getMoveOrCopyMessagesReadXml(eq(new String[]{"url1"}),
eq(false));
}
@Test
public void copyMessages_should_send_copy_command() throws Exception {
WebDavFolder destinationFolder = setupDestinationFolder();
String uid = "uid1";
String url = "url1";
List<WebDavMessage> messages = singletonList(createWebDavMessage(uid));
verifyUrlMappingRequest(uid, url);
setupMoveOrCopy();
folder.copyMessages(messages, destinationFolder);
String moveOrCopyXml = "<xml>MoveOrCopyXml</xml>";
when(mockStore.getMoveOrCopyMessagesReadXml(new String[]{url},false)).thenReturn(moveOrCopyXml);
HashMap<String, String> headers = new HashMap<>();
headers.put("Destination", "https://localhost/webDavStoreUrl/destFolder");
headers.put("Brief", "t");
headers.put("If-Match", "*");
when(mockStore.processRequest("https://localhost/webDavStoreUrl/testFolder", "COPY", moveOrCopyXml, headers, false))
.thenReturn(mockDataSet);
Map<String, String> result = folder.copyMessages(messages, destinationFolder);
assertNull(result);
}
private WebDavMessage createWebDavMessage(String uid) {
WebDavMessage webDavMessage = mock(WebDavMessage.class);
when(webDavMessage.getUid()).thenReturn(uid);
return webDavMessage;
verify(mockStore).processRequest("https://localhost/webDavStoreUrl/testFolder", "BCOPY",
moveOrCopyXml, moveOrCopyHeaders, false);
}
@Test
@ -386,7 +490,6 @@ public class WebDavFolderTest {
when(mockHttpClient.executeOverride(any(HttpUriRequest.class), any(HttpContext.class))).thenReturn(mockHttpResponse);
when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine);
when(mockStatusLine.getStatusCode()).thenReturn(200);
List<Message> existingMessages = new ArrayList<>();
Message existingMessage = mock(Message.class);
existingMessages.add(existingMessage);

View file

@ -16,7 +16,9 @@ import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.ServerSettings.Type;
import com.fsck.k9.mail.filter.Base64;
import com.fsck.k9.mail.store.StoreConfig;
import javax.net.ssl.SSLException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
@ -45,6 +47,9 @@ import org.mockito.stubbing.OngoingStubbing;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.Collections;
import static junit.framework.Assert.assertSame;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
@ -104,22 +109,6 @@ public class WebDavStoreTest {
assertEquals("webdav+ssl+://user:password@example.org:123456/%7C%7C", result);
}
@Test
public void createUri_withSettingsWithExtras_shouldProvideUriWithExtras() {
HashMap<String, String> extras = new HashMap<>();
extras.put(WebDavStoreSettings.PATH_KEY, "path");
extras.put(WebDavStoreSettings.AUTH_PATH_KEY, "authPath");
extras.put(WebDavStoreSettings.MAILBOX_PATH_KEY, "mailboxPath");
ServerSettings serverSettings = new ServerSettings(
ServerSettings.Type.WebDAV, "example.org", 123456,
ConnectionSecurity.NONE, AuthType.PLAIN,
"user", "password", null, extras);
String result = WebDavStore.createUri(serverSettings);
assertEquals("webdav://user:password@example.org:123456/path%7CauthPath%7CmailboxPath", result);
}
@Test(expected = MessagingException.class)
public void constructor_withImapStoreUri_shouldThrow() throws Exception {
StoreConfig storeConfig = createStoreConfig("imap://user:password@imap.example.org");
@ -253,7 +242,6 @@ public class WebDavStoreTest {
assertEquals(WebDavFolder.class, result.getClass());
}
//FIXME: This doesn't check if the same instance is returned. Fails when changed to use assertSame().
@Test
public void getFolder_calledTwice_shouldReturnFirstInstance() throws Exception {
WebDavStore webDavStore = createDefaultWebDavStore();
@ -262,12 +250,51 @@ public class WebDavStoreTest {
Folder result = webDavStore.getFolder(folderName);
assertEquals(webDavFolder, result);
assertSame(webDavFolder, result);
}
//TODO: Break apart. This is testing more than one thing.
@Test
public void getPersonalNamespaces_shouldProvideListOfAllFolders() throws Exception {
public void getPersonalNamespaces_shouldRequestSpecialFolders() throws Exception {
StoreConfig storeConfig = createStoreConfig("webdav://user:password@example.org:80");
WebDavStore webDavStore = new WebDavStore(storeConfig, mockHttpClientFactory);
configureHttpResponses(UNAUTHORIZED_401_RESPONSE, OK_200_RESPONSE, createOkPropfindResponse(),
createOkSearchResponse());
webDavStore.getPersonalNamespaces(true);
List<HttpGeneric> requests = requestCaptor.getAllValues();
assertEquals(4, requests.size()); // AUTH + 2
assertEquals("PROPFIND", requests.get(2).getMethod()); //Special Folders
}
@Test
public void getPersonalNamespaces_shouldSetSpecialFolderNames() throws Exception {
StoreConfig storeConfig = createStoreConfig("webdav://user:password@example.org:80");
WebDavStore webDavStore = new WebDavStore(storeConfig, mockHttpClientFactory);
configureHttpResponses(UNAUTHORIZED_401_RESPONSE, OK_200_RESPONSE, createOkPropfindResponse(),
createOkSearchResponse());
webDavStore.getPersonalNamespaces(true);
verify(storeConfig).setInboxFolderName("Inbox");
}
@Test
public void getPersonalNamespaces_shouldRequestFolderList() throws Exception {
StoreConfig storeConfig = createStoreConfig("webdav://user:password@example.org:80");
WebDavStore webDavStore = new WebDavStore(storeConfig, mockHttpClientFactory);
configureHttpResponses(UNAUTHORIZED_401_RESPONSE, OK_200_RESPONSE, createOkPropfindResponse(),
createOkSearchResponse());
webDavStore.getPersonalNamespaces(true);
List<HttpGeneric> requests = requestCaptor.getAllValues();
assertEquals(4, requests.size()); // AUTH + SPECIALFOLDERS + 1
assertEquals("SEARCH", requests.get(3).getMethod());
}
@Test
public void getPersonalNamespaces_shouldProvideListOfAllFoldersSentFromResponses() throws Exception {
StoreConfig storeConfig = createStoreConfig("webdav://user:password@example.org:80");
WebDavStore webDavStore = new WebDavStore(storeConfig, mockHttpClientFactory);
configureHttpResponses(UNAUTHORIZED_401_RESPONSE, OK_200_RESPONSE, createOkPropfindResponse(),
@ -275,11 +302,8 @@ public class WebDavStoreTest {
List<? extends Folder> folders = webDavStore.getPersonalNamespaces(true);
verify(storeConfig).setInboxFolderName("Inbox");
List<HttpGeneric> requests = requestCaptor.getAllValues();
assertEquals(4, requests.size()); // AUTH + 2
assertEquals("PROPFIND", requests.get(2).getMethod()); //Special Folders
assertEquals("SEARCH", requests.get(3).getMethod()); //Folder List
assertEquals(3, folders.size());
}