diff options
author | Sam Guymer <sam@guymer.me> | 2018-05-21 20:54:10 +1000 |
---|---|---|
committer | Sam Guymer <sam@guymer.me> | 2018-05-21 21:02:24 +1000 |
commit | ccb1afe90e938fc2b8619dd960a1df1937f212be (patch) | |
tree | 0d7a6b1c653393de8422e9704b9e68bb5cf7ed91 /core | |
parent | bcb94e252a96c78b1db29aebe47b18bfd337e764 (diff) | |
download | sttp-ccb1afe90e938fc2b8619dd960a1df1937f212be.tar.gz sttp-ccb1afe90e938fc2b8619dd960a1df1937f212be.tar.bz2 sttp-ccb1afe90e938fc2b8619dd960a1df1937f212be.zip |
Code review updates
Start the test server before each backend test
Diffstat (limited to 'core')
-rw-r--r-- | core/src/test/resources/binaryfile.jpg | bin | 0 -> 42010 bytes | |||
-rw-r--r-- | core/src/test/resources/textfile.txt | 100 | ||||
-rw-r--r-- | core/src/test/scala/com/softwaremill/sttp/testing/HttpTest.scala | 40 | ||||
-rw-r--r-- | core/src/test/scala/com/softwaremill/sttp/testing/TestHttpServer.scala | 236 | ||||
-rw-r--r-- | core/src/test/scala/com/softwaremill/sttp/testing/streaming/StreamingTest.scala | 9 |
5 files changed, 362 insertions, 23 deletions
diff --git a/core/src/test/resources/binaryfile.jpg b/core/src/test/resources/binaryfile.jpg Binary files differnew file mode 100644 index 0000000..b9f5c5a --- /dev/null +++ b/core/src/test/resources/binaryfile.jpg diff --git a/core/src/test/resources/textfile.txt b/core/src/test/resources/textfile.txt new file mode 100644 index 0000000..9904f90 --- /dev/null +++ b/core/src/test/resources/textfile.txt @@ -0,0 +1,100 @@ +- Lorem ipsum dolor sit amet +- Vivamus sem ipsum. +- Ut molestie. +- Donec. +- Fusce non porta. +- Nulla ac metus. Morbi mattis. +- Etiam varius. +- Nulla. +- Phasellus id mollis. +- Suspendisse at. +- Quisque nec leo velit. +- Fusce. +- Maecenas nec tristique senectus et. +- Integer vestibulum lorem fermentum. +- Vestibulum consectetuer dolor. +- Lorem ipsum in. +- Fusce condimentum auctor scelerisque, wisi. +- Quisque. +- Curae. +- Curae, Nullam. +- Curae, Integer. +- Vestibulum dignissim massa. Donec. +- Pellentesque sed sem. +- Vivamus est. +- Maecenas elit sed est. +- Quisque sed tellus. +- Cum sociis natoque penatibus et. +- Fusce aliquam. +- Donec. +- Sed elementum, sapien accumsan odio. +- Nam mattis, magna lectus, tincidunt. +- Pellentesque scelerisque a, sodales. +- Sed sed condimentum. +- Curae, In nonummy. Phasellus adipiscing. +- Vestibulum quis diam mollis. +- Sed eros. Duis ipsum. +- Aenean pellentesque at, mollis tempus. +- Cras ornare facilisis sodales. Aenean. +- Cum sociis natoque penatibus. +- Donec id nulla. +- Quisque ut sapien. +- Phasellus purus. Proin ultricies. +- Aliquam auctor neque. Nunc. +- Nam nunc fringilla non, vehicula. +- Morbi molestie, felis ut lobortis. +- Nulla quis. +- In. +- Phasellus laoreet urna. +- Lorem ipsum. +- Phasellus. +- Class aptent taciti sociosqu ad. +- Sed lacinia. +- Pellentesque dapibus diam. Duis. +- Suspendisse est. Curabitur. +- Fusce condimentum justo. +- Aenean congue quis, faucibus. +- Ut pharetra leo. Donec. +- Fusce. +- Donec porta. +- Pellentesque orci. +- Sed. +- Quisque rutrum, wisi vulputate wisi. +- Cum sociis. +- Cras. +- Sed eros. Curabitur. +- Proin in velit wisi, tempor. +- Quisque eu. +- Proin. +- Nam pellentesque sed, imperdiet aliquam. +- Mauris euismod. Sed euismod orci. +- Etiam. +- Donec. +- Fusce wisi a metus. Proin. +- Phasellus quis. +- Donec non imperdiet. +- Aenean vel bibendum a, laoreet. +- Fusce non enim. Phasellus vulputate. +- Donec urna elit, sit. +- Pellentesque habitant morbi. +- Nulla ante. Curabitur elit. Donec. +- Cum sociis natoque penatibus. +- Maecenas eget leo at. +- Cum sociis natoque penatibus et. +- Vivamus lacus. +- Integer. +- Curae. +- Maecenas rhoncus. Morbi. +- Aenean posuere. +- Duis. +- Suspendisse a odio fermentum libero. +- Nam enim. Fusce enim. In. +- Maecenas. +- Lorem ipsum primis. +- Curabitur ac turpis semper sed. +- Quisque condimentum. Donec sit. +- Integer convallis non, posuere. +- Etiam vulputate, odio. +- Proin id lorem. Donec quis. +- Curae, Sed nec augue. +- Aliquam ut turpis.
\ No newline at end of file diff --git a/core/src/test/scala/com/softwaremill/sttp/testing/HttpTest.scala b/core/src/test/scala/com/softwaremill/sttp/testing/HttpTest.scala index f1fa002..5598aa2 100644 --- a/core/src/test/scala/com/softwaremill/sttp/testing/HttpTest.scala +++ b/core/src/test/scala/com/softwaremill/sttp/testing/HttpTest.scala @@ -22,26 +22,23 @@ trait HttpTest[R[_]] with OptionValues with IntegrationPatience with BeforeAndAfterEach - with BeforeAndAfterAll { - - private val endpoint = "localhost:51823" + with BeforeAndAfterAll + with TestHttpServer { override def afterEach() { val file = File(outPath) if (file.exists) file.delete() } - private val textFile = - new java.io.File("test-server/src/main/resources/textfile.txt") - private val binaryFile = - new java.io.File("test-server/src/main/resources/binaryfile.jpg") + private val textFile = new java.io.File(getClass.getResource("/textfile.txt").getFile) + private val binaryFile = new java.io.File(getClass.getResource("/binaryfile.jpg").getFile) private val outPath = File.newTemporaryDirectory().path private val textWithSpecialCharacters = "Żółć!" implicit val backend: SttpBackend[R, Nothing] implicit val convertToFuture: ConvertToFuture[R] - private val postEcho = sttp.post(uri"$endpoint/echo") + private def postEcho = sttp.post(uri"$endpoint/echo") private val testBody = "this is the body" private val testBodyBytes = testBody.getBytes("UTF-8") private val expectedPostEchoResponse = "POST /echo this is the body" @@ -172,7 +169,7 @@ trait HttpTest[R[_]] } "headers" - { - val getHeaders = sttp.get(uri"$endpoint/set_headers") + def getHeaders = sttp.get(uri"$endpoint/set_headers") "read response headers" in { val response = getHeaders.response(sttpIgnore).send().force() @@ -187,7 +184,7 @@ trait HttpTest[R[_]] } "errors" - { - val getHeaders = sttp.post(uri"$endpoint/set_headers") + def getHeaders = sttp.post(uri"$endpoint/set_headers") "return 405 when method not allowed" in { val response = getHeaders.response(sttpIgnore).send().force() @@ -236,7 +233,7 @@ trait HttpTest[R[_]] } "auth" - { - val secureBasic = sttp.get(uri"$endpoint/secure_basic") + def secureBasic = sttp.get(uri"$endpoint/secure_basic") "return a 401 when authorization fails" in { val req = secureBasic @@ -254,7 +251,7 @@ trait HttpTest[R[_]] } "compression" - { - val compress = sttp.get(uri"$endpoint/compress") + def compress = sttp.get(uri"$endpoint/compress") val decompressedBody = "I'm compressed!" "decompress using the default accept encoding header" in { @@ -374,7 +371,7 @@ trait HttpTest[R[_]] } "multipart" - { - val mp = sttp.post(uri"$endpoint/multipart") + def mp = sttp.post(uri"$endpoint/multipart") "send a multipart message" in { val req = mp.multipartBody(multipart("p1", "v1"), multipart("p2", "v2")) @@ -400,11 +397,11 @@ trait HttpTest[R[_]] } "redirect" - { - val r1 = sttp.post(uri"$endpoint/redirect/r1") - val r2 = sttp.post(uri"$endpoint/redirect/r2") - val r3 = sttp.post(uri"$endpoint/redirect/r3") + def r1 = sttp.post(uri"$endpoint/redirect/r1") + def r2 = sttp.post(uri"$endpoint/redirect/r2") + def r3 = sttp.post(uri"$endpoint/redirect/r3") val r4response = "819" - val loop = sttp.post(uri"$endpoint/redirect/loop") + def loop = sttp.post(uri"$endpoint/redirect/loop") "not redirect when redirects shouldn't be followed (temporary)" in { val resp = r1.followRedirects(false).send().force() @@ -499,10 +496,11 @@ trait HttpTest[R[_]] } "empty response" - { - val postEmptyResponse = sttp - .post(uri"$endpoint/empty_unauthorized_response") - .body("{}") - .contentType("application/json") + def postEmptyResponse = + sttp + .post(uri"$endpoint/empty_unauthorized_response") + .body("{}") + .contentType("application/json") "parse an empty error response as empty string" in { val response = postEmptyResponse.send().force() diff --git a/core/src/test/scala/com/softwaremill/sttp/testing/TestHttpServer.scala b/core/src/test/scala/com/softwaremill/sttp/testing/TestHttpServer.scala new file mode 100644 index 0000000..11fc692 --- /dev/null +++ b/core/src/test/scala/com/softwaremill/sttp/testing/TestHttpServer.scala @@ -0,0 +1,236 @@ +package com.softwaremill.sttp.testing + +import akka.Done +import akka.actor.ActorSystem +import akka.http.scaladsl.Http +import akka.http.scaladsl.coding.{Deflate, Gzip, NoCoding} +import akka.http.scaladsl.model._ +import akka.http.scaladsl.model.headers.CacheDirectives._ +import akka.http.scaladsl.model.headers._ +import akka.http.scaladsl.server.Directives.{entity, path, _} +import akka.http.scaladsl.server.Route +import akka.http.scaladsl.server.directives.Credentials +import akka.stream.ActorMaterializer +import akka.util.ByteString +import scala.concurrent.duration._ +import scala.concurrent.{Await, Future} + +import org.scalatest.BeforeAndAfterAll +import org.scalatest.Suite + +trait TestHttpServer extends BeforeAndAfterAll { this: Suite => + + private val server = new HttpServer(0) + protected var endpoint = "localhost:51823" + + override protected def beforeAll(): Unit = { + import scala.concurrent.ExecutionContext.Implicits.global + + super.beforeAll() + Await.result( + server.start().map { binding => + endpoint = s"localhost:${binding.localAddress.getPort}" + }, + 10.seconds + ) + } + + override protected def afterAll(): Unit = { + server.close() + super.afterAll() + } + +} + +object HttpServer { + + def main(args: Array[String]): Unit = { + val port = args.headOption.map(_.toInt).getOrElse(51823) + + Await.result(new HttpServer(port).start(), 10.seconds) + } +} + +private class HttpServer(port: Int) extends AutoCloseable { + + import scala.concurrent.ExecutionContext.Implicits.global + + private var server: Option[Future[Http.ServerBinding]] = None + + private implicit val actorSystem: ActorSystem = ActorSystem("sttp-test-server") + private implicit val materializer: ActorMaterializer = ActorMaterializer() + + private def paramsToString(m: Map[String, String]): String = + m.toList.sortBy(_._1).map(p => s"${p._1}=${p._2}").mkString(" ") + + private val textFile = new java.io.File(getClass.getResource("/textfile.txt").getFile) + private val binaryFile = new java.io.File(getClass.getResource("/binaryfile.jpg").getFile) + private val textWithSpecialCharacters = "Żółć!" + + val serverRoutes: Route = + pathPrefix("echo") { + pathPrefix("form_params") { + formFieldMap { params => + path("as_string") { + complete(paramsToString(params)) + } ~ + path("as_params") { + complete(FormData(params)) + } + } + } ~ get { + parameterMap { params => + complete(List("GET", "/echo", paramsToString(params)) + .filter(_.nonEmpty) + .mkString(" ")) + } + } ~ + post { + parameterMap { params => + entity(as[String]) { body: String => + complete(List("POST", "/echo", paramsToString(params), body) + .filter(_.nonEmpty) + .mkString(" ")) + } + } + } + } ~ pathPrefix("streaming") { + path("echo") { + post { + parameterMap { _ => + entity(as[String]) { body: String => + complete(body) + } + } + } + } + } ~ path("set_headers") { + get { + respondWithHeader(`Cache-Control`(`max-age`(1000L))) { + respondWithHeader(`Cache-Control`(`no-cache`)) { + complete("ok") + } + } + } + } ~ pathPrefix("set_cookies") { + path("with_expires") { + setCookie(HttpCookie("c", "v", expires = Some(DateTime(1997, 12, 8, 12, 49, 12)))) { + complete("ok") + } + } ~ get { + setCookie( + HttpCookie( + "cookie1", + "value1", + secure = true, + httpOnly = true, + maxAge = Some(123L) + ) + ) { + setCookie(HttpCookie("cookie2", "value2")) { + setCookie( + HttpCookie( + "cookie3", + "", + domain = Some("xyz"), + path = Some("a/b/c") + ) + ) { + complete("ok") + } + } + } + } + } ~ path("secure_basic") { + authenticateBasic("test realm", { + case c @ Credentials.Provided(un) if un == "adam" && c.verify("1234") => + Some(un) + case _ => None + }) { userName => + complete(s"Hello, $userName!") + } + } ~ path("compress") { + encodeResponseWith(Gzip, Deflate, NoCoding) { + complete("I'm compressed!") + } + } ~ pathPrefix("download") { + path("binary") { + getFromFile(binaryFile) + } ~ path("text") { + getFromFile(textFile) + } + } ~ pathPrefix("multipart") { + entity(as[akka.http.scaladsl.model.Multipart.FormData]) { fd => + complete { + fd.parts + .mapAsync(1) { p => + val fv = p.entity.dataBytes.runFold(ByteString())(_ ++ _) + fv.map(_.utf8String) + .map(v => p.name + "=" + v + p.filename.fold("")(fn => s" ($fn)")) + } + .runFold(Vector.empty[String])(_ :+ _) + .map(v => v.mkString(", ")) + } + } + } ~ pathPrefix("redirect") { + path("r1") { + redirect("/redirect/r2", StatusCodes.TemporaryRedirect) + } ~ + path("r2") { + redirect("/redirect/r3", StatusCodes.PermanentRedirect) + } ~ + path("r3") { + redirect("/redirect/r4", StatusCodes.Found) + } ~ + path("r4") { + complete("819") + } ~ + path("loop") { + redirect("/redirect/loop", StatusCodes.Found) + } + } ~ pathPrefix("timeout") { + complete { + akka.pattern.after(1.second, using = actorSystem.scheduler)( + Future.successful("Done") + ) + } + } ~ path("empty_unauthorized_response") { + post { + import akka.http.scaladsl.model._ + complete( + HttpResponse( + status = StatusCodes.Unauthorized, + headers = Nil, + entity = HttpEntity.Empty, + protocol = HttpProtocols.`HTTP/1.1` + )) + } + } ~ path("respond_with_iso_8859_2") { + get { ctx => + val entity = + HttpEntity(MediaTypes.`text/plain`.withCharset(HttpCharset.custom("ISO-8859-2")), textWithSpecialCharacters) + ctx.complete(HttpResponse(200, entity = entity)) + } + } + + def start(): Future[Http.ServerBinding] = { + unbindServer().flatMap { _ => + val server = Http().bindAndHandle(serverRoutes, "localhost", port) + this.server = Some(server) + server + } + } + + def close(): Unit = { + val unbind = unbindServer() + unbind.onComplete(_ => actorSystem.terminate()) + Await.result( + unbind, + 10.seconds + ) + } + + private def unbindServer(): Future[Done] = { + server.map(_.flatMap(_.unbind())).getOrElse(Future.successful(Done)) + } +} diff --git a/core/src/test/scala/com/softwaremill/sttp/testing/streaming/StreamingTest.scala b/core/src/test/scala/com/softwaremill/sttp/testing/streaming/StreamingTest.scala index 27a6eda..40aaf82 100644 --- a/core/src/test/scala/com/softwaremill/sttp/testing/streaming/StreamingTest.scala +++ b/core/src/test/scala/com/softwaremill/sttp/testing/streaming/StreamingTest.scala @@ -6,10 +6,15 @@ import org.scalatest.{AsyncFreeSpec, BeforeAndAfterAll, Matchers} import scala.language.higherKinds import com.softwaremill.sttp.testing.ConvertToFuture +import com.softwaremill.sttp.testing.TestHttpServer -trait StreamingTest[R[_], S] extends AsyncFreeSpec with Matchers with BeforeAndAfterAll with ForceWrapped { +trait StreamingTest[R[_], S] + extends AsyncFreeSpec + with Matchers + with ForceWrapped + with BeforeAndAfterAll + with TestHttpServer { - private val endpoint = "localhost:51823" private val body = "streaming test" implicit def backend: SttpBackend[R, S] |