Here is the few lines of code I would like to unit test for different http response codes. One of them is code 201 . Please advise
val cb = ClientBuilder()
val myClient = cb.build()
val req =
RequestBuilder()
.url(loginUrl)
.setHeader("Content-Type", "application/json")
.buildPost(copiedBuffer(body.getBytes(UTF_8)))
myClient(req).map(http.Response(_)).flatMap { response =>
response.statusCode match {
case 201 => //parse response
case _ =>
println(" some reseponse")
Future(proxy(response))
}
}
Related
I am writing some unit tests for my Rust http server handlers. But when I am running one of the tests it get stuck at the end of the inner function. Here is relevant part of the code:
async fn generate(request: Request<Body>) -> Result<Response<Body>, hyper::Error> {
let result = process_request(request).await;
println!("This message doesn't get printed!!");
let (spec, override) = match result {
Ok((s, o)) => (s, o),
Err(process_error) => {
return Ok(Response::new(Body::from(format!("{}", process_error))));
},
};
...
Ok(Response::new(Body::from(format!("{}", response))))
}
async fn process_request(request: Request<Body>) -> Result<(Spec, Option<Config>), Error> {
let body = body::to_bytes(request.into_body()).await;
let payload: Payload = serde_json::from_slice(&body.unwrap().to_vec()).unwrap();
let spec_str = payload.spec.to_owned();
...
println!("Function runs to this point and prints this message");
Ok((spec, override))
}
#[tokio::test]
async fn test_gen() {
let payload = Payload {
spec: a_spec(),
};
let payload_json = serde_json::to_string_pretty(&payload).unwrap();
let request = Request::builder().body(Body::from(payload_json));
let result = generate(request.unwrap()).await.unwrap();
// Some asserts ...
}
I am wondering what I am doing wrong?
Looks like the inner function starts another thread, so the solution was to decorate the test with:
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
This resolved the issue for my unit tests.
I have the sealed trait below and its case class and I would like to transform it into JSON to give it as a response in my Akka Http app.
sealed trait HttpRestError {
val statusCode: StatusCode
val code: String
val message: String
}
case class UnauthorizedError() extends HttpRestError {
override val statusCode: StatusCode = Unauthorized
override val code: String = "Unauthorized"
override val message: String = "Unauthorized"
}
And the Route part
def login2: Route = {
path("test") {
pathEndOrSingleSlash {
get {
onComplete(authService.testUser.toRestError[UnauthorizedError]) {
case util.Success(f) => f match {
case Left(error) =>
complete((error.statusCode, error))
case Right(v) => complete(OK -> v)
}
case util.Failure(ex) =>
complete(StatusCodes.InternalServerError)
}
}
}
}
}
The problem is that when the Either returns the Left side the response is empty, but the error code is correct.
Any idea?
I have a route in akka-http app which is integrated with a 3rd party service via Http().cachedHostConnectionPoolHttps. I want to test it in a right way. But not sure how it should be :(
Here is how this route looks like:
val routes: Route = pathPrefix("access-tokens") {
pathPrefix(Segment) { userId =>
parameters('refreshToken) { refreshToken =>
onSuccess(accessTokenActor ? GetAccessToken(userId, refreshToken)) {
case token: AccessToken => complete(ok(token.toJson))
case AccessTokenError => complete(internalServerError("There was problems while retriving the access token"))
}
}
}
}
Behind this route hides accessTokenActor where all logic happens, here it is:
class AccessTokenActor extends Actor with ActorLogging with APIConfig {
implicit val actorSystem = context.system
import context.dispatcher
implicit val materializer = ActorMaterializer()
import AccessTokenActor._
val connectionFlow = Http().cachedHostConnectionPoolHttps[String]("www.service.token.provider.com")
override def receive: Receive = {
case get: GetAccessToken => {
val senderActor = sender()
Source.fromFuture(Future.successful(
HttpRequest(
HttpMethods.GET,
"/oauth2/token",
Nil,
FormData(Map(
"clientId" -> youtubeClientId,"clientSecret" -> youtubeSecret,"refreshToken" -> get.refreshToken))
.toEntity(HttpCharsets.`UTF-8`)) -> get.channelId
)
)
.via(connectionFlow)
.map {
case (Success(resp), id) => resp.status match {
case StatusCodes.OK => Unmarshal(resp.entity).to[AccessTokenModel]
.map(senderActor ! AccessToken(_.access_token))
case _ => senderActor ! AccessTokenError
}
case _ => senderActor ! AccessTokenError
}
}.runWith(Sink.head)
case _ => log.info("Unknown message")
}
}
So the question is how it's better to test this route, keeping in mind that the actor with the stream also exist under its hood.
Composition
One difficulty with testing your route logic, as currently organized, is that it is hard to isolate functionality. It is impossible to test your Route logic without an Actor, and it is hard to test your Actor querying without a Route.
I think you would be better served with function composition, that way you can isolate what it is you're trying to test.
First abstract away the Actor querying (ask):
sealed trait TokenResponse
case class AccessToken() extends TokenResponse {...}
case object AccessTokenError extends TokenResponse
val queryActorForToken : (ActorRef) => (GetAccessToken) => Future[TokenResponse] =
(ref) => (getAccessToken) => (ref ? getAccessToken).mapTo[TokenResponse]
Now convert your routes value into a higher-order method which takes in the query function as a parameter:
val actorRef : ActorRef = ??? //not shown in question
type TokenQuery = GetAccessToken => Future[TokenResponse]
val actorTokenQuery : TokenQuery = queryActorForToken(actorRef)
val errorMsg = "There was problems while retriving the access token"
def createRoute(getToken : TokenQuery = actorTokenQuery) : Route =
pathPrefix("access-tokens") {
pathPrefix(Segment) { userId =>
parameters('refreshToken) { refreshToken =>
onSuccess(getToken(GetAccessToken(userId, refreshToken))) {
case token: AccessToken => complete(ok(token.toJson))
case AccessTokenError => complete(internalServerError(errorMsg))
}
}
}
}
//original routes
val routes = createRoute()
Testing
Now you can test queryActorForToken without needing a Route and you can test the createRoute method without needing an actor!
You can test createRoute with an injected function that always returns a pre-defined token:
val testToken : AccessToken = ???
val alwaysSuccceedsRoute = createRoute(_ => Success(testToken))
Get("/access-tokens/fooUser?refreshToken=bar" ~> alwaysSucceedsRoute ~> check {
status shouldEqual StatusCodes.Ok
responseAs[String] shouldEqual testToken.toJson
}
Or, you can test createRoute with an injected function that never returns a token:
val alwaysFailsRoute = createRoute(_ => Success(AccessTokenError))
Get("/access-tokens/fooUser?refreshToken=bar" ~> alwaysFailsRoute ~> check {
status shouldEqual StatusCodes.InternalServerError
responseAs[String] shouldEqual errorMsg
}
I want to configure spray http client in a way that controls max number of request that were sent to the server. I need this because server that i'm sending requests to blocks me if there are more then 2 request were sent. I get
akka.pattern.AskTimeoutException: Ask timed out on [Actor[akka://smallTasks/user/IO-HTTP#151444590]] after [15000 ms]
akka.pattern.AskTimeoutException: Ask timed out on [Actor[akka://smallTasks/user/IO-HTTP#151444590]] after [15000 ms]
akka.pattern.AskTimeoutException: Ask timed out on [Actor[akka://smallTasks/user/IO-HTTP#151444590]] after [15000 ms]
akka.pattern.AskTimeoutException: Ask timed out on
I need to send thousands of requests but i get blocked after i got responses from ~ 100 requests.
I have this method:
implicit val system = ActorSystem("smallTasks")
implicit val timeout = new Timeout(15.seconds)
import system.dispatcher
def doHttpRequest(url: String): Future[HttpResponse] = {
(IO(Http) ? HttpRequest(GET, Uri(url))).mapTo[HttpResponse]
}
And here i catch responses and retry if it fails(recursively):
def getOnlineOffers(modelId: Int, count: Int = 0): Future[Any] = {
val result = Promise[Any]()
AkkaSys.doHttpRequest(Market.modelOffersUrl(modelId)).map(response => {
val responseCode = response.status.intValue
if (List(400, 404).contains(responseCode)) {
result.success("Bad request")
} else if (responseCode == 200) {
Try {
Json.parse(response.entity.asString).asOpt[JsObject]
} match {
case Success(Some(obj)) =>
Try {
(obj \\ "onlineOffers").head.as[Int]
} match {
case Success(offers) => result.success(offers)
case _ => result.success("Can't find property")
}
case _ => result.success("Wrong body")
}
} else {
result.success("Unexpected error")
}
}).recover { case err =>
if (count > 5) {
result.success("Too many tries")
} else {
println(err.toString)
Thread.sleep(200)
getOnlineOffers(modelId, count + 1).map(r => result.success(r))
}
}
result.future
}
How to do this properly? May be i need to configure akka dispatcher somehow?
you can use http://spray.io/documentation/1.2.2/spray-client/ and write you personal pipeline
val pipeline: Future[SendReceive] =
for (
Http.HostConnectorInfo(connector, _) <-
IO(Http) ? Http.HostConnectorSetup("www.spray.io", port = 80)
) yield sendReceive(connector)
val request = Get("/segment1/segment2/...")
val responseFuture: Future[HttpResponse] = pipeline.flatMap(_(request))
to get HttpResponse
import scala.concurrent.Await
import scala.concurrent.duration._
val response: HttpResponse = Aweit(responseFuture, ...)
to convert
import spray.json._
response.entity.asString.parseJson.convertTo[T]
to check
Try(response.entity.asString.parseJson).isSuccess
too many brackets. In scala you can write it shorter
I want to test a controller action using Action composition.
Here's an example of the composed action and its test code.
The Secured trait:
trait Secured {
def username(request: RequestHeader) = request.session.get(Security.username)
def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Auth.login)
def withAuth(f: => String => Request[AnyContent] => Result) = {
Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
}
The controller:
MyController extends Contrller with Secured {
def simple = Action { Ok("ok") }
def simpleWithauth = withAuth { implicit username => implicit request=> Ok("ok") }
}
The test code:
// This work fine
val result1 = controller.simple()(FakeRequest())
// This wont compile
val result2 = controller.simpleWithAuth()(FakeRequest())
The latter would require a Request[Action[AnyContent], AnyContent]
but FakeRequest returns a Request[AnyContent]
Any pointers on how to create a fake request of the appropriate type?
This is what I had to do to test secured action
def wrappedActionResult[A](wrapped: Action[(Action[A], A)], request: Request[A]): Result = wrapped.parser(request).run.await.get match {
case Left(errorResult) => errorResult
case Right((innerAction, _)) => innerAction(request)
}
and the test
running(app) {
val result = wrappedActionResult(FakeController().securedAction, invalidRequest)
status(result) must_== UNAUTHORIZED
contentAsString(result) must_== "must be authenticated"
}
That doesn't work with Play 2.1 though, types have been changed...
UPDATE:
in Play 2.1 it's even easier
I've added some sugar
implicit class ActionExecutor(action: EssentialAction) {
def process[A](request: Request[A]): Result = concurrent.Await.result(action(request).run, Duration(1, "sec"))
}
and the test now looks like this
running(app) {
val result = FakeController().securedAction process invalidRequest
status(result) must_== UNAUTHORIZED
contentAsString(result) must_== "must be authenticated"
}
UPDATE: here is a related blog post i wrote sometime ago
http://www.daodecode.com/blog/2013/03/08/testing-security-dot-authenticated-in-play-2-dot-0-and-2-dot-1/
Have you tried using the routeAndCall approach?
val Some(result) = routeAndCall(FakeRequest(POST, "/someRoute"))
You can add whatever parameters you need in your request with the asFormUrlEncodedBody method and/or adding things to the session with the withSession method.