From e6f0ac0289ad3685e2af5dfc17ee79c3c1170bdf Mon Sep 17 00:00:00 2001 From: Piotr Buda Date: Mon, 31 Jul 2017 11:59:08 +0200 Subject: #7: Add asFile and asPath responses --- .../scala/com/softwaremill/sttp/BasicTests.scala | 115 ++++++++++++++++++++- .../scala/com/softwaremill/sttp/testHelpers.scala | 40 ++++++- 2 files changed, 149 insertions(+), 6 deletions(-) (limited to 'tests/src/test/scala') diff --git a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala index 36c82ef..28e210f 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 @@ -32,14 +33,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") { @@ -115,6 +129,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 @@ -402,6 +422,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) +} -- cgit v1.2.3