Skip to content

Commit 5d708cc

Browse files
committed
Add tests
1 parent 816dca2 commit 5d708cc

File tree

8 files changed

+413
-228
lines changed

8 files changed

+413
-228
lines changed

.idea/codeStyles/Project.xml

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/main/kotlin/at/bitfire/dav4jvm/exception/DavException.kt

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,6 @@ open class DavException(
4545

4646
// constructor from Response
4747

48-
internal constructor(
49-
message: String?,
50-
cause: Throwable? = null,
51-
httpResponseInfo: HttpResponseInfo
52-
): this(
53-
message = message,
54-
cause = cause,
55-
statusCode = httpResponseInfo.statusCode,
56-
requestExcerpt = httpResponseInfo.requestExcerpt,
57-
responseExcerpt = httpResponseInfo.responseExcerpt,
58-
errors = httpResponseInfo.errors
59-
)
60-
6148
/**
6249
* Takes the request, response and errors from a given HTTP response.
6350
*
@@ -69,10 +56,19 @@ open class DavException(
6956
message: String,
7057
cause: Throwable? = null,
7158
@WillNotClose response: Response
72-
) : this(
59+
) : this(message, cause, HttpResponseInfo.fromResponse(response))
60+
61+
private constructor(
62+
message: String?,
63+
cause: Throwable? = null,
64+
httpResponseInfo: HttpResponseInfo
65+
): this(
7366
message = message,
7467
cause = cause,
75-
httpResponseInfo = HttpResponseInfo.fromResponse(response)
68+
statusCode = httpResponseInfo.statusCode,
69+
requestExcerpt = httpResponseInfo.requestExcerpt,
70+
responseExcerpt = httpResponseInfo.responseExcerpt,
71+
errors = httpResponseInfo.errors
7672
)
7773

7874

src/main/kotlin/at/bitfire/dav4jvm/exception/HttpException.kt

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,6 @@ open class HttpException(
2828

2929
// constructor from Response
3030

31-
internal constructor(
32-
httpResponseInfo: HttpResponseInfo,
33-
message: String?,
34-
cause: Throwable? = null
35-
): this(
36-
message = message,
37-
cause = cause,
38-
statusCode = httpResponseInfo.statusCode,
39-
requestExcerpt = httpResponseInfo.requestExcerpt,
40-
responseExcerpt = httpResponseInfo.responseExcerpt,
41-
errors = httpResponseInfo.errors
42-
)
43-
4431
/**
4532
* Takes the request, response and errors from a given HTTP response.
4633
*
@@ -52,11 +39,34 @@ open class HttpException(
5239
@WillNotClose response: Response,
5340
message: String = "HTTP ${response.code} ${response.message}",
5441
cause: Throwable? = null
55-
) : this(
42+
) : this(HttpResponseInfo.fromResponse(response), message, cause)
43+
44+
private constructor(
45+
httpResponseInfo: HttpResponseInfo,
46+
message: String?,
47+
cause: Throwable? = null
48+
): this(
5649
message = message,
5750
cause = cause,
58-
httpResponseInfo = HttpResponseInfo.fromResponse(response)
51+
statusCode = httpResponseInfo.statusCode,
52+
requestExcerpt = httpResponseInfo.requestExcerpt,
53+
responseExcerpt = httpResponseInfo.responseExcerpt,
54+
errors = httpResponseInfo.errors
5955
)
6056

6157

58+
// status code classes
59+
60+
/** Whether the [statusCode] is 3xx and thus indicates a redirection. */
61+
val isRedirect
62+
get() = statusCode / 100 == 3
63+
64+
/** Whether the [statusCode] is 3xx and thus indicates a client error. */
65+
val isClientError
66+
get() = statusCode / 100 == 4
67+
68+
/** Whether the [statusCode] is 3xx and thus indicates a server error. */
69+
val isServerError
70+
get() = statusCode / 100 == 5
71+
6272
}

src/main/kotlin/at/bitfire/dav4jvm/exception/HttpResponseInfo.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ internal class HttpResponseInfo private constructor(
5353
.append(baos.toString())
5454
}
5555
} else
56-
requestExcerptBuilder.append("\n\n<request body>")
56+
requestExcerptBuilder.append("\n\n<request body (${requestBody.contentLength()} bytes)>")
5757
}
5858

5959
// extract response body if it's text

src/test/kotlin/at/bitfire/dav4jvm/exception/DavExceptionTest.kt

Lines changed: 34 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -12,209 +12,64 @@ package at.bitfire.dav4jvm.exception
1212

1313
import at.bitfire.dav4jvm.Error
1414
import at.bitfire.dav4jvm.Property
15-
import mockwebserver3.MockResponse
16-
import mockwebserver3.MockWebServer
17-
import okhttp3.Headers
1815
import okhttp3.MediaType.Companion.toMediaType
19-
import okhttp3.OkHttpClient
16+
import okhttp3.Protocol
2017
import okhttp3.Request
21-
import okhttp3.RequestBody.Companion.toRequestBody
22-
import org.junit.After
18+
import okhttp3.Response
19+
import okhttp3.ResponseBody.Companion.toResponseBody
2320
import org.junit.Assert.assertEquals
24-
import org.junit.Assert.assertNull
25-
import org.junit.Before
21+
import org.junit.Assert.assertTrue
2622
import org.junit.Test
2723
import java.io.ByteArrayInputStream
2824
import java.io.ByteArrayOutputStream
25+
import java.io.FileNotFoundException
2926
import java.io.ObjectInputStream
3027
import java.io.ObjectOutputStream
3128

3229
class DavExceptionTest {
3330

34-
private val client = OkHttpClient.Builder()
35-
.followRedirects(false)
36-
.build()
37-
private val mockServer = MockWebServer()
38-
39-
@Before
40-
fun startServer() = mockServer.start()
41-
42-
@After
43-
fun stopServer() = mockServer.close()
44-
45-
4631
@Test
47-
fun `Construct from closed response`() {
48-
mockServer.enqueue(MockResponse(
49-
code = 404,
50-
body = "Page not found"
51-
))
52-
val response = client.newCall(Request.Builder()
32+
fun fromResponse() {
33+
val request = Request.Builder()
5334
.get()
54-
.url(mockServer.url("/"))
55-
.build()).execute()
56-
response.close()
57-
58-
val result = DavException("Test", response = response)
59-
assertNull(result.responseExcerpt)
60-
}
61-
62-
@Test
63-
fun `requestExcerpt (binary blob)`() {
64-
mockServer.enqueue(MockResponse(
65-
code = 404,
66-
body = "Page not found"
67-
))
68-
val url = mockServer.url("/")
69-
client.newCall(Request.Builder()
70-
.url(url)
71-
.post("Sample".toRequestBody("application/test".toMediaType()))
35+
.url("https://example.com")
7236
.build()
73-
).execute().use { response ->
74-
val result = DavException("Test", response = response)
75-
assertEquals("POST $url\n\n<request body>", result.requestExcerpt)
76-
}
77-
}
78-
79-
@Test
80-
fun `requestExcerpt (large CSS text)`() {
81-
mockServer.enqueue(MockResponse(
82-
code = 404,
83-
body = "Page not found"
84-
))
85-
val url = mockServer.url("/")
86-
client.newCall(Request.Builder()
87-
.url(url)
88-
.post("*".repeat(DavException.MAX_EXCERPT_SIZE * 2).toRequestBody("text/css".toMediaType()))
37+
val cause = Exception()
38+
val result = Response.Builder()
39+
.request(request)
40+
.protocol(Protocol.HTTP_1_1)
41+
.code(200)
42+
.message("OK")
43+
.body("Your Information".toResponseBody("text/plain".toMediaType()))
8944
.build()
90-
).execute().use { response ->
91-
val result = DavException("Test", response = response)
92-
val truncatedText = "*".repeat(DavException.MAX_EXCERPT_SIZE)
93-
assertEquals("POST $url\n\n$truncatedText", result.requestExcerpt)
94-
}
95-
}
96-
97-
@Test
98-
fun `responseExcerpt (binary blob)`() {
99-
mockServer.enqueue(MockResponse(
100-
code = 404,
101-
body = "Evil binary data",
102-
headers = Headers.headersOf("Content-Type", "application/octet-stream")
103-
))
104-
val url = mockServer.url("/")
105-
client.newCall(Request.Builder()
106-
.url(url)
107-
.get()
108-
.build()
109-
).execute().use { response ->
110-
val result = DavException("Test", response = response)
111-
assertNull(result.responseExcerpt)
112-
}
113-
}
114-
115-
@Test
116-
fun `responseExcerpt (HTML)`() {
117-
mockServer.enqueue(MockResponse(
118-
code = 404,
119-
body = "Interesting details about error",
120-
headers = Headers.headersOf("Content-Type", "text/html")
121-
))
122-
val url = mockServer.url("/")
123-
client.newCall(Request.Builder()
124-
.url(url)
125-
.get()
126-
.build()
127-
).execute().use { response ->
128-
val result = DavException("Test", response = response)
129-
assertEquals("Interesting details about error", result.responseExcerpt)
130-
}
131-
}
132-
133-
@Test
134-
fun `responseExcerpt (large HTML)`() {
135-
mockServer.enqueue(MockResponse(
136-
code = 404,
137-
body = "0123456789".repeat(3*1024), // 30 kB
138-
headers = Headers.headersOf("Content-Type", "text/html")
139-
))
140-
val url = mockServer.url("/")
141-
client.newCall(Request.Builder()
142-
.url(url)
143-
.get()
144-
.build()
145-
).execute().use { response ->
146-
val result = DavException("Test", response = response)
147-
assertEquals(
148-
"0123456789".repeat(2*1024), // limited to 20 kB
149-
result.responseExcerpt
150-
)
151-
}
152-
}
153-
154-
@Test
155-
fun `responseExcerpt (no Content-Type)`() {
156-
mockServer.enqueue(MockResponse(
157-
code = 404,
158-
body = "Maybe evil binary data"
159-
))
160-
val url = mockServer.url("/")
161-
client.newCall(Request.Builder()
162-
.url(url)
163-
.get()
164-
.build()
165-
).execute().use { response ->
166-
val result = DavException("Test", response = response)
167-
assertNull(result.responseExcerpt)
168-
}
169-
}
170-
171-
@Test
172-
fun `responseExcerpt (XML with error elements)`() {
173-
val xml = """
174-
<?xml version="1.0" encoding="utf-8" ?>
175-
<D:error xmlns:D="DAV:">
176-
<D:lock-token-submitted>
177-
<D:href>/locked/</D:href>
178-
</D:lock-token-submitted>
179-
</D:error>""".trimIndent()
180-
mockServer.enqueue(MockResponse(
181-
code = 404,
182-
body = xml,
183-
headers = Headers.headersOf("Content-Type", "application/xml")
184-
))
185-
val url = mockServer.url("/")
186-
client.newCall(Request.Builder()
187-
.url(url)
188-
.get()
189-
.build()
190-
).execute().use { response ->
191-
val result = DavException("Test", response = response)
192-
assertEquals(xml, result.responseExcerpt)
193-
assertEquals(
194-
listOf(
195-
Error(Property.Name("DAV:", "lock-token-submitted"))
196-
),
197-
result.errors
198-
)
199-
}
45+
.use { response ->
46+
DavException("Message", cause, response)
47+
}
48+
assertEquals("Message", result.message)
49+
assertEquals(cause, result.cause)
50+
assertEquals(200, result.statusCode)
51+
assertEquals("GET https://example.com/", result.requestExcerpt)
52+
assertEquals("Your Information", result.responseExcerpt)
53+
assertTrue(result.errors.isEmpty())
20054
}
20155

20256
@Test
20357
fun `is Java-serializable`() {
204-
val davException = DavException(
58+
val ex = DavException(
20559
message = "Some Error",
20660
statusCode = 500,
20761
requestExcerpt = "Request Body",
20862
responseExcerpt = "Response Body",
20963
errors = listOf(
21064
Error(Property.Name("Serialized", "Name"))
211-
)
65+
),
66+
cause = FileNotFoundException()
21267
)
21368

21469
// serialize (Java-style as in Serializable interface, not Kotlin serialization)
21570
val blob = ByteArrayOutputStream().use { baos ->
21671
ObjectOutputStream(baos).use { oos ->
217-
oos.writeObject(davException)
72+
oos.writeObject(ex)
21873
}
21974
baos.toByteArray()
22075
}
@@ -223,11 +78,12 @@ class DavExceptionTest {
22378
ByteArrayInputStream(blob).use { bais ->
22479
ObjectInputStream(bais).use { ois ->
22580
val actual = ois.readObject() as DavException
226-
assertEquals(davException.message, actual.message)
227-
assertEquals(davException.statusCode, actual.statusCode)
228-
assertEquals(davException.requestExcerpt, actual.requestExcerpt)
229-
assertEquals(davException.responseExcerpt, actual.responseExcerpt)
230-
assertEquals(davException.errors, actual.errors)
81+
assertEquals(ex.message, actual.message)
82+
assertEquals(ex.statusCode, actual.statusCode)
83+
assertEquals(ex.requestExcerpt, actual.requestExcerpt)
84+
assertEquals(ex.responseExcerpt, actual.responseExcerpt)
85+
assertEquals(ex.errors, actual.errors)
86+
assertTrue(actual.cause is FileNotFoundException)
23187
}
23288
}
23389
}

0 commit comments

Comments
 (0)