aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmar Alejandro Mainegra Sarduy <omainegra@gmail.com>2017-08-04 19:36:07 -0400
committerOmar Alejandro Mainegra Sarduy <omainegra@gmail.com>2017-08-04 19:36:07 -0400
commitaffc8cef21ba2ff8f46b2144b2064b8150840f2b (patch)
tree6e0edb41759f423e77d6efa5053111fc1b48ed94
parent436ba7dc5afbecbe529b68835a8a4473bd92e56b (diff)
parentff76a1737bb88c2664927db309d196e677ba3e98 (diff)
downloadsttp-affc8cef21ba2ff8f46b2144b2064b8150840f2b.tar.gz
sttp-affc8cef21ba2ff8f46b2144b2064b8150840f2b.tar.bz2
sttp-affc8cef21ba2ff8f46b2144b2064b8150840f2b.zip
Merge branch 'master' into okhttp3-monix
# Conflicts: # okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala
-rw-r--r--README.md17
-rw-r--r--akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala19
-rw-r--r--async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala5
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala17
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala24
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/package.scala28
-rw-r--r--okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala7
-rw-r--r--tests/src/test/resources/binaryfile.jpgbin0 -> 42010 bytes
-rw-r--r--tests/src/test/resources/textfile.txt100
-rw-r--r--tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala115
-rw-r--r--tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala40
-rw-r--r--version.sbt2
12 files changed, 340 insertions, 34 deletions
diff --git a/README.md b/README.md
index 358c6ea..1bc88dd 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,7 @@ If you are an [Ammonite](http://ammonite.io) user, you can quickly start
experimenting with sttp by copy-pasting the following:
```scala
-import $ivy.`com.softwaremill.sttp::core:0.0.4`
+import $ivy.`com.softwaremill.sttp::core:0.0.5`
import com.softwaremill.sttp._
implicit val handler = HttpURLConnectionSttpHandler
sttp.get(uri"http://httpbin.org/ip").send()
@@ -70,7 +70,7 @@ sttp.get(uri"http://httpbin.org/ip").send()
SBT dependency:
```scala
-"com.softwaremill.sttp" %% "core" % "0.0.4"
+"com.softwaremill.sttp" %% "core" % "0.0.5"
```
`sttp` is available for Scala 2.11 and 2.12, and requires Java 8. The core
@@ -208,7 +208,7 @@ implicit val sttpHandler = HttpURLConnectionSttpHandler
To use, add the following dependency to your project:
```scala
-"com.softwaremill.sttp" %% "akka-http-handler" % "0.0.4"
+"com.softwaremill.sttp" %% "akka-http-handler" % "0.0.5"
```
This handler depends on [akka-http](http://doc.akka.io/docs/akka-http/current/scala/http/).
@@ -267,13 +267,13 @@ val response: Future[Response[Source[ByteString, Any]]] =
To use, add the following dependency to your project:
```scala
-"com.softwaremill.sttp" %% "async-http-client-handler-future" % "0.0.4"
+"com.softwaremill.sttp" %% "async-http-client-handler-future" % "0.0.5"
// or
-"com.softwaremill.sttp" %% "async-http-client-handler-scalaz" % "0.0.4"
+"com.softwaremill.sttp" %% "async-http-client-handler-scalaz" % "0.0.5"
// or
-"com.softwaremill.sttp" %% "async-http-client-handler-monix" % "0.0.4"
+"com.softwaremill.sttp" %% "async-http-client-handler-monix" % "0.0.5"
// or
-"com.softwaremill.sttp" %% "async-http-client-handler-cats" % "0.0.4"
+"com.softwaremill.sttp" %% "async-http-client-handler-cats" % "0.0.5"
```
This handler depends on [async-http-client](https://github.com/AsyncHttpClient/async-http-client).
@@ -355,7 +355,7 @@ val response: Task[Response[Observable[ByteBuffer]]] =
To use, add the following dependency to your project:
```scala
-"com.softwaremill.sttp" %% "okhttp-client-handler" % "0.0.4"
+"com.softwaremill.sttp" %% "okhttp-client-handler" % "0.0.5"
```
This handler depends on [OkHttp](http://square.github.io/okhttp/), and offers
@@ -443,3 +443,4 @@ and pick a task you'd like to work on!
* [Adam Warski](https://github.com/adamw)
* [Omar Alejandro Mainegra Sarduy](https://github.com/omainegra)
* [Bjørn Madsen](https://github.com/aeons)
+* [Piotr Buda](https://github.com/pbuda)
diff --git a/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala b/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala
index d4ca3d8..0a2a467 100644
--- a/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala
+++ b/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala
@@ -1,6 +1,6 @@
package com.softwaremill.sttp.akkahttp
-import java.io.UnsupportedEncodingException
+import java.io.{File, IOException, UnsupportedEncodingException}
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
@@ -10,7 +10,7 @@ import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers.{HttpEncodings, `Content-Type`}
import akka.http.scaladsl.model.ContentTypes.`application/octet-stream`
import akka.stream.ActorMaterializer
-import akka.stream.scaladsl.{Source, StreamConverters}
+import akka.stream.scaladsl.{FileIO, Source, StreamConverters}
import akka.util.ByteString
import com.softwaremill.sttp._
import com.softwaremill.sttp.model._
@@ -67,6 +67,18 @@ class AkkaHttpSttpHandler private (actorSystem: ActorSystem,
.runFold(ByteString(""))(_ ++ _)
.map(_.toArray[Byte])
+ def saved(file: File, overwrite: Boolean) = {
+ if (!file.exists()) {
+ file.getParentFile.mkdirs()
+ file.createNewFile()
+ } else if (!overwrite) {
+ throw new IOException(
+ s"File ${file.getAbsolutePath} exists - overwriting prohibited")
+ }
+
+ hr.entity.dataBytes.runWith(FileIO.toPath(file.toPath))
+ }
+
rr match {
case MappedResponseAs(raw, g) => bodyFromAkka(raw, hr).map(g)
@@ -82,6 +94,9 @@ class AkkaHttpSttpHandler private (actorSystem: ActorSystem,
case r @ ResponseAsStream() =>
Future.successful(r.responseIsStream(hr.entity.dataBytes))
+
+ case ResponseAsFile(file, overwrite) =>
+ saved(file, overwrite).map(_ => file)
}
}
diff --git a/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala b/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala
index 9043882..8f75030 100644
--- a/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala
+++ b/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala
@@ -222,6 +222,11 @@ abstract class AsyncHttpClientHandler[R[_], S](asyncHttpClient: AsyncHttpClient,
Failure(
new IllegalStateException(
"Requested a streaming response, trying to read eagerly."))
+
+ case ResponseAsFile(file, overwrite) =>
+ Try(
+ ResponseAs
+ .saveFile(file, response.getResponseBodyAsStream, overwrite))
}
}
diff --git a/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala b/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala
index d5c2ccd..29da886 100644
--- a/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala
@@ -115,19 +115,8 @@ object HttpURLConnectionSttpHandler extends SttpHandler[Id, Nothing] {
case ResponseAsByteArray =>
val os = new ByteArrayOutputStream
- var read = 0
- val buf = new Array[Byte](1024)
-
- @tailrec
- def transfer(): Unit = {
- read = is.read(buf, 0, buf.length)
- if (read != -1) {
- os.write(buf, 0, read)
- transfer()
- }
- }
- transfer()
+ transfer(is, os)
os.toByteArray
@@ -135,6 +124,10 @@ object HttpURLConnectionSttpHandler extends SttpHandler[Id, Nothing] {
// only possible when the user requests the response as a stream of
// Nothing. Oh well ...
throw new IllegalStateException()
+
+ case ResponseAsFile(input, overwrite) =>
+ ResponseAs.saveFile(input, is, overwrite)
+
}
}
diff --git a/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala b/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala
index 3862483..24b9b2b 100644
--- a/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala
@@ -1,8 +1,10 @@
package com.softwaremill.sttp.model
+import java.io.{File, FileOutputStream, IOException, InputStream}
import java.net.URLDecoder
+import java.nio.file.Path
-import com.softwaremill.sttp.MonadError
+import com.softwaremill.sttp.{MonadError, transfer}
import scala.collection.immutable.Seq
import scala.language.higherKinds
@@ -38,6 +40,9 @@ case class MappedResponseAs[T, T2, S](raw: BasicResponseAs[T, S], g: T => T2)
MappedResponseAs[T, T3, S](raw, g andThen f)
}
+case class ResponseAsFile(input: File, overwrite: Boolean)
+ extends BasicResponseAs[File, Nothing]
+
object ResponseAs {
private[sttp] def parseParams(s: String,
encoding: String): Seq[(String, String)] = {
@@ -52,6 +57,23 @@ object ResponseAs {
})
}
+ private[sttp] def saveFile(file: File,
+ is: InputStream,
+ overwrite: Boolean): File = {
+ if (!file.exists()) {
+ file.getParentFile.mkdirs()
+ file.createNewFile()
+ } else if (!overwrite) {
+ throw new IOException(
+ s"File ${file.getAbsolutePath} exists - overwriting prohibited")
+ }
+
+ val os = new FileOutputStream(file)
+
+ transfer(is, os)
+ file
+ }
+
/**
* Handles responses according to the given specification when basic
* response specifications can be handled eagerly, that is without
diff --git a/core/src/main/scala/com/softwaremill/sttp/package.scala b/core/src/main/scala/com/softwaremill/sttp/package.scala
index daba574..884d2f9 100644
--- a/core/src/main/scala/com/softwaremill/sttp/package.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/package.scala
@@ -1,12 +1,12 @@
package com.softwaremill
-import java.io.{File, InputStream}
+import java.io._
import java.nio.ByteBuffer
import java.nio.file.Path
import com.softwaremill.sttp.model._
-import scala.annotation.implicitNotFound
+import scala.annotation.{implicitNotFound, tailrec}
import scala.language.higherKinds
import scala.collection.immutable.Seq
@@ -88,6 +88,14 @@ package object sttp {
def asStream[S]: ResponseAs[S, S] = ResponseAsStream[S, S]()
+ def asFile(file: File,
+ overwrite: Boolean = false): ResponseAs[File, Nothing] =
+ ResponseAsFile(file, overwrite)
+
+ def asPath(path: Path,
+ overwrite: Boolean = false): ResponseAs[Path, Nothing] =
+ ResponseAsFile(path.toFile, overwrite).map(_.toPath)
+
// multi part factory methods
/**
@@ -159,6 +167,22 @@ package object sttp {
private[sttp] def contentTypeWithEncoding(ct: String, enc: String) =
s"$ct; charset=$enc"
+ private[sttp] def transfer(is: InputStream, os: OutputStream) {
+ var read = 0
+ val buf = new Array[Byte](1024)
+
+ @tailrec
+ def transfer(): Unit = {
+ read = is.read(buf, 0, buf.length)
+ if (read != -1) {
+ os.write(buf, 0, read)
+ transfer()
+ }
+ }
+
+ transfer()
+ }
+
// uri interpolator
implicit class UriContext(val sc: StringContext) extends AnyVal {
diff --git a/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala b/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala
index af896d4..2090e02 100644
--- a/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala
+++ b/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala
@@ -90,6 +90,8 @@ abstract class OkHttpClientHandler[R[_], S](client: OkHttpClient)
case ResponseAsByteArray => Try(res.body().bytes())
case ras @ ResponseAsStream() =>
responseBodyToStream(res).map(ras.responseIsStream)
+ case ResponseAsFile(file, overwrite) =>
+ Try(ResponseAs.saveFile(file, res.body().byteStream(), overwrite))
}
}
@@ -131,9 +133,10 @@ abstract class OkHttpAsyncClientHandler[R[_], S](client: OkHttpClient,
.enqueue(new Callback {
override def onFailure(call: Call, e: IOException): Unit =
error(e)
-
+
override def onResponse(call: Call, response: OkHttpResponse): Unit =
- success(readResponse(response, r.responseAs))
+ try success(readResponse(response, r.responseAs))
+ catch { case e: Exception => error(e) }
})
})
}
diff --git a/tests/src/test/resources/binaryfile.jpg b/tests/src/test/resources/binaryfile.jpg
new file mode 100644
index 0000000..b9f5c5a
--- /dev/null
+++ b/tests/src/test/resources/binaryfile.jpg
Binary files differ
diff --git a/tests/src/test/resources/textfile.txt b/tests/src/test/resources/textfile.txt
new file mode 100644
index 0000000..9904f90
--- /dev/null
+++ b/tests/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/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
index 6d383f5..d8e3d03 100644
--- a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
+++ b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
@@ -1,7 +1,8 @@
package com.softwaremill.sttp
-import java.io.ByteArrayInputStream
+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}
@@ -14,7 +15,7 @@ import akka.http.scaladsl.server.directives.Credentials
import com.softwaremill.sttp.akkahttp.AkkaHttpSttpHandler
import com.typesafe.scalalogging.StrictLogging
import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures}
-import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers}
+import org.scalatest.{path => _, _}
import better.files._
import com.softwaremill.sttp.asynchttpclient.cats.CatsAsyncHttpClientHandler
import com.softwaremill.sttp.asynchttpclient.future.FutureAsyncHttpClientHandler
@@ -33,14 +34,27 @@ class BasicTests
with Matchers
with BeforeAndAfterAll
with ScalaFutures
+ with OptionValues
with StrictLogging
with IntegrationPatience
with TestHttpServer
- with ForceWrapped {
+ 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")
+
override val serverRoutes: Route =
pathPrefix("echo") {
pathPrefix("form_params") {
@@ -116,6 +130,12 @@ class BasicTests
encodeResponseWith(Gzip, Deflate, NoCoding) {
complete("I'm compressed!")
}
+ } ~ pathPrefix("download") {
+ path("binary") {
+ getFromFile(binaryFile)
+ } ~ path("text") {
+ getFromFile(textFile)
+ }
}
override def port = 51823
@@ -405,6 +425,95 @@ class BasicTests
resp.body 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.body 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.body 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.body 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.body 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.body shouldBe path
+ path.toFile should exist
+ path.toFile should haveSameContentAs(textFile)
+ }
+ }
}
override protected def afterAll(): Unit = {
diff --git a/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala b/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala
index 558e9dd..59e1612 100644
--- a/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala
+++ b/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala
@@ -1,14 +1,20 @@
package com.softwaremill.sttp
+import java.nio.file.{Files, Paths}
+import java.{io, util}
+
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Route
import akka.stream.ActorMaterializer
import org.scalatest.{BeforeAndAfterAll, Suite}
import org.scalatest.concurrent.ScalaFutures
+import org.scalatest.exceptions.TestFailedException
+import org.scalatest.matchers.{MatchResult, Matcher}
import scala.concurrent.Future
import scala.language.higherKinds
+import scalaz._
trait TestHttpServer extends BeforeAndAfterAll with ScalaFutures {
this: Suite =>
@@ -41,23 +47,35 @@ trait ForceWrapped extends ScalaFutures { this: Suite =>
}
val future = new ForceWrappedValue[Future] {
override def force[T](wrapped: Future[T]): T =
- wrapped.futureValue
+ try {
+ wrapped.futureValue
+ } catch {
+ case e: TestFailedException if e.getCause != null => throw e.getCause
+ }
}
val scalazTask = new ForceWrappedValue[scalaz.concurrent.Task] {
override def force[T](wrapped: scalaz.concurrent.Task[T]): T =
- wrapped.unsafePerformSync
+ wrapped.unsafePerformSyncAttempt match {
+ case -\/(error) => throw error
+ case \/-(value) => value
+ }
}
val monixTask = new ForceWrappedValue[monix.eval.Task] {
import monix.execution.Scheduler.Implicits.global
override def force[T](wrapped: monix.eval.Task[T]): T =
- wrapped.runAsync.futureValue
+ try {
+ wrapped.runAsync.futureValue
+ } catch {
+ case e: TestFailedException => throw e.getCause
+ }
}
val catsIo = new ForceWrappedValue[cats.effect.IO] {
override def force[T](wrapped: cats.effect.IO[T]): T =
wrapped.unsafeRunSync
}
}
+
implicit class ForceDecorator[R[_], T](wrapped: R[T]) {
def force()(implicit fwv: ForceWrappedValue[R]): T = fwv.force(wrapped)
}
@@ -72,3 +90,19 @@ object EvalScala {
tb.eval(tb.parse(code))
}
}
+
+object CustomMatchers {
+ class FileContentsMatch(file: java.io.File) extends Matcher[java.io.File] {
+ override def apply(left: io.File): MatchResult = {
+ val inBA = Files.readAllBytes(Paths.get(left.getAbsolutePath))
+ val expectedBA = Files.readAllBytes(Paths.get(file.getAbsolutePath))
+ MatchResult(
+ util.Arrays.equals(inBA, expectedBA),
+ "The files' contents are not the same",
+ "The files' contents are the same"
+ )
+ }
+ }
+
+ def haveSameContentAs(file: io.File) = new FileContentsMatch(file)
+}
diff --git a/version.sbt b/version.sbt
index ccd484c..3b1722c 100644
--- a/version.sbt
+++ b/version.sbt
@@ -1 +1 @@
-version in ThisBuild := "0.0.5-SNAPSHOT"
+version in ThisBuild := "0.0.6-SNAPSHOT"