Skip to content
This repository was archived by the owner on Mar 14, 2025. It is now read-only.

Commit 06dfcda

Browse files
committed
Test WebDAV Provider
1 parent 9390614 commit 06dfcda

24 files changed

+1627
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.cryptomator.cloudaccess.webdav;
2+
3+
import okhttp3.*;
4+
import org.junit.jupiter.api.Test;
5+
import org.mockito.ArgumentMatchers;
6+
import org.mockito.Mockito;
7+
8+
import java.io.IOException;
9+
import java.nio.file.Path;
10+
11+
class HttpLoggingInterceptorTest {
12+
13+
private final HttpLoggingInterceptor.Logger logger = Mockito.mock(HttpLoggingInterceptor.Logger.class);
14+
private final HttpLoggingInterceptor.Logger spyLogger = Mockito.spy(logger);
15+
16+
private final Interceptor.Chain chain = Mockito.mock(Interceptor.Chain.class);
17+
18+
private final Path baseUrl = Path.of("https://www.nextcloud.com/cloud/remote.php/webdav");
19+
20+
@Test
21+
public void testLogging() throws IOException {
22+
23+
final var httpLoggingInterceptor = new HttpLoggingInterceptor(spyLogger);
24+
25+
final var request = new Request.Builder()
26+
.url(baseUrl.toString())
27+
.build();
28+
29+
final var response = new Response.Builder()
30+
.request(request)
31+
.protocol(Protocol.HTTP_1_1)
32+
.code(200)
33+
.addHeader("Authorization", "Basic Fooo")
34+
.addHeader("LoggedHeader", "Bar")
35+
.body(ResponseBody.create("Foo", MediaType.parse("application/json; charset=utf-8")))
36+
.message("")
37+
.build();
38+
39+
Mockito.when(chain.request()).thenReturn(request);
40+
Mockito.when(chain.proceed(ArgumentMatchers.any())).thenReturn(response);
41+
42+
httpLoggingInterceptor.intercept(chain);
43+
44+
Mockito.verify(spyLogger).log("--> GET https://www.nextcloud.com/cloud/remote.php/webdav http/1.1 (unknown length)");
45+
Mockito.verify(spyLogger).log("--> END GET");
46+
Mockito.verify(spyLogger).log("<-- 200 https://www.nextcloud.com/cloud/remote.php/webdav (0ms)");
47+
Mockito.verify(spyLogger).log("LoggedHeader: Bar");
48+
Mockito.verify(spyLogger).log("<-- END HTTP");
49+
}
50+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.cryptomator.cloudaccess.webdav;
2+
3+
import okhttp3.MediaType;
4+
import okio.Buffer;
5+
import org.cryptomator.cloudaccess.api.ProgressListener;
6+
import org.junit.Assert;
7+
import org.junit.jupiter.api.Test;
8+
import org.mockito.Mockito;
9+
10+
import java.io.BufferedReader;
11+
import java.io.IOException;
12+
import java.io.InputStream;
13+
import java.io.InputStreamReader;
14+
import java.nio.charset.StandardCharsets;
15+
import java.util.stream.Collectors;
16+
17+
class ProgressRequestWrapperTest {
18+
19+
@Test
20+
public void updateProgressWhenWriteToProgressRequestWrapper() throws IOException {
21+
final var buffer = new Buffer();
22+
final var content = new BufferedReader(new InputStreamReader(load(), StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n"));
23+
24+
final var progressListener = Mockito.mock(ProgressListener.class);
25+
26+
final var spyProgressListener = Mockito.spy(progressListener);
27+
28+
final var progressRequestWrapper = new ProgressRequestWrapper(InputStreamRequestBody.from(load()), spyProgressListener);
29+
30+
Assert.assertEquals(8193, progressRequestWrapper.contentLength());
31+
Assert.assertEquals(MediaType.parse("application/octet-stream"), progressRequestWrapper.contentType());
32+
33+
progressRequestWrapper.writeTo(buffer);
34+
buffer.flush();
35+
36+
Assert.assertEquals(content, buffer.readString(StandardCharsets.UTF_8));
37+
38+
Mockito.verify(spyProgressListener).onProgress(8192);
39+
Mockito.verify(spyProgressListener).onProgress(8193);
40+
}
41+
42+
private InputStream load() {
43+
return getClass().getResourceAsStream("/progress-request-text.txt");
44+
}
45+
46+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.cryptomator.cloudaccess.webdav;
2+
3+
import okhttp3.MediaType;
4+
import okhttp3.ResponseBody;
5+
import org.cryptomator.cloudaccess.api.ProgressListener;
6+
import org.junit.Assert;
7+
import org.junit.jupiter.api.Test;
8+
import org.mockito.Mockito;
9+
10+
import java.io.BufferedReader;
11+
import java.io.InputStream;
12+
import java.io.InputStreamReader;
13+
import java.nio.charset.StandardCharsets;
14+
import java.util.stream.Collectors;
15+
16+
class ProgressResponseWrapperTest {
17+
18+
@Test
19+
public void updateProgressWhenReadFromProgressResponseWrapper() {
20+
final var thisContent = new BufferedReader(new InputStreamReader(load(), StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n"));
21+
22+
final var responseBody = ResponseBody.create(thisContent, MediaType.parse("application/octet-stream; charset=utf-8"));
23+
24+
final var progressListener = Mockito.mock(ProgressListener.class);
25+
final var spyProgressListener = Mockito.spy(progressListener);
26+
27+
final var progressResponseWrapper = new ProgressResponseWrapper(responseBody, spyProgressListener);
28+
29+
Assert.assertEquals(8193, progressResponseWrapper.contentLength());
30+
Assert.assertEquals(MediaType.parse("application/octet-stream; charset=utf-8"), progressResponseWrapper.contentType());
31+
32+
final var thatContent = new BufferedReader(new InputStreamReader(progressResponseWrapper.byteStream(), StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n"));
33+
34+
Assert.assertEquals(thatContent, thisContent);
35+
36+
Mockito.verify(spyProgressListener).onProgress(8192);
37+
Mockito.verify(spyProgressListener).onProgress(8193);
38+
}
39+
40+
private InputStream load() {
41+
return getClass().getResourceAsStream("/progress-request-text.txt");
42+
}
43+
44+
45+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package org.cryptomator.cloudaccess.webdav;
2+
3+
import org.cryptomator.cloudaccess.api.CloudItemList;
4+
import org.cryptomator.cloudaccess.api.CloudItemMetadata;
5+
import org.cryptomator.cloudaccess.api.CloudItemType;
6+
import org.junit.Assert;
7+
import org.junit.jupiter.api.Assertions;
8+
import org.junit.jupiter.api.BeforeEach;
9+
import org.junit.jupiter.api.Test;
10+
import org.xmlpull.v1.XmlPullParserException;
11+
12+
import java.io.IOException;
13+
import java.io.InputStream;
14+
import java.nio.file.Path;
15+
import java.util.*;
16+
17+
public class PropfindResponseParserTest {
18+
19+
private static final String RESPONSE_EMPTY_DIRECTORY = "empty-directory";
20+
private static final String RESPONSE_ONE_FILE_NO_SERVER = "directory-one-file-no-server";
21+
private static final String RESPONSE_ONE_FILE_AND_FOLDERS = "directory-and-file";
22+
private static final String RESPONSE_MAL_FORMATTED_XMLPULLPARSER_EXCEPTION = "malformatted-response-xmlpullparser";
23+
private static final String RESPONSE_MAL_FORMATTED_DATE = "malformatted-date-response";
24+
private static final String RESPONSE_MAL_FORMATTED_NO_PATH = "directory-and-file-no-path";
25+
private static final String RESPONSE_ONE_FILE_MULTI_STATUS = "file-multi-status";
26+
27+
private static final CloudItemMetadata testFolder = new CloudItemMetadata("Gelöschte Dateien", Path.of("/Gelöschte Dateien"), CloudItemType.FOLDER, Optional.empty(), Optional.empty());
28+
private static final CloudItemMetadata testFile = new CloudItemMetadata(
29+
"0.txt",
30+
Path.of("/0.txt"),
31+
CloudItemType.FILE,
32+
Optional.of(TestUtil.toInstant("Thu, 18 May 2017 9:49:41 GMT")),
33+
Optional.of(54175L));
34+
35+
private PropfindResponseParser propfindResponseParser;
36+
37+
@BeforeEach
38+
public void setup() {
39+
propfindResponseParser = new PropfindResponseParser();
40+
}
41+
42+
@Test
43+
public void testEmptyResponseLeadsToEmptyCloudNodeList() throws XmlPullParserException, IOException {
44+
final var propfindEntryList = propfindResponseParser.parse(load(RESPONSE_EMPTY_DIRECTORY));
45+
final var cloudNodeItemList = processDirList(propfindEntryList);
46+
47+
Assert.assertEquals(Collections.EMPTY_LIST, cloudNodeItemList.getItems());
48+
Assert.assertEquals(Optional.empty(), cloudNodeItemList.getNextPageToken());
49+
}
50+
51+
@Test
52+
public void testFolderWithoutServerPartInHrefResponseLeadsToFolderInCloudNodeListWithCompleteUrl() throws XmlPullParserException, IOException {
53+
final var propfindEntryList = propfindResponseParser.parse(load(RESPONSE_ONE_FILE_NO_SERVER));
54+
final var cloudNodeItemList = processDirList(propfindEntryList);
55+
56+
final var resultFolder = new CloudItemMetadata(
57+
"DYNTZMMHWLW25RZHWYEDHLFWIUZZG2",
58+
Path.of("/User7de989b/asdasdasd/d/OC/DYNTZMMHWLW25RZHWYEDHLFWIUZZG2"),
59+
CloudItemType.FOLDER,
60+
Optional.empty(),
61+
Optional.empty());
62+
63+
Assert.assertEquals(1, cloudNodeItemList.getItems().size());
64+
Assert.assertEquals(List.of(resultFolder), cloudNodeItemList.getItems());
65+
}
66+
67+
@Test
68+
public void testFileResponseLeadsToFileAndFoldersInCloudNodeList() throws XmlPullParserException, IOException {
69+
final var propfindEntryList = propfindResponseParser.parse(load(RESPONSE_ONE_FILE_AND_FOLDERS));
70+
final var cloudNodeItemList = processDirList(propfindEntryList);
71+
72+
Assert.assertEquals(2, cloudNodeItemList.getItems().size());
73+
Assert.assertEquals(List.of(testFile, testFolder), cloudNodeItemList.getItems());
74+
}
75+
76+
@Test
77+
public void testFileWithMalFormattedDateResponseLeadsToFileAndFoldersInCloudNodeListWithoutDate() throws XmlPullParserException, IOException {
78+
final var propfindEntryList = propfindResponseParser.parse(load(RESPONSE_MAL_FORMATTED_DATE));
79+
final var cloudNodeItemList = processDirList(propfindEntryList);
80+
81+
Assert.assertEquals(2, cloudNodeItemList.getItems().size());
82+
Assert.assertEquals(List.of(new CloudItemMetadata("0.txt", Path.of("/0.txt"), CloudItemType.FILE, Optional.empty(), Optional.of(54175L)),
83+
new CloudItemMetadata("Gelöschte Dateien", Path.of("/Gelöschte Dateien"), CloudItemType.FOLDER, Optional.empty(), Optional.empty())),
84+
cloudNodeItemList.getItems());
85+
}
86+
87+
@Test
88+
public void testFileMultiStatusLeadsToFolderInCloudNodeList() throws XmlPullParserException, IOException {
89+
final var propfindEntryList = propfindResponseParser.parse(load(RESPONSE_ONE_FILE_MULTI_STATUS));
90+
final var cloudNodeItemList = processDirList(propfindEntryList);
91+
92+
Assert.assertEquals(1, cloudNodeItemList.getItems().size());
93+
Assert.assertEquals(List.of(testFolder), cloudNodeItemList.getItems());
94+
}
95+
96+
@Test
97+
public void testFileNoPathResponseLeadsToFileAndFoldersInCloudNodeListWithoutDate() throws XmlPullParserException, IOException {
98+
final var propfindEntryList = propfindResponseParser.parse(load(RESPONSE_MAL_FORMATTED_NO_PATH));
99+
final var cloudNodeItemList = processDirList(propfindEntryList);
100+
101+
Assert.assertEquals(0, cloudNodeItemList.getItems().size());
102+
Assert.assertEquals(Collections.EMPTY_LIST, cloudNodeItemList.getItems());
103+
}
104+
105+
private CloudItemList processDirList(final List<PropfindEntryData> entryData) {
106+
var result = new CloudItemList(new ArrayList<>());
107+
108+
if(entryData.isEmpty()) {
109+
return result;
110+
}
111+
112+
entryData.sort(ASCENDING_BY_DEPTH);
113+
// after sorting the first entry is the parent
114+
// because it's depth is 1 smaller than the depth
115+
// ot the other entries, thus we skip the first entry
116+
for (PropfindEntryData childEntry : entryData.subList(1, entryData.size())) {
117+
result = result.add(List.of(childEntry.toCloudItem()));
118+
}
119+
return result;
120+
}
121+
122+
@Test
123+
public void testMallFormattedResponseLeadsToXmlPullParserException() {
124+
Assertions.assertThrows(XmlPullParserException.class, () -> propfindResponseParser.parse(load(RESPONSE_MAL_FORMATTED_XMLPULLPARSER_EXCEPTION)));
125+
}
126+
127+
private InputStream load(String resourceName) {
128+
return getClass().getResourceAsStream("/propfind-test-requests/" + resourceName + ".xml");
129+
}
130+
131+
private final Comparator<PropfindEntryData> ASCENDING_BY_DEPTH = Comparator.comparingInt(PropfindEntryData::getDepth);
132+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.cryptomator.cloudaccess.webdav;
2+
3+
import java.text.ParseException;
4+
import java.text.SimpleDateFormat;
5+
import java.time.Instant;
6+
import java.util.Locale;
7+
8+
public class TestUtil {
9+
10+
public static Instant toInstant(String date) {
11+
try {
12+
return new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US).parse(date).toInstant();
13+
} catch (ParseException e) {
14+
throw new IllegalStateException("Not valid date string provided", e);
15+
}
16+
}
17+
}

0 commit comments

Comments
 (0)