+str #17361: Unified http java/scala projects except marshallers
This commit is contained in:
parent
454a393af1
commit
be82e85ffc
182 changed files with 13693 additions and 0 deletions
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.javadsl.testkit
|
||||
|
||||
import org.junit.rules.ExternalResource
|
||||
import org.junit.{ Rule, Assert }
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.http.scaladsl.model.HttpResponse
|
||||
|
||||
/**
|
||||
* A RouteTest that uses JUnit assertions.
|
||||
*/
|
||||
abstract class JUnitRouteTestBase extends RouteTest {
|
||||
protected def systemResource: ActorSystemResource
|
||||
implicit def system: ActorSystem = systemResource.system
|
||||
implicit def materializer: ActorFlowMaterializer = systemResource.materializer
|
||||
|
||||
protected def createTestResponse(response: HttpResponse): TestResponse =
|
||||
new TestResponse(response, awaitDuration)(system.dispatcher, materializer) {
|
||||
protected def assertEquals(expected: AnyRef, actual: AnyRef, message: String): Unit =
|
||||
Assert.assertEquals(message, expected, actual)
|
||||
|
||||
protected def assertEquals(expected: Int, actual: Int, message: String): Unit =
|
||||
Assert.assertEquals(message, expected, actual)
|
||||
|
||||
protected def assertTrue(predicate: Boolean, message: String): Unit =
|
||||
Assert.assertTrue(message, predicate)
|
||||
|
||||
protected def fail(message: String): Nothing = {
|
||||
Assert.fail(message)
|
||||
throw new IllegalStateException("Assertion should have failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
abstract class JUnitRouteTest extends JUnitRouteTestBase {
|
||||
private[this] val _systemResource = new ActorSystemResource
|
||||
@Rule
|
||||
protected def systemResource: ActorSystemResource = _systemResource
|
||||
}
|
||||
|
||||
class ActorSystemResource extends ExternalResource {
|
||||
protected def createSystem(): ActorSystem = ActorSystem()
|
||||
protected def createFlowMaterializer(system: ActorSystem): ActorFlowMaterializer = ActorFlowMaterializer()(system)
|
||||
|
||||
implicit def system: ActorSystem = _system
|
||||
implicit def materializer: ActorFlowMaterializer = _materializer
|
||||
|
||||
private[this] var _system: ActorSystem = null
|
||||
private[this] var _materializer: ActorFlowMaterializer = null
|
||||
|
||||
override def before(): Unit = {
|
||||
require((_system eq null) && (_materializer eq null))
|
||||
_system = createSystem()
|
||||
_materializer = createFlowMaterializer(_system)
|
||||
}
|
||||
override def after(): Unit = {
|
||||
_system.shutdown()
|
||||
_system.awaitTermination(5.seconds)
|
||||
_system = null
|
||||
_materializer = null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.javadsl.testkit
|
||||
|
||||
import scala.annotation.varargs
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.concurrent.duration._
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.http.scaladsl.server
|
||||
import akka.http.javadsl.model.HttpRequest
|
||||
import akka.http.javadsl.server.{ Route, Directives }
|
||||
import akka.http.impl.util.JavaMapping.Implicits._
|
||||
import akka.http.impl.server.RouteImplementation
|
||||
import akka.http.scaladsl.model.HttpResponse
|
||||
import akka.http.scaladsl.server.{ RouteResult, RoutingSettings, Route ⇒ ScalaRoute }
|
||||
import akka.actor.ActorSystem
|
||||
import akka.event.NoLogging
|
||||
import akka.http.impl.util._
|
||||
|
||||
abstract class RouteTest {
|
||||
implicit def system: ActorSystem
|
||||
implicit def materializer: ActorFlowMaterializer
|
||||
implicit def executionContext: ExecutionContext = system.dispatcher
|
||||
|
||||
protected def awaitDuration: FiniteDuration = 500.millis
|
||||
|
||||
def runRoute(route: Route, request: HttpRequest): TestResponse = {
|
||||
val scalaRoute = ScalaRoute.seal(RouteImplementation(route))
|
||||
val result = scalaRoute(new server.RequestContextImpl(request.asScala, NoLogging, RoutingSettings(system)))
|
||||
|
||||
result.awaitResult(awaitDuration) match {
|
||||
case RouteResult.Complete(response) ⇒ createTestResponse(response)
|
||||
}
|
||||
}
|
||||
|
||||
@varargs
|
||||
def testRoute(first: Route, others: Route*): TestRoute =
|
||||
new TestRoute {
|
||||
val underlying: Route = Directives.route(first, others: _*)
|
||||
|
||||
def run(request: HttpRequest): TestResponse = runRoute(underlying, request)
|
||||
}
|
||||
|
||||
protected def createTestResponse(response: HttpResponse): TestResponse
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.javadsl.testkit
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
import akka.util.ByteString
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.http.scaladsl.unmarshalling.Unmarshal
|
||||
import akka.http.scaladsl.model.HttpResponse
|
||||
import akka.http.impl.util._
|
||||
import akka.http.impl.server.UnmarshallerImpl
|
||||
import akka.http.impl.util.JavaMapping.Implicits._
|
||||
import akka.http.javadsl.server.Unmarshaller
|
||||
import akka.http.javadsl.model._
|
||||
|
||||
/**
|
||||
* A wrapper for responses
|
||||
*/
|
||||
abstract class TestResponse(_response: HttpResponse, awaitAtMost: FiniteDuration)(implicit ec: ExecutionContext, materializer: ActorFlowMaterializer) {
|
||||
lazy val entity: HttpEntityStrict =
|
||||
_response.entity.toStrict(awaitAtMost).awaitResult(awaitAtMost)
|
||||
lazy val response: HttpResponse = _response.withEntity(entity)
|
||||
|
||||
// FIXME: add header getters / assertions
|
||||
|
||||
def mediaType: MediaType = extractFromResponse(_.entity.contentType.mediaType)
|
||||
def mediaTypeString: String = mediaType.toString
|
||||
def entityBytes: ByteString = entity.data()
|
||||
def entityAs[T](unmarshaller: Unmarshaller[T]): T =
|
||||
Unmarshal(response)
|
||||
.to(unmarshaller.asInstanceOf[UnmarshallerImpl[T]].scalaUnmarshaller(ec, materializer), ec)
|
||||
.awaitResult(awaitAtMost)
|
||||
def entityAsString: String = entity.data().utf8String
|
||||
def status: StatusCode = response.status.asJava
|
||||
def statusCode: Int = response.status.intValue
|
||||
def header[T <: HttpHeader](clazz: Class[T]): T =
|
||||
response.header(ClassTag(clazz))
|
||||
.getOrElse(fail(s"Expected header of type ${clazz.getSimpleName} but wasn't found."))
|
||||
|
||||
def assertStatusCode(expected: Int): TestResponse =
|
||||
assertStatusCode(StatusCodes.get(expected))
|
||||
def assertStatusCode(expected: StatusCode): TestResponse =
|
||||
assertEqualsKind(expected, status, "status code")
|
||||
def assertMediaType(expected: String): TestResponse =
|
||||
assertEqualsKind(expected, mediaTypeString, "media type")
|
||||
def assertMediaType(expected: MediaType): TestResponse =
|
||||
assertEqualsKind(expected, mediaType, "media type")
|
||||
def assertEntity(expected: String): TestResponse =
|
||||
assertEqualsKind(expected, entityAsString, "entity")
|
||||
def assertEntityBytes(expected: ByteString): TestResponse =
|
||||
assertEqualsKind(expected, entityBytes, "entity")
|
||||
def assertEntityAs[T <: AnyRef](unmarshaller: Unmarshaller[T], expected: T): TestResponse =
|
||||
assertEqualsKind(expected, entityAs(unmarshaller), "entity")
|
||||
def assertHeaderExists(expected: HttpHeader): TestResponse = {
|
||||
assertTrue(response.headers.exists(_ == expected), s"Header $expected was missing.")
|
||||
this
|
||||
}
|
||||
def assertHeaderKindExists(name: String): TestResponse = {
|
||||
val lowercased = name.toRootLowerCase
|
||||
assertTrue(response.headers.exists(_.is(lowercased)), s"Expected `$name` header was missing.")
|
||||
this
|
||||
}
|
||||
def assertHeaderExists(name: String, value: String): TestResponse = {
|
||||
val lowercased = name.toRootLowerCase
|
||||
val headers = response.headers.filter(_.is(lowercased))
|
||||
if (headers.isEmpty) fail(s"Expected `$name` header was missing.")
|
||||
else assertTrue(headers.exists(_.value == value),
|
||||
s"`$name` header was found but had the wrong value. Found headers: ${headers.mkString(", ")}")
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
private[this] def extractFromResponse[T](f: HttpResponse ⇒ T): T =
|
||||
if (response eq null) fail("Request didn't complete with response")
|
||||
else f(response)
|
||||
|
||||
protected def assertEqualsKind(expected: AnyRef, actual: AnyRef, kind: String): TestResponse = {
|
||||
assertEquals(expected, actual, s"Unexpected $kind!")
|
||||
this
|
||||
}
|
||||
protected def assertEqualsKind(expected: Int, actual: Int, kind: String): TestResponse = {
|
||||
assertEquals(expected, actual, s"Unexpected $kind!")
|
||||
this
|
||||
}
|
||||
|
||||
protected def fail(message: String): Nothing
|
||||
protected def assertEquals(expected: AnyRef, actual: AnyRef, message: String): Unit
|
||||
protected def assertEquals(expected: Int, actual: Int, message: String): Unit
|
||||
protected def assertTrue(predicate: Boolean, message: String): Unit
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.javadsl.testkit
|
||||
|
||||
import akka.http.javadsl.model.HttpRequest
|
||||
import akka.http.javadsl.server.Route
|
||||
|
||||
trait TestRoute {
|
||||
def underlying: Route
|
||||
def run(request: HttpRequest): TestResponse
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.scaladsl.testkit
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{ ExecutionContext, Await }
|
||||
import akka.http.scaladsl.unmarshalling.{ Unmarshal, FromEntityUnmarshaller }
|
||||
import akka.http.scaladsl.marshalling._
|
||||
import akka.http.scaladsl.model.HttpEntity
|
||||
import akka.stream.FlowMaterializer
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
trait MarshallingTestUtils {
|
||||
def marshal[T: ToEntityMarshaller](value: T)(implicit ec: ExecutionContext, mat: FlowMaterializer): HttpEntity.Strict =
|
||||
Await.result(Marshal(value).to[HttpEntity].flatMap(_.toStrict(1.second)), 1.second)
|
||||
|
||||
def unmarshalValue[T: FromEntityUnmarshaller](entity: HttpEntity)(implicit ec: ExecutionContext, mat: FlowMaterializer): T =
|
||||
unmarshal(entity).get
|
||||
|
||||
def unmarshal[T: FromEntityUnmarshaller](entity: HttpEntity)(implicit ec: ExecutionContext, mat: FlowMaterializer): Try[T] = {
|
||||
val fut = Unmarshal(entity).to[T]
|
||||
Await.ready(fut, 1.second)
|
||||
fut.value.get
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.scaladsl.testkit
|
||||
|
||||
import com.typesafe.config.{ ConfigFactory, Config }
|
||||
import scala.collection.immutable
|
||||
import scala.concurrent.{ Await, Future }
|
||||
import scala.concurrent.duration._
|
||||
import scala.util.DynamicVariable
|
||||
import scala.reflect.ClassTag
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.http.scaladsl.client.RequestBuilding
|
||||
import akka.http.scaladsl.util.FastFuture
|
||||
import akka.http.scaladsl.server._
|
||||
import akka.http.scaladsl.unmarshalling._
|
||||
import akka.http.scaladsl.model._
|
||||
import headers.Host
|
||||
import FastFuture._
|
||||
|
||||
trait RouteTest extends RequestBuilding with RouteTestResultComponent with MarshallingTestUtils {
|
||||
this: TestFrameworkInterface ⇒
|
||||
|
||||
/** Override to supply a custom ActorSystem */
|
||||
protected def createActorSystem(): ActorSystem =
|
||||
ActorSystem(actorSystemNameFrom(getClass), testConfig)
|
||||
|
||||
def actorSystemNameFrom(clazz: Class[_]) =
|
||||
clazz.getName
|
||||
.replace('.', '-')
|
||||
.replace('_', '-')
|
||||
.filter(_ != '$')
|
||||
|
||||
def testConfigSource: String = ""
|
||||
def testConfig: Config = {
|
||||
val source = testConfigSource
|
||||
val config = if (source.isEmpty) ConfigFactory.empty() else ConfigFactory.parseString(source)
|
||||
config.withFallback(ConfigFactory.load())
|
||||
}
|
||||
implicit val system = createActorSystem()
|
||||
implicit def executor = system.dispatcher
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
def cleanUp(): Unit = system.shutdown()
|
||||
|
||||
private val dynRR = new DynamicVariable[RouteTestResult](null)
|
||||
private def result =
|
||||
if (dynRR.value ne null) dynRR.value
|
||||
else sys.error("This value is only available inside of a `check` construct!")
|
||||
|
||||
def check[T](body: ⇒ T): RouteTestResult ⇒ T = result ⇒ dynRR.withValue(result.awaitResult)(body)
|
||||
|
||||
def handled: Boolean = result.handled
|
||||
def response: HttpResponse = result.response
|
||||
def responseEntity: HttpEntity = result.entity
|
||||
def chunks: immutable.Seq[HttpEntity.ChunkStreamPart] = result.chunks
|
||||
def entityAs[T: FromEntityUnmarshaller: ClassTag](implicit timeout: Duration = 1.second): T = {
|
||||
def msg(e: Throwable) = s"Could not unmarshal entity to type '${implicitly[ClassTag[T]]}' for `entityAs` assertion: $e\n\nResponse was: $response"
|
||||
Await.result(Unmarshal(responseEntity).to[T].fast.recover[T] { case error ⇒ failTest(msg(error)) }, timeout)
|
||||
}
|
||||
def responseAs[T: FromResponseUnmarshaller: ClassTag](implicit timeout: Duration = 1.second): T = {
|
||||
def msg(e: Throwable) = s"Could not unmarshal response to type '${implicitly[ClassTag[T]]}' for `responseAs` assertion: $e\n\nResponse was: $response"
|
||||
Await.result(Unmarshal(response).to[T].fast.recover[T] { case error ⇒ failTest(msg(error)) }, timeout)
|
||||
}
|
||||
def contentType: ContentType = responseEntity.contentType
|
||||
def mediaType: MediaType = contentType.mediaType
|
||||
def charset: HttpCharset = contentType.charset
|
||||
def definedCharset: Option[HttpCharset] = contentType.definedCharset
|
||||
def headers: immutable.Seq[HttpHeader] = response.headers
|
||||
def header[T <: HttpHeader: ClassTag]: Option[T] = response.header[T]
|
||||
def header(name: String): Option[HttpHeader] = response.headers.find(_.is(name.toLowerCase))
|
||||
def status: StatusCode = response.status
|
||||
|
||||
def closingExtension: String = chunks.lastOption match {
|
||||
case Some(HttpEntity.LastChunk(extension, _)) ⇒ extension
|
||||
case _ ⇒ ""
|
||||
}
|
||||
def trailer: immutable.Seq[HttpHeader] = chunks.lastOption match {
|
||||
case Some(HttpEntity.LastChunk(_, trailer)) ⇒ trailer
|
||||
case _ ⇒ Nil
|
||||
}
|
||||
|
||||
def rejections: immutable.Seq[Rejection] = result.rejections
|
||||
def rejection: Rejection = {
|
||||
val r = rejections
|
||||
if (r.size == 1) r.head else failTest("Expected a single rejection but got %s (%s)".format(r.size, r))
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy that can be used as `~> runRoute` to run the route but without blocking for the result.
|
||||
* The result of the pipeline is the result that can later be checked with `check`. See the
|
||||
* "separate running route from checking" example from ScalatestRouteTestSpec.scala.
|
||||
*/
|
||||
def runRoute: RouteTestResult ⇒ RouteTestResult = akka.http.impl.util.identityFunc
|
||||
|
||||
// there is already an implicit class WithTransformation in scope (inherited from akka.http.scaladsl.testkit.TransformerPipelineSupport)
|
||||
// however, this one takes precedence
|
||||
implicit class WithTransformation2(request: HttpRequest) {
|
||||
def ~>[A, B](f: A ⇒ B)(implicit ta: TildeArrow[A, B]): ta.Out = ta(request, f)
|
||||
}
|
||||
|
||||
abstract class TildeArrow[A, B] {
|
||||
type Out
|
||||
def apply(request: HttpRequest, f: A ⇒ B): Out
|
||||
}
|
||||
|
||||
case class DefaultHostInfo(host: Host, securedConnection: Boolean)
|
||||
object DefaultHostInfo {
|
||||
implicit def defaultHost: DefaultHostInfo = DefaultHostInfo(Host("example.com"), securedConnection = false)
|
||||
}
|
||||
object TildeArrow {
|
||||
implicit object InjectIntoRequestTransformer extends TildeArrow[HttpRequest, HttpRequest] {
|
||||
type Out = HttpRequest
|
||||
def apply(request: HttpRequest, f: HttpRequest ⇒ HttpRequest) = f(request)
|
||||
}
|
||||
implicit def injectIntoRoute(implicit timeout: RouteTestTimeout, setup: RoutingSetup,
|
||||
defaultHostInfo: DefaultHostInfo) =
|
||||
new TildeArrow[RequestContext, Future[RouteResult]] {
|
||||
type Out = RouteTestResult
|
||||
def apply(request: HttpRequest, route: Route): Out = {
|
||||
val routeTestResult = new RouteTestResult(timeout.duration)
|
||||
val effectiveRequest =
|
||||
request.withEffectiveUri(
|
||||
securedConnection = defaultHostInfo.securedConnection,
|
||||
defaultHostHeader = defaultHostInfo.host)
|
||||
val ctx = new RequestContextImpl(effectiveRequest, setup.routingLog.requestLog(effectiveRequest), setup.settings)
|
||||
val sealedExceptionHandler = setup.exceptionHandler.seal(setup.settings)
|
||||
val semiSealedRoute = // sealed for exceptions but not for rejections
|
||||
Directives.handleExceptions(sealedExceptionHandler) { route }
|
||||
val deferrableRouteResult = semiSealedRoute(ctx)
|
||||
deferrableRouteResult.fast.foreach(routeTestResult.handleResult)(setup.executor)
|
||||
routeTestResult
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME: trait Specs2RouteTest extends RouteTest with Specs2Interface
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.scaladsl.testkit
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import scala.collection.immutable
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext
|
||||
import akka.stream.FlowMaterializer
|
||||
import akka.stream.scaladsl._
|
||||
import akka.http.scaladsl.model.HttpEntity.ChunkStreamPart
|
||||
import akka.http.scaladsl.server._
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.impl.util._
|
||||
|
||||
trait RouteTestResultComponent {
|
||||
|
||||
def failTest(msg: String): Nothing
|
||||
|
||||
/**
|
||||
* A receptacle for the response or rejections created by a route.
|
||||
*/
|
||||
class RouteTestResult(timeout: FiniteDuration)(implicit fm: FlowMaterializer) {
|
||||
private[this] var result: Option[Either[immutable.Seq[Rejection], HttpResponse]] = None
|
||||
private[this] val latch = new CountDownLatch(1)
|
||||
|
||||
def handled: Boolean = synchronized { result.isDefined && result.get.isRight }
|
||||
|
||||
def rejections: immutable.Seq[Rejection] = synchronized {
|
||||
result match {
|
||||
case Some(Left(rejections)) ⇒ rejections
|
||||
case Some(Right(response)) ⇒ failTest("Request was not rejected, response was " + response)
|
||||
case None ⇒ failNeitherCompletedNorRejected()
|
||||
}
|
||||
}
|
||||
|
||||
def response: HttpResponse = rawResponse.copy(entity = entity)
|
||||
|
||||
/** Returns a "fresh" entity with a "fresh" unconsumed byte- or chunk stream (if not strict) */
|
||||
def entity: ResponseEntity = entityRecreator()
|
||||
|
||||
def chunks: immutable.Seq[ChunkStreamPart] =
|
||||
entity match {
|
||||
case HttpEntity.Chunked(_, chunks) ⇒ awaitAllElements[ChunkStreamPart](chunks)
|
||||
case _ ⇒ Nil
|
||||
}
|
||||
|
||||
def ~>[T](f: RouteTestResult ⇒ T): T = f(this)
|
||||
|
||||
private def rawResponse: HttpResponse = synchronized {
|
||||
result match {
|
||||
case Some(Right(response)) ⇒ response
|
||||
case Some(Left(Nil)) ⇒ failTest("Request was rejected")
|
||||
case Some(Left(rejection :: Nil)) ⇒ failTest("Request was rejected with rejection " + rejection)
|
||||
case Some(Left(rejections)) ⇒ failTest("Request was rejected with rejections " + rejections)
|
||||
case None ⇒ failNeitherCompletedNorRejected()
|
||||
}
|
||||
}
|
||||
|
||||
private[testkit] def handleResult(rr: RouteResult)(implicit ec: ExecutionContext): Unit =
|
||||
synchronized {
|
||||
if (result.isEmpty) {
|
||||
result = rr match {
|
||||
case RouteResult.Complete(response) ⇒ Some(Right(response))
|
||||
case RouteResult.Rejected(rejections) ⇒ Some(Left(RejectionHandler.applyTransformations(rejections)))
|
||||
}
|
||||
latch.countDown()
|
||||
} else failTest("Route completed/rejected more than once")
|
||||
}
|
||||
|
||||
private[testkit] def awaitResult: this.type = {
|
||||
latch.await(timeout.toMillis, MILLISECONDS)
|
||||
this
|
||||
}
|
||||
|
||||
private[this] lazy val entityRecreator: () ⇒ ResponseEntity =
|
||||
rawResponse.entity match {
|
||||
case s: HttpEntity.Strict ⇒ () ⇒ s
|
||||
|
||||
case HttpEntity.Default(contentType, contentLength, data) ⇒
|
||||
val dataChunks = awaitAllElements(data);
|
||||
{ () ⇒ HttpEntity.Default(contentType, contentLength, Source(dataChunks)) }
|
||||
|
||||
case HttpEntity.CloseDelimited(contentType, data) ⇒
|
||||
val dataChunks = awaitAllElements(data);
|
||||
{ () ⇒ HttpEntity.CloseDelimited(contentType, Source(dataChunks)) }
|
||||
|
||||
case HttpEntity.Chunked(contentType, chunks) ⇒
|
||||
val dataChunks = awaitAllElements(chunks);
|
||||
{ () ⇒ HttpEntity.Chunked(contentType, Source(dataChunks)) }
|
||||
}
|
||||
|
||||
private def failNeitherCompletedNorRejected(): Nothing =
|
||||
failTest("Request was neither completed nor rejected within " + timeout)
|
||||
|
||||
private def awaitAllElements[T](data: Source[T, _]): immutable.Seq[T] =
|
||||
data.grouped(100000).runWith(Sink.head).awaitResult(timeout)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.scaladsl.testkit
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.ActorSystem
|
||||
import akka.testkit._
|
||||
|
||||
case class RouteTestTimeout(duration: FiniteDuration)
|
||||
|
||||
object RouteTestTimeout {
|
||||
implicit def default(implicit system: ActorSystem) = RouteTestTimeout(1.second dilated)
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.scaladsl.testkit
|
||||
|
||||
import scala.util.Try
|
||||
import scala.concurrent.{ ExecutionContext, Future, Await }
|
||||
import scala.concurrent.duration._
|
||||
import org.scalatest.Suite
|
||||
import org.scalatest.matchers.Matcher
|
||||
import akka.stream.FlowMaterializer
|
||||
import akka.http.scaladsl.model.HttpEntity
|
||||
import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller
|
||||
|
||||
trait ScalatestUtils extends MarshallingTestUtils {
|
||||
import org.scalatest.Matchers._
|
||||
def evaluateTo[T](value: T): Matcher[Future[T]] =
|
||||
equal(value).matcher[T] compose (x ⇒ Await.result(x, 1.second))
|
||||
|
||||
def haveFailedWith(t: Throwable): Matcher[Future[_]] =
|
||||
equal(t).matcher[Throwable] compose (x ⇒ Await.result(x.failed, 1.second))
|
||||
|
||||
def unmarshalToValue[T: FromEntityUnmarshaller](value: T)(implicit ec: ExecutionContext, mat: FlowMaterializer): Matcher[HttpEntity] =
|
||||
equal(value).matcher[T] compose (unmarshalValue(_))
|
||||
|
||||
def unmarshalTo[T: FromEntityUnmarshaller](value: Try[T])(implicit ec: ExecutionContext, mat: FlowMaterializer): Matcher[HttpEntity] =
|
||||
equal(value).matcher[Try[T]] compose (unmarshal(_))
|
||||
}
|
||||
|
||||
trait ScalatestRouteTest extends RouteTest with TestFrameworkInterface.Scalatest with ScalatestUtils { this: Suite ⇒ }
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.scaladsl.testkit
|
||||
|
||||
import org.scalatest.exceptions.TestFailedException
|
||||
import org.scalatest.{ BeforeAndAfterAll, Suite }
|
||||
|
||||
//# source-quote
|
||||
trait TestFrameworkInterface {
|
||||
|
||||
def cleanUp()
|
||||
|
||||
def failTest(msg: String): Nothing
|
||||
}
|
||||
//#
|
||||
|
||||
object TestFrameworkInterface {
|
||||
|
||||
trait Scalatest extends TestFrameworkInterface with BeforeAndAfterAll {
|
||||
this: Suite ⇒
|
||||
|
||||
def failTest(msg: String) = throw new TestFailedException(msg, 11)
|
||||
|
||||
abstract override protected def afterAll(): Unit = {
|
||||
cleanUp()
|
||||
super.afterAll()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue