aboutsummaryrefslogtreecommitdiff
path: root/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala')
-rw-r--r--tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala702
1 files changed, 0 insertions, 702 deletions
diff --git a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
deleted file mode 100644
index 55e21b8..0000000
--- a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
+++ /dev/null
@@ -1,702 +0,0 @@
-package com.softwaremill.sttp
-
-import java.io.{ByteArrayInputStream, IOException}
-import java.nio.ByteBuffer
-import java.nio.file.Paths
-import java.time.{ZoneId, ZonedDateTime}
-
-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._
-import akka.http.scaladsl.server.Route
-import akka.http.scaladsl.server.directives.Credentials
-import akka.util.ByteString
-import better.files._
-import com.softwaremill.sttp.akkahttp.AkkaHttpBackend
-import com.softwaremill.sttp.asynchttpclient.cats.AsyncHttpClientCatsBackend
-import com.softwaremill.sttp.asynchttpclient.future.AsyncHttpClientFutureBackend
-import com.softwaremill.sttp.asynchttpclient.monix.AsyncHttpClientMonixBackend
-import com.softwaremill.sttp.asynchttpclient.scalaz.AsyncHttpClientScalazBackend
-import com.softwaremill.sttp.impl.cats.convertCatsIOToFuture
-import com.softwaremill.sttp.impl.monix.convertMonixTaskToFuture
-import com.softwaremill.sttp.impl.scalaz.convertScalazTaskToFuture
-import com.softwaremill.sttp.okhttp.monix.OkHttpMonixBackend
-import com.softwaremill.sttp.okhttp.{OkHttpFutureBackend, OkHttpSyncBackend}
-import com.softwaremill.sttp.testing.streaming.ConvertToFuture
-import com.typesafe.scalalogging.StrictLogging
-import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures}
-import org.scalatest.{path => _, _}
-
-import scala.concurrent.ExecutionContext.Implicits.global
-import scala.concurrent.Future
-import scala.concurrent.duration._
-import scala.language.higherKinds
-
-class BasicTests
- extends FlatSpec
- with Matchers
- with BeforeAndAfterAll
- with ScalaFutures
- with OptionValues
- with StrictLogging
- with IntegrationPatience
- with TestHttpServer
- with ForceWrapped
- with BeforeAndAfterEach {
-
- override def afterEach() {
- val file = File(outPath)
- if (file.exists) file.delete()
- }
-
- 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("tests/src/test/resources/textfile.txt")
- private val binaryFile =
- new java.io.File("tests/src/test/resources/binaryfile.jpg")
- private val outPath = Paths.get("out")
- private val textWithSpecialCharacters = "Żółć!"
-
- override 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(" "))
- }
- }
- }
- } ~ 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))
- }
- }
-
- override def port = 51823
-
- var closeBackends: List[() => Unit] = Nil
-
- runTests("HttpURLConnection")(HttpURLConnectionBackend(), ConvertToFuture.id)
- runTests("TryHttpURLConnection")(TryHttpURLConnectionBackend(), ConvertToFuture.scalaTry)
- runTests("Akka HTTP")(AkkaHttpBackend.usingActorSystem(actorSystem), ConvertToFuture.future)
- runTests("Async Http Client - Future")(AsyncHttpClientFutureBackend(), ConvertToFuture.future)
- runTests("Async Http Client - Scalaz")(AsyncHttpClientScalazBackend(), convertScalazTaskToFuture)
- runTests("Async Http Client - Monix")(AsyncHttpClientMonixBackend(), convertMonixTaskToFuture)
- runTests("Async Http Client - Cats IO")(AsyncHttpClientCatsBackend[cats.effect.IO](), convertCatsIOToFuture)
- runTests("OkHttpSyncClientHandler")(OkHttpSyncBackend(), ConvertToFuture.id)
- runTests("OkHttpAsyncClientHandler - Future")(OkHttpFutureBackend(), ConvertToFuture.future)
- runTests("OkHttpAsyncClientHandler - Monix")(OkHttpMonixBackend(), convertMonixTaskToFuture)
-
- def runTests[R[_]](name: String)(implicit
- backend: SttpBackend[R, Nothing],
- convertToFuture: ConvertToFuture[R]): Unit = {
-
- closeBackends = (() => backend.close()) :: closeBackends
-
- val postEcho = sttp.post(uri"$endpoint/echo")
- val testBody = "this is the body"
- val testBodyBytes = testBody.getBytes("UTF-8")
- val expectedPostEchoResponse = "POST /echo this is the body"
-
- val sttpIgnore = com.softwaremill.sttp.ignore
-
- parseResponseTests()
- parameterTests()
- bodyTests()
- headerTests()
- errorsTests()
- cookiesTests()
- authTests()
- compressionTests()
- downloadFileTests()
- multipartTests()
- redirectTests()
- timeoutTests()
- emptyResponseTests()
- encodingTests()
-
- def parseResponseTests(): Unit = {
- name should "parse response as string" in {
- val response = postEcho.body(testBody).send().force()
- response.unsafeBody should be(expectedPostEchoResponse)
- }
-
- name should "parse response as string with mapping using map" in {
- val response = postEcho
- .body(testBody)
- .response(asString.map(_.length))
- .send()
- .force()
- response.unsafeBody should be(expectedPostEchoResponse.length)
- }
-
- name should "parse response as string with mapping using mapResponse" in {
- val response = postEcho
- .body(testBody)
- .mapResponse(_.length)
- .send()
- .force()
- response.unsafeBody should be(expectedPostEchoResponse.length)
- }
-
- name should "parse response as a byte array" in {
- val response =
- postEcho.body(testBody).response(asByteArray).send().force()
- val fc = new String(response.unsafeBody, "UTF-8")
- fc should be(expectedPostEchoResponse)
- }
-
- name should "parse response as parameters" in {
- val params = List("a" -> "b", "c" -> "d", "e=" -> "&f")
- val response = sttp
- .post(uri"$endpoint/echo/form_params/as_params")
- .body(params: _*)
- .response(asParams)
- .send()
- .force()
- response.unsafeBody.toList should be(params)
- }
- }
-
- def parameterTests(): Unit = {
- name should "make a get request with parameters" in {
- val response = sttp
- .get(uri"$endpoint/echo?p2=v2&p1=v1")
- .send()
- .force()
-
- response.unsafeBody should be("GET /echo p1=v1 p2=v2")
- }
- }
-
- def bodyTests(): Unit = {
- name should "post a string" in {
- val response = postEcho.body(testBody).send().force()
- response.unsafeBody should be(expectedPostEchoResponse)
- }
-
- name should "post a byte array" in {
- val response =
- postEcho.body(testBodyBytes).send().force()
- response.unsafeBody should be(expectedPostEchoResponse)
- }
-
- name should "post an input stream" in {
- val response = postEcho
- .body(new ByteArrayInputStream(testBodyBytes))
- .send()
- .force()
- response.unsafeBody should be(expectedPostEchoResponse)
- }
-
- name should "post a byte buffer" in {
- val response = postEcho
- .body(ByteBuffer.wrap(testBodyBytes))
- .send()
- .force()
- response.unsafeBody should be(expectedPostEchoResponse)
- }
-
- name should "post a file" in {
- val f = File.newTemporaryFile().write(testBody)
- try {
- val response = postEcho.body(f.toJava).send().force()
- response.unsafeBody should be(expectedPostEchoResponse)
- } finally f.delete()
- }
-
- name should "post a path" in {
- val f = File.newTemporaryFile().write(testBody)
- try {
- val response =
- postEcho.body(f.toJava.toPath).send().force()
- response.unsafeBody should be(expectedPostEchoResponse)
- } finally f.delete()
- }
-
- name should "post form data" in {
- val response = sttp
- .post(uri"$endpoint/echo/form_params/as_string")
- .body("a" -> "b", "c" -> "d")
- .send()
- .force()
- response.unsafeBody should be("a=b c=d")
- }
-
- name should "post form data with special characters" in {
- val response = sttp
- .post(uri"$endpoint/echo/form_params/as_string")
- .body("a=" -> "/b", "c:" -> "/d")
- .send()
- .force()
- response.unsafeBody should be("a==/b c:=/d")
- }
-
- name should "post without a body" in {
- val response = postEcho.send().force()
- response.unsafeBody should be("POST /echo")
- }
- }
-
- def headerTests(): Unit = {
- val getHeaders = sttp.get(uri"$endpoint/set_headers")
-
- name should "read response headers" in {
- val response = getHeaders.response(sttpIgnore).send().force()
- response.headers should have length (6)
- response.headers("Cache-Control").toSet should be(Set("no-cache", "max-age=1000"))
- response.header("Server") should be('defined)
- response.header("server") should be('defined)
- response.header("Server").get should startWith("akka-http")
- response.contentType should be(Some("text/plain; charset=UTF-8"))
- response.contentLength should be(Some(2L))
- }
- }
-
- def errorsTests(): Unit = {
- val getHeaders = sttp.post(uri"$endpoint/set_headers")
-
- name should "return 405 when method not allowed" in {
- val response = getHeaders.response(sttpIgnore).send().force()
- response.code should be(405)
- response.isClientError should be(true)
- response.body should be('left)
- }
- }
-
- def cookiesTests(): Unit = {
- name should "read response cookies" in {
- val response =
- sttp
- .get(uri"$endpoint/set_cookies")
- .response(sttpIgnore)
- .send()
- .force()
- response.cookies should have length (3)
- response.cookies.toSet should be(
- Set(
- Cookie("cookie1", "value1", secure = true, httpOnly = true, maxAge = Some(123L)),
- Cookie("cookie2", "value2"),
- Cookie("cookie3", "", domain = Some("xyz"), path = Some("a/b/c"))
- ))
- }
-
- name should "read response cookies with the expires attribute" in {
- val response = sttp
- .get(uri"$endpoint/set_cookies/with_expires")
- .response(sttpIgnore)
- .send()
- .force()
- response.cookies should have length (1)
- val c = response.cookies(0)
-
- c.name should be("c")
- c.value should be("v")
- c.expires.map(_.toInstant.toEpochMilli) should be(
- Some(
- ZonedDateTime
- .of(1997, 12, 8, 12, 49, 12, 0, ZoneId.of("GMT"))
- .toInstant
- .toEpochMilli
- ))
- }
- }
-
- def authTests(): Unit = {
- val secureBasic = sttp.get(uri"$endpoint/secure_basic")
-
- name should "return a 401 when authorization fails" in {
- val req = secureBasic
- val resp = req.send().force()
- resp.code should be(401)
- resp.header("WWW-Authenticate") should be(Some("""Basic realm="test realm",charset=UTF-8"""))
- }
-
- name should "perform basic authorization" in {
- val req = secureBasic.auth.basic("adam", "1234")
- val resp = req.send().force()
- resp.code should be(200)
- resp.unsafeBody should be("Hello, adam!")
- }
- }
-
- def compressionTests(): Unit = {
- val compress = sttp.get(uri"$endpoint/compress")
- val decompressedBody = "I'm compressed!"
-
- name should "decompress using the default accept encoding header" in {
- val req = compress
- val resp = req.send().force()
- resp.unsafeBody should be(decompressedBody)
- }
-
- name should "decompress using gzip" in {
- val req =
- compress.header("Accept-Encoding", "gzip", replaceExisting = true)
- val resp = req.send().force()
- resp.unsafeBody should be(decompressedBody)
- }
-
- name should "decompress using deflate" in {
- val req =
- compress.header("Accept-Encoding", "deflate", replaceExisting = true)
- val resp = req.send().force()
- resp.unsafeBody should be(decompressedBody)
- }
-
- name should "work despite providing an unsupported encoding" in {
- val req =
- compress.header("Accept-Encoding", "br", replaceExisting = true)
- val resp = req.send().force()
- resp.unsafeBody should be(decompressedBody)
- }
- }
-
- def downloadFileTests(): Unit = {
- import CustomMatchers._
-
- name should "download a binary file using asFile" in {
- val file = outPath.resolve("binaryfile.jpg").toFile
- val req =
- sttp.get(uri"$endpoint/download/binary").response(asFile(file))
- val resp = req.send().force()
-
- resp.unsafeBody shouldBe file
- file should exist
- file should haveSameContentAs(binaryFile)
- }
-
- name should "download a text file using asFile" in {
- val file = outPath.resolve("textfile.txt").toFile
- val req =
- sttp.get(uri"$endpoint/download/text").response(asFile(file))
- val resp = req.send().force()
-
- resp.unsafeBody shouldBe file
- file should exist
- file should haveSameContentAs(textFile)
- }
-
- name should "download a binary file using asPath" in {
- val path = outPath.resolve("binaryfile.jpg")
- val req =
- sttp.get(uri"$endpoint/download/binary").response(asPath(path))
- val resp = req.send().force()
-
- resp.unsafeBody shouldBe path
- path.toFile should exist
- path.toFile should haveSameContentAs(binaryFile)
- }
-
- name should "download a text file using asPath" in {
- val path = outPath.resolve("textfile.txt")
- val req =
- sttp.get(uri"$endpoint/download/text").response(asPath(path))
- val resp = req.send().force()
-
- resp.unsafeBody shouldBe path
- path.toFile should exist
- path.toFile should haveSameContentAs(textFile)
- }
-
- name should "fail at trying to save file to a restricted location" in {
- val path = Paths.get("/").resolve("textfile.txt")
- val req =
- sttp.get(uri"$endpoint/download/text").response(asPath(path))
- val caught = intercept[IOException] {
- req.send().force()
- }
-
- caught.getMessage shouldBe "Permission denied"
- }
-
- name should "fail when file exists and overwrite flag is false" in {
- val path = outPath.resolve("textfile.txt")
- path.toFile.getParentFile.mkdirs()
- path.toFile.createNewFile()
- val req =
- sttp.get(uri"$endpoint/download/text").response(asPath(path))
-
- val caught = intercept[IOException] {
- req.send().force()
- }
-
- caught.getMessage shouldBe s"File ${path.toFile.getAbsolutePath} exists - overwriting prohibited"
-
- }
-
- name should "not fail when file exists and overwrite flag is true" in {
- val path = outPath.resolve("textfile.txt")
- path.toFile.getParentFile.mkdirs()
- path.toFile.createNewFile()
- val req =
- sttp
- .get(uri"$endpoint/download/text")
- .response(asPath(path, overwrite = true))
- val resp = req.send().force()
-
- resp.unsafeBody shouldBe path
- path.toFile should exist
- path.toFile should haveSameContentAs(textFile)
- }
- }
-
- def multipartTests(): Unit = {
- val mp = sttp.post(uri"$endpoint/multipart")
-
- name should "send a multipart message" in {
- val req = mp.multipartBody(multipart("p1", "v1"), multipart("p2", "v2"))
- val resp = req.send().force()
- resp.unsafeBody should be("p1=v1, p2=v2")
- }
-
- name should "send a multipart message with filenames" in {
- val req = mp.multipartBody(multipart("p1", "v1").fileName("f1"), multipart("p2", "v2").fileName("f2"))
- val resp = req.send().force()
- resp.unsafeBody should be("p1=v1 (f1), p2=v2 (f2)")
- }
-
- name should "send a multipart message with a file" in {
- val f = File.newTemporaryFile().write(testBody)
- try {
- val req =
- mp.multipartBody(multipart("p1", f.toJava), multipart("p2", "v2"))
- val resp = req.send().force()
- resp.unsafeBody should be(s"p1=$testBody (${f.name}), p2=v2")
- } finally f.delete()
- }
- }
-
- def redirectTests(): Unit = {
- val r1 = sttp.post(uri"$endpoint/redirect/r1")
- val r2 = sttp.post(uri"$endpoint/redirect/r2")
- val r3 = sttp.post(uri"$endpoint/redirect/r3")
- val r4response = "819"
- val loop = sttp.post(uri"$endpoint/redirect/loop")
-
- name should "not redirect when redirects shouldn't be followed (temporary)" in {
- val resp = r1.followRedirects(false).send().force()
- resp.code should be(307)
- resp.body should be('left)
- resp.history should be('empty)
- }
-
- name should "not redirect when redirects shouldn't be followed (permanent)" in {
- val resp = r2.followRedirects(false).send().force()
- resp.code should be(308)
- resp.body should be('left)
- }
-
- name should "redirect when redirects should be followed" in {
- val resp = r2.send().force()
- resp.code should be(200)
- resp.unsafeBody should be(r4response)
- }
-
- name should "redirect twice when redirects should be followed" in {
- val resp = r1.send().force()
- resp.code should be(200)
- resp.unsafeBody should be(r4response)
- }
-
- name should "redirect when redirects should be followed, and the response is parsed" in {
- val resp = r2.response(asString.map(_.toInt)).send().force()
- resp.code should be(200)
- resp.unsafeBody should be(r4response.toInt)
- }
-
- name should "keep a single history entry of redirect responses" in {
- val resp = r3.send().force()
- resp.code should be(200)
- resp.unsafeBody should be(r4response)
- resp.history should have size (1)
- resp.history(0).code should be(302)
- }
-
- name should "keep whole history of redirect responses" in {
- val resp = r1.send().force()
- resp.code should be(200)
- resp.unsafeBody should be(r4response)
- resp.history should have size (3)
- resp.history(0).code should be(307)
- resp.history(1).code should be(308)
- resp.history(2).code should be(302)
- }
-
- name should "break redirect loops" in {
- val resp = loop.send().force()
- resp.code should be(0)
- resp.history should have size (FollowRedirectsBackend.MaxRedirects)
- }
-
- name should "break redirect loops after user-specified count" in {
- val maxRedirects = 10
- val resp = loop.maxRedirects(maxRedirects).send().force()
- resp.code should be(0)
- resp.history should have size (maxRedirects)
- }
-
- name should "not redirect when maxRedirects is less than or equal to 0" in {
- val resp = loop.maxRedirects(-1).send().force()
- resp.code should be(302)
- resp.body should be('left)
- resp.history should be('empty)
- }
- }
-
- def timeoutTests(): Unit = {
- name should "fail if read timeout is not big enough" in {
- val request = sttp
- .get(uri"$endpoint/timeout")
- .readTimeout(200.milliseconds)
- .response(asString)
-
- intercept[Throwable] {
- request.send().force()
- }
- }
-
- name should "not fail if read timeout is big enough" in {
- val request = sttp
- .get(uri"$endpoint/timeout")
- .readTimeout(5.seconds)
- .response(asString)
-
- request.send().force().unsafeBody should be("Done")
- }
- }
-
- def emptyResponseTests(): Unit = {
- val postEmptyResponse = sttp
- .post(uri"$endpoint/empty_unauthorized_response")
- .body("{}")
- .contentType("application/json")
-
- name should "parse an empty error response as empty string" in {
- val response = postEmptyResponse.send().force()
- response.body should be(Left(""))
- }
- }
-
- def encodingTests(): Unit = {
- name should "read response body encoded using ISO-8859-2, as specified in the header, overriding the default" in {
- val request = sttp.get(uri"$endpoint/respond_with_iso_8859_2")
-
- request.send().force().unsafeBody should be(textWithSpecialCharacters)
- }
- }
- }
-
- override protected def afterAll(): Unit = {
- closeBackends.foreach(_())
- super.afterAll()
- }
-}