Skip to content

Commit

Permalink
Define PostFileEndpoint tests
Browse files Browse the repository at this point in the history
  • Loading branch information
macisamuele committed Feb 5, 2020
1 parent f8cc75a commit 16dbd5f
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.yelp.codegen.generatecodesamples

import com.yelp.codegen.generatecodesamples.apis.FileApi
import com.yelp.codegen.generatecodesamples.models.MultiPartInfo
import com.yelp.codegen.generatecodesamples.tools.MockServerApiRule
import com.yelp.codegen.generatecodesamples.tools.setFileApiStatsDispatcher
import okhttp3.MediaType
import okhttp3.RequestBody
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test

class PostFileEndpointTest {

@get:Rule
val rule = MockServerApiRule()

@Test
fun sendFileTest() {
rule.server.setFileApiStatsDispatcher()

val returned = rule.getApi<FileApi>().postFile(
clientFile = RequestBody.create(MediaType.parse("application/json"), "{}")
).blockingGet()

assertNotNull(returned.boundary)
assertEquals(returned.errors?.size, 0)
assertEquals(
returned.parts,
listOf(
MultiPartInfo(
contentDisposition = "form-data; name=\"client_file\"; filename=\"client_file\"",
contentType = "application/json; charset=utf-8",
contentLength = "2",
content = "{}"
)
)
)
}

@Test
fun sendMultipleFilesTest() {
rule.server.setFileApiStatsDispatcher()

val returned = rule.getApi<FileApi>().postMultipleFiles(
clientFile = RequestBody.create(MediaType.parse("application/json"), "{}"),
certificateFile = RequestBody.create(MediaType.parse("text/plain"), "Some Text")
).blockingGet()

assertNotNull(returned.boundary)
assertEquals(returned.errors?.size, 0)
assertEquals(
returned.parts,
listOf(
MultiPartInfo(
contentDisposition = "form-data; name=\"client_file\"; filename=\"client_file\"",
contentType = "application/json; charset=utf-8",
contentLength = "2",
content = "{}"
),
MultiPartInfo(
contentDisposition = "form-data; name=\"certificate_file\"; filename=\"certificate_file\"",
contentType = "text/plain; charset=utf-8",
contentLength = "9",
content = "Some Text"
)
)
)
}

@Test
fun ensureThatEndpointsWithFileAndNotMultipartFormDataAreMarkedAsDeprecated() {
assertTrue(
FileApi::class.java.methods
.first { it.name == "postFileWithoutMultipartFormData" }
.getAnnotationsByType(Deprecated::class.java)
.isNotEmpty()
)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package com.yelp.codegen.generatecodesamples.tools

import com.squareup.moshi.Moshi
import com.yelp.codegen.generatecodesamples.models.FileApiStats
import com.yelp.codegen.generatecodesamples.models.MultiPartInfo
import io.reactivex.schedulers.Schedulers
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.RecordedRequest
import org.junit.rules.ExternalResource
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
Expand Down Expand Up @@ -35,3 +41,90 @@ class MockServerApiRule : ExternalResource() {
return retrofit.create(T::class.java)
}
}

fun processPart(part: Iterable<String>): MultiPartInfo {
val headers = part.asSequence().takeWhile { it.isNotEmpty() }.map { headerLine ->
val (name, value) = headerLine.split(": ", limit = 2)
name to value
}.toMap()
val content = part.asSequence().dropWhile { it.isNotEmpty() }.drop(1).joinToString("\r\n")
return MultiPartInfo(
content = content,
contentDisposition = headers["Content-Disposition"],
contentType = headers["Content-Type"],
contentLength = headers["Content-Length"]
)
}

fun RecordedRequest.getFileApiStats(): FileApiStats {
/**
* requestBody has to be compliant to the HTTP Specifications for MultiPart (RFC 2387)
*
* This means that the body will look like
* --<BOUNDARY>\r\n
* <Content1 Header Name>: <Content1 Header Value>\r\n
* [<Content1 Header Name>: <Content1 Header Value>\r\n]
* \r\n
* <Content1>\r\n
* --<BOUNDARY>\r\n
* <Content# Header Name>: <Content# Header Value>\r\n
* [<Content# Header Name>: <Content# Header Value>\r\n]
* \r\n
* <Content#>\r\n
* --<BOUNDARY>--\r\n
* \r\n
*/
val requestBody = this.body.readUtf8()
val requestBodyLines = requestBody.split("\r\n")

val errors = mutableListOf<String>()
if (!requestBodyLines[0].startsWith("--")) {
errors.add("Boundary should start with '--'")
}

if ("${requestBodyLines[0]}--" != requestBodyLines[requestBodyLines.size - 2]) {
errors.add("Start and End Boundaries are different")
}

if (requestBodyLines.last().isNotEmpty()) {
errors.add("The last line of the body has to be empty")
}

var boundary: String? = null
var parts: List<MultiPartInfo>? = null

if (errors.isEmpty()) {
boundary = requestBodyLines[0].substring(startIndex = 2)
parts = requestBodyLines
.asSequence()
.mapIndexedNotNull { index, s ->
if (s.matches(Regex("^--$boundary(--)?$"))) {
index
} else {
null
}
}
.zipWithNext()
.map { (start, end) -> requestBodyLines.subList(start + 1, end) }
.map { processPart(it) }
.toList()
}

return FileApiStats(
boundary = boundary,
parts = parts,
errors = errors
)
}

fun MockWebServer.setFileApiStatsDispatcher() {
this.setDispatcher(
object : Dispatcher() {
override fun dispatch(recordedRequest: RecordedRequest) = MockResponse().setBody(
Moshi.Builder().build().adapter(FileApiStats::class.java).toJson(
recordedRequest.getFileApiStats()
)
)
}
)
}

0 comments on commit 16dbd5f

Please sign in to comment.