2015-05-21 17:17:55 +02:00
|
|
|
/*
|
|
|
|
|
* 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
|
2015-08-13 10:14:12 +02:00
|
|
|
import akka.stream.Materializer
|
2015-05-21 17:17:55 +02:00
|
|
|
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._
|
|
|
|
|
|
|
|
|
|
/**
|
2015-08-13 10:14:12 +02:00
|
|
|
* A wrapper for responses.
|
|
|
|
|
*
|
|
|
|
|
* To support the testkit API, a third-party testing library needs to implement this class and provide
|
|
|
|
|
* implementations for the abstract assertion methods.
|
2015-05-21 17:17:55 +02:00
|
|
|
*/
|
2015-08-13 10:14:12 +02:00
|
|
|
abstract class TestResponse(_response: HttpResponse, awaitAtMost: FiniteDuration)(implicit ec: ExecutionContext, materializer: Materializer) {
|
2015-07-17 14:01:29 +02:00
|
|
|
/**
|
|
|
|
|
* Returns the strictified entity of the response. It will be strictified on first access.
|
|
|
|
|
*/
|
2015-12-16 15:38:20 +01:00
|
|
|
lazy val entity: HttpEntity.Strict = _response.entity.toStrict(awaitAtMost).awaitResult(awaitAtMost)
|
2015-05-21 17:17:55 +02:00
|
|
|
|
2015-07-17 14:01:29 +02:00
|
|
|
/**
|
|
|
|
|
* Returns a copy of the underlying response with the strictified entity.
|
|
|
|
|
*/
|
|
|
|
|
lazy val response: HttpResponse = _response.withEntity(entity)
|
2015-05-21 17:17:55 +02:00
|
|
|
|
2015-07-17 14:01:29 +02:00
|
|
|
/**
|
|
|
|
|
* Returns the media-type of the the response's content-type
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def mediaType: MediaType = extractFromResponse(_.entity.contentType.mediaType)
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a string representation of the media-type of the the response's content-type
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def mediaTypeString: String = mediaType.toString
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the bytes of the response entity
|
|
|
|
|
*/
|
2015-12-16 15:38:20 +01:00
|
|
|
def entityBytes: ByteString = entity.getData
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the entity of the response unmarshalled with the given ``Unmarshaller``.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def entityAs[T](unmarshaller: Unmarshaller[T]): T =
|
|
|
|
|
Unmarshal(response)
|
2015-07-24 10:11:56 +02:00
|
|
|
.to(unmarshaller.asInstanceOf[UnmarshallerImpl[T]].scalaUnmarshaller, ec, materializer)
|
2015-05-21 17:17:55 +02:00
|
|
|
.awaitResult(awaitAtMost)
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the entity of the response interpreted as an UTF-8 encoded string.
|
|
|
|
|
*/
|
2015-12-16 15:38:20 +01:00
|
|
|
def entityAsString: String = entity.getData.utf8String
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the [[StatusCode]] of the response.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def status: StatusCode = response.status.asJava
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the numeric status code of the response.
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def statusCode: Int = response.status.intValue
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the first header of the response which is of the given class.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def header[T <: HttpHeader](clazz: Class[T]): T =
|
|
|
|
|
response.header(ClassTag(clazz))
|
2015-08-13 10:14:12 +02:00
|
|
|
.getOrElse(doFail(s"Expected header of type ${clazz.getSimpleName} but wasn't found."))
|
2015-05-21 17:17:55 +02:00
|
|
|
|
2015-07-17 14:01:29 +02:00
|
|
|
/**
|
|
|
|
|
* Assert on the numeric status code.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def assertStatusCode(expected: Int): TestResponse =
|
|
|
|
|
assertStatusCode(StatusCodes.get(expected))
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assert on the status code.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def assertStatusCode(expected: StatusCode): TestResponse =
|
|
|
|
|
assertEqualsKind(expected, status, "status code")
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assert on the media type of the response.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def assertMediaType(expected: String): TestResponse =
|
|
|
|
|
assertEqualsKind(expected, mediaTypeString, "media type")
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assert on the media type of the response.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def assertMediaType(expected: MediaType): TestResponse =
|
|
|
|
|
assertEqualsKind(expected, mediaType, "media type")
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assert on the response entity to be a UTF8 representation of the given string.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def assertEntity(expected: String): TestResponse =
|
|
|
|
|
assertEqualsKind(expected, entityAsString, "entity")
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assert on the response entity to equal the given bytes.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def assertEntityBytes(expected: ByteString): TestResponse =
|
|
|
|
|
assertEqualsKind(expected, entityBytes, "entity")
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assert on the response entity to equal the given object after applying an [[Unmarshaller]].
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def assertEntityAs[T <: AnyRef](unmarshaller: Unmarshaller[T], expected: T): TestResponse =
|
|
|
|
|
assertEqualsKind(expected, entityAs(unmarshaller), "entity")
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assert that a given header instance exists in the response.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def assertHeaderExists(expected: HttpHeader): TestResponse = {
|
|
|
|
|
assertTrue(response.headers.exists(_ == expected), s"Header $expected was missing.")
|
|
|
|
|
this
|
|
|
|
|
}
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assert that a header of the given type exists.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
def assertHeaderKindExists(name: String): TestResponse = {
|
|
|
|
|
val lowercased = name.toRootLowerCase
|
|
|
|
|
assertTrue(response.headers.exists(_.is(lowercased)), s"Expected `$name` header was missing.")
|
|
|
|
|
this
|
|
|
|
|
}
|
2015-07-17 14:01:29 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assert that a header of the given name and value exists.
|
|
|
|
|
*/
|
2015-05-21 17:17:55 +02:00
|
|
|
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 =
|
2015-08-13 10:14:12 +02:00
|
|
|
if (response eq null) doFail("Request didn't complete with response")
|
2015-05-21 17:17:55 +02:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-13 10:14:12 +02:00
|
|
|
// allows to `fail` as an expression
|
|
|
|
|
private def doFail(message: String): Nothing = {
|
|
|
|
|
fail(message)
|
|
|
|
|
throw new IllegalStateException("Shouldn't be reached")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected def fail(message: String): Unit
|
2015-05-21 17:17:55 +02:00
|
|
|
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
|
|
|
|
|
}
|