Silhouette not passing a request with `X-Auth-Token` in unit test

#1

I am observing strange behavior. I get the following error but only if I use a certain value. Otherwise I don’t. Why is it?

Error:(1699, 39) could not find implicit value for parameter mat: akka.stream.Materializer
      val responseBody = contentAsJson(response)
Error:(1699, 39) not enough arguments for method contentAsJson: (implicit timeout: akka.util.Timeout, implicit mat: akka.stream.Materializer)play.api.libs.json.JsValue.
Unspecified value parameter mat.
      val responseBody = contentAsJson(response)

I want to test a signout method.

def signOutUser = silhouette.SecuredAction.async { implicit request =>
    println("in sign out")

    silhouette.env.authenticatorService.discard(request.authenticator, Ok(Json.toJson(JsonResultSuccess("signout successful"))))
  }

To unit-test it, I have created the following test case (it creates a JWT authenticator, creates a JWT string (init call), embeds it in the request and then I all my controller’s method with that request

"signOutUser" should {
    "should be called when signout request is received" in {
      val testEnv = new TestEnv(components.configuration)

      val fakeJWTAuthenticatorService = new FakeJWTAuthenticatorService()

      val request = FakeRequest("POST", "ws/users/user-signout")
      val jwtAuthenticator = await[JWTAuthenticator](fakeJWTAuthenticatorService.create(testEnv.loginInfo)(request))

      val jwtInfo = await[String](fakeJWTAuthenticatorService.init(jwtAuthenticator)(request))
      println("jwt init is ", jwtInfo) //prints the JWT content OK
      val securedRequest = fakeJWTAuthenticatorService.embed(jwtInfo,request)

      for(header <- securedRequest.headers.headers) {
        println(s"header ${header}")//just checcking that the header is security there
      }

      val response = testEnv.controller.signOutUser(securedRequest)

      val responseBody = contentAsJson(response)
      println(s"response is ${responseBody}")
      1 mustBe 1
    }
  }

The TestEnv class has two implicits

class TestEnv (configuration:Configuration){
  implicit val sys = ActorSystem("MyTest") //required by materializer
  implicit val mat = ActorMaterializer() //required by akka framework
..
}

Interestingly, if I dont use securedRequest but use just the request, then I don’t get the error but my test case fails because Silhouette cannot authenticate the request and returns HTML instead of JSON (<html><head><title>silhouette.not.authenticated</title></head><body>silhouette.not.authenticated</body></html>)

Question 1 - Why do I get the implicit error when I use securedRequest? Why I don’t get the error if I use request.

Question 2 - Why the implicits defined in TestEnv not getting used?

Question 3
I could work around the problem by explicitly passinng the required implicit parameters

val duration = FiniteDuration(1000,"millis")
      val responseBody = contentAsJson(response)(new akka.util.Timeout(duration),testEnv.mat)

But my test case still fails because silhouette didn’t authenticate it even though the message contains the security token X-Auth-Token.

How can I make my test case pass?

0 Likes

#2

I had to make the request in the following way

    "should be called when signout request is received" in {
      val testEnv = new TestEnv(components.configuration)
      /*
      to pass Silhoutte's secureAction, we need to add authentication information in the request.
      we must create a request that we can pass to our action. Play ships with a test helper called FakeRequest
      which does exactly what we want. But this helper cannot embed an Authenticator into the created fake request
      We can use withAuthencator method which silhouette provides by extending FakeRequest

      In this method, we pass LoginInfo instance for which an authenticator will be created and embedded into the request.
       */
      val request = FakeRequest("POST", "ws/users/user-signout").withAuthenticator(testEnv.loginInfo)(testEnv.fakeEnv)
      val response = testEnv.controller.signOutUser(request)
      val responseBody = contentAsString(response)
      println(s"response is ${responseBody}")

    }```
0 Likes