I've a simple route and some tests that success individually, but collectively fail with timeout. Any idea why?
val route = (requestHandler: ActorRef @@ Web) => {
get {
pathPrefix("apps") {
pathEndOrSingleSlash {
completeWith(implicitly[ToEntityMarshaller[List[String]]]) { callback =>
requestHandler ! GetAppsRequest(callback)
}
} ~ path("stats") {
completeWith(implicitly[ToEntityMarshaller[List[Stats]]]) { callback =>
requestHandler ! GetStatsRequest(callback)
}
}
} ~ path("apps" / Segment / "stats") { app =>
completeWith(implicitly[ToEntityMarshaller[Stats]]) { callback =>
requestHandler ! GetStatsForOneRequest(app, callback)
}
}
}
}
and tests:
val testProbe = TestProbe()
val testProbeActor = testProbe.ref
.taggedWith[Web]
val timeout = 1.minute
"Route" should "respond to get apps request" in {
implicit val routeTestTimout = RouteTestTimeout(timeout.dilated)
Get("/apps") ~> route(testProbeActor) ~> check {
testProbe.receiveOne(timeout) match {
case GetAppsRequest(callback) => {
callback(k8SProperties.apps)
}
}
entityAs[List[String]] should contain("test")
}
testProbe.expectNoMessage(timeout)
}
it should "respond to get stats request for all apps" in {
implicit val routeTestTimout = RouteTestTimeout(timeout.dilated)
val app = "test"
Get("/apps/stats") ~> route(testProbeActor) ~> check {
testProbe.receiveOne(timeout) match {
case GetStatsRequest(callback) => {
callback(List(Stats(app, ChronoUnit.SECONDS, Nil)))
}
case other => fail(s"Unexpected message $other.")
}
entityAs[List[Stats]].size shouldBe (1)
entityAs[List[Stats]].head.app shouldBe (app)
}
testProbe.expectNoMessage(timeout)
}
it should "respond to get stats request for one app" in {
implicit val routeTestTimout = RouteTestTimeout(timeout.dilated)
val app = "test"
Get(s"/apps/$app/stats") ~> route(testProbeActor) ~> check {
testProbe.receiveOne(timeout) match {
case GetStatsForOneRequest(app, callback) => {
callback(Stats(app, ChronoUnit.SECONDS, Nil))
}
case other => fail(s"Unexpected message $other.")
}
entityAs[Stats].app shouldBe (app)
}
testProbe.expectNoMessage(timeout)
}
Edit: Opened https://github.com/akka/akka-http/issues/1615
Working code, thanks to me.
"Routes" should "respond to get apps request" in {
testProbe.setAutoPilot((_: ActorRef, msg: Any) => {
msg match {
case GetAppsRequest(callback) => callback(List("test")); TestActor.KeepRunning
case _ => TestActor.NoAutoPilot
}
})
Get("/apps") ~> handler ~> check {
entityAs[List[String]] should contain("test")
}
}
Putting TestProbe
inside check
hangs forever probably because it creates a deadlock; test waits for callback to be called, and callback isn't called until the body is executed.
Using autopilot sets up an "expectation" that can be fulfilled later thus avoiding the deadlock.