diff --git a/.gitignore b/.gitignore
index ec4ccaaf..3fc8d32c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,4 +14,12 @@ dist
/.target
/.cache
*.DS_Store
-.bsp/
+
+# Metals
+.metals/*
+.bloop/*
+.vscode/*
+**/.bloop/*
+**/metals.sbt
+
+.bsp/*
diff --git a/build.sbt b/build.sbt
index 39c205ac..7d191a36 100644
--- a/build.sbt
+++ b/build.sbt
@@ -3,8 +3,10 @@ import sbt.CrossVersion
lazy val repo: String = "https://s01.oss.sonatype.org"
lazy val scala213: String = "2.13.12"
-lazy val scala3: String = "3.3.1" // Ready for cross build, currently not yet supported by play.
-lazy val supportedScalaVersions: Seq[String] = Seq(scala213 /*, scala3*/)
+lazy val scala3: String = "3.3.1"
+lazy val supportedScalaVersions: Seq[String] = Seq(scala213, scala3)
+
+Global / evictionErrorLevel := Level.Info
ThisBuild / description := "Authentication library for Play Framework applications that supports several authentication methods, including OAuth1, OAuth2, OpenID, CAS, Credentials, Basic Authentication, Two Factor Authentication or custom authentication schemes"
ThisBuild / homepage := Some(url("https://silhouette.readme.io/"))
@@ -17,17 +19,21 @@ ThisBuild / organizationName := "honeycomb-cheesecake"
ThisBuild / scalaVersion := scala213
ThisBuild / versionScheme := Some("early-semver")
ThisBuild / scalacOptions ++= Seq(
- "-unchecked",
- "-deprecation",
"-feature",
- "-encoding", "utf8",
- "-Xfatal-warnings",
- "-Xlint",
- "-Xlint:adapted-args",
- "-Xlint:inaccessible",
- "-Xlint:infer-any",
- "-Xlint:nullary-unit"
-)
+ "-Xfatal-warnings"
+) ++
+ (CrossVersion.partialVersion(scalaVersion.value) match {
+ case Some((2, _)) => Seq(
+ "-encoding", "utf8",
+ "-unchecked",
+ "-deprecation",
+ "-Xlint:adapted-args",
+ "-Xlint:inaccessible",
+ "-Xlint:infer-any",
+ "-Xlint:nullary-unit"
+ )
+ case _ => Seq()
+ })
ThisBuild / Test / scalacOptions ~= { options: Seq[String] =>
// Allow dead code in tests (to support using mockito).
options filterNot (_ == "-Ywarn-dead-code")
@@ -98,8 +104,6 @@ lazy val root = (project in file("."))
lazy val silhouette = (project in file("silhouette"))
.settings(
name := "play-silhouette",
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"),
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-mock"),
libraryDependencies ++=
Library.updates ++ Seq(
Library.Play.cache,
@@ -109,27 +113,26 @@ lazy val silhouette = (project in file("silhouette"))
Library.apacheCommonLang,
Library.Play.specs2 % Test,
Library.Specs2.matcherExtra % Test,
- Library.Specs2.mock % Test,
+ Library.mockito % Test,
Library.scalaGuice % Test,
Library.akkaTestkit % Test
),
resolvers ++= Dependencies.resolvers
)
.enablePlugins(PlayScala)
+ .disablePlugins(PlayAkkaHttpServer)
lazy val silhouetteCas = (project in file("silhouette-cas"))
.settings(
name := "play-silhouette-cas",
dependencyUpdatesFailBuild := false,
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"),
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-mock"),
libraryDependencies ++=
Library.updates ++ Seq(
Library.casClient,
Library.casClientSupportSAML,
Library.Play.specs2 % Test,
Library.Specs2.matcherExtra % Test,
- Library.Specs2.mock % Test,
+ Library.mockito % Test,
Library.scalaGuice % Test
)
)
@@ -139,7 +142,6 @@ lazy val silhouetteTotp = (project in file("silhouette-totp"))
.settings(
name := "play-silhouette-totp",
dependencyUpdatesFailBuild := false,
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"),
libraryDependencies ++=
Library.updates ++ Seq(
Library.googleAuth,
@@ -152,8 +154,6 @@ lazy val silhouetteCryptoJca = (project in file("silhouette-crypto-jca"))
.settings(
name := "play-silhouette-crypto-jca",
dependencyUpdatesFailBuild := false,
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"),
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"),
libraryDependencies ++=
Library.updates ++ Seq(
Library.commonsCodec,
@@ -167,7 +167,6 @@ lazy val silhouetteArgon2 = (project in file("silhouette-password-argon2"))
.settings(
name := "play-silhouette-password-argon2",
dependencyUpdatesFailBuild := false,
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"),
libraryDependencies ++=
Library.updates ++ Seq(
Library.argon2,
@@ -180,7 +179,6 @@ lazy val silhouetteBcrypt = (project in file("silhouette-password-bcrypt"))
.settings(
name := "play-silhouette-password-bcrypt",
dependencyUpdatesFailBuild := false,
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"),
libraryDependencies ++=
Library.updates ++ Seq(
Library.jbcrypt,
@@ -193,14 +191,11 @@ lazy val silhouettePersistence = (project in file("silhouette-persistence"))
.settings(
name := "play-silhouette-persistence",
dependencyUpdatesFailBuild := false,
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"),
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"),
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-mock"),
libraryDependencies ++=
Library.updates ++ Seq(
Library.Specs2.core % Test,
Library.Specs2.matcherExtra % Test,
- Library.Specs2.mock % Test,
+ Library.mockito % Test,
Library.scalaGuice % Test
)
)
@@ -210,17 +205,21 @@ lazy val silhouetteTestkit = (project in file("silhouette-testkit"))
.settings(
name := "play-silhouette-testkit",
dependencyUpdatesFailBuild := false,
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"),
- dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-mock"),
libraryDependencies ++=
Library.updates ++ Seq(
Library.Play.test,
Library.Play.specs2 % Test,
Library.Specs2.matcherExtra % Test,
- Library.Specs2.mock % Test,
+ Library.mockito % Test,
Library.scalaGuice % Test,
Library.akkaTestkit % Test
)
+ ++ {
+ CrossVersion.partialVersion(scalaVersion.value) match {
+ case Some((3, _)) => Seq(Library.izumiReflect)
+ case _ => Seq.empty
+ }
+ }
)
.enablePlugins(PlayScala)
.dependsOn(silhouette)
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 4c8b4267..cb727e68 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -35,10 +35,9 @@ object Dependencies {
}
object Specs2 {
- private val version = "4.20.2" // Versions later than this will fail due to removed dependencies.
+ private val version = "4.20.2"
val core = "org.specs2" %% "specs2-core" % version
val matcherExtra = "org.specs2" %% "specs2-matcher-extra" % version
- val mock = "org.specs2" %% "specs2-mock" % version
}
val argon2 = "de.mkammerer" % "argon2-jvm" % "2.11"
@@ -47,9 +46,11 @@ object Dependencies {
val jwt = "com.auth0" % "java-jwt" % "3.18.2"
val scalaGuice = "net.codingwell" %% "scala-guice" % "6.0.0"
val akkaTestkit = "com.typesafe.akka" %% "akka-testkit" % play.core.PlayVersion.akkaVersion
+ val mockito = "org.mockito" % "mockito-core" % "5.3.0"
val casClient = "org.jasig.cas.client" % "cas-client-core" % "3.6.4"
val casClientSupportSAML = "org.jasig.cas.client" % "cas-client-support-saml" % "3.6.4"
val apacheCommonLang = "org.apache.commons" % "commons-lang3" % "3.13.0"
val googleAuth = "com.warrenstrange" % "googleauth" % "1.5.0"
+ val izumiReflect = "dev.zio" %% "izumi-reflect" % "2.3.8" // Scala 3 replacement for scala 2 reflect universe
}
}
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 28b04e59..f7e3e146 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -4,7 +4,7 @@ addSbtPlugin(dependency = "com.github.sbt" % "sbt-unidoc" % "0.5.0")
addSbtPlugin(dependency = "com.jsuereth" % "sbt-pgp" % "2.1.1")
addSbtPlugin(dependency = "com.timushev.sbt" % "sbt-updates" % "0.6.4")
addSbtPlugin(dependency = "com.typesafe.play" % "sbt-plugin" % "2.9.0")
-addSbtPlugin(dependency = "net.vonbuchholtz" % "sbt-dependency-check" % "4.0.0")
+addSbtPlugin(dependency = "net.vonbuchholtz" % "sbt-dependency-check" % "5.1.0")
addSbtPlugin(dependency = "org.scoverage" % "sbt-scoverage" % "2.0.9")
addSbtPlugin(dependency = "org.scoverage" % "sbt-coveralls" % "1.3.11")
addSbtPlugin(dependency = "org.xerial.sbt" % "sbt-sonatype" % "3.10.0")
diff --git a/silhouette-cas/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/CasProviderSpec.scala b/silhouette-cas/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/CasProviderSpec.scala
index 3d0a93b9..924ee00a 100644
--- a/silhouette-cas/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/CasProviderSpec.scala
+++ b/silhouette-cas/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/CasProviderSpec.scala
@@ -20,7 +20,7 @@ import io.github.honeycombcheesecake.play.silhouette.api.util.HTTPLayer
import io.github.honeycombcheesecake.play.silhouette.api.{ Logger, LoginInfo }
import org.jasig.cas.client.authentication.AttributePrincipal
-import org.specs2.mock.Mockito
+import org.mockito.Mockito._
import org.specs2.specification.Scope
import play.api.mvc.AnyContentAsEmpty
import play.api.test.FakeRequest
@@ -33,7 +33,7 @@ import scala.concurrent.duration._
/**
* Test case for the [[CasProvider]] class.
*/
-class CasProviderSpec extends SocialProviderSpec[CasInfo] with Mockito with Logger {
+class CasProviderSpec extends SocialProviderSpec[CasInfo] with Logger {
"The settings" should {
"fail with a ConfigurationException if casURL is invalid" in new Context {
@@ -95,9 +95,9 @@ class CasProviderSpec extends SocialProviderSpec[CasInfo] with Mockito with Logg
"The `retrieveProfile` method" should {
"return a valid profile if the CAS client validates the ticket" in new Context {
- principal.getName returns userName
- principal.getAttributes returns attr
- client.validateServiceTicket(ticket) returns Future.successful(principal)
+ when(principal.getName).thenReturn(userName)
+ when(principal.getAttributes).thenReturn(attr)
+ when(client.validateServiceTicket(ticket)).thenReturn(Future.successful(principal))
implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/?ticket=%s".format(ticket))
@@ -129,8 +129,8 @@ class CasProviderSpec extends SocialProviderSpec[CasInfo] with Mockito with Logg
redirectURL = "https://cas-redirect/")
lazy val httpLayer = {
- val m = mock[HTTPLayer]
- m.executionContext returns global
+ val m = mock(classOf[HTTPLayer])
+ when(m.executionContext).thenReturn(global)
m
}
@@ -144,7 +144,7 @@ class CasProviderSpec extends SocialProviderSpec[CasInfo] with Mockito with Logg
lazy val casAuthInfo = CasInfo(ticket)
- lazy val principal = mock[AttributePrincipal].smart
+ lazy val principal = mock(classOf[AttributePrincipal], withSettings().defaultAnswer(RETURNS_SMART_NULLS))
lazy val name = "abc123"
lazy val email = "email"
diff --git a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala
index 9e2ed65c..a2a7025c 100644
--- a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala
+++ b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala
@@ -18,7 +18,6 @@ package io.github.honeycombcheesecake.play.silhouette.persistence.daos
import io.github.honeycombcheesecake.play.silhouette.api.{ AuthInfo, LoginInfo }
import io.github.honeycombcheesecake.play.silhouette.test.WaitPatience
import org.specs2.concurrent.ExecutionEnv
-import org.specs2.control.NoLanguageFeatures
import org.specs2.mutable.Specification
import org.specs2.specification.Scope
@@ -29,7 +28,7 @@ import scala.language.postfixOps
/**
* Test case for the [[InMemoryAuthInfoDAO]] trait.
*/
-class InMemoryAuthInfoDAOSpec(implicit ev: ExecutionEnv) extends Specification with NoLanguageFeatures with WaitPatience {
+class InMemoryAuthInfoDAOSpec(implicit ev: ExecutionEnv) extends Specification with WaitPatience {
"The `find` method" should {
"find an OAuth1 info for the given login info" in new Context {
diff --git a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala
index df805c82..781e0f24 100644
--- a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala
+++ b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala
@@ -19,9 +19,9 @@ import io.github.honeycombcheesecake.play.silhouette.api.StorableAuthenticator
import io.github.honeycombcheesecake.play.silhouette.api.util.CacheLayer
import io.github.honeycombcheesecake.play.silhouette.test.WaitPatience
import org.specs2.concurrent.ExecutionEnv
-import org.specs2.mock.Mockito
import org.specs2.mutable.Specification
import org.specs2.specification.Scope
+import org.mockito.Mockito._
import scala.concurrent.Future
import scala.concurrent.duration.Duration
@@ -29,50 +29,50 @@ import scala.concurrent.duration.Duration
/**
* Test case for the [[CacheAuthenticatorRepository]] class.
*/
-class CacheAuthenticatorRepositorySpec(implicit ev: ExecutionEnv) extends Specification with Mockito with WaitPatience {
+class CacheAuthenticatorRepositorySpec(implicit ev: ExecutionEnv) extends Specification with WaitPatience {
"The `find` method" should {
"return value from cache" in new Context {
- cacheLayer.find[StorableAuthenticator]("test-id") returns Future.successful(Some(authenticator))
+ when(cacheLayer.find[StorableAuthenticator]("test-id")).thenReturn(Future.successful(Some(authenticator)))
repository.find("test-id") must beSome(authenticator).awaitWithPatience
- there was one(cacheLayer).find[StorableAuthenticator]("test-id")
+ verify(cacheLayer).find[StorableAuthenticator]("test-id")
}
"return None if value couldn't be found in cache" in new Context {
- cacheLayer.find[StorableAuthenticator]("test-id") returns Future.successful(None)
+ when(cacheLayer.find[StorableAuthenticator]("test-id")).thenReturn(Future.successful(None))
repository.find("test-id") must beNone.awaitWithPatience
- there was one(cacheLayer).find[StorableAuthenticator]("test-id")
+ verify(cacheLayer).find[StorableAuthenticator]("test-id")
}
}
"The `add` method" should {
"add value in cache" in new Context {
- authenticator.id returns "test-id"
- cacheLayer.save("test-id", authenticator, Duration.Inf) returns Future.successful(authenticator)
+ when(authenticator.id).thenReturn("test-id")
+ when(cacheLayer.save("test-id", authenticator, Duration.Inf)).thenReturn(Future.successful(authenticator))
repository.add(authenticator) must beEqualTo(authenticator).awaitWithPatience
- there was one(cacheLayer).save("test-id", authenticator, Duration.Inf)
+ verify(cacheLayer).save("test-id", authenticator, Duration.Inf)
}
}
"The `update` method" should {
"update value in cache" in new Context {
- authenticator.id returns "test-id"
- cacheLayer.save("test-id", authenticator, Duration.Inf) returns Future.successful(authenticator)
+ when(authenticator.id).thenReturn("test-id")
+ when(cacheLayer.save("test-id", authenticator, Duration.Inf)).thenReturn(Future.successful(authenticator))
repository.update(authenticator) must beEqualTo(authenticator).awaitWithPatience
- there was one(cacheLayer).save("test-id", authenticator, Duration.Inf)
+ verify(cacheLayer).save("test-id", authenticator, Duration.Inf)
}
}
"The `remove` method" should {
"remove value from cache" in new Context {
- cacheLayer.remove("test-id") returns Future.successful(())
+ when(cacheLayer.remove("test-id")).thenReturn(Future.successful(()))
repository.remove("test-id") must beEqualTo(()).awaitWithPatience
- there was one(cacheLayer).remove("test-id")
+ verify(cacheLayer).remove("test-id")
}
}
@@ -84,12 +84,12 @@ class CacheAuthenticatorRepositorySpec(implicit ev: ExecutionEnv) extends Specif
/**
* A storable authenticator.
*/
- lazy val authenticator = mock[StorableAuthenticator]
+ lazy val authenticator = mock(classOf[StorableAuthenticator])
/**
* The cache layer implementation.
*/
- lazy val cacheLayer = mock[CacheLayer]
+ lazy val cacheLayer = mock(classOf[CacheLayer])
/**
* The repository to test.
diff --git a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala
index eeefc3df..83fe6f96 100644
--- a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala
+++ b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala
@@ -26,10 +26,9 @@ import io.github.honeycombcheesecake.play.silhouette.persistence.repositories.De
import io.github.honeycombcheesecake.play.silhouette.test.WaitPatience
import net.codingwell.scalaguice.ScalaModule
import org.specs2.concurrent.ExecutionEnv
-import org.specs2.control.NoLanguageFeatures
-import org.specs2.mock.Mockito
import org.specs2.mutable.Specification
import org.specs2.specification.Scope
+import org.mockito.Mockito._
import scala.concurrent.Await
import scala.concurrent.duration._
@@ -39,7 +38,7 @@ import scala.language.postfixOps
* Test case for the [[DelegableAuthInfoRepository]] trait.
*/
class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv)
- extends Specification with Mockito with NoLanguageFeatures with WaitPatience {
+ extends Specification with WaitPatience {
"The `find` method" should {
"delegate the PasswordInfo to the correct DAO" in new Context {
@@ -48,7 +47,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv)
Await.result(passwordInfoDAO.add(loginInfo, passwordInfo), 10 seconds)
service.find[PasswordInfo](loginInfo) must beSome(passwordInfo).awaitWithPatience
- there was one(passwordInfoDAO).find(loginInfo)
+ verify(passwordInfoDAO).find(loginInfo)
}
"delegate the OAuth1Info to the correct DAO" in new Context {
@@ -57,7 +56,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv)
Await.result(oauth1InfoDAO.add(loginInfo, oauth1Info), 10 seconds)
service.find[OAuth1Info](loginInfo) must beSome(oauth1Info).awaitWithPatience
- there was one(oauth1InfoDAO).find(loginInfo)
+ verify(oauth1InfoDAO).find(loginInfo)
}
"delegate the OAuth2Info to the correct DAO" in new Context {
@@ -66,7 +65,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv)
Await.result(oauth2InfoDAO.add(loginInfo, oauth2Info), 10 seconds)
service.find[OAuth2Info](loginInfo) must beSome(oauth2Info).awaitWithPatience
- there was one(oauth2InfoDAO).find(loginInfo)
+ verify(oauth2InfoDAO).find(loginInfo)
}
"throw a ConfigurationException if an unsupported type was given" in new Context {
@@ -83,21 +82,21 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv)
val loginInfo = LoginInfo("credentials", "1")
service.add(loginInfo, passwordInfo) must beEqualTo(passwordInfo).awaitWithPatience
- there was one(passwordInfoDAO).add(loginInfo, passwordInfo)
+ verify(passwordInfoDAO).add(loginInfo, passwordInfo)
}
"delegate the OAuth1Info to the correct DAO" in new Context {
val loginInfo = LoginInfo("credentials", "1")
service.add(loginInfo, oauth1Info) must beEqualTo(oauth1Info).awaitWithPatience
- there was one(oauth1InfoDAO).add(loginInfo, oauth1Info)
+ verify(oauth1InfoDAO).add(loginInfo, oauth1Info)
}
"delegate the OAuth2Info to the correct DAO" in new Context {
val loginInfo = LoginInfo("credentials", "1")
service.add(loginInfo, oauth2Info) must beEqualTo(oauth2Info).awaitWithPatience
- there was one(oauth2InfoDAO).add(loginInfo, oauth2Info)
+ verify(oauth2InfoDAO).add(loginInfo, oauth2Info)
}
"throw a ConfigurationException if an unsupported type was given" in new Context {
@@ -114,21 +113,21 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv)
val loginInfo = LoginInfo("credentials", "1")
service.update(loginInfo, passwordInfo) must beEqualTo(passwordInfo).awaitWithPatience
- there was one(passwordInfoDAO).update(loginInfo, passwordInfo)
+ verify(passwordInfoDAO).update(loginInfo, passwordInfo)
}
"delegate the OAuth1Info to the correct DAO" in new Context {
val loginInfo = LoginInfo("credentials", "1")
service.update(loginInfo, oauth1Info) must beEqualTo(oauth1Info).awaitWithPatience
- there was one(oauth1InfoDAO).update(loginInfo, oauth1Info)
+ verify(oauth1InfoDAO).update(loginInfo, oauth1Info)
}
"delegate the OAuth2Info to the correct DAO" in new Context {
val loginInfo = LoginInfo("credentials", "1")
service.update(loginInfo, oauth2Info) must beEqualTo(oauth2Info).awaitWithPatience
- there was one(oauth2InfoDAO).update(loginInfo, oauth2Info)
+ verify(oauth2InfoDAO).update(loginInfo, oauth2Info)
}
"throw a ConfigurationException if an unsupported type was given" in new Context {
@@ -145,21 +144,21 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv)
val loginInfo = LoginInfo("credentials", "1")
service.save(loginInfo, passwordInfo) must beEqualTo(passwordInfo).awaitWithPatience
- there was one(passwordInfoDAO).save(loginInfo, passwordInfo)
+ verify(passwordInfoDAO).save(loginInfo, passwordInfo)
}
"delegate the OAuth1Info to the correct DAO" in new Context {
val loginInfo = LoginInfo("credentials", "1")
service.save(loginInfo, oauth1Info) must beEqualTo(oauth1Info).awaitWithPatience
- there was one(oauth1InfoDAO).save(loginInfo, oauth1Info)
+ verify(oauth1InfoDAO).save(loginInfo, oauth1Info)
}
"delegate the OAuth2Info to the correct DAO" in new Context {
val loginInfo = LoginInfo("credentials", "1")
service.save(loginInfo, oauth2Info) must beEqualTo(oauth2Info).awaitWithPatience
- there was one(oauth2InfoDAO).save(loginInfo, oauth2Info)
+ verify(oauth2InfoDAO).save(loginInfo, oauth2Info)
}
"throw a ConfigurationException if an unsupported type was given" in new Context {
@@ -178,7 +177,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv)
Await.result(passwordInfoDAO.add(loginInfo, passwordInfo), 10 seconds)
service.remove[PasswordInfo](loginInfo) must beEqualTo(()).awaitWithPatience
- there was one(passwordInfoDAO).remove(loginInfo)
+ verify(passwordInfoDAO).remove(loginInfo)
}
"delegate the OAuth1Info to the correct DAO" in new Context {
@@ -187,7 +186,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv)
Await.result(oauth1InfoDAO.add(loginInfo, oauth1Info), 10 seconds)
service.remove[OAuth1Info](loginInfo) must beEqualTo(()).awaitWithPatience
- there was one(oauth1InfoDAO).remove(loginInfo)
+ verify(oauth1InfoDAO).remove(loginInfo)
}
"delegate the OAuth2Info to the correct DAO" in new Context {
@@ -196,7 +195,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv)
Await.result(oauth2InfoDAO.add(loginInfo, oauth2Info), 10 seconds)
service.remove[OAuth2Info](loginInfo) must beEqualTo(()).awaitWithPatience
- there was one(oauth2InfoDAO).remove(loginInfo)
+ verify(oauth2InfoDAO).remove(loginInfo)
}
"throw a ConfigurationException if an unsupported type was given" in new Context {
diff --git a/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala b/silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala
similarity index 100%
rename from silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala
rename to silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala
diff --git a/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/package.scala b/silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/package.scala
similarity index 100%
rename from silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/package.scala
rename to silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/package.scala
diff --git a/silhouette-testkit/app-3/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala b/silhouette-testkit/app-3/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala
new file mode 100644
index 00000000..09000612
--- /dev/null
+++ b/silhouette-testkit/app-3/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala
@@ -0,0 +1,245 @@
+/**
+ * Copyright 2015 Mohiva Organisation (license at mohiva dot com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.github.honeycombcheesecake.play.silhouette.test
+
+import java.util.UUID
+import io.github.honeycombcheesecake.play.silhouette.api._
+import io.github.honeycombcheesecake.play.silhouette.api.crypto.{ Base64AuthenticatorEncoder, Signer }
+import io.github.honeycombcheesecake.play.silhouette.api.repositories.AuthenticatorRepository
+import io.github.honeycombcheesecake.play.silhouette.api.services.{ AuthenticatorService, IdentityService }
+import io.github.honeycombcheesecake.play.silhouette.api.util.Clock
+import io.github.honeycombcheesecake.play.silhouette.impl.authenticators._
+import io.github.honeycombcheesecake.play.silhouette.impl.util.{ DefaultFingerprintGenerator, SecureRandomIDGenerator }
+import izumi.reflect.Tag
+import play.api.mvc.{ DefaultCookieHeaderEncoding, DefaultSessionCookieBaker, RequestHeader }
+
+import scala.collection.mutable
+import scala.concurrent.ExecutionContext.Implicits.global
+import scala.concurrent.{ ExecutionContext, Future }
+import scala.util
+import scala.util.Success
+
+/**
+ * A fake identity.
+ *
+ * @param loginInfo The linked login info for an identity.
+ */
+final case class FakeIdentity(loginInfo: LoginInfo) extends Identity
+
+/**
+ * A fake identity service implementation which can handle a predefined list of identities.
+ *
+ * @param identities A list of (login info -> identity) pairs this service is responsible for.
+ * @tparam I The type of the identity to handle.
+ */
+class FakeIdentityService[I <: Identity](identities: (LoginInfo, I)*) extends IdentityService[I] {
+
+ /**
+ * Retrieves an identity that matches the specified login info.
+ *
+ * @param loginInfo The login info to retrieve an identity.
+ * @return The retrieved identity or None if no identity could be retrieved for the given login info.
+ */
+ def retrieve(loginInfo: LoginInfo): Future[Option[I]] = {
+ Future.successful(identities.find(_._1 == loginInfo).map(_._2))
+ }
+}
+
+/**
+ * A fake authenticator repository which persists authenticators in memory.
+ *
+ * @tparam T The type of the authenticator to handle.
+ */
+class FakeAuthenticatorRepository[T <: StorableAuthenticator] extends AuthenticatorRepository[T] {
+
+ /**
+ * The data store for the OAuth1 info.
+ */
+ var data: mutable.HashMap[String, T] = mutable.HashMap()
+
+ /**
+ * Finds the authenticator for the given ID.
+ *
+ * @param id The authenticator ID.
+ * @return The found authenticator or None if no authenticator could be found for the given ID.
+ */
+ def find(id: String): Future[Option[T]] = {
+ Future.successful(data.get(id))
+ }
+
+ /**
+ * Adds a new authenticator.
+ *
+ * @param authenticator The authenticator to add.
+ * @return The added authenticator.
+ */
+ def add(authenticator: T): Future[T] = {
+ data += (authenticator.id -> authenticator)
+ Future.successful(authenticator)
+ }
+
+ /**
+ * Updates an already existing authenticator.
+ *
+ * @param authenticator The authenticator to update.
+ * @return The updated authenticator.
+ */
+ def update(authenticator: T): Future[T] = {
+ data += (authenticator.id -> authenticator)
+ Future.successful(authenticator)
+ }
+
+ /**
+ * Removes the authenticator for the given ID.
+ *
+ * @param id The authenticator ID.
+ * @return An empty future.
+ */
+ def remove(id: String): Future[Unit] = {
+ data -= id
+ Future.successful(())
+ }
+}
+
+/**
+ * A fake session authenticator service.
+ */
+case object FakeSessionAuthenticatorService extends SessionAuthenticatorService(
+ SessionAuthenticatorSettings(),
+ new DefaultFingerprintGenerator(),
+ new Base64AuthenticatorEncoder,
+ new DefaultSessionCookieBaker(),
+ Clock())
+
+/**
+ * A fake cookie authenticator service.
+ */
+case object FakeCookieAuthenticatorService extends CookieAuthenticatorService(
+ CookieAuthenticatorSettings(),
+ None,
+ new Signer {
+ def sign(data: String): String = data
+ def extract(message: String): util.Try[String] = Success(message)
+ },
+ new DefaultCookieHeaderEncoding(),
+ new Base64AuthenticatorEncoder,
+ new DefaultFingerprintGenerator(),
+ new SecureRandomIDGenerator(),
+ Clock())
+
+/**
+ * A fake bearer token authenticator service.
+ */
+case object FakeBearerTokenAuthenticatorService extends BearerTokenAuthenticatorService(
+ BearerTokenAuthenticatorSettings(),
+ new FakeAuthenticatorRepository[BearerTokenAuthenticator],
+ new SecureRandomIDGenerator(),
+ Clock())
+
+/**
+ * A fake JWT authenticator service.
+ */
+case object FakeJWTAuthenticatorService extends JWTAuthenticatorService(
+ JWTAuthenticatorSettings(sharedSecret = UUID.randomUUID().toString),
+ None,
+ new Base64AuthenticatorEncoder,
+ new SecureRandomIDGenerator(),
+ Clock())
+
+/**
+ * A fake Dummy authenticator service.
+ */
+case object FakeDummyAuthenticatorService extends DummyAuthenticatorService
+
+/**
+ * A fake authenticator service factory.
+ */
+object FakeAuthenticatorService {
+
+ /**
+ * Creates a new fake authenticator for the given authenticator type.
+ *
+ * @tparam T The type of the authenticator.
+ * @return A fully configured authenticator instance.
+ */
+ def apply[T <: Authenticator: Tag](): AuthenticatorService[T] = {
+ (Tag[T] match {
+ case t if t <:< Tag[SessionAuthenticator] => FakeSessionAuthenticatorService
+ case t if t <:< Tag[CookieAuthenticator] => FakeCookieAuthenticatorService
+ case t if t <:< Tag[BearerTokenAuthenticator] => FakeBearerTokenAuthenticatorService
+ case t if t <:< Tag[JWTAuthenticator] => FakeJWTAuthenticatorService
+ case t if t <:< Tag[DummyAuthenticator] => FakeDummyAuthenticatorService
+ case _ => throw new Exception("Invalid type specified.")
+ }).asInstanceOf[AuthenticatorService[T]]
+ }
+}
+
+/**
+ * A fake authenticator.
+ *
+ * @param loginInfo The linked login info for an identity.
+ * @param id The ID of the authenticator.
+ * @param isValid True if the authenticator is valid, false otherwise.
+ */
+final case class FakeAuthenticator(loginInfo: LoginInfo, id: String = UUID.randomUUID().toString, isValid: Boolean = true) extends StorableAuthenticator
+
+/**
+ * A fake authenticator factory.
+ */
+object FakeAuthenticator {
+
+ /**
+ * Creates a new fake authenticator for the given authenticator type.
+ *
+ * @param loginInfo The login info for which the authenticator should be created.
+ * @param env The Silhouette environment.
+ * @param requestHeader The request header.
+ * @tparam E The type of the environment,
+ * @return A authenticator instance.
+ */
+ def apply[E <: Env](loginInfo: LoginInfo)(implicit env: Environment[E], requestHeader: RequestHeader): A[E] = {
+ env.authenticatorService.create(loginInfo)
+ }
+}
+
+/**
+ * A fake environment implementation.
+ *
+ * @param identities A list of (login info -> identity) pairs to return inside a Silhouette action.
+ * @param requestProviders The list of request providers.
+ * @param eventBus The event bus implementation.
+ * @param executionContext The execution context to handle the asynchronous operations.
+ * @param tt The type tag of the authenticator type.
+ * @tparam E The type of the environment.
+ */
+final case class FakeEnvironment[E <: Env](
+ identities: Seq[(LoginInfo, I[E])],
+ requestProviders: Seq[RequestProvider] = Seq.empty,
+ eventBus: EventBus = EventBus())(
+ implicit
+ val executionContext: ExecutionContext,
+ tt: Tag[A[E]]) extends Environment[E] {
+
+ /**
+ * The identity service implementation.
+ */
+ val identityService: IdentityService[I[E]] = new FakeIdentityService[I[E]](identities: _*)
+
+ /**
+ * The authenticator service implementation.
+ */
+ val authenticatorService: AuthenticatorService[A[E]] = FakeAuthenticatorService[A[E]]()
+}
diff --git a/silhouette-testkit/app-3/io/github/honeycombcheesecake/play/silhouette/test/package.scala b/silhouette-testkit/app-3/io/github/honeycombcheesecake/play/silhouette/test/package.scala
new file mode 100644
index 00000000..0c51c197
--- /dev/null
+++ b/silhouette-testkit/app-3/io/github/honeycombcheesecake/play/silhouette/test/package.scala
@@ -0,0 +1,76 @@
+/**
+ * Copyright 2015 Mohiva Organisation (license at mohiva dot com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.github.honeycombcheesecake.play.silhouette
+
+import io.github.honeycombcheesecake.play.silhouette.api._
+import play.api.mvc.Request
+import play.api.test.FakeRequest
+
+import scala.concurrent.duration._
+import scala.concurrent.{ Await, Future }
+import scala.language.{ implicitConversions, postfixOps }
+
+/**
+ * Test helpers to test a Silhouette application.
+ */
+package object test {
+
+ /**
+ * Resolves a future by waiting of the result.
+ *
+ * @param f The future to block.
+ * @tparam T The type contained in the future.
+ * @return The value contained in the future.
+ */
+ implicit protected[test] def await[T](f: Future[T]): T = Await.result(f, 60 seconds)
+
+ /**
+ * Provides a method which add an authenticator to a fake request.
+ *
+ * @param f A fake request instance.
+ * @tparam A The type of the body.
+ */
+ implicit class FakeRequestWithAuthenticator[F](f: FakeRequest[F]) {
+ implicit val request: FakeRequest[F] = f
+
+ /**
+ * Creates a fake request with an embedded authenticator.
+ *
+ * @param authenticator The authenticator to embed into the request.
+ * @param env The Silhouette environment.
+ * @tparam E The type of the environment.
+ * @return A fake request.
+ */
+ def withAuthenticator[E <: Env](authenticator: A[E])(implicit env: Environment[E]): FakeRequest[F] = {
+ implicit val ec = env.executionContext
+ val rh = env.authenticatorService.init(authenticator).map(v => env.authenticatorService.embed(v, f))
+
+ new FakeRequest(Request.apply(rh, f.body))
+ }
+
+ /**
+ * Creates a fake request with an embedded authenticator.
+ *
+ * @param loginInfo The login info for which the new authenticator should be created.
+ * @param env The Silhouette environment.
+ * @tparam E The type of the environment.
+ * @return A fake request.
+ */
+ def withAuthenticator[E <: Env](loginInfo: LoginInfo)(implicit env: Environment[E]): FakeRequest[F] = {
+ withAuthenticator(FakeAuthenticator[E](loginInfo))
+ }
+ }
+}
diff --git a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala
index 00461e08..1555897c 100644
--- a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala
+++ b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala
@@ -15,17 +15,21 @@
*/
package io.github.honeycombcheesecake.play.silhouette.test
+import com.google.inject.AbstractModule
+
import javax.inject.Inject
import io.github.honeycombcheesecake.play.silhouette.api._
+import io.github.honeycombcheesecake.play.silhouette.api.actions.{ SecuredRequest, UserAwareRequest }
import io.github.honeycombcheesecake.play.silhouette.impl.authenticators._
import io.github.honeycombcheesecake.play.silhouette.test.FakesSpec._
import net.codingwell.scalaguice.ScalaModule
import org.specs2.matcher.JsonMatchers
import org.specs2.specification.Scope
import play.api.inject.guice.GuiceApplicationBuilder
+import play.api.inject.guice.GuiceableModule
import play.api.libs.json.Json
-import play.api.mvc.{ AbstractController, AnyContentAsEmpty, ControllerComponents }
+import play.api.mvc.{ AbstractController, AnyContent, AnyContentAsEmpty, ControllerComponents }
import play.api.test.{ FakeRequest, PlaySpecification, WithApplication }
import scala.concurrent.ExecutionContext.Implicits.global
@@ -109,7 +113,9 @@ class FakesSpec extends PlaySpecification with JsonMatchers {
}
"return a `CookieAuthenticatorService`" in new WithApplication {
- FakeAuthenticatorService[CookieAuthenticator]() must beAnInstanceOf[CookieAuthenticatorService]
+ override def running() = {
+ FakeAuthenticatorService[CookieAuthenticator]() must beAnInstanceOf[CookieAuthenticatorService]
+ }
}
"return a `BearerTokenAuthenticatorService`" in {
@@ -127,92 +133,108 @@ class FakesSpec extends PlaySpecification with JsonMatchers {
"The `FakeAuthenticator` factory" should {
"return a `SessionAuthenticator`" in new WithApplication {
- val loginInfo = LoginInfo("test", "test")
- val identity = FakeIdentity(loginInfo)
- implicit val env: FakeEnvironment[SessionEnv] = FakeEnvironment[SessionEnv](Seq(loginInfo -> identity))
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ override def running() = {
+ val loginInfo = LoginInfo("test", "test")
+ val identity = FakeIdentity(loginInfo)
+ implicit val env: FakeEnvironment[SessionEnv] = FakeEnvironment[SessionEnv](Seq(loginInfo -> identity))
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- FakeAuthenticator(loginInfo) must beAnInstanceOf[SessionAuthenticator]
+ FakeAuthenticator(loginInfo) must beAnInstanceOf[SessionAuthenticator]
+ }
}
"return a `CookieAuthenticator`" in new WithApplication {
- val loginInfo = LoginInfo("test", "test")
- val identity = FakeIdentity(loginInfo)
- implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ override def running() = {
+ val loginInfo = LoginInfo("test", "test")
+ val identity = FakeIdentity(loginInfo)
+ implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- FakeAuthenticator(loginInfo) must beAnInstanceOf[CookieAuthenticator]
+ FakeAuthenticator(loginInfo) must beAnInstanceOf[CookieAuthenticator]
+ }
}
"return a `BearerTokenAuthenticator`" in new WithApplication {
- val loginInfo = LoginInfo("test", "test")
- val identity = FakeIdentity(loginInfo)
- implicit val env: FakeEnvironment[BearerTokenEnv] = FakeEnvironment[BearerTokenEnv](Seq(loginInfo -> identity))
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ override def running() = {
+ val loginInfo = LoginInfo("test", "test")
+ val identity = FakeIdentity(loginInfo)
+ implicit val env: FakeEnvironment[BearerTokenEnv] = FakeEnvironment[BearerTokenEnv](Seq(loginInfo -> identity))
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- FakeAuthenticator(loginInfo) must beAnInstanceOf[BearerTokenAuthenticator]
+ FakeAuthenticator(loginInfo) must beAnInstanceOf[BearerTokenAuthenticator]
+ }
}
"return a `JWTAuthenticator`" in new WithApplication {
- val loginInfo = LoginInfo("test", "test")
- val identity = FakeIdentity(loginInfo)
- implicit val env: FakeEnvironment[JWTEnv] = FakeEnvironment[JWTEnv](Seq(loginInfo -> identity))
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ override def running() = {
+ val loginInfo = LoginInfo("test", "test")
+ val identity = FakeIdentity(loginInfo)
+ implicit val env: FakeEnvironment[JWTEnv] = FakeEnvironment[JWTEnv](Seq(loginInfo -> identity))
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- FakeAuthenticator(loginInfo) must beAnInstanceOf[JWTAuthenticator]
+ FakeAuthenticator(loginInfo) must beAnInstanceOf[JWTAuthenticator]
+ }
}
"return a `DummyAuthenticator`" in new WithApplication {
- val loginInfo = LoginInfo("test", "test")
- val identity = FakeIdentity(loginInfo)
- implicit val env: FakeEnvironment[DummyEnv] = FakeEnvironment[DummyEnv](Seq(loginInfo -> identity))
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ override def running() = {
+ val loginInfo = LoginInfo("test", "test")
+ val identity = FakeIdentity(loginInfo)
+ implicit val env: FakeEnvironment[DummyEnv] = FakeEnvironment[DummyEnv](Seq(loginInfo -> identity))
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- FakeAuthenticator(loginInfo) must beAnInstanceOf[DummyAuthenticator]
+ FakeAuthenticator(loginInfo) must beAnInstanceOf[DummyAuthenticator]
+ }
}
}
"The `securedAction` method of the `SecuredController`" should {
"return a 401 status code if no authenticator was found" in new InjectorContext {
new WithApplication(app) {
- val loginInfo = LoginInfo("test", "test")
- val identity = FakeIdentity(loginInfo)
- val env = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
- val request = FakeRequest()
+ override def running() = {
+ val loginInfo = LoginInfo("test", "test")
+ val identity = FakeIdentity(loginInfo)
+ val env = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
+ val request = FakeRequest()
- val controller = app.injector.instanceOf[SecuredController]
- val result = controller.defaultSecuredAction(request)
+ val controller = app.injector.instanceOf[SecuredController]
+ val result = controller.defaultSecuredAction(request)
- status(result) must equalTo(UNAUTHORIZED)
+ status(result) must equalTo(UNAUTHORIZED)
+ }
}
}
"return a 401 status code if authenticator but no identity was found" in new InjectorContext {
new WithApplication(app) {
- val loginInfo = LoginInfo("test", "test")
- val identity = FakeIdentity(loginInfo)
- implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
- val request = FakeRequest().withAuthenticator(LoginInfo("invalid", "invalid"))
+ override def running() = {
+ val loginInfo = LoginInfo("test", "test")
+ val identity = FakeIdentity(loginInfo)
+ implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
+ val request = FakeRequest().withAuthenticator(LoginInfo("invalid", "invalid"))
- val controller = app.injector.instanceOf[SecuredController]
- val result = controller.defaultSecuredAction(request)
+ val controller = app.injector.instanceOf[SecuredController]
+ val result = controller.defaultSecuredAction(request)
- status(result) must equalTo(UNAUTHORIZED)
+ status(result) must equalTo(UNAUTHORIZED)
+ }
}
}
"return a 200 status code if authenticator and identity was found" in new InjectorContext {
new WithApplication(app) {
- val loginInfo = LoginInfo("test", "test")
- val identity = FakeIdentity(loginInfo)
- implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
- val request = FakeRequest().withAuthenticator(loginInfo)
-
- val controller = app.injector.instanceOf[SecuredController]
- val result = controller.defaultSecuredAction(request)
-
- status(result) must equalTo(OK)
- contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "test")
+ override def running() = {
+ val loginInfo = LoginInfo("test", "test")
+ val identity = FakeIdentity(loginInfo)
+ implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
+ val request = FakeRequest().withAuthenticator(loginInfo)
+
+ val controller = app.injector.instanceOf[SecuredController]
+ val result = controller.defaultSecuredAction(request)
+
+ status(result) must equalTo(OK)
+ contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "test")
+ }
}
}
}
@@ -220,45 +242,51 @@ class FakesSpec extends PlaySpecification with JsonMatchers {
"The `userAwareAction` method of the `SecuredController`" should {
"return a 401 status code if no authenticator was found" in new InjectorContext {
new WithApplication(app) {
- val loginInfo = LoginInfo("test", "test")
- val identity = FakeIdentity(loginInfo)
- val env = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
- val request = FakeRequest()
+ override def running() = {
+ val loginInfo = LoginInfo("test", "test")
+ val identity = FakeIdentity(loginInfo)
+ val env = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
+ val request = FakeRequest()
- val controller = app.injector.instanceOf[SecuredController]
- val result = controller.defaultUserAwareAction(request)
+ val controller = app.injector.instanceOf[SecuredController]
+ val result = controller.defaultUserAwareAction(request)
- status(result) must equalTo(UNAUTHORIZED)
+ status(result) must equalTo(UNAUTHORIZED)
+ }
}
}
"return a 401 status code if authenticator but no identity was found" in new InjectorContext {
new WithApplication(app) {
- val loginInfo = LoginInfo("test", "test")
- val identity = FakeIdentity(loginInfo)
- implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
- val request = FakeRequest().withAuthenticator(LoginInfo("invalid", "invalid"))
+ override def running() = {
+ val loginInfo = LoginInfo("test", "test")
+ val identity = FakeIdentity(loginInfo)
+ implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
+ val request = FakeRequest().withAuthenticator(LoginInfo("invalid", "invalid"))
- val controller = app.injector.instanceOf[SecuredController]
- val result = controller.defaultUserAwareAction(request)
+ val controller = app.injector.instanceOf[SecuredController]
+ val result = controller.defaultUserAwareAction(request)
- status(result) must equalTo(UNAUTHORIZED)
+ status(result) must equalTo(UNAUTHORIZED)
+ }
}
}
"return a 200 status code if authenticator and identity was found" in new InjectorContext {
new WithApplication(app) {
- val loginInfo = LoginInfo("test", "test")
- val identity = FakeIdentity(loginInfo)
+ override def running() = {
+ val loginInfo = LoginInfo("test", "test")
+ val identity = FakeIdentity(loginInfo)
- implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
- val request = FakeRequest().withAuthenticator(loginInfo)
+ implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity))
+ val request = FakeRequest().withAuthenticator(loginInfo)
- val controller = app.injector.instanceOf[SecuredController]
- val result = controller.defaultUserAwareAction(request)
+ val controller = app.injector.instanceOf[SecuredController]
+ val result = controller.defaultUserAwareAction(request)
- status(result) must equalTo(OK)
- contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "test")
+ status(result) must equalTo(OK)
+ contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "test")
+ }
}
}
}
@@ -287,13 +315,13 @@ class FakesSpec extends PlaySpecification with JsonMatchers {
* The guice application builder.
*/
lazy val app = new GuiceApplicationBuilder()
- .bindings(new GuiceModule)
+ .bindings(GuiceableModule.guiceable(new GuiceModule))
.build()
/**
* The guice module.
*/
- class GuiceModule extends ScalaModule {
+ class GuiceModule extends AbstractModule with ScalaModule {
override def configure(): Unit = {
bind[Silhouette[CookieEnv]].to[SilhouetteProvider[CookieEnv]]
bind[Environment[CookieEnv]].toInstance(env)
@@ -363,7 +391,7 @@ object FakesSpec {
*
* @return The result to send to the client.
*/
- def defaultSecuredAction = silhouette.SecuredAction { implicit request =>
+ def defaultSecuredAction = silhouette.SecuredAction { implicit request: SecuredRequest[CookieEnv, AnyContent] =>
Ok(Json.toJson(request.identity.loginInfo))
}
@@ -372,7 +400,7 @@ object FakesSpec {
*
* @return The result to send to the client.
*/
- def defaultUserAwareAction = silhouette.UserAwareAction { implicit request =>
+ def defaultUserAwareAction = silhouette.UserAwareAction { implicit request: UserAwareRequest[CookieEnv, AnyContent] =>
request.identity match {
case Some(identity) => Ok(Json.toJson(identity.loginInfo))
case None => Unauthorized
diff --git a/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala b/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala
index 0812cf18..fc4ca1e8 100644
--- a/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala
+++ b/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala
@@ -17,7 +17,8 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers
import io.github.honeycombcheesecake.play.silhouette.api.util.{ Credentials, PasswordInfo }
import com.warrenstrange.googleauth.GoogleAuthenticator
-import org.specs2.mock.Mockito
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
import play.api.test.WithApplication
import scala.concurrent.ExecutionContext.Implicits.global
@@ -25,58 +26,76 @@ import scala.concurrent.ExecutionContext.Implicits.global
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.providers.GoogleTotpProvider#GoogleTOTPProvider]] class.
*/
-class GoogleTotpProviderSpec extends PasswordProviderSpec with Mockito {
+class GoogleTotpProviderSpec extends PasswordProviderSpec {
"The `authenticate` with verification code method" should {
"return None when the sharedKey is null or empty" in new WithApplication with Context {
- await(provider.authenticate(null.asInstanceOf[String], testVerificationCode)) should be(None)
- await(provider.authenticate("", testVerificationCode)) should be(None)
+ override def running() = {
+ await(provider.authenticate(null.asInstanceOf[String], testVerificationCode)) should be(None)
+ await(provider.authenticate("", testVerificationCode)) should be(None)
+ }
}
"return None when the verification code is null or empty" in new WithApplication with Context {
- await(provider.authenticate(testSharedKey, null)) should be(None)
- await(provider.authenticate(testSharedKey, "")) should be(None)
+ override def running() = {
+ await(provider.authenticate(testSharedKey, null)) should be(None)
+ await(provider.authenticate(testSharedKey, "")) should be(None)
+ }
}
"return None when the verification code isn't a number" in new WithApplication with Context {
- await(provider.authenticate(testSharedKey, testWrongVerificationCode)) should be(None)
+ override def running() = {
+ await(provider.authenticate(testSharedKey, testWrongVerificationCode)) should be(None)
+ }
}
"return valid `Some(TotpInfo)` when the verification code is correct" in new WithApplication with Context {
- val googleAuthenticator = new GoogleAuthenticator()
- val validVerificationCode = googleAuthenticator.getTotpPassword(testSharedKey)
- await(provider.authenticate(testSharedKey, validVerificationCode.toString)) should not be empty
+ override def running() = {
+ val googleAuthenticator = new GoogleAuthenticator()
+ val validVerificationCode = googleAuthenticator.getTotpPassword(testSharedKey)
+ await(provider.authenticate(testSharedKey, validVerificationCode.toString)) should not be None
+ }
}
}
"The `createCredentials` method" should {
"return the correct TotpCredentials shared key" in new WithApplication with Context {
- val result = provider.createCredentials(credentials.identifier)
- result.totpInfo.sharedKey must not be ""
- result.totpInfo.scratchCodes must not be empty
- result.qrUrl must not be ""
+ override def running() = {
+ val result = provider.createCredentials(credentials.identifier)
+ result.totpInfo.sharedKey.nonEmpty must beTrue
+ result.totpInfo.scratchCodes.nonEmpty must beTrue
+ result.qrUrl.nonEmpty must beTrue
+ }
}
}
"The `authenticate` with verification code method" should {
"throw NullPointerException when the input totpInfo is null" in new WithApplication with Context {
- await(provider.authenticate(null.asInstanceOf[GoogleTotpInfo], testWrongVerificationCode)) must throwA[NullPointerException]
+ override def running() = {
+ await(provider.authenticate(null.asInstanceOf[GoogleTotpInfo], testWrongVerificationCode)) must throwA[NullPointerException]
+ }
}
"return throw NullPointerException when the plain scratch code is null" in new WithApplication with Context {
- val result = provider.createCredentials(credentials.identifier)
- await(provider.authenticate(result.totpInfo, null.asInstanceOf[String])) must throwA[NullPointerException]
+ override def running() = {
+ val result = provider.createCredentials(credentials.identifier)
+ await(provider.authenticate(result.totpInfo, null.asInstanceOf[String])) must throwA[NullPointerException]
+ }
}
"return None when the plain scratch code is empty" in new WithApplication with Context {
- val result = provider.createCredentials(credentials.identifier)
- await(provider.authenticate(result.totpInfo, "")) should be(None)
+ override def running() = {
+ val result = provider.createCredentials(credentials.identifier)
+ await(provider.authenticate(result.totpInfo, "")) should be(None)
+ }
}
"return Some(PasswordInfo,TotpInfo) when the plain scratch code is valid" in new WithApplication with Context {
- fooHasher.hash(any()) returns testPasswordInfo
- barHasher.matches(testPasswordInfo, testScratchCode) returns true
- val result = provider.createCredentials(credentials.identifier)
- await(provider.authenticate(result.totpInfo, testScratchCode)) should not be empty
+ override def running() = {
+ when(fooHasher.hash(any())).thenReturn(testPasswordInfo)
+ when(barHasher.matches(testPasswordInfo, testScratchCode)).thenReturn(true)
+ val result = provider.createCredentials(credentials.identifier)
+ await(provider.authenticate(result.totpInfo, testScratchCode)) should not be None
+ }
}
}
diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala
similarity index 100%
rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala
rename to silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala
diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala
similarity index 100%
rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala
rename to silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala
diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala
similarity index 100%
rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala
rename to silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala
diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala
similarity index 100%
rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala
rename to silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala
diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala
similarity index 100%
rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala
rename to silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala
diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala
similarity index 100%
rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala
rename to silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala
diff --git a/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala
new file mode 100644
index 00000000..5ab693ae
--- /dev/null
+++ b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala
@@ -0,0 +1,175 @@
+/**
+ * Original work: SecureSocial (https://github.com/jaliss/securesocial)
+ * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss
+ *
+ * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette)
+ * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.github.honeycombcheesecake.play.silhouette.api
+
+import io.github.honeycombcheesecake.play.silhouette.api.Authenticator.Implicits._
+
+import java.time.ZonedDateTime
+import scala.concurrent.duration.FiniteDuration
+
+/**
+ * An authenticator recognizes an authenticated user.
+ */
+trait Authenticator {
+
+ /**
+ * The Type of the generated value an authenticator will be serialized to.
+ */
+ type Value
+
+ /**
+ * The type of the settings an authenticator can handle.
+ */
+ type Settings
+
+ /**
+ * Gets the linked login info for an identity.
+ *
+ * @return The linked login info for an identity.
+ */
+ def loginInfo: LoginInfo
+
+ /**
+ * Checks if the authenticator valid.
+ *
+ * @return True if the authenticator valid, false otherwise.
+ */
+ def isValid: Boolean
+}
+
+
+// match types
+type Value[A <: Authenticator] = A match
+ case Authenticator.AuxV[v] => v // lower case is significant
+
+type Settings[A <: Authenticator] = A match
+ case Authenticator.AuxS[s] => s // lower case is significant
+
+/**
+ * The `Authenticator` companion object.
+ */
+object Authenticator {
+
+ type AuxV[_V] = Authenticator {type Value = _V}
+ type AuxS[_S] = Authenticator {type Settings = _S}
+
+ /**
+ * Some implicits.
+ */
+ object Implicits {
+
+ /**
+ * Defines additional methods on a `ZonedDateTime` instance.
+ *
+ * @param dateTime The `ZonedDateTime` instance on which the additional methods should be defined.
+ */
+ implicit class RichDateTime(dateTime: ZonedDateTime) {
+
+ /**
+ * Adds a duration to a date/time.
+ *
+ * @param duration The duration to add.
+ * @return A date/time instance with the added duration.
+ */
+ def +(duration: FiniteDuration): ZonedDateTime = {
+ dateTime.plusSeconds(duration.toSeconds.toInt)
+ }
+
+ /**
+ * Subtracts a duration from a date/time.
+ *
+ * @param duration The duration to subtract.
+ * @return A date/time instance with the subtracted duration.
+ */
+ def -(duration: FiniteDuration): ZonedDateTime = {
+ dateTime.minusSeconds(duration.toSeconds.toInt)
+ }
+
+ /**
+ * Compares a date/time with the current time
+ *
+ * @return Is the current time before the time supplied by the Clock
+ */
+ def isBeforeNow: Boolean = {
+ dateTime.isBefore(ZonedDateTime.now)
+ }
+ }
+ }
+}
+
+/**
+ * An authenticator which can be stored in a backing store.
+ */
+trait StorableAuthenticator extends Authenticator {
+
+ /**
+ * Gets the ID to reference the authenticator in the backing store.
+ *
+ * @return The ID to reference the authenticator in the backing store.
+ */
+ def id: String
+}
+
+/**
+ * An authenticator that may expire.
+ */
+trait ExpirableAuthenticator extends Authenticator {
+
+ /**
+ * The last used date/time.
+ */
+ val lastUsedDateTime: ZonedDateTime
+
+ /**
+ * The expiration date/time.
+ */
+ val expirationDateTime: ZonedDateTime
+
+ /**
+ * The duration an authenticator can be idle before it timed out.
+ */
+ val idleTimeout: Option[FiniteDuration]
+
+ /**
+ * Checks if the authenticator isn't expired and isn't timed out.
+ *
+ * @return True if the authenticator isn't expired and isn't timed out.
+ */
+ override def isValid: Boolean = !isExpired && !isTimedOut
+
+ /**
+ * Checks if the authenticator is expired. This is an absolute timeout since the creation of
+ * the authenticator.
+ *
+ * @return True if the authenticator is expired, false otherwise.
+ */
+ def isExpired: Boolean = expirationDateTime.isBeforeNow
+
+ /**
+ * Checks if the time elapsed since the last time the authenticator was used, is longer than
+ * the maximum idle timeout specified in the properties.
+ *
+ * @return True if sliding window expiration is activated and the authenticator is timed out, false otherwise.
+ */
+ def isTimedOut: Boolean = idleTimeout match {
+ case Some(idle) => (lastUsedDateTime + idle).isBeforeNow
+ case _ => false
+ }
+}
diff --git a/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala
new file mode 100644
index 00000000..9f776d8c
--- /dev/null
+++ b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala
@@ -0,0 +1,121 @@
+/**
+ * Copyright 2015 Mohiva Organisation (license at mohiva dot com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.github.honeycombcheesecake.play.silhouette.api
+
+import io.github.honeycombcheesecake.play.silhouette.api.services.{ AuthenticatorService, IdentityService }
+import io.github.honeycombcheesecake.play.silhouette.api.util.ExecutionContextProvider
+
+import scala.concurrent.ExecutionContext
+
+/**
+ * The environment type.
+ *
+ * Defines the [[Identity]] and [[Authenticator]] types for an environment. It is possible
+ * to implement as many types as needed. This has the advantage that an application isn't
+ * bound only to a single `Identity` -> `Authenticator` combination.
+ *
+ * To define a new environment type create a new trait with the appropriate [[Identity]] and
+ * [[Authenticator]] types:
+ *
+ * {{{
+ * trait SessionEnv extends Env {
+ * type I = User
+ * type A = SessionAuthenticator
+ * }
+ * trait JWTEnv extends Env {
+ * type I = User
+ * type A = JWTAuthenticator
+ * }
+ * }}}
+ */
+trait Env {
+ type I <: Identity
+ type A <: Authenticator
+}
+
+object Env {
+ type AuxI[_I <: Identity] = Env {type I = _I}
+ type AuxA[_A <: Authenticator] = Env {type A = _A}
+}
+
+ // match types
+type I[E <: Env] <: Identity = E match
+ case Env.AuxI[i] => i // lower case is significant
+
+type A[E <: Env] <: Authenticator = E match
+ case Env.AuxA[a] => a
+
+/**
+ * Provides the components needed to handle a secured request.
+ *
+ * It's possible to declare different environments for different environment types. The
+ * [[io.github.honeycombcheesecake.play.silhouette.api.services.IdentityService]] and the
+ * [[io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorService]] are bound to the appropriate types
+ * defined in the environment type. But the [[EventBus]] and the list of [[RequestProvider]]
+ * instances can be defined as needed for every environment type.
+ */
+trait Environment[E <: Env] extends ExecutionContextProvider {
+
+ /**
+ * Gets the identity service implementation.
+ *
+ * @return The identity service implementation.
+ */
+ def identityService: IdentityService[I[E]]
+
+ /**
+ * Gets the authenticator service implementation.
+ *
+ * @return The authenticator service implementation.
+ */
+ def authenticatorService: AuthenticatorService[A[E]]
+
+ /**
+ * Gets the list of request providers.
+ *
+ * @return The list of request providers.
+ */
+ def requestProviders: Seq[RequestProvider]
+
+ /**
+ * The event bus implementation.
+ *
+ * @return The event bus implementation.
+ */
+ def eventBus: EventBus
+}
+
+/**
+ * Companion object to easily create environment instances.
+ *
+ * {{{
+ * Environment[SessionEnv](...)
+ * Environment[JWTEnv](...)
+ * }}}
+ */
+object Environment {
+ def apply[E <: Env](
+ identityServiceImpl: IdentityService[I[E]],
+ authenticatorServiceImpl: AuthenticatorService[A[E]],
+ requestProvidersImpl: Seq[RequestProvider],
+ eventBusImpl: EventBus)(implicit ec: ExecutionContext) = new Environment[E] {
+ val identityService = identityServiceImpl
+ val authenticatorService = authenticatorServiceImpl
+ val requestProviders = requestProvidersImpl
+ val eventBus = eventBusImpl
+ val executionContext = ec
+ }
+}
diff --git a/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala
new file mode 100644
index 00000000..dd8872ff
--- /dev/null
+++ b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala
@@ -0,0 +1,227 @@
+/**
+ * Original work: SecureSocial (https://github.com/jaliss/securesocial)
+ * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss
+ *
+ * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette)
+ * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.github.honeycombcheesecake.play.silhouette.api
+
+import io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorResult
+import io.github.honeycombcheesecake.play.silhouette.api.util.ExecutionContextProvider
+import play.api.mvc.{ Result, RequestHeader, Request, AnyContent }
+
+import scala.concurrent.ExecutionContext
+import scala.concurrent.Future
+
+/**
+ * A result which can transport a result as also additional data through the request handler process.
+ *
+ * @param result A Play Framework result.
+ * @param data Additional data to transport in the result.
+ * @tparam T The type of the data.
+ */
+final case class HandlerResult[+T](result: Result, data: Option[T] = None)
+
+/**
+ * Base implementation for building request handlers.
+ *
+ * The base implementations to handle secured endpoints are encapsulated into request handlers which
+ * can execute an arbitrary block of code and must return a HandlerResult. This HandlerResult consists
+ * of a normal Play result and arbitrary additional data which can be transported out of these handlers.
+ *
+ * @tparam E The type of the environment.
+ * @tparam R The type of the request.
+ */
+trait RequestHandlerBuilder[E <: Env, +R[_]] extends ExecutionContextProvider {
+
+ /**
+ * Provides an `extract` method on an `Either` which contains the same types.
+ */
+ protected implicit class ExtractEither[T](r: Either[T, T]) {
+ def extract: T = r.fold(identity, identity)
+ }
+
+ /**
+ * The execution context to handle the asynchronous operations.
+ */
+ implicit /*lazy*/ val executionContext: ExecutionContext = environment.executionContext
+
+ /**
+ * The environment instance to handle the request.
+ */
+ val environment: Environment[E]
+
+ /**
+ * Constructs a request handler with default content.
+ *
+ * @param block The block of code to invoke.
+ * @param request The current request.
+ * @tparam T The type of the data included in the handler result.
+ * @return A handler result.
+ */
+ final def apply[T](block: R[AnyContent] => Future[HandlerResult[T]])(implicit request: Request[AnyContent]): Future[HandlerResult[T]] = {
+ invokeBlock(block)
+ }
+
+ /**
+ * Constructs a request handler with the content of the given request.
+ *
+ * @param request The current request.
+ * @param block The block of code to invoke.
+ * @tparam B The type of the request body.
+ * @tparam T The type of the data included in the handler result.
+ * @return A handler result.
+ */
+ final def apply[B, T](request: Request[B])(block: R[B] => Future[HandlerResult[T]]): Future[HandlerResult[T]] = {
+ invokeBlock(block)(request)
+ }
+
+ /**
+ * Invoke the block.
+ *
+ * This is the main method that an request handler has to implement.
+ *
+ * @param block The block of code to invoke.
+ * @param request The current request.
+ * @tparam B The type of the request body.
+ * @tparam T The type of the data included in the handler result.
+ * @return A handler result.
+ */
+ def invokeBlock[B, T](block: R[B] => Future[HandlerResult[T]])(implicit request: Request[B]): Future[HandlerResult[T]]
+
+ /**
+ * Handles a block for an authenticator.
+ *
+ * Invokes the block with the authenticator and handles the result. See `handleInitializedAuthenticator` and
+ * `handleUninitializedAuthenticator` methods too see how the different authenticator types will be handled.
+ *
+ * @param authenticator An already initialized authenticator on the left and a new authenticator on the right.
+ * @param block The block to handle with the authenticator.
+ * @param request The current request header.
+ * @tparam T The type of the data included in the handler result.
+ * @return A handler result.
+ */
+ protected def handleBlock[T](authenticator: Either[A[E], A[E]], block: A[E] => Future[HandlerResult[T]])(implicit request: RequestHeader) = {
+ authenticator match {
+ case Left(a) => handleInitializedAuthenticator(a, block)
+ case Right(a) => handleUninitializedAuthenticator(a, block)
+ }
+ }
+
+ /**
+ * Handles the authentication of an identity.
+ *
+ * As first it checks for authenticators in requests, then it tries to authenticate against a request provider.
+ * This method marks the returned authenticators by returning already initialized authenticators on the
+ * left and new authenticators on the right. All new authenticators must be initialized later in the flow,
+ * with the result returned from the invoked block.
+ *
+ * @param request The current request.
+ * @tparam B The type of the request body.
+ * @return A tuple which consists of (maybe the existing authenticator on the left or a
+ * new authenticator on the right -> maybe the identity).
+ */
+ protected def handleAuthentication[B](implicit request: Request[B]): Future[(Option[Either[A[E], A[E]]], Option[I[E]])] = {
+ environment.authenticatorService.retrieve.flatMap {
+ // A valid authenticator was found so we retrieve also the identity
+ case Some(a) if a.isValid => environment.identityService.retrieve(a.loginInfo).map(i => Some(Left(a)) -> i)
+ // An invalid authenticator was found so we needn't retrieve the identity
+ case Some(a) => Future.successful(Some(Left(a)) -> None)
+ // No authenticator was found so we try to authenticate with a request provider
+ case None => handleRequestProviderAuthentication.flatMap {
+ // Authentication was successful, so we retrieve the identity and create a new authenticator for it
+ case Some(loginInfo) => environment.identityService.retrieve(loginInfo).flatMap { i =>
+ environment.authenticatorService.create(loginInfo).map(a => Some(Right(a)) -> i)
+ }
+ // No identity and no authenticator was found
+ case None => Future.successful(None -> None)
+ }
+ }
+ }
+
+ /**
+ * Handles already initialized authenticators.
+ *
+ * The authenticator handled by this method was found in the current request. So it was initialized on
+ * a previous request and must now be updated if it was touched and no authenticator result was found.
+ *
+ * @param authenticator The authenticator to handle.
+ * @param block The block to handle with the authenticator.
+ * @param request The current request header.
+ * @tparam T The type of the data included in the handler result.
+ * @return A handler result.
+ */
+ private def handleInitializedAuthenticator[T](authenticator: A[E], block: A[E] => Future[HandlerResult[T]])(implicit request: RequestHeader) = {
+ val auth = environment.authenticatorService.touch(authenticator)
+ block(auth.fold(identity, identity)).flatMap {
+ case hr @ HandlerResult(pr: AuthenticatorResult, _) => Future.successful(hr)
+ case hr @ HandlerResult(pr, _) => auth match {
+ // Authenticator was touched so we update the authenticator and maybe the result
+ case Left(a) => environment.authenticatorService.update(a, pr).map(pr => hr.copy(pr))
+ // Authenticator was not touched so we return the original result
+ case Right(a) => Future.successful(hr)
+ }
+ }
+ }
+
+ /**
+ * Handles not initialized authenticators.
+ *
+ * The authenticator handled by this method was newly created after authentication with a request provider.
+ * So it must be initialized with the result of the invoked block if no authenticator result was found.
+ *
+ * @param authenticator The authenticator to handle.
+ * @param block The block to handle with the authenticator.
+ * @param request The current request header.
+ * @tparam T The type of the data included in the handler result.
+ * @return A handler result.
+ */
+ private def handleUninitializedAuthenticator[T](authenticator: A[E], block: A[E] => Future[HandlerResult[T]])(implicit request: RequestHeader) = {
+ block(authenticator).flatMap {
+ case hr @ HandlerResult(pr: AuthenticatorResult, _) => Future.successful(hr)
+ case hr @ HandlerResult(pr, _) =>
+ environment.authenticatorService.init(authenticator).flatMap { value =>
+ environment.authenticatorService.embed(value, pr)
+ }.map(pr => hr.copy(pr))
+ }
+ }
+
+ /**
+ * Handles the authentication with the request providers.
+ *
+ * Silhouette supports chaining of request providers. So if more as one request provider is defined
+ * it tries to authenticate until one provider returns an identity. The order of the providers
+ * isn't guaranteed.
+ *
+ * @param request The current request.
+ * @tparam B The type of the request body.
+ * @return Some identity or None if authentication was not successful.
+ */
+ private def handleRequestProviderAuthentication[B](implicit request: Request[B]): Future[Option[LoginInfo]] = {
+ def auth(providers: Seq[RequestProvider]): Future[Option[LoginInfo]] = {
+ providers match {
+ case Nil => Future.successful(None)
+ case h :: t => h.authenticate(request).flatMap {
+ case Some(i) => Future.successful(Some(i))
+ case None => if (t.isEmpty) Future.successful(None) else auth(t)
+ }
+ case _ => Future.successful(None)
+ }
+ }
+
+ auth(environment.requestProviders)
+ }
+}
diff --git a/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala
new file mode 100644
index 00000000..a8e3bbc7
--- /dev/null
+++ b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala
@@ -0,0 +1,411 @@
+/**
+ * Original work: SecureSocial (https://github.com/jaliss/securesocial)
+ * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss
+ *
+ * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette)
+ * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.github.honeycombcheesecake.play.silhouette.api.actions
+
+import javax.inject.Inject
+
+import io.github.honeycombcheesecake.play.silhouette.api._
+import play.api.i18n.MessagesApi
+import play.api.inject.Module
+import play.api.mvc._
+import play.api.{ Configuration, Environment => PlayEnv }
+
+import scala.annotation.nowarn
+import scala.concurrent.{ ExecutionContext, Future }
+import scala.reflect.ClassTag
+
+/**
+ * A request header that only allows access if an identity is authenticated and authorized.
+ *
+ * @tparam E The type of the environment.
+ */
+trait SecuredRequestHeader[E <: Env] extends RequestHeader {
+ /**
+ * @return The identity implementation.
+ */
+ def identity: I[E]
+
+ /**
+ * @return The authenticator implementation.
+ */
+ def authenticator: A[E]
+}
+
+/**
+ * A request that only allows access if an identity is authenticated and authorized.
+ *
+ * @tparam E The type of the environment.
+ * @tparam B The type of the request body.
+ */
+trait SecuredRequest[E <: Env, +B] extends Request[B] with SecuredRequestHeader[E]
+
+object SecuredRequest {
+ /**
+ * A request that only allows access if an identity is authenticated and authorized.
+ *
+ * @param identity The identity implementation.
+ * @param authenticator The authenticator implementation.
+ * @param request The current request.
+ * @tparam E The type of the environment.
+ * @tparam B The type of the request body.
+ */
+ def apply[E <: Env, B](identity: I[E], authenticator: A[E], request: Request[B]): SecuredRequest[E, B] = {
+ new DefaultSecuredRequest(identity, authenticator, request)
+ }
+
+ /**
+ * Unapply method for secured request.
+ *
+ * @param securedRequest the secured request.
+ * @tparam E The type of the environment.
+ * @tparam B The type of the request body.
+ */
+ @nowarn
+ def unapply[E <: Env, B](securedRequest: SecuredRequest[E, B]): Option[(I[E], A[E], Request[B])] = {
+ securedRequest match {
+ case dsr: DefaultSecuredRequest[E, B] =>
+ Some((dsr.identity, dsr.authenticator, dsr.request))
+ case sr: SecuredRequest[E, B] =>
+ Some((sr.identity, sr.authenticator, sr))
+ }
+ }
+}
+
+class DefaultSecuredRequest[E <: Env, B](
+ val identity: I[E],
+ val authenticator: A[E],
+ val request: Request[B]) extends WrappedRequest(request) with SecuredRequest[E, B]
+
+/**
+ * Request handler builder implementation to provide the foundation for secured request handlers.
+ *
+ * @param environment The environment instance to handle the request.
+ * @param errorHandler The instance of the secured error handler.
+ * @param authorization Maybe an authorization instance.
+ * @tparam E The type of the environment.
+ */
+final case class SecuredRequestHandlerBuilder[E <: Env](
+ environment: Environment[E],
+ errorHandler: SecuredErrorHandler,
+ authorization: Option[Authorization[I[E], A[E]]])
+ extends RequestHandlerBuilder[E, ({ type R[B] = SecuredRequest[E, B] })#R] {
+
+ /**
+ * Creates a secured action handler builder with a new error handler in place.
+ *
+ * @param securedErrorHandler An error handler instance.
+ * @return A secured action handler builder with a new error handler in place.
+ */
+ def apply(securedErrorHandler: SecuredErrorHandler): SecuredRequestHandlerBuilder[E] =
+ SecuredRequestHandlerBuilder[E](environment, securedErrorHandler, authorization)
+
+ /**
+ * Creates a secured action handler builder with an authorization in place.
+ *
+ * @param specifiedAuthorization An authorization object that checks if the user is authorized to invoke the action.
+ * @return A secured action handler builder with an authorization in place.
+ */
+ def apply(specifiedAuthorization: Authorization[I[E], A[E]]): SecuredRequestHandlerBuilder[E] =
+ SecuredRequestHandlerBuilder[E](environment, errorHandler, Some(specifiedAuthorization))
+
+ /**
+ * Invokes the block.
+ *
+ * @param block The block of code to invoke.
+ * @param request The current request.
+ * @tparam B The type of the request body.
+ * @tparam T The type of the data included in the handler result.
+ * @return A handler result.
+ */
+ override def invokeBlock[B, T](block: SecuredRequest[E, B] => Future[HandlerResult[T]])(
+ implicit
+ request: Request[B]): Future[HandlerResult[T]] = {
+ withAuthorization(handleAuthentication).flatMap {
+ // A user is both authenticated and authorized. The request will be granted
+ case (Some(authenticator), Some(identity), Some(authorized)) if authorized =>
+ environment.eventBus.publish(AuthenticatedEvent(identity, request))
+ handleBlock(authenticator, a => block(SecuredRequest(identity, a, request)))
+ // A user is authenticated but not authorized. The request will be forbidden
+ case (Some(authenticator), Some(identity), _) =>
+ environment.eventBus.publish(NotAuthorizedEvent(identity, request))
+ handleBlock(authenticator, _ => errorHandler.onNotAuthorized.map(r => HandlerResult(r)))
+ // An authenticator but no user was found. The request will ask for authentication and the authenticator will be discarded
+ case (Some(authenticator), None, _) =>
+ environment.eventBus.publish(NotAuthenticatedEvent(request))
+ for {
+ result <- errorHandler.onNotAuthenticated
+ discardedResult <- environment.authenticatorService.discard(authenticator.extract, result)
+ } yield HandlerResult(discardedResult)
+ // No authenticator and no user was found. The request will ask for authentication
+ case _ =>
+ environment.eventBus.publish(NotAuthenticatedEvent(request))
+ errorHandler.onNotAuthenticated.map(r => HandlerResult(r))
+ }
+ }
+
+ /**
+ * Adds the authorization status to the authentication result.
+ *
+ * @param result The authentication result.
+ * @param request The current request.
+ * @tparam B The type of the request body.
+ * @return The authentication result with the additional authorization status.
+ */
+ private def withAuthorization[B](result: Future[(Option[Either[A[E], A[E]]], Option[I[E]])])(implicit request: Request[B]) = {
+ result.flatMap {
+ case (Some(a), Some(i)) =>
+ authorization.map(_.isAuthorized(i, a.extract)).getOrElse(Future.successful(true)).map(b => (Some(a), Some(i), Some(b)))
+ case (a, i) =>
+ Future.successful((a, i, None))
+ }
+ }
+}
+
+/**
+ * A secured request handler.
+ *
+ * A handler which intercepts requests and checks if there is an authenticated user.
+ * If there is one, the execution continues and the enclosed code is invoked.
+ *
+ * If the user is not authenticated or not authorized, the request is forwarded to
+ * the [[io.github.honeycombcheesecake.play.silhouette.api.actions.SecuredErrorHandler.onNotAuthenticated]] or
+ * the [[io.github.honeycombcheesecake.play.silhouette.api.actions.SecuredErrorHandler.onNotAuthorized]] methods.
+ */
+trait SecuredRequestHandler {
+
+ /**
+ * The instance of the secured error handler.
+ */
+ val errorHandler: SecuredErrorHandler
+
+ /**
+ * Applies the environment to the request handler stack.
+ *
+ * @param environment The environment instance to handle the request.
+ * @tparam E The type of the environment.
+ * @return A secured request handler builder.
+ */
+ def apply[E <: Env](environment: Environment[E]): SecuredRequestHandlerBuilder[E]
+}
+
+/**
+ * Default implementation of the [[SecuredRequestHandler]].
+ *
+ * @param errorHandler The instance of the secured error handler.
+ */
+class DefaultSecuredRequestHandler @Inject() (val errorHandler: SecuredErrorHandler)
+ extends SecuredRequestHandler {
+
+ /**
+ * Applies the environment to the request handler stack.
+ *
+ * @param environment The environment instance to handle the request.
+ * @tparam E The type of the environment.
+ * @return A secured request handler builder.
+ */
+ override def apply[E <: Env](environment: Environment[E]): SecuredRequestHandlerBuilder[E] =
+ SecuredRequestHandlerBuilder[E](environment, errorHandler, None)
+}
+
+/**
+ * Action builder implementation to provide the foundation for secured actions.
+ *
+ * @param requestHandler The request handler instance.
+ * @param parser The body parser.
+ * @tparam E The type of the environment.
+ * @tparam P The type of the request body.
+ */
+final case class SecuredActionBuilder[E <: Env, P](
+ requestHandler: SecuredRequestHandlerBuilder[E],
+ parser: BodyParser[P]) extends ActionBuilder[({ type R[B] = SecuredRequest[E, B] })#R, P] {
+
+ /**
+ * Creates a secured action builder with a new error handler in place.
+ *
+ * @param errorHandler An error handler instance.
+ * @return A secured action builder.
+ */
+ def apply(errorHandler: SecuredErrorHandler): SecuredActionBuilder[E, P] = SecuredActionBuilder[E, P](requestHandler(errorHandler), parser)
+
+ /**
+ * Creates a secured action builder with an authorization in place.
+ *
+ * @param authorization An authorization object that checks if the user is authorized to invoke the action.
+ * @return A secured action builder.
+ */
+ def apply(authorization: Authorization[I[E], A[E]]): SecuredActionBuilder[E, P] = SecuredActionBuilder[E, P](requestHandler(authorization), parser)
+
+ /**
+ * Invokes the block.
+ *
+ * @param request The current request.
+ * @param block The block of code to invoke.
+ * @tparam B The type of the request body.
+ * @return A handler result.
+ */
+ override def invokeBlock[B](request: Request[B], block: SecuredRequest[E, B] => Future[Result]) = {
+ implicit val ec: ExecutionContext = executionContext
+ implicit val req: Request[B] = request
+ val b = (r: SecuredRequest[E, B]) => block(r).map(r => HandlerResult(r))
+
+ requestHandler(request)(b).map(_.result).recoverWith(requestHandler.errorHandler.exceptionHandler)
+ }
+
+ /**
+ * Get the execution context to run the request in.
+ *
+ * @return The execution context.
+ */
+ override protected def executionContext: ExecutionContext = requestHandler.executionContext
+}
+
+/**
+ * An action based on the [[SecuredRequestHandler]].
+ */
+trait SecuredAction {
+
+ /**
+ * The instance of the secured request handler.
+ */
+ val requestHandler: SecuredRequestHandler
+
+ /**
+ * The default body parser.
+ */
+ val bodyParser: BodyParsers.Default
+
+ /**
+ * Applies the environment to the action stack.
+ *
+ * @param environment The environment instance to handle the request.
+ * @tparam E The type of the environment.
+ * @return A secured action builder.
+ */
+ def apply[E <: Env](environment: Environment[E]): SecuredActionBuilder[E, AnyContent]
+}
+
+/**
+ * Default implementation of the [[SecuredAction]].
+ *
+ * @param requestHandler The instance of the secured request handler.
+ * @param bodyParser The default body parser.
+ */
+class DefaultSecuredAction @Inject() (
+ val requestHandler: SecuredRequestHandler,
+ val bodyParser: BodyParsers.Default) extends SecuredAction {
+
+ /**
+ * Applies the environment to the action stack.
+ *
+ * @param environment The environment instance to handle the request.
+ * @tparam E The type of the environment.
+ * @return A secured action builder.
+ */
+ override def apply[E <: Env](environment: Environment[E]): SecuredActionBuilder[E, AnyContent] =
+ SecuredActionBuilder[E, AnyContent](requestHandler[E](environment), bodyParser)
+}
+
+/**
+ * Error handler for secured actions.
+ */
+trait SecuredErrorHandler extends NotAuthenticatedErrorHandler with NotAuthorizedErrorHandler {
+
+ /**
+ * Exception handler which chains the exceptions handlers from the sub types.
+ *
+ * @param request The request header.
+ * @return A partial function which maps an exception to a Play result.
+ */
+ override def exceptionHandler(implicit request: RequestHeader): PartialFunction[Throwable, Future[Result]] = {
+ super[NotAuthenticatedErrorHandler].exceptionHandler orElse
+ super[NotAuthorizedErrorHandler].exceptionHandler
+ }
+}
+
+/**
+ * Default implementation of the [[SecuredErrorHandler]].
+ *
+ * @param messagesApi The Play messages API.
+ */
+class DefaultSecuredErrorHandler @Inject() (val messagesApi: MessagesApi)
+ extends SecuredErrorHandler
+ with DefaultNotAuthenticatedErrorHandler
+ with DefaultNotAuthorizedErrorHandler {
+
+ /**
+ * Exception handler which chains the exceptions handlers from the sub types.
+ *
+ * @param request The request header.
+ * @return A partial function which maps an exception to a Play result.
+ */
+ override def exceptionHandler(implicit request: RequestHeader): PartialFunction[Throwable, Future[Result]] = {
+ super[DefaultNotAuthenticatedErrorHandler].exceptionHandler orElse
+ super[DefaultNotAuthorizedErrorHandler].exceptionHandler
+ }
+}
+
+/**
+ * Play module for providing the secured action components.
+ */
+class SecuredActionModule extends Module {
+ def bindings(environment: PlayEnv, configuration: Configuration) = {
+ Seq(
+ bind[SecuredAction].to[DefaultSecuredAction],
+ bind[SecuredRequestHandler].to[DefaultSecuredRequestHandler])
+ }
+}
+
+/**
+ * Play module to provide the secured error handler component.
+ *
+ * We provide an extra module so that it can be easily replaced with a custom implementation
+ * without to declare bindings for the other secured action module.
+ */
+class SecuredErrorHandlerModule extends Module {
+ def bindings(environment: PlayEnv, configuration: Configuration) = {
+ Seq(
+ bind[SecuredErrorHandler].to[DefaultSecuredErrorHandler])
+ }
+}
+
+/**
+ * Injection helper for secured action components
+ */
+trait SecuredActionComponents {
+
+ def securedErrorHandler: SecuredErrorHandler
+ def securedBodyParser: BodyParsers.Default
+
+ lazy val securedRequestHandler: SecuredRequestHandler = new DefaultSecuredRequestHandler(securedErrorHandler)
+ lazy val securedAction: SecuredAction = new DefaultSecuredAction(securedRequestHandler, securedBodyParser)
+}
+
+/**
+ * Injection helper for secured error handler component.
+ *
+ * We provide an extra component so that it can be easily replaced with a custom implementation
+ * without to declare bindings for the other secured action component.
+ */
+trait SecuredErrorHandlerComponents {
+
+ def messagesApi: MessagesApi
+
+ lazy val securedErrorHandler: SecuredErrorHandler = new DefaultSecuredErrorHandler(messagesApi)
+}
diff --git a/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala
new file mode 100644
index 00000000..0d4783d7
--- /dev/null
+++ b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala
@@ -0,0 +1,261 @@
+/**
+ * Original work: SecureSocial (https://github.com/jaliss/securesocial)
+ * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss
+ *
+ * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette)
+ * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.github.honeycombcheesecake.play.silhouette.api.actions
+
+import javax.inject.Inject
+
+import io.github.honeycombcheesecake.play.silhouette.api._
+import play.api.{ Configuration, Environment => PlayEnv }
+import play.api.inject.Module
+import play.api.mvc._
+
+import scala.annotation.nowarn
+import scala.concurrent.{ ExecutionContext, Future }
+
+/**
+ * A request that adds maybe the identity and maybe the authenticator for the current call.
+ *
+ * @tparam E The type of the environment.
+ */
+trait UserAwareRequestHeader[E <: Env] extends RequestHeader {
+ /**
+ * @return Some identity implementation if authentication was successful, None otherwise.
+ */
+ def identity: Option[I[E]]
+
+ /**
+ * @return Some authenticator implementation if authentication was successful, None otherwise.
+ */
+ def authenticator: Option[A[E]]
+}
+
+trait UserAwareRequest[E <: Env, +B] extends Request[B] with UserAwareRequestHeader[E]
+
+object UserAwareRequest {
+
+ /**
+ * A request that adds maybe the identity and maybe the authenticator for the current call.
+ *
+ * @param identity Some identity implementation if authentication was successful, None otherwise.
+ * @param authenticator Some authenticator implementation if authentication was successful, None otherwise.
+ * @param request The current request.
+ * @tparam E The type of the environment.
+ * @tparam B The type of the request body.
+ */
+ def apply[E <: Env, B](
+ identity: Option[I[E]],
+ authenticator: Option[A[E]],
+ request: Request[B]): UserAwareRequest[E, B] = {
+ new DefaultUserAwareRequest(identity, authenticator, request)
+ }
+
+ /**
+ * Unapply method for user aware request.
+ *
+ * @param userAwareRequest the user aware request.
+ * @tparam E The type of the environment.
+ * @tparam B The type of the request body.
+ */
+ @nowarn
+ def unapply[E <: Env, B](userAwareRequest: UserAwareRequest[E, B]): Option[(Option[I[E]], Option[A[E]], Request[B])] = {
+ userAwareRequest match {
+ case duar: DefaultUserAwareRequest[E, B] =>
+ Some((duar.identity, duar.authenticator, duar.request))
+ case uar: UserAwareRequest[E, B] =>
+ Some((uar.identity, uar.authenticator, uar))
+ }
+ }
+}
+
+class DefaultUserAwareRequest[E <: Env, B](
+ val identity: Option[I[E]],
+ val authenticator: Option[A[E]],
+ val request: Request[B]) extends WrappedRequest(request) with UserAwareRequest[E, B]
+
+/**
+ * Request handler builder implementation to provide the foundation for user-aware request handlers.
+ *
+ * @param environment The environment instance to handle the request.
+ * @tparam E The type of the environment.
+ */
+final case class UserAwareRequestHandlerBuilder[E <: Env](environment: Environment[E])
+ extends RequestHandlerBuilder[E, ({ type R[B] = UserAwareRequest[E, B] })#R] {
+
+ /**
+ * Invokes the block.
+ *
+ * @param block The block of code to invoke.
+ * @param request The current request.
+ * @tparam B The type of the request body.
+ * @tparam T The type of the data included in the handler result.
+ * @return A handler result.
+ */
+ override def invokeBlock[B, T](block: UserAwareRequest[E, B] => Future[HandlerResult[T]])(implicit request: Request[B]): Future[HandlerResult[T]] = {
+ handleAuthentication.flatMap {
+ // A valid authenticator was found and the identity may be exists
+ case (Some(authenticator), identity) if authenticator.extract.isValid =>
+ handleBlock(authenticator, a => block(UserAwareRequest(identity, Some(a), request)))
+ // An invalid authenticator was found. The authenticator will be discarded
+ case (Some(authenticator), _) if !authenticator.extract.isValid =>
+ block(UserAwareRequest(None, None, request)).flatMap {
+ case hr @ HandlerResult(pr, _) =>
+ environment.authenticatorService.discard(authenticator.extract, pr).map(r => hr.copy(r))
+ }
+ // No authenticator and no user was found
+ case _ => block(UserAwareRequest(None, None, request))
+ }
+ }
+}
+
+/**
+ * A user-aware request handler.
+ *
+ * A handler that can be used for endpoints that need to know if there is a current user but
+ * can be executed even if there isn't one.
+ */
+trait UserAwareRequestHandler {
+
+ /**
+ * Applies the environment to the request handler stack.
+ *
+ * @param environment The environment instance to handle the request.
+ * @tparam E The type of the environment.
+ * @return A user-aware request handler builder.
+ */
+ def apply[E <: Env](environment: Environment[E]): UserAwareRequestHandlerBuilder[E]
+}
+
+/**
+ * Default implementation of the [[UserAwareRequestHandler]].
+ */
+class DefaultUserAwareRequestHandler extends UserAwareRequestHandler {
+
+ /**
+ * Applies the environment to the request handler stack.
+ *
+ * @param environment The environment instance to handle the request.
+ * @tparam E The type of the environment.
+ * @return A user-aware request handler builder.
+ */
+ override def apply[E <: Env](environment: Environment[E]): UserAwareRequestHandlerBuilder[E] = UserAwareRequestHandlerBuilder[E](environment)
+}
+
+/**
+ * Action builder implementation to provide the foundation for user-aware actions.
+ *
+ * @param requestHandler The request handler instance.
+ * @param parser The body parser.
+ * @tparam E The type of the environment.
+ * @tparam P The type of the request body.
+ */
+final case class UserAwareActionBuilder[E <: Env, P](
+ requestHandler: UserAwareRequestHandlerBuilder[E],
+ parser: BodyParser[P]) extends ActionBuilder[({ type R[B] = UserAwareRequest[E, B] })#R, P] {
+
+ /**
+ * Invokes the block.
+ *
+ * @param request The current request.
+ * @param block The block of code to invoke.
+ * @tparam B The type of the request body.
+ * @return The result to send to the client.
+ */
+ override def invokeBlock[B](request: Request[B], block: UserAwareRequest[E, B] => Future[Result]) = {
+ implicit val ec: ExecutionContext = executionContext
+ requestHandler(request) { req =>
+ block(req).map(r => HandlerResult(r))
+ }.map(_.result)
+ }
+
+ /**
+ * Get the execution context to run the request in.
+ *
+ * @return The execution context.
+ */
+ override protected def executionContext: ExecutionContext = requestHandler.executionContext
+}
+
+/**
+ * An action based on the [[UserAwareRequestHandler]].
+ */
+trait UserAwareAction {
+
+ /**
+ * The instance of the user-aware request handler.
+ */
+ val requestHandler: UserAwareRequestHandler
+
+ /**
+ * The default body parser.
+ */
+ val bodyParser: BodyParsers.Default
+
+ /**
+ * Applies the environment to the action stack.
+ *
+ * @param environment The environment instance to handle the request.
+ * @tparam E The type of the environment.
+ * @return A user-aware action builder.
+ */
+ def apply[E <: Env](environment: Environment[E]): UserAwareActionBuilder[E, AnyContent]
+}
+
+/**
+ * Default implementation of the [[UserAwareAction]].
+ *
+ * @param requestHandler The instance of the user-aware request handler.
+ * @param bodyParser The default body parser.
+ */
+class DefaultUserAwareAction @Inject() (
+ val requestHandler: UserAwareRequestHandler,
+ val bodyParser: BodyParsers.Default) extends UserAwareAction {
+
+ /**
+ * Applies the environment to the action stack.
+ *
+ * @param environment The environment instance to handle the request.
+ * @tparam E The type of the environment.
+ * @return A user-aware action builder.
+ */
+ override def apply[E <: Env](environment: Environment[E]): UserAwareActionBuilder[E, AnyContent] =
+ UserAwareActionBuilder[E, AnyContent](requestHandler[E](environment), bodyParser)
+}
+
+/**
+ * Play module for providing the user-aware action components.
+ */
+class UserAwareActionModule extends Module {
+ def bindings(environment: PlayEnv, configuration: Configuration) = {
+ Seq(
+ bind[UserAwareAction].to[DefaultUserAwareAction],
+ bind[UserAwareRequestHandler].to[DefaultUserAwareRequestHandler])
+ }
+}
+
+/**
+ * Injection helper for user-aware action components
+ */
+trait UserAwareActionComponents {
+
+ def userAwareBodyParser: BodyParsers.Default
+
+ lazy val userAwareRequestHandler: UserAwareRequestHandler = new DefaultUserAwareRequestHandler()
+ lazy val userAwareAction: UserAwareAction = new DefaultUserAwareAction(userAwareRequestHandler, userAwareBodyParser)
+}
diff --git a/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala
new file mode 100644
index 00000000..8ab07dcb
--- /dev/null
+++ b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala
@@ -0,0 +1,226 @@
+/**
+ * Original work: SecureSocial (https://github.com/jaliss/securesocial)
+ * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss
+ *
+ * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette)
+ * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.github.honeycombcheesecake.play.silhouette.api.services
+
+import io.github.honeycombcheesecake.play.silhouette.api.util.{ ExtractableRequest, ExecutionContextProvider }
+import io.github.honeycombcheesecake.play.silhouette.api.{ Authenticator, LoginInfo, Value }
+import play.api.http.HttpEntity
+import play.api.libs.typedmap.TypedMap
+import play.api.mvc._
+
+import scala.concurrent.Future
+
+/**
+ * A marker result which indicates that an operation on an authenticator was processed and
+ * therefore it shouldn't updated automatically.
+ *
+ * Due the fact that the update method gets called on every subsequent request to update the
+ * authenticator related data in the backing store and in the result, it isn't possible to
+ * discard or renew the authenticator simultaneously. This is because the "update" method would
+ * override the result created by the "renew" or "discard" method, because it will be executed
+ * as last in the chain.
+ *
+ * As example:
+ * If we discard the session in a Silhouette action then it will be removed from session. But
+ * at the end the update method will embed the session again, because it gets called with the
+ * result of the action.
+ *
+ * @param result The result to wrap.
+ */
+class AuthenticatorResult(result: Result)
+ extends Result(result.header, result.body, result.newSession, result.newFlash, result.newCookies, result.attrs) {
+
+ /**
+ * Creates a new copy of a `AuthenticatorResult`.
+ *
+ * @param header The response header, which contains status code and HTTP headers.
+ * @param body The response body.
+ * @param newSession A new session.
+ * @param newFlash A new flash.
+ * @param newCookies Some new cookies.
+ * @return A copy of a `AuthenticatorResult`.
+ */
+ override def copy(
+ header: ResponseHeader,
+ body: HttpEntity,
+ newSession: Option[Session],
+ newFlash: Option[Flash],
+ newCookies: Seq[Cookie],
+ attrs: TypedMap) = {
+ AuthenticatorResult(super.copy(header, body, newSession, newFlash, newCookies, attrs))
+ }
+}
+
+/**
+ * The companion object.
+ */
+object AuthenticatorResult {
+
+ /**
+ * Instantiates a new authenticator result.
+ *
+ * @param result The result to wrap.
+ * @return An authenticator result.
+ */
+ def apply(result: Result) = new AuthenticatorResult(result)
+}
+
+/**
+ * Handles authenticators for the Silhouette module.
+ *
+ * @tparam T The type of the authenticator this service is responsible for.
+ */
+trait AuthenticatorService[T <: Authenticator] extends ExecutionContextProvider {
+
+ /**
+ * Creates a new authenticator for the specified login info.
+ *
+ * @param loginInfo The login info for which the authenticator should be created.
+ * @param request The request header.
+ * @return An authenticator.
+ */
+ def create(loginInfo: LoginInfo)(implicit request: RequestHeader): Future[T]
+
+ /**
+ * Retrieves the authenticator from request.
+ *
+ * @param request The request to retrieve the authenticator from.
+ * @tparam B The type of the request body.
+ * @return Some authenticator or None if no authenticator could be found in request.
+ */
+ def retrieve[B](implicit request: ExtractableRequest[B]): Future[Option[T]]
+
+ /**
+ * Initializes an authenticator and instead of embedding into the the request or result, it returns
+ * the serialized value.
+ *
+ * @param authenticator The authenticator instance.
+ * @param request The request header.
+ * @return The serialized authenticator value.
+ */
+ def init(authenticator: T)(implicit request: RequestHeader): Future[Value[T]]
+
+ /**
+ * Embeds authenticator specific artifacts into the response.
+ *
+ * @param value The authenticator value to embed.
+ * @param result The result to manipulate.
+ * @param request The request header.
+ * @return The manipulated result.
+ */
+ def embed(value: Value[T], result: Result)(implicit request: RequestHeader): Future[AuthenticatorResult]
+
+ /**
+ * Embeds authenticator specific artifacts into the request.
+ *
+ * This method can be used to embed an authenticator in a existing request. This can be useful
+ * in Play filters. So before executing a SecuredAction we can embed the authenticator in
+ * the request to lead the action to believe that the request is a new request which contains
+ * a valid authenticator.
+ *
+ * If an existing authenticator exists, then it will be overridden.
+ *
+ * @param value The authenticator value to embed.
+ * @param request The request header.
+ * @return The manipulated request header.
+ */
+ def embed(value: Value[T], request: RequestHeader): RequestHeader
+
+ /**
+ * Touches an authenticator.
+ *
+ * An authenticator can use sliding window expiration. This means that the authenticator times
+ * out after a certain time if it wasn't used. So to mark an authenticator as used it will be
+ * touched on every request to a Silhouette action. If an authenticator should not be touched
+ * because of the fact that sliding window expiration is disabled, then it should be returned
+ * on the right, otherwise it should be returned on the left. An untouched authenticator needn't
+ * be updated later by the [[update]] method.
+ *
+ * @param authenticator The authenticator to touch.
+ * @return The touched authenticator on the left or the untouched authenticator on the right.
+ */
+ def touch(authenticator: T): Either[T, T]
+
+ /**
+ * Updates a touched authenticator.
+ *
+ * If the authenticator was updated, then the updated artifacts should be embedded into the response.
+ * This method gets called on every subsequent request if an identity accesses a Silhouette action,
+ * expect the authenticator was not touched.
+ *
+ * @param authenticator The authenticator to update.
+ * @param result The result to manipulate.
+ * @param request The request header.
+ * @return The original or a manipulated result.
+ */
+ def update(authenticator: T, result: Result)(implicit request: RequestHeader): Future[AuthenticatorResult]
+
+ /**
+ * Renews the expiration of an authenticator without embedding it into the result.
+ *
+ * Based on the implementation, the renew method should revoke the given authenticator first, before
+ * creating a new one. If the authenticator was updated, then the updated artifacts should be returned.
+ *
+ * @param authenticator The authenticator to renew.
+ * @param request The request header.
+ * @return The serialized expression of the authenticator.
+ */
+ def renew(authenticator: T)(implicit request: RequestHeader): Future[Value[T]]
+
+ /**
+ * Renews the expiration of an authenticator.
+ *
+ * Based on the implementation, the renew method should revoke the given authenticator first, before
+ * creating a new one. If the authenticator was updated, then the updated artifacts should be embedded
+ * into the response.
+ *
+ * @param authenticator The authenticator to renew.
+ * @param result The result to manipulate.
+ * @param request The request header.
+ * @return The original or a manipulated result.
+ */
+ def renew(authenticator: T, result: Result)(implicit request: RequestHeader): Future[AuthenticatorResult]
+
+ /**
+ * Manipulates the response and removes authenticator specific artifacts before sending it to the client.
+ *
+ * @param authenticator The authenticator instance.
+ * @param result The result to manipulate.
+ * @param request The request header.
+ * @return The manipulated result.
+ */
+ def discard(authenticator: T, result: Result)(implicit request: RequestHeader): Future[AuthenticatorResult]
+}
+
+/**
+ * The companion object.
+ */
+object AuthenticatorService {
+
+ /**
+ * The error messages.
+ */
+ val CreateError = "[Silhouette][%s] Could not create authenticator for login info: %s"
+ val RetrieveError = "[Silhouette][%s] Could not retrieve authenticator"
+ val InitError = "[Silhouette][%s] Could not init authenticator: %s"
+ val UpdateError = "[Silhouette][%s] Could not update authenticator: %s"
+ val RenewError = "[Silhouette][%s] Could not renew authenticator: %s"
+ val DiscardError = "[Silhouette][%s] Could not discard authenticator: %s"
+}
diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala
index e3786b53..a3effdd0 100644
--- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala
+++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala
@@ -30,10 +30,12 @@ import io.github.honeycombcheesecake.play.silhouette.api.util._
import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.CookieAuthenticatorService._
import play.api.libs.json.{ Json, OFormat }
import play.api.mvc._
+import play.api.mvc.{ Cookie, CookieHeaderEncoding }
import play.api.mvc.request.{ Cell, RequestAttrKey }
import java.time.ZonedDateTime
import scala.concurrent.duration._
+import scala.concurrent.duration.FiniteDuration
import scala.concurrent.{ ExecutionContext, Future }
import scala.util.{ Failure, Success, Try }
@@ -280,7 +282,7 @@ class CookieAuthenticatorService(
val combinedCookies = filteredCookies :+ cookie
val cookies = Cookies(combinedCookies)
- request.withAttrs(request.attrs.updated(RequestAttrKey.Cookies.bindValue(Cell(cookies))))
+ request.withAttrs(request.attrs.updated(RequestAttrKey.Cookies, Cell(cookies)))
}
/**
diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala
index 7112179d..92cd5084 100644
--- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala
+++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala
@@ -25,10 +25,12 @@ import io.github.honeycombcheesecake.play.silhouette.api.{ Authenticator, Expira
import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.SessionAuthenticatorService._
import play.api.libs.json.{ Json, OFormat }
import play.api.mvc._
+import play.api.mvc.SessionCookieBaker
import play.api.mvc.request.{ Cell, RequestAttrKey }
import java.time.ZonedDateTime
import scala.concurrent.duration._
+import scala.concurrent.duration.FiniteDuration
import scala.concurrent.{ ExecutionContext, Future }
import scala.util.{ Failure, Success, Try }
@@ -214,7 +216,7 @@ class SessionAuthenticatorService(
case None => session
}
- request.withAttrs(request.attrs.updated(RequestAttrKey.Session.bindValue(Cell(s))))
+ request.withAttrs(request.attrs.updated(RequestAttrKey.Session, Cell(s)))
}
/**
diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2Provider.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2Provider.scala
index 92b54e6a..863abc40 100644
--- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2Provider.scala
+++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2Provider.scala
@@ -29,6 +29,7 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.OAuth2Provid
import io.github.honeycombcheesecake.play.silhouette.impl.providers.state.UserStateItemHandler
import play.api.libs.functional.syntax._
import play.api.libs.json._
+import play.api.libs.ws.DefaultBodyWritables.writeableOf_urlEncodedForm
import play.api.libs.ws.WSResponse
import play.api.mvc._
diff --git a/silhouette/test/Helpers.scala b/silhouette/test/Helpers.scala
index 0684f6f6..da261fd7 100644
--- a/silhouette/test/Helpers.scala
+++ b/silhouette/test/Helpers.scala
@@ -19,8 +19,6 @@ import io.github.honeycombcheesecake.play.silhouette.api.AuthInfo
import io.github.honeycombcheesecake.play.silhouette.impl.providers.{ SocialProfile, SocialStateItem, StatefulAuthInfo }
import org.specs2.execute.{ AsResult, Result => Specs2Result }
import org.specs2.matcher.{ JsonMatchers, MatchResult }
-import org.specs2.mock.Mockito
-import org.specs2.mutable.Around
import play.api.libs.json.{ JsValue, Json }
import play.api.mvc.{ Result => PlayResult }
import play.api.test.PlaySpecification
@@ -28,42 +26,12 @@ import play.api.test.PlaySpecification
import scala.concurrent.Future
import scala.io.{ Codec, Source }
import scala.reflect.ClassTag
-
-/**
- * Executes a before method in the context of the around method.
- */
-trait BeforeWithinAround extends Around {
- def before: Any
- abstract override def around[T: AsResult](t: => T): Specs2Result = super.around {
- before; t
- }
-}
-
-/**
- * Executes an after method in the context of the around method.
- */
-trait AfterWithinAround extends Around {
- def after: Any
- abstract override def around[T: AsResult](t: => T): Specs2Result = super.around {
- try { t } finally { after }
- }
-}
-
-/**
- * Executes before and after methods in the context of the around method.
- */
-trait BeforeAfterWithinAround extends Around {
- def before: Any
- def after: Any
- abstract override def around[T: AsResult](t: => T): Specs2Result = super.around {
- try { before; t } finally { after }
- }
-}
+import org.mockito.Mockito
/**
* Base test case for the social providers.
*/
-trait SocialProviderSpec[A <: AuthInfo] extends PlaySpecification with Mockito with JsonMatchers {
+trait SocialProviderSpec[A <: AuthInfo] extends PlaySpecification with JsonMatchers {
/**
* Applies a matcher on a simple result.
@@ -121,7 +89,7 @@ trait SocialProviderSpec[A <: AuthInfo] extends PlaySpecification with Mockito w
lazy val result = await(providerResult.failed)
- result must not[Any](throwAn[E])
+ result must not[Throwable](throwAn[E])
result.rethrow must throwAn[E].like(f)
}
}
@@ -177,4 +145,15 @@ object Helper {
case None => throw new Exception("Cannot load file: " + file)
}
}
+
+ /**
+ * Mock related helpers
+ */
+
+ def mock[A](implicit a: ClassTag[A]): A =
+ Mockito.mock(a.runtimeClass).asInstanceOf[A]
+
+ def mockSmart[A](implicit a: ClassTag[A]): A =
+ Mockito.mock(a.runtimeClass, Mockito.withSettings().defaultAnswer(Mockito.RETURNS_SMART_NULLS)).asInstanceOf[A]
+
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala
index 4b577b95..f90b48d1 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala
@@ -30,125 +30,173 @@ class ErrorHandlerSpec extends PlaySpecification {
"The `DefaultNotAuthenticatedErrorHandler.notAuthenticated` method" should {
"return an HTML response for an HTML request" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = Some(HTML),
- expectedStatus = UNAUTHORIZED,
- expectedContentType = HTML,
- expectedResponseFragment = "",
- expectedMessage = "silhouette.not.authenticated",
- f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = Some(HTML),
+ expectedStatus = UNAUTHORIZED,
+ expectedContentType = HTML,
+ expectedResponseFragment = "",
+ expectedMessage = "silhouette.not.authenticated",
+ f = {
+ notAuthenticated.onNotAuthenticated(_: RequestHeader)
+ })
+ }
}
"return a JSON response for a JSON request" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = Some(JSON),
- expectedStatus = UNAUTHORIZED,
- expectedContentType = JSON,
- expectedResponseFragment = "\"success\":false",
- expectedMessage = "silhouette.not.authenticated",
- f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = Some(JSON),
+ expectedStatus = UNAUTHORIZED,
+ expectedContentType = JSON,
+ expectedResponseFragment = "\"success\":false",
+ expectedMessage = "silhouette.not.authenticated",
+ f = {
+ notAuthenticated.onNotAuthenticated(_: RequestHeader)
+ })
+ }
}
"return a XML response for a XML request" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = Some(XML),
- expectedStatus = UNAUTHORIZED,
- expectedContentType = XML,
- expectedResponseFragment = "false",
- expectedMessage = "silhouette.not.authenticated",
- f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = Some(XML),
+ expectedStatus = UNAUTHORIZED,
+ expectedContentType = XML,
+ expectedResponseFragment = "false",
+ expectedMessage = "silhouette.not.authenticated",
+ f = {
+ notAuthenticated.onNotAuthenticated(_: RequestHeader)
+ })
+ }
}
"return a plain text response for a plain text request" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = Some(TEXT),
- expectedStatus = UNAUTHORIZED,
- expectedContentType = TEXT,
- expectedResponseFragment = messagesApi("silhouette.not.authenticated"),
- expectedMessage = "silhouette.not.authenticated",
- f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = Some(TEXT),
+ expectedStatus = UNAUTHORIZED,
+ expectedContentType = TEXT,
+ expectedResponseFragment = messagesApi("silhouette.not.authenticated"),
+ expectedMessage = "silhouette.not.authenticated",
+ f = {
+ notAuthenticated.onNotAuthenticated(_: RequestHeader)
+ })
+ }
}
"return a plain text response for other requests" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = Some(BINARY),
- expectedStatus = UNAUTHORIZED,
- expectedContentType = TEXT,
- expectedResponseFragment = messagesApi("silhouette.not.authenticated"),
- expectedMessage = "silhouette.not.authenticated",
- f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = Some(BINARY),
+ expectedStatus = UNAUTHORIZED,
+ expectedContentType = TEXT,
+ expectedResponseFragment = messagesApi("silhouette.not.authenticated"),
+ expectedMessage = "silhouette.not.authenticated",
+ f = {
+ notAuthenticated.onNotAuthenticated(_: RequestHeader)
+ })
+ }
}
"return an HTML response for a request without an Accept header" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = None,
- expectedStatus = UNAUTHORIZED,
- expectedContentType = HTML,
- expectedResponseFragment = messagesApi("silhouette.not.authenticated"),
- expectedMessage = "silhouette.not.authenticated",
- f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = None,
+ expectedStatus = UNAUTHORIZED,
+ expectedContentType = HTML,
+ expectedResponseFragment = messagesApi("silhouette.not.authenticated"),
+ expectedMessage = "silhouette.not.authenticated",
+ f = {
+ notAuthenticated.onNotAuthenticated(_: RequestHeader)
+ })
+ }
}
}
"The `DefaultNotAuthorizedErrorHandler.onNotAuthorized` method" should {
"return an HTML response for an HTML request" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = Some(HTML),
- expectedStatus = FORBIDDEN,
- expectedContentType = HTML,
- expectedResponseFragment = "",
- expectedMessage = "silhouette.not.authorized",
- f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = Some(HTML),
+ expectedStatus = FORBIDDEN,
+ expectedContentType = HTML,
+ expectedResponseFragment = "",
+ expectedMessage = "silhouette.not.authorized",
+ f = {
+ notAuthorized.onNotAuthorized(_: RequestHeader)
+ })
+ }
}
"return a JSON response for a JSON request" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = Some(JSON),
- expectedStatus = FORBIDDEN,
- expectedContentType = JSON,
- expectedResponseFragment = "\"success\":false",
- expectedMessage = "silhouette.not.authorized",
- f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = Some(JSON),
+ expectedStatus = FORBIDDEN,
+ expectedContentType = JSON,
+ expectedResponseFragment = "\"success\":false",
+ expectedMessage = "silhouette.not.authorized",
+ f = {
+ notAuthorized.onNotAuthorized(_: RequestHeader)
+ })
+ }
}
"return a XML response for a XML request" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = Some(XML),
- expectedStatus = FORBIDDEN,
- expectedContentType = XML,
- expectedResponseFragment = "false",
- expectedMessage = "silhouette.not.authorized",
- f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = Some(XML),
+ expectedStatus = FORBIDDEN,
+ expectedContentType = XML,
+ expectedResponseFragment = "false",
+ expectedMessage = "silhouette.not.authorized",
+ f = {
+ notAuthorized.onNotAuthorized(_: RequestHeader)
+ })
+ }
}
"return a plain text response for a plain text request" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = Some(TEXT),
- expectedStatus = FORBIDDEN,
- expectedContentType = TEXT,
- expectedResponseFragment = messagesApi("silhouette.not.authorized"),
- expectedMessage = "silhouette.not.authorized",
- f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = Some(TEXT),
+ expectedStatus = FORBIDDEN,
+ expectedContentType = TEXT,
+ expectedResponseFragment = messagesApi("silhouette.not.authorized"),
+ expectedMessage = "silhouette.not.authorized",
+ f = {
+ notAuthorized.onNotAuthorized(_: RequestHeader)
+ })
+ }
}
"return a plain text response for other requests" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = Some(BINARY),
- expectedStatus = FORBIDDEN,
- expectedContentType = TEXT,
- expectedResponseFragment = messagesApi("silhouette.not.authorized"),
- expectedMessage = "silhouette.not.authorized",
- f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = Some(BINARY),
+ expectedStatus = FORBIDDEN,
+ expectedContentType = TEXT,
+ expectedResponseFragment = messagesApi("silhouette.not.authorized"),
+ expectedMessage = "silhouette.not.authorized",
+ f = {
+ notAuthorized.onNotAuthorized(_: RequestHeader)
+ })
+ }
}
"return an HTML response for a request without an Accept header" in new WithApplication with Context {
- testResponse(
- acceptedMediaType = None,
- expectedStatus = FORBIDDEN,
- expectedContentType = HTML,
- expectedResponseFragment = messagesApi("silhouette.not.authorized"),
- expectedMessage = "silhouette.not.authorized",
- f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) })
+ override def running() = {
+ testResponse(
+ acceptedMediaType = None,
+ expectedStatus = FORBIDDEN,
+ expectedContentType = HTML,
+ expectedResponseFragment = messagesApi("silhouette.not.authorized"),
+ expectedMessage = "silhouette.not.authorized",
+ f = {
+ notAuthorized.onNotAuthorized(_: RequestHeader)
+ })
+ }
}
}
@@ -193,7 +241,7 @@ class ErrorHandlerSpec extends PlaySpecification {
expectedMessage: String,
f: RequestHeader => Future[Result]) = {
implicit val request = acceptedMediaType match {
- case Some(mediaType) => FakeRequest().withHeaders(ACCEPT -> mediaType)
+ case Some(mediaType) => FakeRequest().withHeaders((ACCEPT, mediaType))
case None => FakeRequest()
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala
index e7109774..a5e244e3 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala
@@ -17,7 +17,6 @@ package io.github.honeycombcheesecake.play.silhouette.api
import akka.actor.{ Actor, ActorSystem, Props }
import akka.testkit.TestProbe
-import org.specs2.control.NoLanguageFeatures
import org.specs2.specification.Scope
import play.api.test.{ FakeRequest, PlaySpecification, WithApplication }
@@ -27,106 +26,120 @@ import scala.language.postfixOps
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.api.EventBus]] class.
*/
-class EventBusSpec extends PlaySpecification with NoLanguageFeatures {
+class EventBusSpec extends PlaySpecification {
"The event bus" should {
"handle an subclass event" in new WithApplication with Context {
- val eventBus = new EventBus
- val listener = system.actorOf(Props(new Actor {
- def receive = {
- case e => theProbe.ref ! e
- }
- }))
-
- eventBus.subscribe(listener, classOf[SilhouetteEvent])
-
- eventBus.publish(loginEvent)
- theProbe.expectMsg(500 millis, loginEvent)
-
- eventBus.publish(logoutEvent)
- theProbe.expectMsg(500 millis, logoutEvent)
+ override def running() = {
+ val eventBus = new EventBus
+ val listener = system.actorOf(Props(new Actor {
+ def receive = {
+ case e => theProbe.ref ! e
+ }
+ }))
+
+ eventBus.subscribe(listener, classOf[SilhouetteEvent])
+
+ eventBus.publish(loginEvent)
+ theProbe.expectMsg(500 millis, loginEvent)
+
+ eventBus.publish(logoutEvent)
+ theProbe.expectMsg(500 millis, logoutEvent)
+ }
}
"handle an event" in new WithApplication with Context {
- val eventBus = new EventBus
- val listener = system.actorOf(Props(new Actor {
- def receive = {
- case e @ LoginEvent(_, _) => theProbe.ref ! e
- }
- }))
-
- eventBus.subscribe(listener, classOf[LoginEvent[TestIdentity]])
- eventBus.publish(loginEvent)
-
- theProbe.expectMsg(500 millis, loginEvent)
+ override def running() = {
+ val eventBus = new EventBus
+ val listener = system.actorOf(Props(new Actor {
+ def receive = {
+ case e@LoginEvent(_, _) => theProbe.ref ! e
+ }
+ }))
+
+ eventBus.subscribe(listener, classOf[LoginEvent[TestIdentity]])
+ eventBus.publish(loginEvent)
+
+ theProbe.expectMsg(500 millis, loginEvent)
+ }
}
"handle multiple events" in new WithApplication with Context {
- val eventBus = new EventBus
- val listener = system.actorOf(Props(new Actor {
- def receive = {
- case e @ LoginEvent(_, _) => theProbe.ref ! e
- case e @ LogoutEvent(_, _) => theProbe.ref ! e
- }
- }))
-
- eventBus.subscribe(listener, classOf[LoginEvent[TestIdentity]])
- eventBus.subscribe(listener, classOf[LogoutEvent[TestIdentity]])
- eventBus.publish(loginEvent)
- eventBus.publish(logoutEvent)
-
- theProbe.expectMsg(500 millis, loginEvent)
- theProbe.expectMsg(500 millis, logoutEvent)
+ override def running() = {
+ val eventBus = new EventBus
+ val listener = system.actorOf(Props(new Actor {
+ def receive = {
+ case e@LoginEvent(_, _) => theProbe.ref ! e
+ case e@LogoutEvent(_, _) => theProbe.ref ! e
+ }
+ }))
+
+ eventBus.subscribe(listener, classOf[LoginEvent[TestIdentity]])
+ eventBus.subscribe(listener, classOf[LogoutEvent[TestIdentity]])
+ eventBus.publish(loginEvent)
+ eventBus.publish(logoutEvent)
+
+ theProbe.expectMsg(500 millis, loginEvent)
+ theProbe.expectMsg(500 millis, logoutEvent)
+ }
}
"differentiate between event classes" in new WithApplication with Context {
- val eventBus = new EventBus
- val listener = system.actorOf(Props(new Actor {
- def receive = {
- case e @ LoginEvent(_, _) => theProbe.ref ! e
- }
- }))
-
- eventBus.subscribe(listener, classOf[LogoutEvent[TestIdentity]])
- eventBus.publish(logoutEvent)
-
- theProbe.expectNoMessage(500 millis)
+ override def running() = {
+ val eventBus = new EventBus
+ val listener = system.actorOf(Props(new Actor {
+ def receive = {
+ case e@LoginEvent(_, _) => theProbe.ref ! e
+ }
+ }))
+
+ eventBus.subscribe(listener, classOf[LogoutEvent[TestIdentity]])
+ eventBus.publish(logoutEvent)
+
+ theProbe.expectNoMessage(500 millis)
+ }
}
"not handle not subscribed events" in new WithApplication with Context {
- val eventBus = new EventBus
- val listener = system.actorOf(Props(new Actor {
- def receive = {
- case e @ LoginEvent(_, _) => theProbe.ref ! e
- }
- }))
-
- eventBus.publish(loginEvent)
-
- theProbe.expectNoMessage(500 millis)
+ override def running() = {
+ val eventBus = new EventBus
+ val listener = system.actorOf(Props(new Actor {
+ def receive = {
+ case e@LoginEvent(_, _) => theProbe.ref ! e
+ }
+ }))
+
+ eventBus.publish(loginEvent)
+
+ theProbe.expectNoMessage(500 millis)
+ }
}
"not handle events between different event buses" in new WithApplication with Context {
- val eventBus1 = new EventBus
- val eventBus2 = new EventBus
+ override def running() = {
+ val eventBus1 = new EventBus
+ val eventBus2 = new EventBus
- val listener = system.actorOf(Props(new Actor {
- def receive = {
- case e @ LoginEvent(_, _) => theProbe.ref ! e
- }
- }))
+ val listener = system.actorOf(Props(new Actor {
+ def receive = {
+ case e@LoginEvent(_, _) => theProbe.ref ! e
+ }
+ }))
- eventBus1.subscribe(listener, classOf[LoginEvent[TestIdentity]])
- eventBus2.publish(loginEvent)
+ eventBus1.subscribe(listener, classOf[LoginEvent[TestIdentity]])
+ eventBus2.publish(loginEvent)
- theProbe.expectNoMessage(500 millis)
+ theProbe.expectNoMessage(500 millis)
+ }
}
"returns a singleton event bus" in new WithApplication with Context {
- val eventBus1 = EventBus()
- val eventBus2 = EventBus()
+ override def running() = {
+ val eventBus1 = EventBus()
+ val eventBus2 = EventBus()
- eventBus1 ==== eventBus2
+ eventBus1 ==== eventBus2
+ }
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala
index 4864c218..a55d5640 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala
@@ -15,6 +15,8 @@
*/
package io.github.honeycombcheesecake.play.silhouette.api.actions
+import com.google.inject.AbstractModule
+
import javax.inject.Inject
import akka.actor.{ Actor, ActorSystem, Props }
@@ -24,16 +26,18 @@ import io.github.honeycombcheesecake.play.silhouette.api.actions.SecuredActionSp
import io.github.honeycombcheesecake.play.silhouette.api.exceptions.{ NotAuthenticatedException, NotAuthorizedException }
import io.github.honeycombcheesecake.play.silhouette.api.services.{ AuthenticatorResult, AuthenticatorService, IdentityService }
import net.codingwell.scalaguice.ScalaModule
-import org.specs2.control.NoLanguageFeatures
import org.specs2.matcher.JsonMatchers
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.inject.bind
import play.api.inject.guice.GuiceApplicationBuilder
+import play.api.inject.guice.GuiceableModule
import play.api.libs.json.Json
import play.api.mvc.Results._
import play.api.mvc._
import play.api.test.{ FakeRequest, PlaySpecification, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
+import test.Helper.mock
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -44,403 +48,443 @@ import scala.reflect.ClassTag
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.api.actions.SecuredActionSpec]].
*/
-class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers with NoLanguageFeatures {
+class SecuredActionSpec extends PlaySpecification with JsonMatchers {
"The `SecuredAction` action" should {
"restrict access if no valid authenticator can be retrieved" in new InjectorContext {
new WithApplication(app) with Context {
- withEvent[NotAuthenticatedEvent] {
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
+ override def running() = {
+ withEvent[NotAuthenticatedEvent] {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
- val result = controller.defaultAction(request)
+ val result = controller.defaultAction(request)
- status(result) must equalTo(UNAUTHORIZED)
- contentAsString(result) must contain("global.not.authenticated")
- theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request))
+ status(result) must equalTo(UNAUTHORIZED)
+ contentAsString(result) must contain("global.not.authenticated")
+ theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request))
+ }
}
}
}
"restrict access and discard authenticator if an invalid authenticator can be retrieved" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator.copy(isValid = false)))
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator.copy(isValid = false))))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
- withEvent[NotAuthenticatedEvent] {
- val result = controller.defaultAction(request)
+ withEvent[NotAuthenticatedEvent] {
+ val result = controller.defaultAction(request)
- status(result) must equalTo(UNAUTHORIZED)
- contentAsString(result) must contain("global.not.authenticated")
- there was one(env.authenticatorService).discard(any(), any())(any())
- theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request))
+ status(result) must equalTo(UNAUTHORIZED)
+ contentAsString(result) must contain("global.not.authenticated")
+ verify(env.authenticatorService).discard(any(), any())(any())
+ theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request))
+ }
}
}
}
"restrict access and discard authenticator if no identity could be found for an authenticator" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(None)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None))
- withEvent[NotAuthenticatedEvent] {
- val result = controller.defaultAction(request)
+ withEvent[NotAuthenticatedEvent] {
+ val result = controller.defaultAction(request)
- status(result) must equalTo(UNAUTHORIZED)
- contentAsString(result) must contain("global.not.authenticated")
- there was one(env.authenticatorService).discard(any(), any())(any())
- theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request))
+ status(result) must equalTo(UNAUTHORIZED)
+ contentAsString(result) must contain("global.not.authenticated")
+ verify(env.authenticatorService).discard(any(), any())(any())
+ theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request))
+ }
}
}
}
"display local not-authenticated result if user isn't authenticated[authorization and error handler]" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(None)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None))
- val result = controller.actionWithAuthorizationAndErrorHandler(request)
+ val result = controller.actionWithAuthorizationAndErrorHandler(request)
- status(result) must equalTo(UNAUTHORIZED)
- contentAsString(result) must contain("local.not.authenticated")
+ status(result) must equalTo(UNAUTHORIZED)
+ contentAsString(result) must contain("local.not.authenticated")
+ }
}
}
"display local not-authenticated result if user isn't authenticated[error handler only]" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(None)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None))
- val result = controller.actionWithErrorHandler(request)
+ val result = controller.actionWithErrorHandler(request)
- status(result) must equalTo(UNAUTHORIZED)
- contentAsString(result) must contain("local.not.authenticated")
+ status(result) must equalTo(UNAUTHORIZED)
+ contentAsString(result) must contain("local.not.authenticated")
+ }
}
}
"display global not-authenticated result if user isn't authenticated" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(None)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None))
- val result = controller.defaultAction(request)
+ val result = controller.defaultAction(request)
- status(result) must equalTo(UNAUTHORIZED)
- contentAsString(result) must contain("global.not.authenticated")
+ status(result) must equalTo(UNAUTHORIZED)
+ contentAsString(result) must contain("global.not.authenticated")
+ }
}
}
"restrict access and update authenticator if a user is authenticated but not authorized" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
- authorization.isAuthorized(any(), any())(any()) returns Future.successful(false)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+ when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false))
- withEvent[NotAuthorizedEvent[FakeIdentity]] {
- val result = controller.actionWithAuthorization(request)
+ withEvent[NotAuthorizedEvent[FakeIdentity]] {
+ val result = controller.actionWithAuthorization(request)
- status(result) must equalTo(FORBIDDEN)
- contentAsString(result) must contain("global.not.authorized")
- there was one(env.authenticatorService).update(any(), any())(any())
- theProbe.expectMsg(500 millis, NotAuthorizedEvent(identity, request))
+ status(result) must equalTo(FORBIDDEN)
+ contentAsString(result) must contain("global.not.authorized")
+ verify(env.authenticatorService).update(any(), any())(any())
+ theProbe.expectMsg(500 millis, NotAuthorizedEvent(identity, request))
+ }
}
}
}
"display local not-authorized result if user isn't authorized" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
- authorization.isAuthorized(any(), any())(any()) returns Future.successful(false)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+ when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false))
- val result = controller.actionWithAuthorizationAndErrorHandler(request)
+ val result = controller.actionWithAuthorizationAndErrorHandler(request)
- status(result) must equalTo(FORBIDDEN)
- contentAsString(result) must contain("local.not.authorized")
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).update(any(), any())(any())
+ status(result) must equalTo(FORBIDDEN)
+ contentAsString(result) must contain("local.not.authorized")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).update(any(), any())(any())
+ }
}
}
"display global not-authorized result if user isn't authorized" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
- authorization.isAuthorized(any(), any())(any()) returns Future.successful(false)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+ when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false))
- val result = controller.actionWithAuthorization(request)
+ val result = controller.actionWithAuthorization(request)
- status(result) must equalTo(FORBIDDEN)
- contentAsString(result) must contain("global.not.authorized")
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).update(any(), any())(any())
+ status(result) must equalTo(FORBIDDEN)
+ contentAsString(result) must contain("global.not.authorized")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).update(any(), any())(any())
+ }
}
}
"invoke action without authorization if user is authenticated" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- withEvent[AuthenticatedEvent[FakeIdentity]] {
- val result = controller.defaultAction(request)
+ withEvent[AuthenticatedEvent[FakeIdentity]] {
+ val result = controller.defaultAction(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain("full.access")
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).update(any(), any())(any())
- theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("full.access")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).update(any(), any())(any())
+ theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ }
}
}
}
"invoke action with authorization if user is authenticated but not authorized" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- withEvent[AuthenticatedEvent[FakeIdentity]] {
- val result = controller.actionWithAuthorization(request)
+ withEvent[AuthenticatedEvent[FakeIdentity]] {
+ val result = controller.actionWithAuthorization(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain("full.access")
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).update(any(), any())(any())
- theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("full.access")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).update(any(), any())(any())
+ theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ }
}
}
}
"use next request provider in the chain if first isn't responsible" in new InjectorContext with WithRequestProvider {
new WithApplication(app) with Context {
- tokenRequestProvider.authenticate(any()) returns Future.successful(None)
- basicAuthRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo))
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
- env.authenticatorService.create(any())(any()) returns Future.successful(authenticator)
- env.authenticatorService.init(any())(any[RequestHeader]()) answers { p: Any =>
- Future.successful(p.asInstanceOf[FakeAuthenticator#Value])
- }
- env.authenticatorService.embed(any(), any[Result]())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(None))
+ when(basicAuthRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo)))
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
+ when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator))
+ when(env.authenticatorService.init(any())(any[RequestHeader]())).thenAnswer { p =>
+ Future.successful(p.getArgument(0).asInstanceOf[FakeAuthenticator#Value])
+ }
+ when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- withEvent[AuthenticatedEvent[FakeIdentity]] {
- val result = controller.actionWithAuthorization(request)
+ withEvent[AuthenticatedEvent[FakeIdentity]] {
+ val result = controller.actionWithAuthorization(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain("full.access")
- there was one(env.authenticatorService).create(any())(any())
- there was one(env.authenticatorService).init(any())(any())
- theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("full.access")
+ verify(env.authenticatorService).create(any())(any())
+ verify(env.authenticatorService).init(any())(any())
+ theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ }
}
}
}
"update an initialized authenticator if it was touched" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
- withEvent[AuthenticatedEvent[FakeIdentity]] {
- val result = controller.actionWithAuthorization(request)
+ withEvent[AuthenticatedEvent[FakeIdentity]] {
+ val result = controller.actionWithAuthorization(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain("full.access")
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).update(any(), any())(any())
- theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("full.access")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).update(any(), any())(any())
+ theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ }
}
}
}
"do not update an initialized authenticator if it was not touched" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Right(authenticator)
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
-
- withEvent[AuthenticatedEvent[FakeIdentity]] {
- val result = controller.actionWithAuthorization(request)
-
- status(result) must equalTo(OK)
- contentAsString(result) must contain("full.access")
- there was one(env.authenticatorService).touch(any())
- there was no(env.authenticatorService).update(any(), any())(any())
- theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Right(authenticator))
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+
+ withEvent[AuthenticatedEvent[FakeIdentity]] {
+ val result = controller.actionWithAuthorization(request)
+
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("full.access")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService, never()).update(any(), any())(any())
+ theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ }
}
}
}
"init an uninitialized authenticator" in new InjectorContext with WithRequestProvider {
new WithApplication(app) with Context {
- tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo))
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
- env.authenticatorService.create(any())(any()) returns Future.successful(authenticator)
- env.authenticatorService.init(any())(any[RequestHeader]()) answers { p: Any =>
- Future.successful(p.asInstanceOf[FakeAuthenticator#Value])
- }
- env.authenticatorService.embed(any(), any[Result]())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo)))
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
+ when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator))
+ when(env.authenticatorService.init(any())(any[RequestHeader]())).thenAnswer { p =>
+ Future.successful(p.getArgument(0).asInstanceOf[FakeAuthenticator#Value])
+ }
+ when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- withEvent[AuthenticatedEvent[FakeIdentity]] {
- val result = controller.actionWithAuthorization(request)
+ withEvent[AuthenticatedEvent[FakeIdentity]] {
+ val result = controller.actionWithAuthorization(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain("full.access")
- there was one(env.authenticatorService).create(any())(any())
- there was one(env.authenticatorService).init(any())(any())
- theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("full.access")
+ verify(env.authenticatorService).create(any())(any())
+ verify(env.authenticatorService).init(any())(any())
+ theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ }
}
}
}
"renew an initialized authenticator" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.renew(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- withEvent[AuthenticatedEvent[FakeIdentity]] {
- val result = controller.renewAction(request)
+ withEvent[AuthenticatedEvent[FakeIdentity]] {
+ val result = controller.renewAction(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain("renewed")
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).renew(any(), any())(any())
- there was no(env.authenticatorService).update(any(), any())(any())
- theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("renewed")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).renew(any(), any())(any())
+ verify(env.authenticatorService, never()).update(any(), any())(any())
+ theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ }
}
}
}
"renew an uninitialized authenticator" in new InjectorContext with WithRequestProvider {
new WithApplication(app) with Context {
- tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo))
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
- env.authenticatorService.create(any())(any()) returns Future.successful(authenticator)
- env.authenticatorService.renew(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo)))
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
+ when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator))
+ when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- withEvent[AuthenticatedEvent[FakeIdentity]] {
- val result = controller.renewAction(request)
+ withEvent[AuthenticatedEvent[FakeIdentity]] {
+ val result = controller.renewAction(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain("renewed")
- there was one(env.authenticatorService).create(any())(any())
- there was one(env.authenticatorService).renew(any(), any())(any())
- theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("renewed")
+ verify(env.authenticatorService).create(any())(any())
+ verify(env.authenticatorService).renew(any(), any())(any())
+ theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ }
}
}
}
"discard an initialized authenticator" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- withEvent[AuthenticatedEvent[FakeIdentity]] {
- val result = controller.discardAction(request)
+ withEvent[AuthenticatedEvent[FakeIdentity]] {
+ val result = controller.discardAction(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain("discarded")
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).discard(any(), any())(any())
- there was no(env.authenticatorService).update(any(), any())(any())
- theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("discarded")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).discard(any(), any())(any())
+ verify(env.authenticatorService, never()).update(any(), any())(any())
+ theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ }
}
}
}
"discard an uninitialized authenticator" in new InjectorContext with WithRequestProvider {
new WithApplication(app) with Context {
- tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo))
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
- env.authenticatorService.create(any())(any()) returns Future.successful(authenticator)
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo)))
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
+ when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- withEvent[AuthenticatedEvent[FakeIdentity]] {
- val result = controller.discardAction(request)
+ withEvent[AuthenticatedEvent[FakeIdentity]] {
+ val result = controller.discardAction(request)
- status(result) must equalTo(OK)
- there was one(env.authenticatorService).create(any())(any())
- there was one(env.authenticatorService).discard(any(), any())(any())
- theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ status(result) must equalTo(OK)
+ verify(env.authenticatorService).create(any())(any())
+ verify(env.authenticatorService).discard(any(), any())(any())
+ theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request))
+ }
}
}
}
"handle an Ajax request" in new InjectorContext {
new WithApplication(app) with Context {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders("Accept" -> "application/json")
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders("Accept" -> "application/json")
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- withEvent[AuthenticatedEvent[FakeIdentity]] {
- val result = controller.defaultAction(req)
+ withEvent[AuthenticatedEvent[FakeIdentity]] {
+ val result = controller.defaultAction(req)
- status(result) must equalTo(OK)
- contentType(result) must beSome("application/json")
- contentAsString(result) must /("result" -> "full.access")
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).update(any(), any())(any())
- theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, req))
+ status(result) must equalTo(OK)
+ contentType(result) must beSome("application/json")
+ contentAsString(result) must /("result" -> "full.access")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).update(any(), any())(any())
+ theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, req))
+ }
}
}
}
@@ -449,31 +493,35 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers
"The `SecuredRequestHandler`" should {
"return status 401 if authentication was not successful" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
- val result = controller.defaultHandler(request)
+ val result = controller.defaultHandler(request)
- status(result) must equalTo(UNAUTHORIZED)
- there was no(env.authenticatorService).touch(any())
- there was no(env.authenticatorService).update(any(), any())(any())
+ status(result) must equalTo(UNAUTHORIZED)
+ verify(env.authenticatorService, never()).touch(any())
+ verify(env.authenticatorService, never()).update(any(), any())(any())
+ }
}
}
"return the user if authentication was successful" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- val result = controller.defaultHandler(request)
+ val result = controller.defaultHandler(request)
- status(result) must equalTo(OK)
- contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "1")
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).update(any(), any())(any())
+ status(result) must equalTo(OK)
+ contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "1")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).update(any(), any())(any())
+ }
}
}
}
@@ -481,29 +529,33 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers
"The `exceptionHandler` method of the SecuredErrorHandler" should {
"translate an ForbiddenException into a 403 Forbidden result" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
- val failed = Future.failed(new NotAuthorizedException("Access denied"))
- val result = controller.recover(failed)
+ val failed = Future.failed(new NotAuthorizedException("Access denied"))
+ val result = controller.recover(failed)
- status(result) must equalTo(FORBIDDEN)
+ status(result) must equalTo(FORBIDDEN)
+ }
}
}
"translate an UnauthorizedException into a 401 Unauthorized result" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
- val failed = Future.failed(new NotAuthenticatedException("Not authenticated"))
- val result = controller.recover(failed)
+ val failed = Future.failed(new NotAuthenticatedException("Not authenticated"))
+ val result = controller.recover(failed)
- status(result) must equalTo(UNAUTHORIZED)
+ status(result) must equalTo(UNAUTHORIZED)
+ }
}
}
}
@@ -527,7 +579,7 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers
*/
lazy val authorization = {
val a = mock[Authorization[SecuredEnv#I, SecuredEnv#A]]
- a.isAuthorized(any(), any())(any()) returns Future.successful(true)
+ when(a.isAuthorized(any(), any())(any())).thenReturn(Future.successful(true))
a
}
@@ -535,14 +587,14 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers
* The guice application builder.
*/
lazy val app = new GuiceApplicationBuilder()
- .bindings(new GuiceModule)
+ .bindings(GuiceableModule.guiceable(new GuiceModule))
.overrides(bind[SecuredErrorHandler].to[GlobalSecuredErrorHandler])
.build()
/**
* The guice module.
*/
- class GuiceModule extends ScalaModule {
+ class GuiceModule extends AbstractModule with ScalaModule {
override def configure(): Unit = {
bind[Environment[SecuredEnv]].toInstance(env)
bind[Authorization[SecuredEnv#I, SecuredEnv#A]].toInstance(authorization)
@@ -755,7 +807,7 @@ object SecuredActionSpec {
*
* @return The result to send to the client.
*/
- def defaultAction = silhouette.SecuredAction { implicit request =>
+ def defaultAction = silhouette.SecuredAction { implicit request: SecuredRequest[SecuredEnv, AnyContent] =>
render {
case Accepts.Json() => Ok(Json.obj("result" -> "full.access"))
case Accepts.Html() => Ok("full.access")
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala
index 36ca80a7..89078a97 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala
@@ -15,6 +15,8 @@
*/
package io.github.honeycombcheesecake.play.silhouette.api.actions
+import com.google.inject.AbstractModule
+
import javax.inject.Inject
import akka.actor.{ Actor, ActorSystem, Props }
@@ -24,15 +26,17 @@ import io.github.honeycombcheesecake.play.silhouette.api.actions.UnsecuredAction
import io.github.honeycombcheesecake.play.silhouette.api.exceptions.NotAuthorizedException
import io.github.honeycombcheesecake.play.silhouette.api.services.{ AuthenticatorResult, AuthenticatorService, IdentityService }
import net.codingwell.scalaguice.ScalaModule
-import org.specs2.control.NoLanguageFeatures
import org.specs2.matcher.JsonMatchers
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.inject.bind
import play.api.inject.guice.GuiceApplicationBuilder
+import play.api.inject.guice.GuiceableModule
import play.api.mvc.Results._
import play.api.mvc._
import play.api.test.{ FakeRequest, PlaySpecification, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
+import test.Helper.mock
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -41,80 +45,90 @@ import scala.reflect.ClassTag
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.api.actions.UnsecuredActionSpec]].
*/
-class UnsecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers with NoLanguageFeatures {
+class UnsecuredActionSpec extends PlaySpecification with JsonMatchers {
"The `UnsecuredAction` action" should {
"grant access if no valid authenticator can be retrieved" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any) returns Future.successful(None)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None))
- val result = controller.defaultAction(request)
+ val result = controller.defaultAction(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain("full.access")
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("full.access")
+ }
}
}
"grant access and discard authenticator if an invalid authenticator can be retrieved" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any) returns Future.successful(Some(authenticator.copy(isValid = false)))
- env.authenticatorService.discard(any, any)(any) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
+ override def running() = {
+ when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator.copy(isValid = false))))
+ when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
- val result = controller.defaultAction(request)
+ val result = controller.defaultAction(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain("full.access")
- there was one(env.authenticatorService).discard(any, any)(any)
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("full.access")
+ verify(env.authenticatorService).discard(any, any)(any)
+ }
}
}
"grant access and discard authenticator if no identity could be found for an authenticator" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any) returns Future.successful(Some(authenticator))
- env.authenticatorService.discard(any, any)(any) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(None)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None))
- val result = controller.defaultAction(request)
+ val result = controller.defaultAction(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain("full.access")
- there was one(env.authenticatorService).discard(any, any)(any)
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("full.access")
+ verify(env.authenticatorService).discard(any, any)(any)
+ }
}
}
"display local not-authorized result if user is authenticated" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any) returns Left(authenticator)
- env.authenticatorService.update(any, any)(any) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any, any)(any)).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- val result = controller.actionWithErrorHandler(request)
+ val result = controller.actionWithErrorHandler(request)
- status(result) must equalTo(FORBIDDEN)
- contentAsString(result) must contain("local.not.authorized")
+ status(result) must equalTo(FORBIDDEN)
+ contentAsString(result) must contain("local.not.authorized")
+ }
}
}
"display global not-authorized result if user is authenticated" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any) returns Left(authenticator)
- env.authenticatorService.update(any, any)(any) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any, any)(any)).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- val result = controller.defaultAction(request)
+ val result = controller.defaultAction(request)
- status(result) must equalTo(FORBIDDEN)
- contentAsString(result) must contain("global.not.authorized")
+ status(result) must equalTo(FORBIDDEN)
+ contentAsString(result) must contain("global.not.authorized")
+ }
}
}
}
@@ -122,31 +136,35 @@ class UnsecuredActionSpec extends PlaySpecification with Mockito with JsonMatche
"The `UnsecuredRequestHandler`" should {
"return status 403 if user is authenticated" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any) returns Left(authenticator)
- env.authenticatorService.update(any, any)(any) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any, any)(any)).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- val result = controller.defaultHandler(request)
+ val result = controller.defaultHandler(request)
- status(result) must equalTo(FORBIDDEN)
- there was one(env.authenticatorService).touch(any)
- there was one(env.authenticatorService).update(any, any)(any)
+ status(result) must equalTo(FORBIDDEN)
+ verify(env.authenticatorService).touch(any)
+ verify(env.authenticatorService).update(any, any)(any)
+ }
}
}
"return the data if user is not authenticated" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any) returns Future.successful(None)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None))
- val result = controller.defaultHandler(request)
+ val result = controller.defaultHandler(request)
- status(result) must equalTo(OK)
- contentAsString(result) must be equalTo "data"
- there was no(env.authenticatorService).touch(any)
- there was no(env.authenticatorService).update(any, any)(any)
+ status(result) must equalTo(OK)
+ contentAsString(result) must be equalTo "data"
+ verify(env.authenticatorService, never()).touch(any)
+ verify(env.authenticatorService, never()).update(any, any)(any)
+ }
}
}
}
@@ -154,15 +172,17 @@ class UnsecuredActionSpec extends PlaySpecification with Mockito with JsonMatche
"The `exceptionHandler` method of the UnsecuredErrorHandler" should {
"translate an ForbiddenException into a 403 Forbidden result" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any) returns Future.successful(None)
- env.authenticatorService.discard(any, any)(any) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
+ override def running() = {
+ when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None))
+ when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
- val failed = Future.failed(new NotAuthorizedException("Access denied"))
- val result = controller.recover(failed)
+ val failed = Future.failed(new NotAuthorizedException("Access denied"))
+ val result = controller.recover(failed)
- status(result) must equalTo(FORBIDDEN)
+ status(result) must equalTo(FORBIDDEN)
+ }
}
}
}
@@ -185,14 +205,14 @@ class UnsecuredActionSpec extends PlaySpecification with Mockito with JsonMatche
* The guice application builder.
*/
lazy val app = new GuiceApplicationBuilder()
- .bindings(new GuiceModule)
+ .bindings(GuiceableModule.guiceable(new GuiceModule))
.overrides(bind[UnsecuredErrorHandler].to[GlobalUnsecuredErrorHandler])
.build()
/**
* The guice module.
*/
- class GuiceModule extends ScalaModule {
+ class GuiceModule extends AbstractModule with ScalaModule {
override def configure(): Unit = {
bind[Environment[UnsecuredEnv]].toInstance(env)
bind[Silhouette[UnsecuredEnv]].to[SilhouetteProvider[UnsecuredEnv]]
@@ -378,7 +398,7 @@ object UnsecuredActionSpec {
* An unsecured request handler.
*/
def defaultHandler = Action.async { implicit request =>
- silhouette.UnsecuredRequestHandler { _ =>
+ silhouette.UnsecuredRequestHandler { (_: Request[AnyContent]) =>
Future.successful(HandlerResult(Ok, Some("data")))
}.map {
case HandlerResult(r, Some(data)) => Ok(data)
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala
index 0f20705c..7740aa99 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala
@@ -15,21 +15,25 @@
*/
package io.github.honeycombcheesecake.play.silhouette.api.actions
+import com.google.inject.AbstractModule
+
import javax.inject.Inject
import io.github.honeycombcheesecake.play.silhouette.api._
import io.github.honeycombcheesecake.play.silhouette.api.actions.UserAwareActionSpec._
import io.github.honeycombcheesecake.play.silhouette.api.services.{ AuthenticatorResult, AuthenticatorService, IdentityService }
import net.codingwell.scalaguice.ScalaModule
-import org.specs2.control.NoLanguageFeatures
import org.specs2.matcher.JsonMatchers
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.i18n.{ Lang, Langs, MessagesApi }
import play.api.inject.guice.GuiceApplicationBuilder
+import play.api.inject.guice.GuiceableModule
import play.api.libs.json.Json
import play.api.mvc.{ ControllerComponents, _ }
import play.api.test.{ FakeRequest, PlaySpecification, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
+import test.Helper.mock
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -37,217 +41,241 @@ import scala.concurrent.Future
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.api.actions.UserAwareAction]].
*/
-class UserAwareActionSpec extends PlaySpecification with Mockito with JsonMatchers with NoLanguageFeatures {
+class UserAwareActionSpec extends PlaySpecification with JsonMatchers {
"The `UserAwareAction` action" should {
"invoke action without identity and authenticator if no authenticator could be found" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
- val result = controller.defaultAction(request)
+ val result = controller.defaultAction(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain(messagesApi("without.identity.and.authenticator"))
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain(messagesApi("without.identity.and.authenticator"))
+ }
}
}
"invoke action without identity and authenticator if invalid authenticator was found" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator.copy(isValid = false)))
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
- }
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator.copy(isValid = false))))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
- val result = controller.defaultAction(request)
+ val result = controller.defaultAction(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain(messagesApi("without.identity.and.authenticator"))
- there was one(env.authenticatorService).discard(any(), any())(any())
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain(messagesApi("without.identity.and.authenticator"))
+ verify(env.authenticatorService).discard(any(), any())(any())
+ }
}
}
"invoke action with valid authenticator if no identity could be found" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None))
+
+ val result = controller.defaultAction(request)
+
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain(messagesApi("without.identity.and.with.authenticator"))
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).update(any(), any())(any())
}
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(None)
-
- val result = controller.defaultAction(request)
-
- status(result) must equalTo(OK)
- contentAsString(result) must contain(messagesApi("without.identity.and.with.authenticator"))
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).update(any(), any())(any())
}
}
"invoke action with authenticator and identity" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+
+ val result = controller.defaultAction(request)
+
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain(messagesApi("with.identity.and.authenticator"))
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).update(any(), any())(any())
}
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
-
- val result = controller.defaultAction(request)
-
- status(result) must equalTo(OK)
- contentAsString(result) must contain(messagesApi("with.identity.and.authenticator"))
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).update(any(), any())(any())
}
}
"use next request provider in the chain if first isn't responsible" in new InjectorContext with WithRequestProvider {
new WithApplication(app) with Context {
- tokenRequestProvider.authenticate(any()) returns Future.successful(None)
- basicAuthRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo))
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
- env.authenticatorService.create(any())(any()) returns Future.successful(authenticator)
- env.authenticatorService.init(any())(any()) answers { p: Any => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) }
- env.authenticatorService.embed(any(), any[Result]())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
+ override def running() = {
+ when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(None))
+ when(basicAuthRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo)))
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
+ when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator))
+ when(env.authenticatorService.init(any())(any())).thenAnswer { p => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) }
+ when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+
+ val result = controller.defaultAction(request)
+
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("with.identity.and.authenticator")
+ verify(env.authenticatorService).create(any())(any())
+ verify(env.authenticatorService).init(any())(any())
}
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
-
- val result = controller.defaultAction(request)
-
- status(result) must equalTo(OK)
- contentAsString(result) must contain("with.identity.and.authenticator")
- there was one(env.authenticatorService).create(any())(any())
- there was one(env.authenticatorService).init(any())(any())
}
}
"update an initialized authenticator if it was touched" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+
+ val result = controller.defaultAction(request)
+
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("with.identity.and.authenticator")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).update(any(), any())(any())
}
-
- val result = controller.defaultAction(request)
-
- status(result) must equalTo(OK)
- contentAsString(result) must contain("with.identity.and.authenticator")
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).update(any(), any())(any())
}
}
"do not update an initialized authenticator if it was not touched" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Right(authenticator)
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Right(authenticator))
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
- val result = controller.defaultAction(request)
+ val result = controller.defaultAction(request)
- status(result) must equalTo(OK)
- contentAsString(result) must contain(messagesApi("with.identity.and.authenticator"))
- there was one(env.authenticatorService).touch(any())
- there was no(env.authenticatorService).update(any(), any())(any())
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain(messagesApi("with.identity.and.authenticator"))
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService, never()).update(any(), any())(any())
+ }
}
}
"init an uninitialized authenticator" in new InjectorContext with WithRequestProvider {
new WithApplication(app) with Context {
- tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo))
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
- env.authenticatorService.create(any())(any()) returns Future.successful(authenticator)
- env.authenticatorService.init(any())(any()) answers { p: Any => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) }
- env.authenticatorService.embed(any(), any[Result]())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
+ override def running() = {
+ when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo)))
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
+ when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator))
+ when(env.authenticatorService.init(any())(any())).thenAnswer { p => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) }
+ when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+
+ val result = controller.defaultAction(request)
+
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("with.identity.and.authenticator")
+ verify(env.authenticatorService).create(any())(any())
+ verify(env.authenticatorService).init(any())(any())
}
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
-
- val result = controller.defaultAction(request)
-
- status(result) must equalTo(OK)
- contentAsString(result) must contain("with.identity.and.authenticator")
- there was one(env.authenticatorService).create(any())(any())
- there was one(env.authenticatorService).init(any())(any())
}
}
"renew an initialized authenticator" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.renew(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+
+ val result = controller.renewAction(request)
+
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain(messagesApi("renewed"))
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).renew(any(), any())(any())
+ verify(env.authenticatorService, never()).update(any(), any())(any())
}
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
-
- val result = controller.renewAction(request)
-
- status(result) must equalTo(OK)
- contentAsString(result) must contain(messagesApi("renewed"))
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).renew(any(), any())(any())
- there was no(env.authenticatorService).update(any(), any())(any())
}
}
"renew an uninitialized authenticator" in new InjectorContext with WithRequestProvider {
new WithApplication(app) with Context {
- tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo))
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
- env.authenticatorService.create(any())(any()) returns Future.successful(authenticator)
- env.authenticatorService.renew(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
+ override def running() = {
+ when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo)))
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
+ when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator))
+ when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+
+ val result = controller.renewAction(request)
+
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain("renewed")
+ verify(env.authenticatorService).create(any())(any())
+ verify(env.authenticatorService).renew(any(), any())(any())
}
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
-
- val result = controller.renewAction(request)
-
- status(result) must equalTo(OK)
- contentAsString(result) must contain("renewed")
- there was one(env.authenticatorService).create(any())(any())
- there was one(env.authenticatorService).renew(any(), any())(any())
}
}
"discard an initialized authenticator" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+
+ val result = controller.discardAction(request)
+
+ status(result) must equalTo(OK)
+ contentAsString(result) must contain(messagesApi("discarded"))
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).discard(any(), any())(any())
+ verify(env.authenticatorService, never()).update(any(), any())(any())
}
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
-
- val result = controller.discardAction(request)
-
- status(result) must equalTo(OK)
- contentAsString(result) must contain(messagesApi("discarded"))
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).discard(any(), any())(any())
- there was no(env.authenticatorService).update(any(), any())(any())
}
}
"discard an uninitialized authenticator" in new InjectorContext with WithRequestProvider {
new WithApplication(app) with Context {
- tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo))
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
- env.authenticatorService.create(any())(any()) returns Future.successful(authenticator)
- env.authenticatorService.discard(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
+ override def running() = {
+ when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo)))
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
+ when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator))
+ when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+
+ val result = controller.discardAction(request)
+
+ status(result) must equalTo(OK)
+ verify(env.authenticatorService).create(any())(any())
+ verify(env.authenticatorService).discard(any(), any())(any())
}
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
-
- val result = controller.discardAction(request)
-
- status(result) must equalTo(OK)
- there was one(env.authenticatorService).create(any())(any())
- there was one(env.authenticatorService).discard(any(), any())(any())
}
}
}
@@ -255,31 +283,35 @@ class UserAwareActionSpec extends PlaySpecification with Mockito with JsonMatche
"The `UserAwareRequestHandler`" should {
"return status 401 if authentication was not successful" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(None)
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None))
- val result = controller.defaultHandler(request)
+ val result = controller.defaultHandler(request)
- status(result) must equalTo(UNAUTHORIZED)
- there was no(env.authenticatorService).touch(any())
- there was no(env.authenticatorService).update(any(), any())(any())
+ status(result) must equalTo(UNAUTHORIZED)
+ verify(env.authenticatorService, never()).touch(any())
+ verify(env.authenticatorService, never()).update(any(), any())(any())
+ }
}
}
"return the user if authentication was successful" in new InjectorContext {
new WithApplication(app) with Context {
- env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator))
- env.authenticatorService.touch(any()) returns Left(authenticator)
- env.authenticatorService.update(any(), any())(any()) answers { (a, m) =>
- Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result]))
+ override def running() = {
+ when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator)))
+ when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator))
+ when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m =>
+ Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result]))
+ }
+ when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity)))
+
+ val result = controller.defaultHandler(request)
+
+ status(result) must equalTo(OK)
+ contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "1")
+ verify(env.authenticatorService).touch(any())
+ verify(env.authenticatorService).update(any(), any())(any())
}
- env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity))
-
- val result = controller.defaultHandler(request)
-
- status(result) must equalTo(OK)
- contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "1")
- there was one(env.authenticatorService).touch(any())
- there was one(env.authenticatorService).update(any(), any())(any())
}
}
}
@@ -302,13 +334,13 @@ class UserAwareActionSpec extends PlaySpecification with Mockito with JsonMatche
* The guice application builder.
*/
lazy val app = new GuiceApplicationBuilder()
- .bindings(new GuiceModule)
+ .bindings(GuiceableModule.guiceable(new GuiceModule))
.build()
/**
* The guice module.
*/
- class GuiceModule extends ScalaModule {
+ class GuiceModule extends AbstractModule with ScalaModule {
override def configure(): Unit = {
bind[Silhouette[UserAwareEnv]].to[SilhouetteProvider[UserAwareEnv]]
bind[Environment[UserAwareEnv]].toInstance(env)
@@ -433,7 +465,7 @@ object UserAwareActionSpec {
*
* @return The result to send to the client.
*/
- def defaultAction = silhouette.UserAwareAction { implicit request =>
+ def defaultAction = silhouette.UserAwareAction { implicit request: UserAwareRequest[UserAwareEnv, AnyContent] =>
if (request.identity.isDefined && request.authenticator.isDefined) {
Ok("with.identity.and.authenticator")
} else if (request.authenticator.isDefined) {
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorResultSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorResultSpec.scala
index 3bed0824..b437739b 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorResultSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorResultSpec.scala
@@ -34,28 +34,34 @@ class AuthenticatorResultSpec extends PlaySpecification {
"The `withSession` method" should {
"return new a new instance of an authenticator result" in new WithApplication {
- val result = Results.Ok
- val authenticatorResult = AuthenticatorResult(result)
+ override def running() = {
+ val result = Results.Ok
+ val authenticatorResult = AuthenticatorResult(result)
- authenticatorResult.withSession("name" -> "value") must beAnInstanceOf[AuthenticatorResult]
+ authenticatorResult.withSession("name" -> "value") must beAnInstanceOf[AuthenticatorResult]
+ }
}
}
"The `withCookies` method" should {
"return new a new instance of an authenticator result" in new WithApplication {
- val result = Results.Ok
- val authenticatorResult = AuthenticatorResult(result)
+ override def running() = {
+ val result = Results.Ok
+ val authenticatorResult = AuthenticatorResult(result)
- authenticatorResult.withCookies(Cookie("name", "value")) must beAnInstanceOf[AuthenticatorResult]
+ authenticatorResult.withCookies(Cookie("name", "value")) must beAnInstanceOf[AuthenticatorResult]
+ }
}
}
"The `withHeaders` method" should {
"return new a new instance of an authenticator result" in new WithApplication {
- val result = Results.Ok
- val authenticatorResult = AuthenticatorResult(result)
+ override def running() = {
+ val result = Results.Ok
+ val authenticatorResult = AuthenticatorResult(result)
- authenticatorResult.withHeaders("name" -> "value") must beAnInstanceOf[AuthenticatorResult]
+ authenticatorResult.withHeaders("name" -> "value") must beAnInstanceOf[AuthenticatorResult]
+ }
}
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/util/PlayHTTPLayerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/util/PlayHTTPLayerSpec.scala
index bbd54a01..de9cdb76 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/util/PlayHTTPLayerSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/util/PlayHTTPLayerSpec.scala
@@ -27,13 +27,15 @@ class PlayHTTPLayerSpec extends PlaySpecification {
"The `url` method" should {
"return a new WS.WSRequest instance" in new WithApplication {
- val url = "http://silhouette.mohiva.com"
- val client = app.injector.instanceOf[WSClient]
- val httpLayer = new PlayHTTPLayer(client)
- val requestHolder = httpLayer.url(url)
+ override def running() = {
+ val url = "http://silhouette.mohiva.com"
+ val client = app.injector.instanceOf[WSClient]
+ val httpLayer = new PlayHTTPLayer(client)
+ val requestHolder = httpLayer.url(url)
- requestHolder should beAnInstanceOf[WSRequest]
- requestHolder.url must be equalTo url
+ requestHolder should beAnInstanceOf[WSRequest]
+ requestHolder.url must be equalTo url
+ }
}
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala
index 05284333..98caa376 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala
@@ -22,11 +22,12 @@ import io.github.honeycombcheesecake.play.silhouette.api.repositories.Authentica
import io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorService._
import io.github.honeycombcheesecake.play.silhouette.api.util.{ RequestPart, Clock, IDGenerator }
import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.BearerTokenAuthenticatorService._
-import org.specs2.control.NoLanguageFeatures
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.mvc.{ Results, AnyContentAsEmpty }
import play.api.test.{ FakeRequest, PlaySpecification, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
+import test.Helper.mockSmart
import java.time.ZonedDateTime
import scala.concurrent.ExecutionContext.Implicits.global
@@ -37,7 +38,7 @@ import scala.language.postfixOps
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.BearerTokenAuthenticator]].
*/
-class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with NoLanguageFeatures {
+class BearerTokenAuthenticatorSpec extends PlaySpecification {
"The `isValid` method of the authenticator" should {
"return false if the authenticator is expired" in new Context {
@@ -61,8 +62,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val id = "test-id"
- idGenerator.generate returns Future.successful(id)
- clock.now returns ZonedDateTime.now
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(ZonedDateTime.now)
await(service.create(loginInfo)).id must be equalTo id
}
@@ -71,8 +72,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val now = ZonedDateTime.now
- idGenerator.generate returns Future.successful("test-id")
- clock.now returns now
+ when(idGenerator.generate).thenReturn(Future.successful("test-id"))
+ when(clock.now).thenReturn(now)
await(service.create(loginInfo)).lastUsedDateTime must be equalTo now
}
@@ -81,8 +82,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val now = ZonedDateTime.now
- idGenerator.generate returns Future.successful("test-id")
- clock.now returns now
+ when(idGenerator.generate).thenReturn(Future.successful("test-id"))
+ when(clock.now).thenReturn(now)
await(service.create(loginInfo)).expirationDateTime must be equalTo now + 12.hours
}
@@ -92,9 +93,9 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
val sixHours = 6 hours
val now = ZonedDateTime.now
- settings.authenticatorExpiry returns sixHours
- idGenerator.generate returns Future.successful("test-id")
- clock.now returns now
+ when(settings.authenticatorExpiry).thenReturn(sixHours)
+ when(idGenerator.generate).thenReturn(Future.successful("test-id"))
+ when(clock.now).thenReturn(now)
await(service.create(loginInfo)).expirationDateTime must be equalTo now + sixHours
}
@@ -102,7 +103,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
"throws an AuthenticatorCreationException exception if an error occurred during creation" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- idGenerator.generate returns Future.failed(new Exception("Could not generate ID"))
+ when(idGenerator.generate).thenReturn(Future.failed(new Exception("Could not generate ID")))
await(service.create(loginInfo)) must throwA[AuthenticatorCreationException].like {
case e =>
@@ -121,7 +122,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
"return None if no authenticator is stored for the token located in the headers" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> authenticator.id)
- repository.find(authenticator.id) returns Future.successful(None)
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(None))
await(service.retrieve) must beNone
}
@@ -129,7 +130,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
"return authenticator if an authenticator is stored for token located in the header" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> authenticator.id)
- repository.find(authenticator.id) returns Future.successful(Some(authenticator))
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator)))
await(service.retrieve) must beSome(authenticator)
}
@@ -137,8 +138,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
"return authenticator if an authenticator is stored for the token located in the query string" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${authenticator.id}")
- settings.requestParts returns Some(Seq(RequestPart.QueryString))
- repository.find(authenticator.id) returns Future.successful(Some(authenticator))
+ when(settings.requestParts).thenReturn(Some(Seq(RequestPart.QueryString)))
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator)))
await(service.retrieve) must beSome(authenticator)
}
@@ -146,7 +147,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
"throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> authenticator.id)
- repository.find(authenticator.id) returns Future.failed(new RuntimeException("Cannot find authenticator"))
+ when(repository.find(authenticator.id)).thenReturn(Future.failed(new RuntimeException("Cannot find authenticator")))
await(service.retrieve) must throwA[AuthenticatorRetrievalException].like {
case e =>
@@ -157,17 +158,17 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
"The `init` method of the service" should {
"save the authenticator in backing store" in new Context {
- repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) }
+ when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[BearerTokenAuthenticator]) }
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val token = await(service.init(authenticator))
token must be equalTo authenticator.id
- there was one(repository).add(authenticator)
+ verify(repository).add(authenticator)
}
"throws an AuthenticatorInitializationException exception if an error occurred during initialization" in new Context {
- repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator"))
+ when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator")))
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
@@ -222,39 +223,43 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
"The `touch` method of the service" should {
"update the last used date if idle timeout is defined" in new WithApplication with Context {
- settings.authenticatorIdleTimeout returns Some(1 second)
- clock.now returns ZonedDateTime.now
-
- service.touch(authenticator) must beLeft[BearerTokenAuthenticator].like {
- case a =>
- a.lastUsedDateTime must be equalTo clock.now
+ override def running() = {
+ when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second))
+ when(clock.now).thenReturn(ZonedDateTime.now)
+
+ service.touch(authenticator) must beLeft[BearerTokenAuthenticator].like {
+ case a =>
+ a.lastUsedDateTime must be equalTo clock.now
+ }
}
}
"do not update the last used date if idle timeout is not defined" in new WithApplication with Context {
- settings.authenticatorIdleTimeout returns None
- clock.now returns ZonedDateTime.now
-
- service.touch(authenticator) must beRight[BearerTokenAuthenticator].like {
- case a =>
- a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime
+ override def running() = {
+ when(settings.authenticatorIdleTimeout).thenReturn(None)
+ when(clock.now).thenReturn(ZonedDateTime.now)
+
+ service.touch(authenticator) must beRight[BearerTokenAuthenticator].like {
+ case a =>
+ a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime
+ }
}
}
}
"The `update` method of the service" should {
"update the authenticator in backing store" in new Context {
- repository.update(any()) answers { _: Any => Future.successful(authenticator) }
+ when(repository.update(any())).thenAnswer { _ => Future.successful(authenticator) }
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
await(service.update(authenticator, Results.Ok))
- there was one(repository).update(authenticator)
+ verify(repository).update(authenticator)
}
"return the result if the authenticator could be stored in backing store" in new Context {
- repository.update(any()) answers { p: Any => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) }
+ when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[BearerTokenAuthenticator]) }
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val result = service.update(authenticator, Results.Ok)
@@ -263,7 +268,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
}
"throws an AuthenticatorUpdateException exception if an error occurred during update" in new Context {
- repository.update(any()) returns Future.failed(new Exception("Cannot store authenticator"))
+ when(repository.update(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator")))
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
@@ -280,14 +285,14 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
val now = ZonedDateTime.now
val id = "new-test-id"
- repository.remove(authenticator.id) returns Future.successful(())
- repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) }
- idGenerator.generate returns Future.successful(id)
- clock.now returns now
+ when(repository.remove(authenticator.id)).thenReturn(Future.successful(()))
+ when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[BearerTokenAuthenticator]) }
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(now)
await(service.renew(authenticator, Results.Ok))
- there was one(repository).remove(authenticator.id)
+ verify(repository).remove(authenticator.id)
}
"renew the authenticator and return the response with a new bearer token" in new Context {
@@ -295,10 +300,10 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
val now = ZonedDateTime.now
val id = "new-test-id"
- repository.remove(any()) returns Future.successful(())
- repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) }
- idGenerator.generate returns Future.successful(id)
- clock.now returns now
+ when(repository.remove(any())).thenReturn(Future.successful(()))
+ when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[BearerTokenAuthenticator]) }
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(now)
val result = service.renew(authenticator, Results.Ok)
@@ -310,10 +315,10 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
val now = ZonedDateTime.now
val id = "new-test-id"
- repository.remove(any()) returns Future.successful(())
- repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator"))
- idGenerator.generate returns Future.successful(id)
- clock.now returns now
+ when(repository.remove(any())).thenReturn(Future.successful(()))
+ when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator")))
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(now)
await(service.renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like {
case e =>
@@ -326,18 +331,18 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
"remove authenticator from backing store" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- repository.remove(authenticator.id) returns Future.successful(authenticator)
+ when(repository.remove(authenticator.id)).thenReturn(Future.unit)
await(service.discard(authenticator, Results.Ok))
- there was one(repository).remove(authenticator.id)
+ verify(repository).remove(authenticator.id)
}
"throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val okResult = Results.Ok
- repository.remove(authenticator.id) returns Future.failed(new Exception("Cannot remove authenticator"))
+ when(repository.remove(authenticator.id)).thenReturn(Future.failed(new Exception("Cannot remove authenticator")))
await(service.discard(authenticator, okResult)) must throwA[AuthenticatorDiscardingException].like {
case e =>
@@ -354,17 +359,17 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N
/**
* The repository implementation.
*/
- lazy val repository = mock[AuthenticatorRepository[BearerTokenAuthenticator]].smart
+ lazy val repository = mockSmart[AuthenticatorRepository[BearerTokenAuthenticator]]
/**
* The ID generator implementation.
*/
- lazy val idGenerator = mock[IDGenerator].smart
+ lazy val idGenerator = mockSmart[IDGenerator]
/**
* The clock implementation.
*/
- lazy val clock = mock[Clock].smart
+ lazy val clock = mockSmart[Clock]
/**
* The settings.
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala
index 91837f5e..35c01b39 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala
@@ -26,12 +26,13 @@ import io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorS
import io.github.honeycombcheesecake.play.silhouette.api.util.{ Clock, FingerprintGenerator, IDGenerator }
import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.CookieAuthenticator._
import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.CookieAuthenticatorService._
-import org.specs2.control.NoLanguageFeatures
import org.specs2.matcher.MatchResult
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.mvc.{ AnyContentAsEmpty, Cookie, DefaultCookieHeaderEncoding, Results }
import play.api.test.{ FakeRequest, PlaySpecification, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
+import test.Helper.mockSmart
import java.time.ZonedDateTime
import scala.concurrent.ExecutionContext.Implicits.global
@@ -43,7 +44,7 @@ import scala.util.{ Failure, Success }
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.CookieAuthenticator]].
*/
-class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLanguageFeatures {
+class CookieAuthenticatorSpec extends PlaySpecification {
"The `isValid` method of the authenticator" should {
"return false if the authenticator is expired" in new Context {
@@ -64,48 +65,60 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
"The `serialize` method of the authenticator" should {
"sign the cookie" in new WithApplication with Context {
- serialize(authenticator, signer, authenticatorEncoder)
+ override def running() = {
+ serialize(authenticator, signer, authenticatorEncoder)
- there was one(signer).sign(any())
+ verify(signer).sign(any())
+ }
}
"encode the cookie" in new WithApplication with Context {
- serialize(authenticator, signer, authenticatorEncoder)
+ override def running() = {
+ serialize(authenticator, signer, authenticatorEncoder)
- there was one(authenticatorEncoder).encode(any())
+ verify(authenticatorEncoder).encode(any())
+ }
}
}
"The `unserialize` method of the authenticator" should {
"throw an AuthenticatorException if the given value can't be parsed as Json" in new WithApplication with Context {
- val value = "invalid"
- val msg = Pattern.quote(InvalidJson.format(ID, value))
+ override def running() = {
+ val value = "invalid"
+ val msg = Pattern.quote(InvalidJson.format(ID, value))
- unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ }
}
"throw an AuthenticatorException if the given value is in the wrong Json format" in new WithApplication with Context {
- val value = "{}"
- val msg = "^" + Pattern.quote(InvalidJsonFormat.format(ID, "")) + ".*"
+ override def running() = {
+ val value = "{}"
+ val msg = "^" + Pattern.quote(InvalidJsonFormat.format(ID, "")) + ".*"
- unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ }
}
"throw an AuthenticatorException if the cookie signer declines the authenticator" in new WithApplication with Context {
- val value = "value"
- val msg = "^" + Pattern.quote(InvalidCookieSignature.format(ID, "")) + ".*"
+ override def running() = {
+ val value = "value"
+ val msg = "^" + Pattern.quote(InvalidCookieSignature.format(ID, "")) + ".*"
- signer.extract(any()) returns Failure(new Exception("invalid"))
+ when(signer.extract(any())).thenReturn(Failure(new Exception("invalid")))
- unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ }
}
}
"The `serialize/unserialize` method of the authenticator" should {
"serialize/unserialize an authenticator" in new WithApplication with Context {
- val value = serialize(authenticator, signer, authenticatorEncoder)
+ override def running() = {
+ val value = serialize(authenticator, signer, authenticatorEncoder)
- unserialize(value, signer, authenticatorEncoder) must beSuccessfulTry.withValue(authenticator)
+ unserialize(value, signer, authenticatorEncoder) must beSuccessfulTry.withValue(authenticator)
+ }
}
}
@@ -113,10 +126,10 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
"return a fingerprinted authenticator" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- idGenerator.generate returns Future.successful("test-id")
- clock.now returns ZonedDateTime.now
- fingerprintGenerator.generate(any()) returns "test"
- settings.useFingerprinting returns true
+ when(idGenerator.generate).thenReturn(Future.successful("test-id"))
+ when(clock.now).thenReturn(ZonedDateTime.now)
+ when(fingerprintGenerator.generate(any())).thenReturn("test")
+ when(settings.useFingerprinting).thenReturn(true)
await(service(Some(repository)).create(loginInfo)).fingerprint must beSome("test")
}
@@ -124,9 +137,9 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
"return a non fingerprinted authenticator" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- idGenerator.generate returns Future.successful("test-id")
- clock.now returns ZonedDateTime.now
- settings.useFingerprinting returns false
+ when(idGenerator.generate).thenReturn(Future.successful("test-id"))
+ when(clock.now).thenReturn(ZonedDateTime.now)
+ when(settings.useFingerprinting).thenReturn(false)
await(service(Some(repository)).create(loginInfo)).fingerprint must beNone
}
@@ -135,8 +148,8 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val id = "test-id"
- idGenerator.generate returns Future.successful(id)
- clock.now returns ZonedDateTime.now
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(ZonedDateTime.now)
await(service(Some(repository)).create(loginInfo)).id must be equalTo id
}
@@ -145,8 +158,8 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val now = ZonedDateTime.now
- idGenerator.generate returns Future.successful("test-id")
- clock.now returns now
+ when(idGenerator.generate).thenReturn(Future.successful("test-id"))
+ when(clock.now).thenReturn(now)
await(service(Some(repository)).create(loginInfo)).lastUsedDateTime must be equalTo now
}
@@ -155,8 +168,8 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val now = ZonedDateTime.now
- idGenerator.generate returns Future.successful("test-id")
- clock.now returns now
+ when(idGenerator.generate).thenReturn(Future.successful("test-id"))
+ when(clock.now).thenReturn(now)
await(service(Some(repository)).create(loginInfo)).expirationDateTime must be equalTo now + 12.hours
}
@@ -166,9 +179,9 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
val sixHours = 6 hours
val now = ZonedDateTime.now
- settings.authenticatorExpiry returns sixHours
- idGenerator.generate returns Future.successful("test-id")
- clock.now returns now
+ when(settings.authenticatorExpiry).thenReturn(sixHours)
+ when(idGenerator.generate).thenReturn(Future.successful("test-id"))
+ when(clock.now).thenReturn(now)
await(service(Some(repository)).create(loginInfo)).expirationDateTime must be equalTo now + sixHours
}
@@ -176,7 +189,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
"throws an AuthenticatorCreationException exception if an error occurred during creation" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- idGenerator.generate returns Future.failed(new Exception("Could not generate ID"))
+ when(idGenerator.generate).thenReturn(Future.failed(new Exception("Could not generate ID")))
await(service(Some(repository)).create(loginInfo)) must throwA[AuthenticatorCreationException].like {
case e =>
@@ -195,87 +208,95 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
"[stateful] return None if no authenticator for the cookie is stored in backing store" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id))
- repository.find(authenticator.id) returns Future.successful(None)
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(None))
await(service(Some(repository)).retrieve) must beNone
}
"[stateless] return None if no authenticator could be unserialized from cookie" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticatorEncoder.encode("invalid")))
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticatorEncoder.encode("invalid")))
- await(service(None).retrieve) must beNone
- there was no(repository).find(any())
+ await(service(None).retrieve) must beNone
+ verify(repository, never()).find(any())
+ }
}
"[stateful] return None if authenticator fingerprint doesn't match current fingerprint" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id))
- fingerprintGenerator.generate(any()) returns "false"
- settings.useFingerprinting returns true
- authenticator.fingerprint returns Some("test")
- repository.find(authenticator.id) returns Future.successful(Some(authenticator))
+ when(fingerprintGenerator.generate(any())).thenReturn("false")
+ when(settings.useFingerprinting).thenReturn(true)
+ when(authenticator.fingerprint).thenReturn(Some("test"))
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator)))
await(service(Some(repository)).retrieve) must beNone
}
"[stateless] return None if authenticator fingerprint doesn't match current fingerprint" in new WithApplication with Context {
- fingerprintGenerator.generate(any()) returns "false"
- settings.useFingerprinting returns true
- authenticator.fingerprint returns Some("test")
+ override def running() = {
+ when(fingerprintGenerator.generate(any())).thenReturn("false")
+ when(settings.useFingerprinting).thenReturn(true)
+ when(authenticator.fingerprint).thenReturn(Some("test"))
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder)))
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder)))
- await(service(None).retrieve) must beNone
- there was no(repository).find(any())
+ await(service(None).retrieve) must beNone
+ verify(repository, never()).find(any())
+ }
}
"[stateful] return authenticator if authenticator fingerprint matches current fingerprint" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id))
- fingerprintGenerator.generate(any()) returns "test"
- settings.useFingerprinting returns true
- authenticator.fingerprint returns Some("test")
- repository.find(authenticator.id) returns Future.successful(Some(authenticator))
+ when(fingerprintGenerator.generate(any())).thenReturn("test")
+ when(settings.useFingerprinting).thenReturn(true)
+ when(authenticator.fingerprint).thenReturn(Some("test"))
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator)))
await(service(Some(repository)).retrieve) must beSome(authenticator)
}
"[stateless] return authenticator if authenticator fingerprint matches current fingerprint" in new WithApplication with Context {
- fingerprintGenerator.generate(any()) returns "test"
- settings.useFingerprinting returns true
- authenticator.fingerprint returns Some("test")
+ override def running() = {
+ when(fingerprintGenerator.generate(any())).thenReturn("test")
+ when(settings.useFingerprinting).thenReturn(true)
+ when(authenticator.fingerprint).thenReturn(Some("test"))
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder)))
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder)))
- await(service(None).retrieve) must beSome(authenticator)
- there was no(repository).find(any())
+ await(service(None).retrieve) must beSome(authenticator)
+ verify(repository, never()).find(any())
+ }
}
"[stateful] return authenticator if fingerprinting is disabled" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id))
- settings.useFingerprinting returns false
- repository.find(authenticator.id) returns Future.successful(Some(authenticator))
+ when(settings.useFingerprinting).thenReturn(false)
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator)))
await(service(Some(repository)).retrieve) must beSome(authenticator)
}
"[stateless] return authenticator if fingerprinting is disabled" in new WithApplication with Context {
- settings.useFingerprinting returns false
+ override def running() = {
+ when(settings.useFingerprinting).thenReturn(false)
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder)))
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder)))
- repository.find(authenticator.id) returns Future.successful(Some(authenticator))
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator)))
- await(service(None).retrieve) must beSome(authenticator)
+ await(service(None).retrieve) must beSome(authenticator)
+ }
}
"throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id))
- fingerprintGenerator.generate(any()) throws new RuntimeException("Could not generate fingerprint")
- settings.useFingerprinting returns true
- repository.find(authenticator.id) returns Future.successful(Some(authenticator))
+ when(fingerprintGenerator.generate(any())).thenThrow(new RuntimeException("Could not generate fingerprint"))
+ when(settings.useFingerprinting).thenReturn(true)
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator)))
await(service(Some(repository)).retrieve) must throwA[AuthenticatorRetrievalException].like {
case e =>
@@ -286,25 +307,27 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
"The `init` method of the service" should {
"[stateful] return a cookie with the authenticator ID if the authenticator could be saved in backing store" in new Context {
- repository.add(any()) answers { _: Any => Future.successful(authenticator) }
+ when(repository.add(any())).thenAnswer { _ => Future.successful(authenticator) }
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
await(service(Some(repository)).init(authenticator)) must be equalTo statefulCookie
- there was one(repository).add(any())
+ verify(repository).add(any())
}
"[stateless] return a cookie with a serialized authenticator" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val cookie = await(service(None).init(authenticator))
+ val cookie = await(service(None).init(authenticator))
- unserialize(cookie.value, signer, authenticatorEncoder) must be equalTo unserialize(statelessCookie.value, signer, authenticatorEncoder)
- there was no(repository).add(any())
+ unserialize(cookie.value, signer, authenticatorEncoder) must be equalTo unserialize(statelessCookie.value, signer, authenticatorEncoder)
+ verify(repository, never()).add(any())
+ }
}
"throws an AuthenticatorInitializationException exception if an error occurred during initialization" in new Context {
- repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator"))
+ when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator")))
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
@@ -358,39 +381,43 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
"The `touch` method of the service" should {
"update the last used date if idle timeout is defined" in new WithApplication with Context {
- settings.authenticatorIdleTimeout returns Some(1 second)
- clock.now returns ZonedDateTime.now
-
- service(Some(repository)).touch(authenticator) must beLeft[CookieAuthenticator].like {
- case a =>
- a.lastUsedDateTime must be equalTo clock.now
+ override def running() = {
+ when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second))
+ when(clock.now).thenReturn(ZonedDateTime.now)
+
+ service(Some(repository)).touch(authenticator) must beLeft[CookieAuthenticator].like {
+ case a =>
+ a.lastUsedDateTime must be equalTo clock.now
+ }
}
}
"do not update the last used date if idle timeout is not defined" in new WithApplication with Context {
- settings.authenticatorIdleTimeout returns None
- clock.now returns ZonedDateTime.now
-
- service(Some(repository)).touch(authenticator) must beRight[CookieAuthenticator].like {
- case a =>
- a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime
+ override def running() = {
+ when(settings.authenticatorIdleTimeout).thenReturn(None)
+ when(clock.now).thenReturn(ZonedDateTime.now)
+
+ service(Some(repository)).touch(authenticator) must beRight[CookieAuthenticator].like {
+ case a =>
+ a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime
+ }
}
}
}
"The `update` method of the service" should {
"[stateful] update the authenticator in backing store" in new Context {
- repository.update(any()) answers { _: Any => Future.successful(authenticator) }
+ when(repository.update(any())).thenAnswer { _ => Future.successful(authenticator) }
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
await(service(Some(repository)).update(authenticator, Results.Ok))
- there was one(repository).update(authenticator)
+ verify(repository).update(authenticator)
}
"[stateful] return the result if the authenticator could be stored in backing store" in new Context {
- repository.update(any()) answers { p: Any => Future.successful(p.asInstanceOf[CookieAuthenticator]) }
+ when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[CookieAuthenticator]) }
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val result = service(Some(repository)).update(authenticator, Results.Ok)
@@ -399,16 +426,18 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
}
"[stateless] update the cookie for the updated authenticator" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val result = service(None).update(authenticator, Results.Ok)
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val result = service(None).update(authenticator, Results.Ok)
- status(result) must be equalTo OK
- cookies(result).get(settings.cookieName) should beSome[Cookie].which(statelessResponseCookieMatcher(authenticator))
- there was no(repository).update(authenticator)
+ status(result) must be equalTo OK
+ cookies(result).get(settings.cookieName) should beSome[Cookie].which(statelessResponseCookieMatcher(authenticator))
+ verify(repository, never()).update(authenticator)
+ }
}
"throws an AuthenticatorUpdateException exception if an error occurred during update" in new Context {
- repository.update(any()) returns Future.failed(new Exception("Cannot store authenticator"))
+ when(repository.update(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator")))
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
@@ -425,14 +454,14 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
val now = ZonedDateTime.now
val id = "new-test-id"
- repository.remove(authenticator.id) returns Future.successful(())
- repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[CookieAuthenticator]) }
- idGenerator.generate returns Future.successful(id)
- clock.now returns now
+ when(repository.remove(authenticator.id)).thenReturn(Future.successful(()))
+ when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[CookieAuthenticator]) }
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(now)
await(service(Some(repository)).renew(authenticator, Results.Ok))
- there was one(repository).remove(authenticator.id)
+ verify(repository).remove(authenticator.id)
}
"[stateful] renew the authenticator and return the response with the updated cookie value" in new Context {
@@ -440,31 +469,33 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
val now = ZonedDateTime.now
val id = "new-test-id"
- repository.remove(any()) returns Future.successful(())
- repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[CookieAuthenticator]) }
- idGenerator.generate returns Future.successful(id)
- clock.now returns now
+ when(repository.remove(any())).thenReturn(Future.successful(()))
+ when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[CookieAuthenticator]) }
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(now)
val result = service(Some(repository)).renew(authenticator, Results.Ok)
cookies(result).get(settings.cookieName) should beSome[Cookie].which(statefulResponseCookieMatcher(id))
- there was one(repository).add(any())
+ verify(repository).add(any())
}
"[stateless] renew the authenticator and return the response with the updated cookie value" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val now = ZonedDateTime.now
- val id = "new-test-id"
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val now = ZonedDateTime.now
+ val id = "new-test-id"
- settings.useFingerprinting returns false
- idGenerator.generate returns Future.successful(id)
- clock.now returns now
+ when(settings.useFingerprinting).thenReturn(false)
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(now)
- val result = service(None).renew(authenticator, Results.Ok)
+ val result = service(None).renew(authenticator, Results.Ok)
- cookies(result).get(settings.cookieName) should beSome[Cookie].which(statelessResponseCookieMatcher(
- authenticator.copy(id = id, lastUsedDateTime = now, expirationDateTime = now + settings.authenticatorExpiry)))
- there was no(repository).add(any())
+ cookies(result).get(settings.cookieName) should beSome[Cookie].which(statelessResponseCookieMatcher(
+ authenticator.copy(id = id, lastUsedDateTime = now, expirationDateTime = now + settings.authenticatorExpiry)))
+ verify(repository, never()).add(any())
+ }
}
"throws an AuthenticatorRenewalException exception if an error occurred during renewal" in new Context {
@@ -472,10 +503,10 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
val now = ZonedDateTime.now
val id = "new-test-id"
- repository.remove(any()) returns Future.successful(())
- repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator"))
- idGenerator.generate returns Future.successful(id)
- clock.now returns now
+ when(repository.remove(any())).thenReturn(Future.successful(()))
+ when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator")))
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(now)
await(service(Some(repository)).renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like {
case e =>
@@ -488,7 +519,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
"[stateful] discard the cookie from response and remove it from backing store" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- repository.remove(any()) returns Future.successful(())
+ when(repository.remove(any())).thenReturn(Future.successful(()))
val result = service(Some(repository)).discard(authenticator, Results.Ok.withCookies(statefulCookie))
@@ -500,29 +531,31 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
c.domain must be equalTo settings.cookieDomain
c.secure must be equalTo settings.secureCookie
}
- there was one(repository).remove(authenticator.id)
+ verify(repository).remove(authenticator.id)
}
"[stateless] discard the cookie from response" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val result = service(None).discard(authenticator, Results.Ok.withCookies(statelessCookie))
-
- cookies(result).get(settings.cookieName) should beSome[Cookie].which { c =>
- c.name must be equalTo settings.cookieName
- c.value must be equalTo ""
- c.maxAge must beSome(Cookie.DiscardedMaxAge)
- c.path must be equalTo settings.cookiePath
- c.domain must be equalTo settings.cookieDomain
- c.secure must be equalTo settings.secureCookie
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val result = service(None).discard(authenticator, Results.Ok.withCookies(statelessCookie))
+
+ cookies(result).get(settings.cookieName) should beSome[Cookie].which { c =>
+ c.name must be equalTo settings.cookieName
+ c.value must be equalTo ""
+ c.maxAge must beSome(Cookie.DiscardedMaxAge)
+ c.path must be equalTo settings.cookiePath
+ c.domain must be equalTo settings.cookieDomain
+ c.secure must be equalTo settings.secureCookie
+ }
+ verify(repository, never()).remove(authenticator.id)
}
- there was no(repository).remove(authenticator.id)
}
"throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val okResult = Results.Ok
- repository.remove(any()) returns Future.failed(new Exception("Cannot store authenticator"))
+ when(repository.remove(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator")))
await(service(Some(repository)).discard(authenticator, okResult)) must throwA[AuthenticatorDiscardingException].like {
case e =>
@@ -539,27 +572,27 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
/**
* The repository implementation.
*/
- lazy val repository = mock[AuthenticatorRepository[CookieAuthenticator]].smart
+ lazy val repository = mockSmart[AuthenticatorRepository[CookieAuthenticator]]
/**
* The ID generator implementation.
*/
- lazy val fingerprintGenerator = mock[FingerprintGenerator].smart
+ lazy val fingerprintGenerator = mockSmart[FingerprintGenerator]
/**
* The ID generator implementation.
*/
- lazy val idGenerator = mock[IDGenerator].smart
+ lazy val idGenerator = mockSmart[IDGenerator]
/**
* The signer implementation.
*
- * The signer returns the same value as passed to the methods. This is enough for testing.
+ * The signer).thenReturn(the same value as passed to the methods. This is enough for testing.)
*/
lazy val signer = {
- val c = mock[Signer].smart
- c.sign(any()) answers { p: Any => p.asInstanceOf[String] }
- c.extract(any()) answers { p: Any => Success(p.asInstanceOf[String]) }
+ val c = mockSmart[Signer]
+ when(c.sign(any())).thenAnswer { _.getArgument(0).asInstanceOf[String] }
+ when(c.extract(any())).thenAnswer { p => Success(p.getArgument(0).asInstanceOf[String]) }
c
}
@@ -574,7 +607,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang
/**
* The clock implementation.
*/
- lazy val clock = mock[Clock].smart
+ lazy val clock = mockSmart[Clock]
/**
* The settings.
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala
index a28d7978..bfe3d49c 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala
@@ -17,7 +17,6 @@ package io.github.honeycombcheesecake.play.silhouette.impl.authenticators
import io.github.honeycombcheesecake.play.silhouette.api.LoginInfo
import io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorResult
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.mvc.{ AnyContentAsEmpty, Results }
import play.api.test.{ FakeRequest, PlaySpecification, WithApplication }
@@ -27,7 +26,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.DummyAuthenticator]].
*/
-class DummyAuthenticatorSpec extends PlaySpecification with Mockito {
+class DummyAuthenticatorSpec extends PlaySpecification {
"The `isValid` method of the authenticator" should {
"return true" in new Context {
@@ -78,9 +77,11 @@ class DummyAuthenticatorSpec extends PlaySpecification with Mockito {
"The `touch` method of the service" should {
"not update the authenticator" in new WithApplication with Context {
- service.touch(authenticator) must beRight[DummyAuthenticator].like {
- case a =>
- a.loginInfo must be equalTo loginInfo
+ override def running() = {
+ service.touch(authenticator) must beRight[DummyAuthenticator].like {
+ case a =>
+ a.loginInfo must be equalTo loginInfo
+ }
}
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala
index 33fabf57..b0b1d68e 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala
@@ -25,13 +25,14 @@ import io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorS
import io.github.honeycombcheesecake.play.silhouette.api.util.{ Clock, IDGenerator, RequestPart }
import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.JWTAuthenticator._
import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.JWTAuthenticatorService._
-import org.specs2.control.NoLanguageFeatures
import org.specs2.matcher.JsonMatchers
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.libs.json.{ JsNull, JsObject, Json }
import play.api.mvc.{ AnyContentAsEmpty, Results }
import play.api.test.{ FakeRequest, PlaySpecification, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
+import test.Helper.mockSmart
import java.time.temporal.ChronoField
import java.time.{ ZoneId, ZonedDateTime }
@@ -43,7 +44,7 @@ import scala.language.postfixOps
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.JWTAuthenticator]].
*/
-class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatchers with NoLanguageFeatures {
+class JWTAuthenticatorSpec extends PlaySpecification with JsonMatchers {
"The `isValid` method of the authenticator" should {
"return false if the authenticator is expired" in new Context {
@@ -64,128 +65,153 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
"The `serialize` method of the authenticator" should {
"return a JWT with an expiration time" in new WithApplication with Context {
- val jwt = serialize(authenticator, authenticatorEncoder, settings)
- val json = Base64.decode(jwt.split('.').apply(1))
+ override def running() = {
+ val jwt = serialize(authenticator, authenticatorEncoder, settings)
+ val json = Base64.decode(jwt.split('.').apply(1))
- json must /("exp" -> authenticator.expirationDateTime.toEpochSecond.toInt)
+ json must /("exp" -> authenticator.expirationDateTime.toEpochSecond.toInt)
+ }
}
"return a JWT with an encoded subject" in new WithApplication with Context {
- val jwt = serialize(authenticator, authenticatorEncoder, settings)
- val json = Json.parse(Base64.decode(jwt.split('.').apply(1)))
- val sub = Json.parse(authenticatorEncoder.decode((json \ "sub").as[String])).as[LoginInfo]
+ override def running() = {
+ val jwt = serialize(authenticator, authenticatorEncoder, settings)
+ val json = Json.parse(Base64.decode(jwt.split('.').apply(1)))
+ val sub = Json.parse(authenticatorEncoder.decode((json \ "sub").as[String])).as[LoginInfo]
- sub must be equalTo authenticator.loginInfo
+ sub must be equalTo authenticator.loginInfo
+ }
}
"return a JWT with an issuer" in new WithApplication with Context {
- val jwt = serialize(authenticator, authenticatorEncoder, settings)
- val json = Base64.decode(jwt.split('.').apply(1))
+ override def running() = {
+ val jwt = serialize(authenticator, authenticatorEncoder, settings)
+ val json = Base64.decode(jwt.split('.').apply(1))
- json must /("iss" -> settings.issuerClaim)
+ json must /("iss" -> settings.issuerClaim)
+ }
}
"return a JWT with an issued-at time" in new WithApplication with Context {
- val jwt = serialize(authenticator, authenticatorEncoder, settings)
- val json = Base64.decode(jwt.split('.').apply(1))
+ override def running() = {
+ val jwt = serialize(authenticator, authenticatorEncoder, settings)
+ val json = Base64.decode(jwt.split('.').apply(1))
- json must /("iat" -> authenticator.lastUsedDateTime.toEpochSecond.toInt)
+ json must /("iat" -> authenticator.lastUsedDateTime.toEpochSecond.toInt)
+ }
}
"throw an AuthenticatorException if a reserved claim will be overridden" in new WithApplication with Context {
- val claims = Json.obj(
- "jti" -> "reserved")
+ override def running() = {
+ val claims = Json.obj(
+ "jti" -> "reserved")
- serialize(authenticator.copy(customClaims = Some(claims)), authenticatorEncoder, settings) must throwA[AuthenticatorException].like {
- case e => e.getMessage must startWith(OverrideReservedClaim.format(ID, "jti", ""))
+ serialize(authenticator.copy(customClaims = Some(claims)), authenticatorEncoder, settings) must throwA[AuthenticatorException].like {
+ case e => e.getMessage must startWith(OverrideReservedClaim.format(ID, "jti", ""))
+ }
}
}
"throw an AuthenticatorException if an unexpected value was found in the arbitrary claims" in new WithApplication with Context {
- val claims = Json.obj(
- "null" -> JsNull)
+ override def running() = {
+ val claims = Json.obj(
+ "null" -> JsNull)
- serialize(authenticator.copy(customClaims = Some(claims)), authenticatorEncoder, settings) must throwA[AuthenticatorException].like {
- case e => e.getMessage must startWith(UnexpectedJsonValue.format(ID, ""))
+ serialize(authenticator.copy(customClaims = Some(claims)), authenticatorEncoder, settings) must throwA[AuthenticatorException].like {
+ case e => e.getMessage must startWith(UnexpectedJsonValue.format(ID, ""))
+ }
}
}
"return a JWT with arbitrary claims" in new WithApplication with Context {
- val jwt = serialize(authenticator.copy(customClaims = Some(customClaims)), authenticatorEncoder, settings)
- val json = Base64.decode(jwt.split('.').apply(1))
-
- json must /("boolean" -> true)
- json must /("string" -> "string")
- json must /("number" -> 1234567890)
- json must /("array") /# 0 / 1
- json must /("array") /# 1 / 2
- json must /("object") / "array" /# 0 / "string1"
- json must /("object") / "array" /# 1 / "string2"
- json must /("object") / "object" / "array" /# 0 / "string"
- json must /("object") / "object" / "array" /# 1 / false
- json must /("object") / "object" / "array" /# 2 / ("number" -> 1)
+ override def running() = {
+ val jwt = serialize(authenticator.copy(customClaims = Some(customClaims)), authenticatorEncoder, settings)
+ val json = Base64.decode(jwt.split('.').apply(1))
+
+ json must /("boolean" -> true)
+ json must /("string" -> "string")
+ json must /("number" -> 1234567890)
+ json must /("array") /# 0 / 1
+ json must /("array") /# 1 / 2
+ json must /("object") / "array" /# 0 / "string1"
+ json must /("object") / "array" /# 1 / "string2"
+ json must /("object") / "object" / "array" /# 0 / "string"
+ json must /("object") / "object" / "array" /# 1 / false
+ json must /("object") / "object" / "array" /# 2 / ("number" -> 1)
+ }
}
}
"The `unserialize` method of the authenticator" should {
"throw an AuthenticatorException if the given token can't be parsed" in new WithApplication with Context {
- val jwt = "invalid"
- val msg = Pattern.quote(InvalidJWTToken.format(ID, jwt))
+ override def running() = {
+ val jwt = "invalid"
+ val msg = Pattern.quote(InvalidJWTToken.format(ID, jwt))
- unserialize(jwt, authenticatorEncoder, settings) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ unserialize(jwt, authenticatorEncoder, settings) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ }
}
"throw an AuthenticatorException if the given token couldn't be verified" in new WithApplication with Context {
- val jwt = serialize(authenticator, authenticatorEncoder, settings) + "-wrong-sig"
- val msg = Pattern.quote(InvalidJWTToken.format(ID, jwt))
+ override def running() = {
+ val jwt = serialize(authenticator, authenticatorEncoder, settings) + "-wrong-sig"
+ val msg = Pattern.quote(InvalidJWTToken.format(ID, jwt))
- unserialize(jwt, authenticatorEncoder, settings) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ unserialize(jwt, authenticatorEncoder, settings) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ }
}
"unserialize a JWT" in new WithApplication with Context {
- val jwt = serialize(authenticator, authenticatorEncoder, settings)
+ override def running() = {
+ val jwt = serialize(authenticator, authenticatorEncoder, settings)
- unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticator.copy(
- expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
- lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
+ unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticator.copy(
+ expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
+ lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
+ }
}
"unserialize a JWT with a custom clock" in new WithApplication with Context {
+ override def running() = {
+ val lastUsedDateTime: ZonedDateTime = ZonedDateTime
+ .of(2015, 2, 25, 19, 0, 0, 0, ZoneId.systemDefault())
+ .`with`(ChronoField.MILLI_OF_SECOND, 0)
- val lastUsedDateTime: ZonedDateTime = ZonedDateTime
- .of(2015, 2, 25, 19, 0, 0, 0, ZoneId.systemDefault())
- .`with`(ChronoField.MILLI_OF_SECOND, 0)
-
- val authenticatorCustomClock: JWTAuthenticator = authenticator
- .copy(
- expirationDateTime = lastUsedDateTime + settings.authenticatorExpiry,
- lastUsedDateTime = lastUsedDateTime)
+ val authenticatorCustomClock: JWTAuthenticator = authenticator
+ .copy(
+ expirationDateTime = lastUsedDateTime + settings.authenticatorExpiry,
+ lastUsedDateTime = lastUsedDateTime)
- val jwt: String = serialize(authenticatorCustomClock, authenticatorEncoder, settings)
+ val jwt: String = serialize(authenticatorCustomClock, authenticatorEncoder, settings)
- clock.now returns lastUsedDateTime
- implicit val customClock: Option[Clock] = Some(clock)
+ when(clock.now).thenReturn(lastUsedDateTime)
+ implicit val customClock: Option[Clock] = Some(clock)
- unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticatorCustomClock.copy(
- expirationDateTime = authenticatorCustomClock.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
- lastUsedDateTime = authenticatorCustomClock.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
+ unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticatorCustomClock.copy(
+ expirationDateTime = authenticatorCustomClock.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
+ lastUsedDateTime = authenticatorCustomClock.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
+ }
}
"unserialize a JWT with arbitrary claims" in new WithApplication with Context {
- val jwt = serialize(authenticator.copy(customClaims = Some(customClaims)), authenticatorEncoder, settings)
+ override def running() = {
+ val jwt = serialize(authenticator.copy(customClaims = Some(customClaims)), authenticatorEncoder, settings)
- unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.like {
- case a =>
- a.customClaims must beSome(customClaims)
+ unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.like {
+ case a =>
+ a.customClaims must beSome(customClaims)
+ }
}
}
}
"The `serialize/unserialize` method of the authenticator" should {
"serialize/unserialize an authenticator" in new WithApplication with Context {
- val jwt = serialize(authenticator, authenticatorEncoder, settings)
+ override def running() = {
+ val jwt = serialize(authenticator, authenticatorEncoder, settings)
- unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticator)
+ unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticator)
+ }
}
}
@@ -194,8 +220,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val id = "test-id"
- idGenerator.generate returns Future.successful(id)
- clock.now returns ZonedDateTime.now
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(ZonedDateTime.now)
await(service(None).create(loginInfo)).id must be equalTo id
}
@@ -204,8 +230,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val now = ZonedDateTime.now
- idGenerator.generate returns Future.successful("test-id")
- clock.now returns now
+ when(idGenerator.generate).thenReturn(Future.successful("test-id"))
+ when(clock.now).thenReturn(now)
await(service(None).create(loginInfo)).lastUsedDateTime must be equalTo now
}
@@ -214,8 +240,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val now = ZonedDateTime.now
- idGenerator.generate returns Future.successful("test-id")
- clock.now returns now
+ when(idGenerator.generate).thenReturn(Future.successful("test-id"))
+ when(clock.now).thenReturn(now)
await(service(None).create(loginInfo)).expirationDateTime must be equalTo now + 12.hours
}
@@ -225,9 +251,9 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
val sixHours = 6 hours
val now = ZonedDateTime.now
- settings.authenticatorExpiry returns sixHours
- idGenerator.generate returns Future.successful("test-id")
- clock.now returns now
+ when(settings.authenticatorExpiry).thenReturn(sixHours)
+ when(idGenerator.generate).thenReturn(Future.successful("test-id"))
+ when(clock.now).thenReturn(now)
await(service(None).create(loginInfo)).expirationDateTime must be equalTo now + sixHours
}
@@ -235,7 +261,7 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
"throws an AuthenticatorCreationException exception if an error occurred during creation" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- idGenerator.generate returns Future.failed(new Exception("Could not generate ID"))
+ when(idGenerator.generate).thenReturn(Future.failed(new Exception("Could not generate ID")))
await(service(None).create(loginInfo)) must throwA[AuthenticatorCreationException].like {
case e =>
@@ -254,94 +280,108 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
"return None if DAO is enabled and no authenticator is stored for the token located in the header" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> "not-stored")
- repository.find(authenticator.id) returns Future.successful(None)
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(None))
await(service(Some(repository)).retrieve) must beNone
}
"return authenticator if DAO is enabled and an authenticator is stored for the token located in the the header" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings))
- clock.now returns ZonedDateTime.now
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings))
+ when(clock.now).thenReturn(ZonedDateTime.now)
- repository.find(authenticator.id) returns Future.successful(Some(authenticator.copy(
- expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
- lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))))
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator.copy(
+ expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
+ lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))))
- await(service(Some(repository)).retrieve) must beSome(authenticator.copy(
- expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
- lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
+ await(service(Some(repository)).retrieve) must beSome(authenticator.copy(
+ expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
+ lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
+ }
}
"return authenticator if DAO is enabled and an authenticator is stored for the token located in the the query string" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${serialize(authenticator, authenticatorEncoder, settings)}")
- clock.now returns ZonedDateTime.now
-
- settings.requestParts returns Some(Seq(RequestPart.QueryString))
- repository.find(authenticator.id) returns Future.successful(Some(authenticator.copy(
- expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
- lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))))
-
- await(service(Some(repository)).retrieve) must beSome(authenticator.copy(
- expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
- lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${serialize(authenticator, authenticatorEncoder, settings)}")
+ when(clock.now).thenReturn(ZonedDateTime.now)
+
+ when(settings.requestParts).thenReturn(Some(Seq(RequestPart.QueryString)))
+ when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator.copy(
+ expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
+ lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))))
+
+ await(service(Some(repository)).retrieve) must beSome(authenticator.copy(
+ expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
+ lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
+ }
}
"return authenticator if DAO is disabled and authenticator was found in the header" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings))
- clock.now returns ZonedDateTime.now
-
- await(service(None).retrieve) must beSome(authenticator.copy(
- expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
- lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
- there was no(repository).find(any())
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings))
+ when(clock.now).thenReturn(ZonedDateTime.now)
+
+ await(service(None).retrieve) must beSome(authenticator.copy(
+ expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
+ lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
+ verify(repository, never()).find(any())
+ }
}
"return authenticator if DAO is disabled and authenticator was found in the query string" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${serialize(authenticator, authenticatorEncoder, settings)}")
- clock.now returns ZonedDateTime.now
-
- settings.requestParts returns Some(Seq(RequestPart.QueryString))
- await(service(None).retrieve) must beSome(authenticator.copy(
- expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
- lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
- there was no(repository).find(any())
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${serialize(authenticator, authenticatorEncoder, settings)}")
+ when(clock.now).thenReturn(ZonedDateTime.now)
+
+ when(settings.requestParts).thenReturn(Some(Seq(RequestPart.QueryString)))
+ await(service(None).retrieve) must beSome(authenticator.copy(
+ expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0),
+ lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))
+ verify(repository, never()).find(any())
+ }
}
"throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings))
- clock.now returns ZonedDateTime.now
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings))
+ when(clock.now).thenReturn(ZonedDateTime.now)
- repository.find(authenticator.id) returns Future.failed(new RuntimeException("Cannot find authenticator"))
+ when(repository.find(authenticator.id)).thenReturn(Future.failed(new RuntimeException("Cannot find authenticator")))
- await(service(Some(repository)).retrieve) must throwA[AuthenticatorRetrievalException].like {
- case e =>
- e.getMessage must startWith(RetrieveError.format(ID, ""))
+ await(service(Some(repository)).retrieve) must throwA[AuthenticatorRetrievalException].like {
+ case e =>
+ e.getMessage must startWith(RetrieveError.format(ID, ""))
+ }
}
}
}
"The `init` method of the service" should {
"return the token if DAO is enabled and authenticator could be saved in backing store" in new WithApplication with Context {
- repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[JWTAuthenticator]) }
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ override def running() = {
+ when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) }
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val token = await(service(Some(repository)).init(authenticator))
+ val token = await(service(Some(repository)).init(authenticator))
- unserialize(token, authenticatorEncoder, settings).get must be equalTo authenticator
- there was one(repository).add(any())
+ unserialize(token, authenticatorEncoder, settings).get must be equalTo authenticator
+ verify(repository).add(any())
+ }
}
"return the token if DAO is disabled" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val token = await(service(None).init(authenticator))
+ val token = await(service(None).init(authenticator))
- unserialize(token, authenticatorEncoder, settings).get must be equalTo authenticator
- there was no(repository).add(any())
+ unserialize(token, authenticatorEncoder, settings).get must be equalTo authenticator
+ verify(repository, never()).add(any())
+ }
}
"throws an AuthenticatorInitializationException exception if an error occurred during initialization" in new Context {
- repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator"))
+ when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator")))
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val okResult = Future.successful(Results.Ok)
@@ -355,171 +395,199 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
"The result `embed` method of the service" should {
"return the response with a header" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val token = serialize(authenticator, authenticatorEncoder, settings)
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val token = serialize(authenticator, authenticatorEncoder, settings)
- val result = service(Some(repository)).embed(token, Results.Ok)
+ val result = service(Some(repository)).embed(token, Results.Ok)
- header(settings.fieldName, result) should beSome(token)
+ header(settings.fieldName, result) should beSome(token)
+ }
}
}
"The request `embed` method of the service" should {
"return the request with a header " in new WithApplication with Context {
- val token = serialize(authenticator, authenticatorEncoder, settings)
- val request = service(Some(repository)).embed(token, FakeRequest())
+ override def running() = {
+ val token = serialize(authenticator, authenticatorEncoder, settings)
+ val request = service(Some(repository)).embed(token, FakeRequest())
- unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator
+ unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator
+ }
}
"override an existing token" in new WithApplication with Context {
- val token = serialize(authenticator, authenticatorEncoder, settings)
- val request = service(Some(repository)).embed(token, FakeRequest().withHeaders(settings.fieldName -> "test"))
+ override def running() = {
+ val token = serialize(authenticator, authenticatorEncoder, settings)
+ val request = service(Some(repository)).embed(token, FakeRequest().withHeaders(settings.fieldName -> "test"))
- unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator
+ unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator
+ }
}
"keep non authenticator related headers" in new WithApplication with Context {
- val token = serialize(authenticator, authenticatorEncoder, settings)
- val request = service(Some(repository)).embed(token, FakeRequest().withHeaders("test" -> "test"))
+ override def running() = {
+ val token = serialize(authenticator, authenticatorEncoder, settings)
+ val request = service(Some(repository)).embed(token, FakeRequest().withHeaders("test" -> "test"))
- unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator
- request.headers.get("test") should beSome("test")
+ unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator
+ request.headers.get("test") should beSome("test")
+ }
}
"keep other request parts" in new WithApplication with Context {
- val token = serialize(authenticator, authenticatorEncoder, settings)
- val request = service(Some(repository)).embed(token, FakeRequest().withSession("test" -> "test"))
+ override def running() = {
+ val token = serialize(authenticator, authenticatorEncoder, settings)
+ val request = service(Some(repository)).embed(token, FakeRequest().withSession("test" -> "test"))
- unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator
- request.session.get("test") should beSome("test")
+ unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator
+ request.session.get("test") should beSome("test")
+ }
}
}
"The `touch` method of the service" should {
"update the last used date if idle timeout is defined" in new WithApplication with Context {
- settings.authenticatorIdleTimeout returns Some(1 second)
- clock.now returns ZonedDateTime.now
-
- service(None).touch(authenticator) must beLeft[JWTAuthenticator].like {
- case a =>
- a.lastUsedDateTime must be equalTo clock.now
+ override def running() = {
+ when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second))
+ when(clock.now).thenReturn(ZonedDateTime.now)
+
+ service(None).touch(authenticator) must beLeft[JWTAuthenticator].like {
+ case a =>
+ a.lastUsedDateTime must be equalTo clock.now
+ }
}
}
"do not update the last used date if idle timeout is not defined" in new WithApplication with Context {
- settings.authenticatorIdleTimeout returns None
- clock.now returns ZonedDateTime.now
-
- service(None).touch(authenticator) must beRight[JWTAuthenticator].like {
- case a =>
- a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime
+ override def running() = {
+ when(settings.authenticatorIdleTimeout).thenReturn(None)
+ when(clock.now).thenReturn(ZonedDateTime.now)
+
+ service(None).touch(authenticator) must beRight[JWTAuthenticator].like {
+ case a =>
+ a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime
+ }
}
}
}
"The `update` method of the service" should {
"update the authenticator in backing store" in new WithApplication with Context {
- repository.update(any()) answers { _: Any => Future.successful(authenticator) }
+ override def running() = {
+ when(repository.update(any())).thenAnswer { _ => Future.successful(authenticator) }
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- await(service(Some(repository)).update(authenticator, Results.Ok))
+ await(service(Some(repository)).update(authenticator, Results.Ok))
- there was one(repository).update(authenticator)
+ verify(repository).update(authenticator)
+ }
}
"return the result if the authenticator could be stored in backing store" in new WithApplication with Context {
- repository.update(any()) answers { p: Any => Future.successful(p.asInstanceOf[JWTAuthenticator]) }
+ override def running() = {
+ when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) }
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val result = service(Some(repository)).update(authenticator, Results.Ok)
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val result = service(Some(repository)).update(authenticator, Results.Ok)
- status(result) must be equalTo OK
- unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator
- there was one(repository).update(authenticator)
+ status(result) must be equalTo OK
+ unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator
+ verify(repository).update(authenticator)
+ }
}
"return the result if backing store is disabled" in new WithApplication with Context {
- repository.update(any()) answers { p: Any => Future.successful(p.asInstanceOf[JWTAuthenticator]) }
+ override def running() = {
+ when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) }
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val result = service(None).update(authenticator, Results.Ok)
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val result = service(None).update(authenticator, Results.Ok)
- status(result) must be equalTo OK
- unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator
- there was no(repository).update(any())
+ status(result) must be equalTo OK
+ unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator
+ verify(repository, never()).update(any())
+ }
}
"throws an AuthenticatorUpdateException exception if an error occurred during update" in new WithApplication with Context {
- repository.update(any()) returns Future.failed(new Exception("Cannot store authenticator"))
+ override def running() = {
+ when(repository.update(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator")))
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- await(service(Some(repository)).update(authenticator, Results.Ok)) must throwA[AuthenticatorUpdateException].like {
- case e =>
- e.getMessage must startWith(UpdateError.format(ID, ""))
+ await(service(Some(repository)).update(authenticator, Results.Ok)) must throwA[AuthenticatorUpdateException].like {
+ case e =>
+ e.getMessage must startWith(UpdateError.format(ID, ""))
+ }
}
}
}
"The `renew` method of the service" should {
"renew the authenticator and return the response with a new JWT if DAO is enabled" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val id = "new-test-id"
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val id = "new-test-id"
- repository.remove(any()) answers { _: Any => Future.successful(()) }
- repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[JWTAuthenticator]) }
- idGenerator.generate returns Future.successful(id)
- clock.now returns ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)
+ when(repository.remove(any())).thenAnswer { _ => Future.successful(()) }
+ when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) }
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0))
- val result = service(Some(repository)).renew(authenticator, Results.Ok)
+ val result = service(Some(repository)).renew(authenticator, Results.Ok)
- unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy(
- id = id,
- expirationDateTime = clock.now + settings.authenticatorExpiry,
- lastUsedDateTime = clock.now)
+ unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy(
+ id = id,
+ expirationDateTime = clock.now + settings.authenticatorExpiry,
+ lastUsedDateTime = clock.now)
- there was one(repository).add(any())
- there was one(repository).remove(authenticator.id)
+ verify(repository).add(any())
+ verify(repository).remove(authenticator.id)
+ }
}
"renew an authenticator with custom claims" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val id = "new-test-id"
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val id = "new-test-id"
- repository.remove(any()) returns Future.successful(())
- repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[JWTAuthenticator]) }
- idGenerator.generate returns Future.successful(id)
- clock.now returns ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)
+ when(repository.remove(any())).thenReturn(Future.successful(()))
+ when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) }
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0))
- val result = service(Some(repository)).renew(authenticator.copy(customClaims = Some(customClaims)), Results.Ok)
+ val result = service(Some(repository)).renew(authenticator.copy(customClaims = Some(customClaims)), Results.Ok)
- unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy(
- id = id,
- expirationDateTime = clock.now + settings.authenticatorExpiry,
- lastUsedDateTime = clock.now,
- customClaims = Some(customClaims))
+ unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy(
+ id = id,
+ expirationDateTime = clock.now + settings.authenticatorExpiry,
+ lastUsedDateTime = clock.now,
+ customClaims = Some(customClaims))
- there was one(repository).add(any())
- there was one(repository).remove(authenticator.id)
+ verify(repository).add(any())
+ verify(repository).remove(authenticator.id)
+ }
}
"renew the authenticator and return the response with a new JWT if DAO is disabled" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val id = "new-test-id"
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val id = "new-test-id"
- idGenerator.generate returns Future.successful(id)
- clock.now returns ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0))
- val result = service(None).renew(authenticator, Results.Ok)
+ val result = service(None).renew(authenticator, Results.Ok)
- unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy(
- id = id,
- expirationDateTime = clock.now + settings.authenticatorExpiry,
- lastUsedDateTime = clock.now)
- there was no(repository).remove(any())
- there was no(repository).add(any())
+ unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy(
+ id = id,
+ expirationDateTime = clock.now + settings.authenticatorExpiry,
+ lastUsedDateTime = clock.now)
+ verify(repository, never()).remove(any())
+ verify(repository, never()).add(any())
+ }
}
"throws an AuthenticatorRenewalException exception if an error occurred during renewal" in new Context {
@@ -527,10 +595,10 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
val now = ZonedDateTime.now
val id = "new-test-id"
- repository.remove(any()) returns Future.successful(())
- repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator"))
- idGenerator.generate returns Future.successful(id)
- clock.now returns now
+ when(repository.remove(any())).thenReturn(Future.successful(()))
+ when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator")))
+ when(idGenerator.generate).thenReturn(Future.successful(id))
+ when(clock.now).thenReturn(now)
await(service(Some(repository)).renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like {
case e =>
@@ -543,11 +611,11 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
"remove authenticator from backing store if DAO is enabled" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- repository.remove(authenticator.id) returns Future.successful(authenticator)
+ when(repository.remove(authenticator.id)).thenReturn(Future.unit)
await(service(Some(repository)).discard(authenticator, Results.Ok))
- there was one(repository).remove(authenticator.id)
+ verify(repository).remove(authenticator.id)
}
"do not remove the authenticator from backing store if DAO is disabled" in new Context {
@@ -555,14 +623,14 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
await(service(None).discard(authenticator, Results.Ok))
- there was no(repository).remove(authenticator.id)
+ verify(repository, never()).remove(authenticator.id)
}
"throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val okResult = Results.Ok
- repository.remove(authenticator.id) returns Future.failed(new Exception("Cannot remove authenticator"))
+ when(repository.remove(authenticator.id)).thenReturn(Future.failed(new Exception("Cannot remove authenticator")))
await(service(Some(repository)).discard(authenticator, okResult)) must throwA[AuthenticatorDiscardingException].like {
case e =>
@@ -576,12 +644,12 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
*/
trait Context extends Scope {
- private val lastUsedDateTime = ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)
+ private lazy val lastUsedDateTime = ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)
/**
* The repository implementation.
*/
- lazy val repository = mock[AuthenticatorRepository[JWTAuthenticator]].smart
+ lazy val repository = mockSmart[AuthenticatorRepository[JWTAuthenticator]]
/**
* The authenticator encoder implementation.
@@ -591,12 +659,12 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch
/**
* The ID generator implementation.
*/
- lazy val idGenerator = mock[IDGenerator].smart
+ lazy val idGenerator = mockSmart[IDGenerator]
/**
* The clock implementation.
*/
- lazy val clock: Clock = mock[Clock].smart
+ lazy val clock: Clock = mockSmart[Clock]
/**
* The settings.
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala
index 97aae2c0..c6b807cf 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala
@@ -25,12 +25,13 @@ import io.github.honeycombcheesecake.play.silhouette.api.util.{ Clock, Fingerpri
import io.github.honeycombcheesecake.play.silhouette.api.{ Authenticator, LoginInfo }
import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.SessionAuthenticator._
import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.SessionAuthenticatorService._
-import org.specs2.control.NoLanguageFeatures
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.libs.json.Json
import play.api.mvc._
import play.api.test.{ FakeRequest, PlaySpecification, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
+import test.Helper.{ mockSmart, mock }
import java.time.ZonedDateTime
import scala.concurrent.ExecutionContext.Implicits.global
@@ -41,7 +42,7 @@ import scala.language.postfixOps
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.SessionAuthenticator]].
*/
-class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLanguageFeatures {
+class SessionAuthenticatorSpec extends PlaySpecification {
"The `isValid` method of the authenticator" should {
"return false if the authenticator is expired" in new Context {
@@ -62,25 +63,31 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
"The `unserialize` method of the authenticator" should {
"throw an AuthenticatorException if the given value can't be parsed as Json" in new WithApplication with Context {
- val value = "invalid"
- val msg = Pattern.quote(JsonParseError.format(ID, value))
+ override def running() = {
+ val value = "invalid"
+ val msg = Pattern.quote(JsonParseError.format(ID, value))
- unserialize(authenticatorEncoder.encode(value), authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ unserialize(authenticatorEncoder.encode(value), authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ }
}
"throw an AuthenticatorException if the given value is in the wrong Json format" in new WithApplication with Context {
- val value = "{}"
- val msg = "^" + Pattern.quote(InvalidJsonFormat.format(ID, "")) + ".*"
+ override def running() = {
+ val value = "{}"
+ val msg = "^" + Pattern.quote(InvalidJsonFormat.format(ID, "")) + ".*"
- unserialize(authenticatorEncoder.encode(value), authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ unserialize(authenticatorEncoder.encode(value), authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg)
+ }
}
}
"The `serialize/unserialize` method of the authenticator" should {
"serialize/unserialize an authenticator" in new WithApplication with Context {
- val value = serialize(authenticator, authenticatorEncoder)
+ override def running() = {
+ val value = serialize(authenticator, authenticatorEncoder)
- unserialize(value, authenticatorEncoder) must beSuccessfulTry.withValue(authenticator)
+ unserialize(value, authenticatorEncoder) must beSuccessfulTry.withValue(authenticator)
+ }
}
}
@@ -88,9 +95,9 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
"return a fingerprinted authenticator" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- clock.now returns ZonedDateTime.now
- fingerprintGenerator.generate(any) returns "test"
- settings.useFingerprinting returns true
+ when(clock.now).thenReturn(ZonedDateTime.now)
+ when(fingerprintGenerator.generate(any)).thenReturn("test")
+ when(settings.useFingerprinting).thenReturn(true)
await(service.create(loginInfo)).fingerprint must beSome("test")
}
@@ -98,8 +105,8 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
"return a non fingerprinted authenticator" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- clock.now returns ZonedDateTime.now
- settings.useFingerprinting returns false
+ when(clock.now).thenReturn(ZonedDateTime.now)
+ when(settings.useFingerprinting).thenReturn(false)
await(service.create(loginInfo)).fingerprint must beNone
}
@@ -108,7 +115,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val now = ZonedDateTime.now
- clock.now returns now
+ when(clock.now).thenReturn(now)
await(service.create(loginInfo)).lastUsedDateTime must be equalTo now
}
@@ -117,7 +124,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val now = ZonedDateTime.now
- clock.now returns now
+ when(clock.now).thenReturn(now)
await(service.create(loginInfo)).expirationDateTime must be equalTo now + 12.hours
}
@@ -127,8 +134,8 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
val sixHours = 6 hours
val now = ZonedDateTime.now
- clock.now returns now
- settings.authenticatorExpiry returns sixHours
+ when(clock.now).thenReturn(now)
+ when(settings.authenticatorExpiry).thenReturn(sixHours)
await(service.create(loginInfo)).expirationDateTime must be equalTo now + sixHours
}
@@ -136,7 +143,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
"throws an AuthenticatorCreationException exception if an error occurred during creation" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- clock.now throws new RuntimeException("Could not get date")
+ when(clock.now).thenThrow(new RuntimeException("Could not get date"))
await(service.create(loginInfo)) must throwA[AuthenticatorCreationException].like {
case e =>
@@ -153,216 +160,260 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
}
"return None if session contains invalid json" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode("{"))
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode("{"))
- settings.useFingerprinting returns false
+ when(settings.useFingerprinting).thenReturn(false)
- await(service.retrieve) must beNone
+ await(service.retrieve) must beNone
+ }
}
"return None if session contains valid json but invalid authenticator" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode("{ \"test\": \"test\" }"))
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode("{ \"test\": \"test\" }"))
- settings.useFingerprinting returns false
+ when(settings.useFingerprinting).thenReturn(false)
- await(service.retrieve) must beNone
+ await(service.retrieve) must beNone
+ }
}
"return None if authenticator fingerprint doesn't match current fingerprint" in new WithApplication with Context {
- fingerprintGenerator.generate(any) returns "false"
- settings.useFingerprinting returns true
- authenticator.fingerprint returns Some("test")
+ override def running() = {
+ when(fingerprintGenerator.generate(any)).thenReturn("false")
+ when(settings.useFingerprinting).thenReturn(true)
+ when(authenticator.fingerprint).thenReturn(Some("test"))
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString()))
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString()))
- await(service.retrieve) must beNone
+ await(service.retrieve) must beNone
+ }
}
"return authenticator if authenticator fingerprint matches current fingerprint" in new WithApplication with Context {
- fingerprintGenerator.generate(any) returns "test"
- settings.useFingerprinting returns true
- authenticator.fingerprint returns Some("test")
+ override def running() = {
+ when(fingerprintGenerator.generate(any)).thenReturn("test")
+ when(settings.useFingerprinting).thenReturn(true)
+ when(authenticator.fingerprint).thenReturn(Some("test"))
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString()))
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString()))
- await(service.retrieve) must beSome(authenticator)
+ await(service.retrieve) must beSome(authenticator)
+ }
}
"return authenticator if fingerprinting is disabled" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString()))
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString()))
- settings.useFingerprinting returns false
+ when(settings.useFingerprinting).thenReturn(false)
- await(service.retrieve) must beSome(authenticator)
+ await(service.retrieve) must beSome(authenticator)
+ }
}
"decode an authenticator" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- .withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString()))
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ .withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString()))
- settings.useFingerprinting returns false
+ when(settings.useFingerprinting).thenReturn(false)
- await(service.retrieve) must beSome(authenticator)
+ await(service.retrieve) must beSome(authenticator)
+ }
}
"throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString()))
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString()))
- fingerprintGenerator.generate(any) throws new RuntimeException("Could not generate fingerprint")
- settings.useFingerprinting returns true
+ when(fingerprintGenerator.generate(any)).thenThrow(new RuntimeException("Could not generate fingerprint"))
+ when(settings.useFingerprinting).thenReturn(true)
- await(service.retrieve) must throwA[AuthenticatorRetrievalException].like {
- case e =>
- e.getMessage must startWith(RetrieveError.format(ID, ""))
+ await(service.retrieve) must throwA[AuthenticatorRetrievalException].like {
+ case e =>
+ e.getMessage must startWith(RetrieveError.format(ID, ""))
+ }
}
}
}
"The `init` method of the service" should {
"return a session with an encoded authenticator" in new WithApplication with AppContext {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
- val session = await(service.init(authenticator))
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
+ val session = await(service.init(authenticator))
- session must be equalTo sessionCookieBaker.deserialize(Map(settings.sessionKey -> data))
+ session must be equalTo sessionCookieBaker.deserialize(Map(settings.sessionKey -> data))
+ }
}
"override existing authenticator from request" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing")
- val session = await(service.init(authenticator))
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing")
+ val session = await(service.init(authenticator))
- unserialize(session.get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator
+ unserialize(session.get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator
+ }
}
"keep non authenticator related session data" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("test" -> "test")
- val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
- val session = await(service.init(authenticator))
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("test" -> "test")
+ val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
+ val session = await(service.init(authenticator))
- session.get(settings.sessionKey) should beSome(data)
- session.get("test") should beSome("test")
+ session.get(settings.sessionKey) should beSome(data)
+ session.get("test") should beSome("test")
+ }
}
}
"The result `embed` method of the service" should {
"return the response with the session" in new WithApplication with AppContext {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
- val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok)
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
+ val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok)
- session(result).get(settings.sessionKey) should beSome(data)
+ session(result).get(settings.sessionKey) should beSome(data)
+ }
}
"override existing authenticator from request" in new WithApplication with AppContext {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing")
- val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
- val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok)
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing")
+ val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
+ val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok)
- session(result).get(settings.sessionKey) should beSome(data)
+ session(result).get(settings.sessionKey) should beSome(data)
+ }
}
"keep non authenticator related session data" in new WithApplication with AppContext {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep")
- val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
- val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok.addingToSession(
- "result-other" -> "keep"))
-
- session(result).get(settings.sessionKey) should beSome(data)
- session(result).get("request-other") should beSome("keep")
- session(result).get("result-other") should beSome("keep")
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep")
+ val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
+ val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok.addingToSession(
+ "result-other" -> "keep"))
+
+ session(result).get(settings.sessionKey) should beSome(data)
+ session(result).get("request-other") should beSome("keep")
+ session(result).get("result-other") should beSome("keep")
+ }
}
}
"The request `embed` method of the service" should {
"return the request with the session" in new WithApplication with AppContext {
- val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
- val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> data))
- val request = service.embed(session, FakeRequest())
+ override def running() = {
+ val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
+ val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> data))
+ val request = service.embed(session, FakeRequest())
- request.session.get(settings.sessionKey) should beSome(data)
+ request.session.get(settings.sessionKey) should beSome(data)
+ }
}
"override an existing session" in new WithApplication with AppContext {
- val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
- val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> data))
- val request = service.embed(session, FakeRequest().withSession(settings.sessionKey -> "test"))
+ override def running() = {
+ val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
+ val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> data))
+ val request = service.embed(session, FakeRequest().withSession(settings.sessionKey -> "test"))
- request.session.get(settings.sessionKey) should beSome(data)
+ request.session.get(settings.sessionKey) should beSome(data)
+ }
}
"should not remove an existing session key" in new WithApplication with AppContext {
- val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> "test"))
- val request = service.embed(session, FakeRequest().withSession("existing" -> "test"))
+ override def running() = {
+ val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> "test"))
+ val request = service.embed(session, FakeRequest().withSession("existing" -> "test"))
- request.session.get("existing") should beSome("test")
- request.session.get(settings.sessionKey) should beSome("test")
+ request.session.get("existing") should beSome("test")
+ request.session.get(settings.sessionKey) should beSome("test")
+ }
}
"keep other request parts" in new WithApplication with AppContext {
- val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> "test"))
- val request = service.embed(session, FakeRequest().withCookies(Cookie("test", "test")))
-
- request.session.get(settings.sessionKey) should beSome("test")
- request.cookies.get("test") should beSome[Cookie].which { c =>
- c.name must be equalTo "test"
- c.value must be equalTo "test"
+ override def running() = {
+ val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> "test"))
+ val request = service.embed(session, FakeRequest().withCookies(Cookie("test", "test")))
+
+ request.session.get(settings.sessionKey) should beSome("test")
+ request.cookies.get("test") should beSome[Cookie].which { c =>
+ c.name must be equalTo "test"
+ c.value must be equalTo "test"
+ }
}
}
}
"The `touch` method of the service" should {
"update the last used date if idle timeout is defined" in new WithApplication with Context {
- settings.authenticatorIdleTimeout returns Some(1 second)
- clock.now returns ZonedDateTime.now
-
- service.touch(authenticator) must beLeft[SessionAuthenticator].like {
- case a =>
- a.lastUsedDateTime must be equalTo clock.now
+ override def running() = {
+ when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second))
+ when(clock.now).thenReturn(ZonedDateTime.now)
+
+ service.touch(authenticator) must beLeft[SessionAuthenticator].like {
+ case a =>
+ a.lastUsedDateTime must be equalTo clock.now
+ }
}
}
"do not update the last used date if idle timeout is not defined" in new WithApplication with Context {
- settings.authenticatorIdleTimeout returns None
- clock.now returns ZonedDateTime.now
-
- service.touch(authenticator) must beRight[SessionAuthenticator].like {
- case a =>
- a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime
+ override def running() = {
+ when(settings.authenticatorIdleTimeout).thenReturn(None)
+ when(clock.now).thenReturn(ZonedDateTime.now)
+
+ service.touch(authenticator) must beRight[SessionAuthenticator].like {
+ case a =>
+ a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime
+ }
}
}
}
"The `update` method of the service" should {
"update the session" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
- val result = service.update(authenticator, Results.Ok)
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString())
+ val result = service.update(authenticator, Results.Ok)
- status(result) must be equalTo OK
- session(result).get(settings.sessionKey) should beSome(data)
+ status(result) must be equalTo OK
+ session(result).get(settings.sessionKey) should beSome(data)
+ }
}
"override existing authenticator from request" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing")
- val result = service.update(authenticator, Results.Ok)
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing")
+ val result = service.update(authenticator, Results.Ok)
- unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator
+ unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator
+ }
}
"non authenticator related session data" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep")
- val result = service.update(authenticator, Results.Ok.addingToSession(
- "result-other" -> "keep"))
-
- unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator
- session(result).get("request-other") should beSome("keep")
- session(result).get("result-other") should beSome("keep")
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep")
+ val result = service.update(authenticator, Results.Ok.addingToSession(
+ "result-other" -> "keep"))
+
+ unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator
+ session(result).get("request-other") should beSome("keep")
+ session(result).get("result-other") should beSome("keep")
+ }
}
"throws an AuthenticatorUpdateException exception if an error occurred during update" in new Context {
implicit val request: FakeRequest[AnyContentAsEmpty.type] = spy(FakeRequest())
- request.session throws new RuntimeException("Cannot get session")
+ when(request.session).thenThrow(new RuntimeException("Cannot get session"))
await(service.update(authenticator, Results.Ok)) must throwA[AuthenticatorUpdateException].like {
case e =>
@@ -373,49 +424,55 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
"The `renew` method of the service" should {
"renew the session" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val now = ZonedDateTime.now
- val data = authenticatorEncoder.encode(Json.toJson(authenticator.copy(
- lastUsedDateTime = now,
- expirationDateTime = now + settings.authenticatorExpiry)).toString())
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val now = ZonedDateTime.now
+ val data = authenticatorEncoder.encode(Json.toJson(authenticator.copy(
+ lastUsedDateTime = now,
+ expirationDateTime = now + settings.authenticatorExpiry)).toString())
- settings.useFingerprinting returns false
- clock.now returns now
+ when(settings.useFingerprinting).thenReturn(false)
+ when(clock.now).thenReturn(now)
- val result = service.renew(authenticator, Results.Ok)
+ val result = service.renew(authenticator, Results.Ok)
- session(result).get(settings.sessionKey) should beSome(data)
+ session(result).get(settings.sessionKey) should beSome(data)
+ }
}
"override existing authenticator from request" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing")
- val now = ZonedDateTime.now
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing")
+ val now = ZonedDateTime.now
- settings.useFingerprinting returns false
- clock.now returns now
+ when(settings.useFingerprinting).thenReturn(false)
+ when(clock.now).thenReturn(now)
- val result = service.renew(authenticator, Results.Ok)
+ val result = service.renew(authenticator, Results.Ok)
- unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator.copy(
- lastUsedDateTime = now,
- expirationDateTime = now + settings.authenticatorExpiry)
+ unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator.copy(
+ lastUsedDateTime = now,
+ expirationDateTime = now + settings.authenticatorExpiry)
+ }
}
"non authenticator related session data" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep")
- val now = ZonedDateTime.now
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep")
+ val now = ZonedDateTime.now
- settings.useFingerprinting returns false
- clock.now returns now
+ when(settings.useFingerprinting).thenReturn(false)
+ when(clock.now).thenReturn(now)
- val result = service.renew(authenticator, Results.Ok.addingToSession(
- "result-other" -> "keep"))
+ val result = service.renew(authenticator, Results.Ok.addingToSession(
+ "result-other" -> "keep"))
- unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator.copy(
- lastUsedDateTime = now,
- expirationDateTime = now + settings.authenticatorExpiry)
- session(result).get("request-other") should beSome("keep")
- session(result).get("result-other") should beSome("keep")
+ unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator.copy(
+ lastUsedDateTime = now,
+ expirationDateTime = now + settings.authenticatorExpiry)
+ session(result).get("request-other") should beSome("keep")
+ session(result).get("result-other") should beSome("keep")
+ }
}
"throws an AuthenticatorRenewalException exception if an error occurred during renewal" in new Context {
@@ -423,9 +480,9 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
val now = ZonedDateTime.now
val okResult = (_: Authenticator) => Future.successful(Results.Ok)
- request.session throws new RuntimeException("Cannot get session")
- settings.useFingerprinting returns false
- clock.now returns now
+ when(request.session).thenThrow(new RuntimeException("Cannot get session"))
+ when(settings.useFingerprinting).thenReturn(false)
+ when(clock.now).thenReturn(now)
await(service.renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like {
case e =>
@@ -436,32 +493,38 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
"The `discard` method of the service" should {
"discard the authenticator from session" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val result = service.discard(authenticator, Results.Ok.withSession(
- settings.sessionKey -> "test"))
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val result = service.discard(authenticator, Results.Ok.withSession(
+ settings.sessionKey -> "test"))
- session(result).get(settings.sessionKey) should beNone
+ session(result).get(settings.sessionKey) should beNone
+ }
}
"non authenticator related session data" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep", settings.sessionKey -> "test")
- val result = service.discard(authenticator, Results.Ok.addingToSession(
- "result-other" -> "keep"))
-
- session(result).get(settings.sessionKey) should beNone
- session(result).get("request-other") should beSome("keep")
- session(result).get("result-other") should beSome("keep")
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep", settings.sessionKey -> "test")
+ val result = service.discard(authenticator, Results.Ok.addingToSession(
+ "result-other" -> "keep"))
+
+ session(result).get(settings.sessionKey) should beNone
+ session(result).get("request-other") should beSome("keep")
+ session(result).get("result-other") should beSome("keep")
+ }
}
"throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new WithApplication with Context {
- implicit val request: FakeRequest[AnyContentAsEmpty.type] = spy(FakeRequest()).withSession(settings.sessionKey -> "test")
- val result = mock[Result]
+ override def running() = {
+ implicit val request: FakeRequest[AnyContentAsEmpty.type] = spy(FakeRequest()).withSession(settings.sessionKey -> "test")
+ val result = mock[Result]
- result.removingFromSession(any)(any) throws new RuntimeException("Cannot get session")
+ when(result.removingFromSession(any)(any)).thenThrow(new RuntimeException("Cannot get session"))
- await(service.discard(authenticator, result)) must throwA[AuthenticatorDiscardingException].like {
- case e =>
- e.getMessage must startWith(DiscardError.format(ID, ""))
+ await(service.discard(authenticator, result)) must throwA[AuthenticatorDiscardingException].like {
+ case e =>
+ e.getMessage must startWith(DiscardError.format(ID, ""))
+ }
}
}
}
@@ -474,7 +537,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
/**
* The ID generator implementation.
*/
- lazy val fingerprintGenerator = mock[FingerprintGenerator].smart
+ lazy val fingerprintGenerator = mockSmart[FingerprintGenerator]
/**
* The authenticator encoder implementation.
@@ -484,7 +547,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan
/**
* The clock implementation.
*/
- lazy val clock = mock[Clock].smart
+ lazy val clock = mockSmart[Clock]
/**
* The settings.
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala
index 48f94168..5a138f4a 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala
@@ -21,6 +21,7 @@ import io.github.honeycombcheesecake.play.silhouette.api.exceptions._
import io.github.honeycombcheesecake.play.silhouette.api.util._
import io.github.honeycombcheesecake.play.silhouette.impl.providers.PasswordProvider._
import play.api.test.{ FakeRequest, WithApplication }
+import org.mockito.Mockito._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -32,103 +33,123 @@ class BasicAuthProviderSpec extends PasswordProviderSpec {
"The `authenticate` method" should {
"throw ConfigurationException if unsupported hasher is stored" in new WithApplication with Context {
- val passwordInfo = PasswordInfo("unknown", "hashed(s3cr3t)")
- val loginInfo = LoginInfo(provider.id, credentials.identifier)
- val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
+ override def running() = {
+ val passwordInfo = PasswordInfo("unknown", "hashed(s3cr3t)")
+ val loginInfo = LoginInfo(provider.id, credentials.identifier)
+ val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo))
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo)))
- await(provider.authenticate(request)) must throwA[ConfigurationException].like {
- case e => e.getMessage must beEqualTo(HasherIsNotRegistered.format(provider.id, "unknown", "foo, bar"))
+ await(provider.authenticate(request)) must throwA[ConfigurationException].like {
+ case e => e.getMessage must beEqualTo(HasherIsNotRegistered.format(provider.id, "unknown", "foo, bar"))
+ }
}
}
"return None if no auth info could be found for the given credentials" in new WithApplication with Context {
- val loginInfo = new LoginInfo(provider.id, credentials.identifier)
- val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
+ override def running() = {
+ val loginInfo = new LoginInfo(provider.id, credentials.identifier)
+ val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(None)
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(None))
- await(provider.authenticate(request)) must beNone
+ await(provider.authenticate(request)) must beNone
+ }
}
"return None if password does not match" in new WithApplication with Context {
- val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
- val loginInfo = LoginInfo(provider.id, credentials.identifier)
- val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
+ override def running() = {
+ val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
+ val loginInfo = LoginInfo(provider.id, credentials.identifier)
+ val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
- fooHasher.matches(passwordInfo, credentials.password) returns false
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo))
+ when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(false)
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo)))
- await(provider.authenticate(request)) must beNone
+ await(provider.authenticate(request)) must beNone
+ }
}
"return None if provider isn't responsible" in new WithApplication with Context {
- await(provider.authenticate(FakeRequest())) must beNone
+ override def running() = {
+ await(provider.authenticate(FakeRequest())) must beNone
+ }
}
"return None for wrong encoded credentials" in new WithApplication with Context {
- val request = FakeRequest().withHeaders(AUTHORIZATION -> "wrong")
+ override def running() = {
+ val request = FakeRequest().withHeaders(AUTHORIZATION -> "wrong")
- await(provider.authenticate(request)) must beNone
+ await(provider.authenticate(request)) must beNone
+ }
}
"return login info if passwords does match" in new WithApplication with Context {
- val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
- val loginInfo = LoginInfo(provider.id, credentials.identifier)
- val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
+ override def running() = {
+ val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
+ val loginInfo = LoginInfo(provider.id, credentials.identifier)
+ val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
- fooHasher.matches(passwordInfo, credentials.password) returns true
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo))
+ when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true)
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo)))
- await(provider.authenticate(request)) must beSome(loginInfo)
+ await(provider.authenticate(request)) must beSome(loginInfo)
+ }
}
"handle a colon in a password" in new WithApplication with Context {
- val credentialsWithColon = Credentials("apollonia.vanova@watchmen.com", "s3c:r3t")
- val passwordInfo = PasswordInfo("foo", "hashed(s3c:r3t)")
- val loginInfo = LoginInfo(provider.id, credentialsWithColon.identifier)
- val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentialsWithColon))
+ override def running() = {
+ val credentialsWithColon = Credentials("apollonia.vanova@watchmen.com", "s3c:r3t")
+ val passwordInfo = PasswordInfo("foo", "hashed(s3c:r3t)")
+ val loginInfo = LoginInfo(provider.id, credentialsWithColon.identifier)
+ val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentialsWithColon))
- fooHasher.matches(passwordInfo, credentialsWithColon.password) returns true
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo))
+ when(fooHasher.matches(passwordInfo, credentialsWithColon.password)).thenReturn(true)
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo)))
- await(provider.authenticate(request)) must beSome(loginInfo)
+ await(provider.authenticate(request)) must beSome(loginInfo)
+ }
}
"re-hash password with new hasher if hasher is deprecated" in new WithApplication with Context {
- val passwordInfo = PasswordInfo("bar", "hashed(s3cr3t)")
- val loginInfo = LoginInfo(provider.id, credentials.identifier)
- val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
-
- fooHasher.hash(credentials.password) returns passwordInfo
- barHasher.matches(passwordInfo, credentials.password) returns true
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo))
- authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo) returns Future.successful(passwordInfo)
-
- await(provider.authenticate(request)) must beSome(loginInfo)
- there was one(authInfoRepository).update(loginInfo, passwordInfo)
+ override def running() = {
+ val passwordInfo = PasswordInfo("bar", "hashed(s3cr3t)")
+ val loginInfo = LoginInfo(provider.id, credentials.identifier)
+ val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
+
+ when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo)
+ when(barHasher.matches(passwordInfo, credentials.password)).thenReturn(true)
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo)))
+ when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo))
+
+ await(provider.authenticate(request)) must beSome(loginInfo)
+ verify(authInfoRepository).update(loginInfo, passwordInfo)
+ }
}
"re-hash password with new hasher if password info is deprecated" in new WithApplication with Context {
- val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
- val loginInfo = LoginInfo(provider.id, credentials.identifier)
- val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
-
- fooHasher.isDeprecated(passwordInfo) returns Some(true)
- fooHasher.hash(credentials.password) returns passwordInfo
- fooHasher.matches(passwordInfo, credentials.password) returns true
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo))
- authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo) returns Future.successful(passwordInfo)
-
- await(provider.authenticate(request)) must beSome(loginInfo)
- there was one(authInfoRepository).update(loginInfo, passwordInfo)
+ override def running() = {
+ val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
+ val loginInfo = LoginInfo(provider.id, credentials.identifier)
+ val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials))
+
+ when(fooHasher.isDeprecated(passwordInfo)).thenReturn(Some(true))
+ when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo)
+ when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true)
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo)))
+ when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo))
+
+ await(provider.authenticate(request)) must beSome(loginInfo)
+ verify(authInfoRepository).update(loginInfo, passwordInfo)
+ }
}
"return None if Authorization method is not Basic and Base64 decoded header has ':'" in new WithApplication with Context {
- val request = FakeRequest().withHeaders(AUTHORIZATION -> Base64.encode("NotBasic foo:bar"))
+ override def running() = {
+ val request = FakeRequest().withHeaders(AUTHORIZATION -> Base64.encode("NotBasic foo:bar"))
- await(provider.authenticate(request)) must beNone
+ await(provider.authenticate(request)) must beNone
+ }
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala
index 93ca5b6b..72065b2d 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala
@@ -21,6 +21,7 @@ import io.github.honeycombcheesecake.play.silhouette.api.util.{ Credentials, Pas
import io.github.honeycombcheesecake.play.silhouette.impl.exceptions.{ IdentityNotFoundException, InvalidPasswordException }
import io.github.honeycombcheesecake.play.silhouette.impl.providers.PasswordProvider._
import play.api.test.WithApplication
+import org.mockito.Mockito._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -32,73 +33,85 @@ class CredentialsProviderSpec extends PasswordProviderSpec {
"The `authenticate` method" should {
"throw IdentityNotFoundException if no auth info could be found for the given credentials" in new WithApplication with Context {
- val loginInfo = new LoginInfo(provider.id, credentials.identifier)
+ override def running() = {
+ val loginInfo = new LoginInfo(provider.id, credentials.identifier)
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(None)
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(None))
- await(provider.authenticate(credentials)) must throwA[IdentityNotFoundException].like {
- case e => e.getMessage must beEqualTo(PasswordInfoNotFound.format(provider.id, loginInfo))
+ await(provider.authenticate(credentials)) must throwA[IdentityNotFoundException].like {
+ case e => e.getMessage must beEqualTo(PasswordInfoNotFound.format(provider.id, loginInfo))
+ }
}
}
"throw InvalidPasswordException if password does not match" in new WithApplication with Context {
- val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
- val loginInfo = LoginInfo(provider.id, credentials.identifier)
+ override def running() = {
+ val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
+ val loginInfo = LoginInfo(provider.id, credentials.identifier)
- fooHasher.matches(passwordInfo, credentials.password) returns false
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo))
+ when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(false)
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo)))
- await(provider.authenticate(credentials)) must throwA[InvalidPasswordException].like {
- case e => e.getMessage must beEqualTo(PasswordDoesNotMatch.format(provider.id))
+ await(provider.authenticate(credentials)) must throwA[InvalidPasswordException].like {
+ case e => e.getMessage must beEqualTo(PasswordDoesNotMatch.format(provider.id))
+ }
}
}
"throw ConfigurationException if unsupported hasher is stored" in new WithApplication with Context {
- val passwordInfo = PasswordInfo("unknown", "hashed(s3cr3t)")
- val loginInfo = LoginInfo(provider.id, credentials.identifier)
+ override def running() = {
+ val passwordInfo = PasswordInfo("unknown", "hashed(s3cr3t)")
+ val loginInfo = LoginInfo(provider.id, credentials.identifier)
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo))
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo)))
- await(provider.authenticate(credentials)) must throwA[ConfigurationException].like {
- case e => e.getMessage must beEqualTo(HasherIsNotRegistered.format(provider.id, "unknown", "foo, bar"))
+ await(provider.authenticate(credentials)) must throwA[ConfigurationException].like {
+ case e => e.getMessage must beEqualTo(HasherIsNotRegistered.format(provider.id, "unknown", "foo, bar"))
+ }
}
}
"return login info if passwords does match" in new WithApplication with Context {
- val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
- val loginInfo = LoginInfo(provider.id, credentials.identifier)
+ override def running() = {
+ val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
+ val loginInfo = LoginInfo(provider.id, credentials.identifier)
- fooHasher.matches(passwordInfo, credentials.password) returns true
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo))
+ when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true)
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo)))
- await(provider.authenticate(credentials)) must be equalTo loginInfo
+ await(provider.authenticate(credentials)) must be equalTo loginInfo
+ }
}
"re-hash password with new hasher if hasher is deprecated" in new WithApplication with Context {
- val passwordInfo = PasswordInfo("bar", "hashed(s3cr3t)")
- val loginInfo = LoginInfo(provider.id, credentials.identifier)
+ override def running() = {
+ val passwordInfo = PasswordInfo("bar", "hashed(s3cr3t)")
+ val loginInfo = LoginInfo(provider.id, credentials.identifier)
- fooHasher.hash(credentials.password) returns passwordInfo
- barHasher.matches(passwordInfo, credentials.password) returns true
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo))
- authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo) returns Future.successful(passwordInfo)
+ when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo)
+ when(barHasher.matches(passwordInfo, credentials.password)).thenReturn(true)
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo)))
+ when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo))
- await(provider.authenticate(credentials)) must be equalTo loginInfo
- there was one(authInfoRepository).update(loginInfo, passwordInfo)
+ await(provider.authenticate(credentials)) must be equalTo loginInfo
+ verify(authInfoRepository).update(loginInfo, passwordInfo)
+ }
}
"re-hash password with new hasher if hasher is deprecated" in new WithApplication with Context {
- val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
- val loginInfo = LoginInfo(provider.id, credentials.identifier)
-
- fooHasher.isDeprecated(passwordInfo) returns Some(true)
- fooHasher.hash(credentials.password) returns passwordInfo
- fooHasher.matches(passwordInfo, credentials.password) returns true
- authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo))
- authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo) returns Future.successful(passwordInfo)
-
- await(provider.authenticate(credentials)) must be equalTo loginInfo
- there was one(authInfoRepository).update(loginInfo, passwordInfo)
+ override def running() = {
+ val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)")
+ val loginInfo = LoginInfo(provider.id, credentials.identifier)
+
+ when(fooHasher.isDeprecated(passwordInfo)).thenReturn(Some(true))
+ when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo)
+ when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true)
+ when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo)))
+ when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo))
+
+ await(provider.authenticate(credentials)) must be equalTo loginInfo
+ verify(authInfoRepository).update(loginInfo, passwordInfo)
+ }
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/DefaultSocialStateHandlerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/DefaultSocialStateHandlerSpec.scala
index b6e28e70..053e2611 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/DefaultSocialStateHandlerSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/DefaultSocialStateHandlerSpec.scala
@@ -21,11 +21,13 @@ import io.github.honeycombcheesecake.play.silhouette.api.util.ExtractableRequest
import io.github.honeycombcheesecake.play.silhouette.impl.providers.DefaultSocialStateHandler._
import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialStateItem.ItemStructure
import org.specs2.matcher.JsonMatchers
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.libs.json.Json
import play.api.mvc.{ AnyContentAsEmpty, Results }
import play.api.test.{ FakeRequest, PlaySpecification }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
+import test.Helper.mockSmart
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -34,11 +36,11 @@ import scala.util.{ Failure, Success }
/**
* Test case for the [[DefaultSocialStateHandler]] class.
*/
-class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with JsonMatchers {
+class DefaultSocialStateHandlerSpec extends PlaySpecification with JsonMatchers {
"The `withHandler` method" should {
"return a new state handler with the given item handler added" in new Context {
- val newHandler = mock[SocialStateItemHandler].smart
+ val newHandler = mockSmart[SocialStateItemHandler]
stateHandler.handlers.size must be equalTo 2
stateHandler.withHandler(newHandler).handlers.size must be equalTo 3
@@ -47,8 +49,8 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with
"The `state` method" should {
"return the social state" in new Context {
- Default.itemHandler.item returns Future.successful(Default.item)
- Publishable.itemHandler.item returns Future.successful(Publishable.item)
+ when(Default.itemHandler.item).thenReturn(Future.successful(Default.item))
+ when(Publishable.itemHandler.item).thenReturn(Future.successful(Publishable.item))
await(stateHandler.state) must be equalTo state
}
@@ -66,13 +68,13 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with
}
"return the serialized social state" in new Context {
- Default.itemHandler.canHandle(Publishable.item) returns None
- Default.itemHandler.canHandle(Default.item) returns Some(Default.item)
- Default.itemHandler.serialize(Default.item) returns Default.structure
+ when(Default.itemHandler.canHandle(Publishable.item)).thenReturn(None)
+ when(Default.itemHandler.canHandle(Default.item)).thenReturn(Some(Default.item))
+ when(Default.itemHandler.serialize(Default.item)).thenReturn(Default.structure)
- Publishable.itemHandler.canHandle(Default.item) returns None
- Publishable.itemHandler.canHandle(Publishable.item) returns Some(Publishable.item)
- Publishable.itemHandler.serialize(Publishable.item) returns Publishable.structure
+ when(Publishable.itemHandler.canHandle(Default.item)).thenReturn(None)
+ when(Publishable.itemHandler.canHandle(Publishable.item)).thenReturn(Some(Publishable.item))
+ when(Publishable.itemHandler.serialize(Publishable.item)).thenReturn(Publishable.structure)
stateHandler.serialize(state) must be equalTo s"${Publishable.structure.asString}.${Default.structure.asString}"
}
@@ -85,7 +87,7 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with
await(stateHandler.unserialize(""))
- there was no(signer).extract(any[String]())
+ verify(signer, never()).extract(any[String]())
}
"throw an Exception for an empty string" in new Context {
@@ -111,8 +113,8 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with
implicit val request: ExtractableRequest[AnyContentAsEmpty.type] = new ExtractableRequest(FakeRequest())
val serialized = s"${Default.structure.asString}"
- Default.itemHandler.canHandle(any[ItemStructure]())(any()) returns false
- Publishable.itemHandler.canHandle(any[ItemStructure]())(any()) returns false
+ when(Default.itemHandler.canHandle(any[ItemStructure]())(any())).thenReturn(false)
+ when(Publishable.itemHandler.canHandle(any[ItemStructure]())(any())).thenReturn(false)
await(stateHandler.unserialize(serialized)) must throwA[ProviderException].like {
case e =>
@@ -124,13 +126,13 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with
implicit val request: ExtractableRequest[AnyContentAsEmpty.type] = new ExtractableRequest(FakeRequest())
val serialized = s"${Default.structure.asString}.${Publishable.structure.asString}"
- Default.itemHandler.canHandle(Publishable.structure) returns false
- Default.itemHandler.canHandle(Default.structure) returns true
- Default.itemHandler.unserialize(Default.structure) returns Future.successful(Default.item)
+ when(Default.itemHandler.canHandle(Publishable.structure)).thenReturn(false)
+ when(Default.itemHandler.canHandle(Default.structure)).thenReturn(true)
+ when(Default.itemHandler.unserialize(Default.structure)).thenReturn(Future.successful(Default.item))
- Publishable.itemHandler.canHandle(Default.structure) returns false
- Publishable.itemHandler.canHandle(Publishable.structure) returns true
- Publishable.itemHandler.unserialize(Publishable.structure) returns Future.successful(Publishable.item)
+ when(Publishable.itemHandler.canHandle(Default.structure)).thenReturn(false)
+ when(Publishable.itemHandler.canHandle(Publishable.structure)).thenReturn(true)
+ when(Publishable.itemHandler.unserialize(Publishable.structure)).thenReturn(Future.successful(Publishable.item))
await(stateHandler.unserialize(serialized)) must be equalTo SocialState(Set(Default.item, Publishable.item))
}
@@ -142,9 +144,9 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with
val result = Results.Ok
val publishedResult = Results.Ok.withHeaders("X-PUBLISHED" -> "true")
- Publishable.itemHandler.publish(Publishable.item, result) returns publishedResult
- Publishable.itemHandler.canHandle(Default.item) returns None
- Publishable.itemHandler.canHandle(Publishable.item) returns Some(Publishable.item)
+ when(Publishable.itemHandler.publish(Publishable.item, result)).thenReturn(publishedResult)
+ when(Publishable.itemHandler.canHandle(Default.item)).thenReturn(None)
+ when(Publishable.itemHandler.canHandle(Publishable.item)).thenReturn(Some(Publishable.item))
stateHandler.publish(result, state) must be equalTo publishedResult
}
@@ -153,8 +155,8 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
val result = Results.Ok
- Publishable.itemHandler.canHandle(Default.item) returns None
- Publishable.itemHandler.canHandle(Publishable.item) returns None
+ when(Publishable.itemHandler.canHandle(Default.item)).thenReturn(None)
+ when(Publishable.itemHandler.canHandle(Publishable.item)).thenReturn(None)
stateHandler.publish(result, state) must be equalTo result
}
@@ -173,7 +175,7 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with
type Item = DefaultItem
}
object Default {
- val itemHandler = mock[DefaultItemHandler].smart
+ val itemHandler = mockSmart[DefaultItemHandler]
val item = DefaultItem()
val structure = ItemStructure("default", Json.obj())
}
@@ -186,7 +188,7 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with
type Item = PublishableItem
}
object Publishable {
- val itemHandler = mock[PublishableItemHandler].smart
+ val itemHandler = mockSmart[PublishableItemHandler]
val item = PublishableItem()
val structure = ItemStructure("publishable", Json.obj())
}
@@ -197,10 +199,10 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with
* The signer returns the same value as passed to the methods. This is enough for testing.
*/
val signer = {
- val c = mock[Signer].smart
- c.sign(any()) answers { p: Any => p.asInstanceOf[String] }
- c.extract(any()) answers { p: Any =>
- p.asInstanceOf[String] match {
+ val c = mockSmart[Signer]
+ when(c.sign(any())).thenAnswer(_.getArgument(0).asInstanceOf[String])
+ when(c.extract(any())).thenAnswer { p =>
+ p.getArgument(0).asInstanceOf[String] match {
case "" => Failure(new RuntimeException("Wrong state format"))
case s => Success(s)
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala
index 8450b3b4..3228ce6e 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala
@@ -20,12 +20,16 @@ import io.github.honeycombcheesecake.play.silhouette.impl.exceptions.{ AccessDen
import io.github.honeycombcheesecake.play.silhouette.impl.providers.OAuth1Provider._
import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.services.PlayOAuth1Service
import org.specs2.matcher.ThrownExpectations
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.mvc.{ AnyContent, AnyContentAsEmpty, Result, Results }
import play.api.test.{ FakeHeaders, FakeRequest, WithApplication }
import play.mvc.Http.HeaderNames
+import org.hamcrest.core.IsAnything
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
+import org.mockito.hamcrest.MockitoHamcrest
import test.SocialProviderSpec
+import test.Helper.mock
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -41,100 +45,119 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] {
"The provider" should {
val c = context
"throw a RuntimeException if the unsafe 1.0 specification should be used" in new WithApplication {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Denied + "=")
- c.oAuthService.use10a returns false
- c.provider.authenticate() must throwA[RuntimeException]
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Denied + "=")
+ when(c.oAuthService.use10a).thenReturn(false)
+ c.provider.authenticate() must throwA[RuntimeException]
+ }
}
}
"The authenticate method" should {
val c = context
"fail with an AccessDeniedException if denied key exists in query string" in new WithApplication {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Denied + "=")
- failed[AccessDeniedException](c.provider.authenticate()) {
- case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, ""))
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Denied + "=")
+ failed[AccessDeniedException](c.provider.authenticate()) {
+ case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, ""))
+ }
}
}
"fail with an UnexpectedResponseException if request token cannot be retrieved" in new WithApplication {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL) returns Future.failed(new Exception(""))
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ when(c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL)).thenReturn(Future.failed(new Exception("")))
- failed[UnexpectedResponseException](c.provider.authenticate()) {
- case e => e.getMessage must startWith(ErrorRequestToken.format(c.provider.id, ""))
+ failed[UnexpectedResponseException](c.provider.authenticate()) {
+ case e => e.getMessage must startWith(ErrorRequestToken.format(c.provider.id, ""))
+ }
}
}
"redirect to authorization URL if request token could be retrieved" in new WithApplication {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val serializedTokenSecret = "my.serialized.token.secret"
-
- c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL) returns Future.successful(c.oAuthInfo)
- c.oAuthService.redirectUrl(any()) answers { _: Any => c.oAuthSettings.authorizationURL }
- c.oAuthTokenSecretProvider.build(any())(any(), any()) returns Future.successful(c.oAuthTokenSecret)
- c.oAuthTokenSecretProvider.publish(any(), any())(any()) answers { (a, _) =>
- a.asInstanceOf[Array[Any]](0).asInstanceOf[Result]
- }
-
- result(c.provider.authenticate()) { result =>
- status(result) must equalTo(SEE_OTHER)
- redirectLocation(result) must beSome.which(_ == c.oAuthSettings.authorizationURL)
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val serializedTokenSecret = "my.serialized.token.secret"
+
+ when(c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL)).thenReturn(Future.successful(c.oAuthInfo))
+ when(c.oAuthService.redirectUrl(any())).thenAnswer(_ => c.oAuthSettings.authorizationURL)
+ when(c.oAuthTokenSecretProvider.build(any())(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret))
+ // when(c.oAuthTokenSecretProvider.publish(any(), any())(any())).thenAnswer(_.getArguments.asInstanceOf[Array[Any]](0).asInstanceOf[Result])
+ when(c.oAuthTokenSecretProvider.publish(any(), any())(any())).thenAnswer(_.getArgument(0).asInstanceOf[Result])
+
+ result(c.provider.authenticate()) { result =>
+ status(result) must equalTo(SEE_OTHER)
+ redirectLocation(result) must beSome[String].which(_ == c.oAuthSettings.authorizationURL)
+ }
}
}
"resolves relative redirectURLs before starting the flow" in new WithApplication {
- verifyCallbackURLResolution("/callback-url", secure = false, "http://www.example.com/callback-url")
+ override def running() = {
+ verifyCallbackURLResolution("/callback-url", secure = false, "http://www.example.com/callback-url")
+ }
}
"resolves path relative redirectURLS before starting the flow" in new WithApplication {
- verifyCallbackURLResolution("callback-url", secure = false, "http://www.example.com/request-path/callback-url")
- }
-
- "resolves relative redirectURLs before starting the flow over https" in new WithApplication {
- verifyCallbackURLResolution("/callback-url", secure = true, "https://www.example.com/callback-url")
+ override def running() = {
+ verifyCallbackURLResolution("callback-url", secure = false, "http://www.example.com/request-path/callback-url")
+ }
}
def verifyCallbackURLResolution(callbackURL: String, secure: Boolean, resolvedCallbackURL: String) = {
implicit val req = FakeRequest[AnyContent](
method = GET,
uri = "/request-path/something",
- headers = FakeHeaders(Seq(HeaderNames.HOST -> "www.example.com")),
+ headers = FakeHeaders(Seq((HeaderNames.HOST, "www.example.com"))),
body = AnyContentAsEmpty,
secure = secure)
- c.oAuthSettings.callbackURL returns callbackURL
+ when(c.oAuthSettings.callbackURL).thenReturn(callbackURL)
- c.oAuthService.retrieveRequestToken(any())(any()) returns Future.successful(c.oAuthInfo)
- c.oAuthService.redirectUrl(c.oAuthInfo.token) returns c.oAuthSettings.authorizationURL
- c.oAuthTokenSecretProvider.build(any())(any(), any()) returns Future.successful(c.oAuthTokenSecret)
- c.oAuthTokenSecretProvider.publish(any(), any())(any()) answers { _: Any => Results.Redirect(c.oAuthSettings.authorizationURL) }
+ when(c.oAuthService.retrieveRequestToken(any())(any())).thenReturn(Future.successful(c.oAuthInfo))
+
+ when(c.oAuthService.redirectUrl(c.oAuthInfo.token)).thenAnswer(_ => c.oAuthSettings.authorizationURL)
+
+ when(c.oAuthTokenSecretProvider.build(any())(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret))
+ when(c.oAuthTokenSecretProvider.publish(any(), any())(any())).thenAnswer(_ => Results.Redirect(c.oAuthSettings.authorizationURL))
await(c.provider.authenticate())
- there was one(c.oAuthService).retrieveRequestToken(resolvedCallbackURL)
+ verify(c.oAuthService).retrieveRequestToken(resolvedCallbackURL)
+ }
+
+ "resolves relative redirectURLs before starting the flow over https" in new WithApplication {
+ override def running() = {
+ verifyCallbackURLResolution("/callback-url", secure = true, "https://www.example.com/callback-url")
+ }
}
"fail with an UnexpectedResponseException if access token cannot be retrieved" in new WithApplication {
- val tokenSecret = "my.token.secret"
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + OAuthVerifier + "=my.verifier&" + OAuthToken + "=my.token")
+ override def running() = {
+ val tokenSecret = "my.token.secret"
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + OAuthVerifier + "=my.verifier&" + OAuthToken + "=my.token")
- c.oAuthTokenSecret.value returns tokenSecret
- c.oAuthTokenSecretProvider.retrieve(any(), any()) returns Future.successful(c.oAuthTokenSecret)
- c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier") returns Future.failed(new Exception(""))
+ when(c.oAuthTokenSecret.value).thenReturn(tokenSecret)
+ when(c.oAuthTokenSecretProvider.retrieve(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret))
+ when(c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier")).thenReturn(Future.failed(new Exception("")))
- failed[UnexpectedResponseException](c.provider.authenticate()) {
- case e => e.getMessage must startWith(ErrorAccessToken.format(c.provider.id, ""))
+ failed[UnexpectedResponseException](c.provider.authenticate()) {
+ case e => e.getMessage must startWith(ErrorAccessToken.format(c.provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication {
- val tokenSecret = "my.token.secret"
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + OAuthVerifier + "=my.verifier&" + OAuthToken + "=my.token")
+ override def running() = {
+ val tokenSecret = "my.token.secret"
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + OAuthVerifier + "=my.verifier&" + OAuthToken + "=my.token")
- c.oAuthTokenSecret.value returns tokenSecret
- c.oAuthTokenSecretProvider.retrieve(any(), any()) returns Future.successful(c.oAuthTokenSecret)
- c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier") returns Future.successful(c.oAuthInfo)
+ when(c.oAuthTokenSecret.value).thenReturn(tokenSecret)
+ when(c.oAuthTokenSecretProvider.retrieve(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret))
+ when(c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier")).thenReturn(Future.successful(c.oAuthInfo))
- authInfo(c.provider.authenticate())(_ must be equalTo c.oAuthInfo)
+ authInfo(c.provider.authenticate())(_ must be equalTo c.oAuthInfo)
+ }
}
}
@@ -156,7 +179,7 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] {
/**
* Context for the OAuth1ProviderSpec.
*/
-trait OAuth1ProviderSpecContext extends Scope with Mockito with ThrownExpectations {
+trait OAuth1ProviderSpecContext extends Scope with ThrownExpectations {
abstract class TestSecret extends OAuth1TokenSecret
abstract class TestStateProvider extends OAuth1TokenSecretProvider {
@@ -168,7 +191,7 @@ trait OAuth1ProviderSpecContext extends Scope with Mockito with ThrownExpectatio
*/
lazy val httpLayer = {
val m = mock[MockHTTPLayer]
- m.executionContext returns global
+ when(m.executionContext).thenReturn(global)
m
}
@@ -181,10 +204,11 @@ trait OAuth1ProviderSpecContext extends Scope with Mockito with ThrownExpectatio
* The OAuth1 service mock.
*/
lazy val oAuthService: OAuth1Service = {
+
val s = mock[PlayOAuth1Service]
- s.use10a returns true
- s.withSettings(anyFunction1) returns s
- s.redirectUrl(anyString) returns "TESTING"
+ when(s.use10a).thenReturn(true)
+ when(s.withSettings(MockitoHamcrest.argThat(new IsAnything[OAuth1Settings => OAuth1Settings]))).thenReturn(s)
+ when(s.redirectUrl(anyString)).thenReturn("TESTING")
s
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala
index 7d386e25..f93dc674 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala
@@ -24,13 +24,15 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.OAuth2Provid
import io.github.honeycombcheesecake.play.silhouette.impl.providers.state.UserStateItem
import io.github.honeycombcheesecake.play.silhouette.helpers.Transform._
import org.specs2.matcher.ThrownExpectations
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.libs.json.{ JsValue, Json }
import play.api.mvc.{ AnyContent, AnyContentAsEmpty, Result }
import play.api.test.{ FakeHeaders, FakeRequest, WithApplication }
import play.mvc.Http.HeaderNames
import test.SocialStateProviderSpec
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
+import test.Helper.{ mockSmart, mock }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{ ExecutionContext, Future }
@@ -46,103 +48,125 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So
"The `authenticate` method" should {
val c = context
"fail with an AccessDeniedException if `error` key with value `access_denied` exists in query string" in new WithApplication {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Error + "=" + AccessDenied)
- failed[AccessDeniedException](c.provider.authenticate()) {
- case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, ""))
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Error + "=" + AccessDenied)
+ failed[AccessDeniedException](c.provider.authenticate()) {
+ case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, ""))
+ }
}
}
"fail with an UnexpectedResponseException if `error` key with unspecified value exists in query string" in new WithApplication {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Error + "=unspecified")
- failed[UnexpectedResponseException](c.provider.authenticate()) {
- case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, "unspecified"))
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Error + "=unspecified")
+ failed[UnexpectedResponseException](c.provider.authenticate()) {
+ case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, "unspecified"))
+ }
}
}
"fail with an ConfigurationException if authorization URL is undefined when it's needed" in new WithApplication {
- c.oAuthSettings.authorizationURL match {
- case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass)
- case Some(_) =>
- implicit val req = FakeRequest(GET, "/")
-
- c.stateProvider.serialize(c.state) returns "session-value"
- c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state)
- c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state)
- c.oAuthSettings.authorizationURL returns None
-
- failed[ConfigurationException](c.provider.authenticate()) {
- case e => e.getMessage must startWith(AuthorizationURLUndefined.format(c.provider.id))
- }
+ override def running() = {
+ c.oAuthSettings.authorizationURL match {
+ case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass)
+ case Some(_) =>
+ implicit val req = FakeRequest(GET, "/")
+
+ when(c.stateProvider.serialize(c.state)).thenReturn("session-value")
+ when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.oAuthSettings.authorizationURL).thenReturn(None)
+
+ failed[ConfigurationException](c.provider.authenticate()) {
+ case e => e.getMessage must startWith(AuthorizationURLUndefined.format(c.provider.id))
+ }
+ }
}
}
"redirect to authorization URL if authorization code doesn't exists in request" in new WithApplication {
- c.oAuthSettings.authorizationURL match {
- case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass)
- case Some(authorizationURL) =>
- implicit val req = FakeRequest(GET, "/")
- val sessionKey = "session-key"
- val sessionValue = "session-value"
-
- c.stateProvider.serialize(c.state) returns sessionValue
- c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state)
- c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state)
- c.stateProvider.publish(any, any)(any) answers { (a, _) =>
- val result = a.asInstanceOf[Array[Any]](0).asInstanceOf[Result]
- val state = a.asInstanceOf[Array[Any]](1).asInstanceOf[c.TestState]
-
- result.withSession(sessionKey -> c.stateProvider.serialize(state))
- }
+ override def running() = {
+ c.oAuthSettings.authorizationURL match {
+ case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass)
+ case Some(authorizationURL) =>
+ implicit val req = FakeRequest(GET, "/")
+ val sessionKey = "session-key"
+ val sessionValue = "session-value"
+
+ when(c.stateProvider.serialize(c.state)).thenReturn(sessionValue)
+ when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.stateProvider.publish(any, any)(any)).thenAnswer { m =>
+ val result = m.getArgument(0).asInstanceOf[Result]
+ val state = m.getArgument(1).asInstanceOf[c.TestState]
+
+ result.withSession(sessionKey -> c.stateProvider.serialize(state))
+ }
- result(c.provider.authenticate()) { result =>
- status(result) must equalTo(SEE_OTHER)
- session(result).get(sessionKey) must beSome(c.stateProvider.serialize(c.state))
- redirectLocation(result) must beSome.which { url =>
- val urlParams = c.urlParams(url)
- val redirectParam = c.oAuthSettings.redirectURL match {
- case Some(rUri) => List((RedirectURI, rUri))
- case None => Nil
- }
- val params = c.oAuthSettings.scope.foldLeft(List(
- (ClientID, c.oAuthSettings.clientID),
- (ResponseType, Code),
- (State, urlParams(State))) ++ c.oAuthSettings.authorizationParams.toList ++ redirectParam) {
- case (p, s) => (Scope, s) :: p
+ result(c.provider.authenticate()) { result =>
+ status(result) must equalTo(SEE_OTHER)
+ session(result).get(sessionKey) must beSome(c.stateProvider.serialize(c.state))
+ redirectLocation(result) must beSome[String].which { url =>
+ val urlParams = c.urlParams(url)
+ val redirectParam = c.oAuthSettings.redirectURL match {
+ case Some(rUri) => List((RedirectURI, rUri))
+ case None => Nil
+ }
+ val params = c.oAuthSettings.scope.foldLeft(List(
+ (ClientID, c.oAuthSettings.clientID),
+ (ResponseType, Code),
+ (State, urlParams(State))) ++ c.oAuthSettings.authorizationParams.toList ++ redirectParam) {
+ case (p, s) => (Scope, s) :: p
+ }
+ url must be equalTo (authorizationURL + params.map { p =>
+ encode(p._1, "UTF-8") + "=" + encode(p._2, "UTF-8")
+ }.mkString("?", "&", ""))
}
- url must be equalTo (authorizationURL + params.map { p =>
- encode(p._1, "UTF-8") + "=" + encode(p._2, "UTF-8")
- }.mkString("?", "&", ""))
}
- }
+ }
}
}
"resolves relative redirectURLs before starting the flow" in new WithApplication {
- verifyRelativeRedirectResolution("/redirect-url", secure = false, "http://www.example.com/redirect-url")
+ override def running() = {
+ verifyRelativeRedirectResolution("/redirect-url", secure = false, "http://www.example.com/redirect-url")
+ }
}
"resolves path relative redirectURLs before starting the flow" in new WithApplication {
- verifyRelativeRedirectResolution("redirect-url", secure = false, "http://www.example.com/request-path/redirect-url")
+ override def running() = {
+ verifyRelativeRedirectResolution("redirect-url", secure = false, "http://www.example.com/request-path/redirect-url")
+ }
}
"resolves relative redirectURLs before starting the flow over https" in new WithApplication {
- verifyRelativeRedirectResolution("/redirect-url", secure = true, "https://www.example.com/redirect-url")
+ override def running() = {
+ verifyRelativeRedirectResolution("/redirect-url", secure = true, "https://www.example.com/redirect-url")
+ }
}
"verifying presence of redirect param in the access token post request" in new WithApplication {
- verifyPresenceOrAbsenceOfRedirectURL(Some("/redirect-url"), secure = false, "http://www.example.com/redirect-url")
+ override def running() = {
+ verifyPresenceOrAbsenceOfRedirectURL(Some("/redirect-url"), secure = false, "http://www.example.com/redirect-url")
+ }
}
"verifying presence of redirect param in the access token post request over https" in new WithApplication {
- verifyPresenceOrAbsenceOfRedirectURL(Some("/redirect-url"), secure = true, "https://www.example.com/redirect-url")
+ override def running() = {
+ verifyPresenceOrAbsenceOfRedirectURL(Some("/redirect-url"), secure = true, "https://www.example.com/redirect-url")
+ }
}
"verifying absence of redirect param in the access token post request" in new WithApplication {
- verifyPresenceOrAbsenceOfRedirectURL(None, secure = false, "http://www.example.com/request-path/redirect-url")
+ override def running() = {
+ verifyPresenceOrAbsenceOfRedirectURL(None, secure = false, "http://www.example.com/request-path/redirect-url")
+ }
}
"verifying absence of redirect param in the access token post request over https" in new WithApplication {
- verifyPresenceOrAbsenceOfRedirectURL(None, secure = true, "https://www.example.com/redirect-url")
+ override def running() = {
+ verifyPresenceOrAbsenceOfRedirectURL(None, secure = true, "https://www.example.com/redirect-url")
+ }
}
def verifyRelativeRedirectResolution(redirectURL: String, secure: Boolean, resolvedRedirectURL: String) = {
@@ -152,26 +176,26 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So
implicit val req = FakeRequest[AnyContent](
method = GET,
uri = "/request-path/something",
- headers = FakeHeaders(Seq(HeaderNames.HOST -> "www.example.com")),
+ headers = FakeHeaders(Seq((HeaderNames.HOST, "www.example.com"))),
body = AnyContentAsEmpty,
secure = secure)
val sessionKey = "session-key"
val sessionValue = "session-value"
- c.oAuthSettings.redirectURL returns Some(redirectURL)
+ when(c.oAuthSettings.redirectURL).thenReturn(Some(redirectURL))
- c.stateProvider.serialize(c.state) returns sessionValue
- c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state)
- c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state)
- c.stateProvider.publish(any, any)(any) answers { (a, _) =>
- val result = a.asInstanceOf[Array[Any]](0).asInstanceOf[Result]
+ when(c.stateProvider.serialize(c.state)).thenReturn(sessionValue)
+ when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.stateProvider.publish(any, any)(any)).thenAnswer { m =>
+ val result = m.getArgument(0).asInstanceOf[Result]
result.withSession(sessionKey -> c.stateProvider.serialize(c.state))
}
result(c.provider.authenticate()) { result =>
- redirectLocation(result) must beSome.which { url =>
+ redirectLocation(result) must beSome[String].which { url =>
url must contain(s"$RedirectURI=${encode(resolvedRedirectURL, "UTF-8")}")
}
}
@@ -185,20 +209,20 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So
implicit val req = FakeRequest[AnyContent](
method = GET,
uri = "/request-path/something",
- headers = FakeHeaders(Seq(HeaderNames.HOST -> "www.example.com")),
+ headers = FakeHeaders(Seq((HeaderNames.HOST, "www.example.com"))),
body = AnyContentAsEmpty,
secure = secure)
val sessionKey = "session-key"
val sessionValue = "session-value"
- c.oAuthSettings.redirectURL returns redirectURL
+ when(c.oAuthSettings.redirectURL).thenReturn(redirectURL)
- c.stateProvider.serialize(c.state) returns sessionValue
- c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state)
- c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state)
- c.stateProvider.publish(any, any)(any) answers { (a, _) =>
- val result = a.asInstanceOf[Array[Any]](0).asInstanceOf[Result]
+ when(c.stateProvider.serialize(c.state)).thenReturn(sessionValue)
+ when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.stateProvider.publish(any, any)(any)).thenAnswer { m =>
+ val result = m.getArgument(0).asInstanceOf[Result]
result.withSession(sessionKey -> c.stateProvider.serialize(c.state))
}
@@ -206,13 +230,13 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So
redirectURL match {
case Some(_) =>
result(c.provider.authenticate()) { result =>
- redirectLocation(result) must beSome.which { url =>
+ redirectLocation(result) must beSome[String].which { url =>
url must contain(s"$RedirectURI=${encode(resolvedRedirectURL, "UTF-8")}")
}
}
case None =>
result(c.provider.authenticate()) { result =>
- redirectLocation(result) must beSome.which { url =>
+ redirectLocation(result) must beSome[String].which { url =>
url must not contain s"$RedirectURI="
}
}
@@ -221,76 +245,82 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So
}
"not send state param if state is empty" in new WithApplication {
- c.oAuthSettings.authorizationURL match {
- case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass)
- case Some(_) =>
- implicit val req = FakeRequest(GET, "/")
-
- c.stateProvider.serialize(c.state) returns ""
- c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state)
- c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state)
- c.stateProvider.publish(any, any)(any) answers { (a, _) =>
- a.asInstanceOf[Array[Any]](0).asInstanceOf[Result]
- }
+ override def running() = {
+ c.oAuthSettings.authorizationURL match {
+ case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass)
+ case Some(_) =>
+ implicit val req = FakeRequest(GET, "/")
+
+ when(c.stateProvider.serialize(c.state)).thenReturn("")
+ when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.stateProvider.publish(any, any)(any)).thenAnswer { m =>
+ m.getArgument(0).asInstanceOf[Result]
+ }
- result(c.provider.authenticate())(result =>
- redirectLocation(result) must beSome.which(_ must not contain State))
+ result(c.provider.authenticate())(result =>
+ redirectLocation(result) must beSome[String].which(_ must not contain State))
+ }
}
}
"submit the proper params to the access token post request" in new WithApplication {
- val wsRequest = mock[MockWSRequest]
- val redirectParam = c.oAuthSettings.redirectURL match {
- case Some(rUri) =>
- List((RedirectURI, rUri))
- case None => Nil
- }
- val params = Map(
- ClientID -> Seq(c.oAuthSettings.clientID),
- ClientSecret -> Seq(c.oAuthSettings.clientSecret),
- GrantType -> Seq(AuthorizationCode),
- Code -> Seq("my.code")) ++ c.oAuthSettings.accessTokenParams.transformValues(Seq(_)) ++ redirectParam.toMap.transformValues(Seq(_))
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsRequest.withHttpHeaders(any) returns wsRequest
-
- // We must use this neat trick here because it isn't possible to check the post call with a verification,
- // because of the implicit params needed for the post call. On the other hand we can test it in the abstract
- // spec, because we throw an exception in both cases which stops the test once the post method was called.
- // This protects as for an NPE because of the not mocked dependencies. The other solution would be to execute
- // this test in every provider with the full mocked dependencies.
- wsRequest.post[Map[String, Seq[String]]](any)(any) answers { (a, _) =>
- if (a.asInstanceOf[Array[Any]](0).asInstanceOf[Map[String, Seq[String]]].equals(params)) {
- throw new RuntimeException("success")
- } else {
- throw new RuntimeException("failure")
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val redirectParam = c.oAuthSettings.redirectURL match {
+ case Some(rUri) =>
+ List((RedirectURI, rUri))
+ case None => Nil
}
- }
- c.httpLayer.url(c.oAuthSettings.accessTokenURL) returns wsRequest
- c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state)
- c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state)
+ val params = Map(
+ ClientID -> Seq(c.oAuthSettings.clientID),
+ ClientSecret -> Seq(c.oAuthSettings.clientSecret),
+ GrantType -> Seq(AuthorizationCode),
+ Code -> Seq("my.code")) ++ c.oAuthSettings.accessTokenParams.transformValues(Seq(_)) ++ redirectParam.toMap.transformValues(Seq(_))
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+
+ // We must use this neat trick here because it isn't possible to check the post call with a verification,
+ // because of the implicit params needed for the post call. On the other hand we can test it in the abstract
+ // spec, because we throw an exception in both cases which stops the test once the post method was called.
+ // This protects as for an NPE because of the not mocked dependencies. The other solution would be to execute
+ // this test in every provider with the full mocked dependencies.
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenAnswer { m =>
+ if (m.getArgument(0).asInstanceOf[Map[String, Seq[String]]].equals(params)) {
+ throw new RuntimeException("success")
+ } else {
+ throw new RuntimeException("failure")
+ }
+ }
+ when(c.httpLayer.url(c.oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state))
- failed[RuntimeException](c.provider.authenticate()) {
- case e => e.getMessage must startWith("success")
+ failed[RuntimeException](c.provider.authenticate()) {
+ case e => e.getMessage must startWith("success")
+ }
}
}
"fail with UnexpectedResponseException if Json cannot be parsed from response" in new WithApplication {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
-
- wsResponse.status returns 200
- wsResponse.json throws new RuntimeException("Unexpected character ('<' (code 60))")
- wsResponse.body returns ""
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- c.httpLayer.url(c.oAuthSettings.accessTokenURL) returns wsRequest
- c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state)
- c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state)
-
- failed[UnexpectedResponseException](c.provider.authenticate()) {
- case e => e.getMessage must startWith(
- JsonParseError.format(c.provider.id, "", "java.lang.RuntimeException: Unexpected character ('<' (code 60))"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenThrow(new RuntimeException("Unexpected character ('<' (code 60))"))
+ when(wsResponse.body).thenReturn("")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(c.httpLayer.url(c.oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state))
+ when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state))
+
+ failed[UnexpectedResponseException](c.provider.authenticate()) {
+ case e => e.getMessage must startWith(
+ JsonParseError.format(c.provider.id, "", "java.lang.RuntimeException: Unexpected character ('<' (code 60))"))
+ }
}
}
}
@@ -313,7 +343,7 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So
/**
* Context for the OAuth2ProviderSpec.
*/
-trait OAuth2ProviderSpecContext extends Scope with Mockito with ThrownExpectations {
+trait OAuth2ProviderSpecContext extends Scope with ThrownExpectations {
abstract class TestState extends SocialState(Set.empty)
abstract class TestStateProvider extends SocialStateHandler {
@@ -326,8 +356,8 @@ trait OAuth2ProviderSpecContext extends Scope with Mockito with ThrownExpectatio
* The HTTP layer mock.
*/
lazy val httpLayer = {
- val m = mock[MockHTTPLayer].smart
- m.executionContext returns global
+ val m = mockSmart[MockHTTPLayer]
+ when(m.executionContext).thenReturn(global)
m
}
@@ -343,7 +373,7 @@ trait OAuth2ProviderSpecContext extends Scope with Mockito with ThrownExpectatio
/**
* The OAuth2 state.
*/
- lazy val state = mock[TestState].smart
+ lazy val state = mockSmart[TestState]
/**
* A user state item.
@@ -353,7 +383,7 @@ trait OAuth2ProviderSpecContext extends Scope with Mockito with ThrownExpectatio
/**
* The OAuth2 state provider.
*/
- lazy val stateProvider = mock[TestStateProvider].smart
+ lazy val stateProvider = mockSmart[TestStateProvider]
/**
* The stateful auth info.
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala
index 626d32e6..f31a7430 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala
@@ -19,12 +19,15 @@ import io.github.honeycombcheesecake.play.silhouette.api.util.HTTPLayer
import io.github.honeycombcheesecake.play.silhouette.impl.exceptions.UnexpectedResponseException
import io.github.honeycombcheesecake.play.silhouette.impl.providers.OpenIDProvider._
import org.specs2.matcher.ThrownExpectations
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito._
+import org.mockito.ArgumentCaptor
import play.api.mvc.{ AnyContent, AnyContentAsEmpty }
import play.api.test.{ FakeHeaders, FakeRequest, WithApplication }
import play.mvc.Http.HeaderNames
import test.SocialProviderSpec
+import test.Helper.mock
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -40,76 +43,94 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] {
"The authenticate method" should {
val c = context
"fail with an UnexpectedResponseException if redirect URL couldn't be retrieved" in new WithApplication {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- c.openIDService.redirectURL(any(), any())(any()) returns Future.failed(new Exception(""))
+ when(c.openIDService.redirectURL(any(), any())(any())).thenReturn(Future.failed(new Exception("")))
- failed[UnexpectedResponseException](c.provider.authenticate()) {
- case e => e.getMessage must startWith(ErrorRedirectURL.format(c.provider.id, ""))
+ failed[UnexpectedResponseException](c.provider.authenticate()) {
+ case e => e.getMessage must startWith(ErrorRedirectURL.format(c.provider.id, ""))
+ }
}
}
"redirect to provider by using the provider URL" in new WithApplication {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- c.openIDService.redirectURL(any(), any())(any()) answers { _: Any => Future.successful(c.openIDSettings.providerURL) }
-
- result(c.provider.authenticate()) { result =>
- status(result) must equalTo(SEE_OTHER)
- redirectLocation(result) must beSome.which(_ == c.openIDSettings.providerURL)
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ when(c.openIDService.redirectURL(any(), any())(any())).thenAnswer(_ => Future.successful(c.openIDSettings.providerURL))
+
+ result(c.provider.authenticate()) { result =>
+ status(result) must equalTo(SEE_OTHER)
+ redirectLocation(result) must beSome[String].which(_ == c.openIDSettings.providerURL)
+ }
}
}
"redirect to provider by using a openID" in new WithApplication {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?openID=my.open.id")
- c.openIDService.redirectURL(any(), any())(any()) answers { _: Any => Future.successful(c.openIDSettings.providerURL) }
-
- result(c.provider.authenticate()) { result =>
- status(result) must equalTo(SEE_OTHER)
- redirectLocation(result) must beSome.which(_ == c.openIDSettings.providerURL)
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?openID=my.open.id")
+ when(c.openIDService.redirectURL(any(), any())(any())).thenAnswer(_ => Future.successful(c.openIDSettings.providerURL))
+
+ result(c.provider.authenticate()) { result =>
+ status(result) must equalTo(SEE_OTHER)
+ redirectLocation(result) must beSome[String].which(_ == c.openIDSettings.providerURL)
+ }
}
}
"resolves relative callbackURLs before starting the flow" in new WithApplication {
- verifyRelativeCallbackURLResolution("/callback-url", secure = false, "http://www.example.com/callback-url")
+ override def running() = {
+ verifyRelativeCallbackURLResolution("/callback-url", secure = false, "http://www.example.com/callback-url")
+ }
}
"resolves path relative callbackURLs before starting the flow" in new WithApplication {
- verifyRelativeCallbackURLResolution("callback-url", secure = false, "http://www.example.com/request-path/callback-url")
+ override def running() = {
+ verifyRelativeCallbackURLResolution("callback-url", secure = false, "http://www.example.com/request-path/callback-url")
+ }
}
"resolves relative callbackURLs before starting the flow over https" in new WithApplication {
- verifyRelativeCallbackURLResolution("/callback-url", secure = true, "https://www.example.com/callback-url")
+ override def running() = {
+ verifyRelativeCallbackURLResolution("/callback-url", secure = true, "https://www.example.com/callback-url")
+ }
}
def verifyRelativeCallbackURLResolution(callbackURL: String, secure: Boolean, resolvedCallbackURL: String) = {
implicit val req = FakeRequest[AnyContent](
method = GET,
uri = "/request-path/something",
- headers = FakeHeaders(Seq(HeaderNames.HOST -> "www.example.com")),
+ headers = FakeHeaders(Seq((HeaderNames.HOST, "www.example.com"))),
body = AnyContentAsEmpty,
secure = secure)
- c.openIDSettings.callbackURL returns callbackURL
- c.openIDService.redirectURL(any(), any())(any()) answers { _: Any => Future.successful(c.openIDSettings.providerURL) }
+ when(c.openIDSettings.callbackURL).thenReturn(callbackURL)
+ when(c.openIDService.redirectURL(any(), any())(any())).thenAnswer(_ => Future.successful(c.openIDSettings.providerURL))
await(c.provider.authenticate())
- there was one(c.openIDService).redirectURL(any(), ===(resolvedCallbackURL))(any())
+ val argument = ArgumentCaptor.forClass(classOf[String])
+ verify(c.openIDService).redirectURL(any(), argument.capture())(any())
+ assert(argument.getValue() === resolvedCallbackURL)
}
"fail with an UnexpectedResponseException if auth info cannot be retrieved" in new WithApplication {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Mode + "=id_res")
- c.openIDService.verifiedID(any(), any()) returns Future.failed(new Exception(""))
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Mode + "=id_res")
+ when(c.openIDService.verifiedID(any(), any())).thenReturn(Future.failed(new Exception("")))
- failed[UnexpectedResponseException](c.provider.authenticate()) {
- case e => e.getMessage must startWith(ErrorVerification.format(c.provider.id, ""))
+ failed[UnexpectedResponseException](c.provider.authenticate()) {
+ case e => e.getMessage must startWith(ErrorVerification.format(c.provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Mode + "=id_res")
- c.openIDService.verifiedID(any(), any()) answers { _: Any => Future.successful(c.openIDInfo) }
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Mode + "=id_res")
+ when(c.openIDService.verifiedID(any(), any())).thenAnswer(_ => Future.successful(c.openIDInfo))
- authInfo(c.provider.authenticate())(_ must be equalTo c.openIDInfo)
+ authInfo(c.provider.authenticate())(_ must be equalTo c.openIDInfo)
+ }
}
}
@@ -131,14 +152,14 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] {
/**
* Context for the OpenIDProviderSpec.
*/
-trait OpenIDProviderSpecContext extends Scope with Mockito with ThrownExpectations {
+trait OpenIDProviderSpecContext extends Scope with ThrownExpectations {
/**
* The HTTP layer mock.
*/
lazy val httpLayer = {
val m = mock[HTTPLayer]
- m.executionContext returns global
+ when(m.executionContext).thenReturn(global)
m
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/PasswordProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/PasswordProviderSpec.scala
index 07bd244f..6f4fe2d5 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/PasswordProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/PasswordProviderSpec.scala
@@ -17,14 +17,16 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers
import io.github.honeycombcheesecake.play.silhouette.api.repositories.AuthInfoRepository
import io.github.honeycombcheesecake.play.silhouette.api.util._
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
+import test.Helper.mock
import play.api.test.PlaySpecification
/**
* Abstract test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.providers.PasswordProvider]] based class.
*/
-trait PasswordProviderSpec extends PlaySpecification with Mockito {
+trait PasswordProviderSpec extends PlaySpecification {
/**
* The context.
@@ -59,11 +61,11 @@ trait PasswordProviderSpec extends PlaySpecification with Mockito {
*/
private def hasher(id: String) = {
val h = mock[PasswordHasher]
- h.id returns id
- h.isSuitable(any()) answers { p: Any =>
- p.asInstanceOf[PasswordInfo].hasher == h.id
+ when(h.id).thenReturn(id)
+ when(h.isSuitable(any())).thenAnswer { p =>
+ p.getArgument(0).asInstanceOf[PasswordInfo].hasher == h.id
}
- h.isDeprecated(any()) returns Some(false)
+ when(h.isDeprecated(any())).thenReturn(Some(false))
h
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala
index 607d665f..7cebdb84 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala
@@ -18,14 +18,15 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers
import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.TwitterProvider
import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.{ GoogleProvider, FacebookProvider }
import io.github.honeycombcheesecake.play.silhouette.impl.providers.openid.YahooProvider
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.test.PlaySpecification
+import org.mockito.Mockito._
+import test.Helper.mock
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialProviderRegistry]] class.
*/
-class SocialProviderRegistrySpec extends PlaySpecification with Mockito {
+class SocialProviderRegistrySpec extends PlaySpecification {
"The `get` method" should {
"return a provider by its type" in new Context {
@@ -39,7 +40,7 @@ class SocialProviderRegistrySpec extends PlaySpecification with Mockito {
"return a provider by its ID as SocialProvider" in new Context {
val provider = registry.get[SocialProvider](GoogleProvider.ID)
- provider must beSome.like {
+ provider must beSome[SocialProvider].like {
case value =>
value.id must be equalTo providers(1).id
value must beAnInstanceOf[SocialProvider]
@@ -49,7 +50,7 @@ class SocialProviderRegistrySpec extends PlaySpecification with Mockito {
"return a provider by its ID as OAuth2Provider" in new Context {
val provider = registry.get[OAuth2Provider](GoogleProvider.ID)
- provider must beSome.like {
+ provider must beSome[OAuth2Provider].like {
case value =>
value.id must be equalTo providers(1).id
value must beAnInstanceOf[OAuth2Provider]
@@ -79,11 +80,11 @@ class SocialProviderRegistrySpec extends PlaySpecification with Mockito {
*/
val providers = {
val facebook = mock[FacebookProvider]
- facebook.id returns FacebookProvider.ID
+ when(facebook.id).thenReturn(FacebookProvider.ID)
val google = mock[GoogleProvider]
- google.id returns GoogleProvider.ID
+ when(google.id).thenReturn(GoogleProvider.ID)
val twitter = mock[TwitterProvider]
- twitter.id returns TwitterProvider.ID
+ when(twitter.id).thenReturn(TwitterProvider.ID)
Seq(
facebook,
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala
index 1a858d9d..72892d38 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala
@@ -27,6 +27,9 @@ import play.api.libs.json.{ JsValue, Json }
import play.api.mvc.AnyContentAsEmpty
import play.api.test.{ FakeRequest, WithApplication }
import test.Helper
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyString
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{ ExecutionContext, Future }
@@ -38,113 +41,127 @@ class FacebookProviderSpec extends OAuth2ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = provider.withSettings { s =>
- s.copy(accessTokenURL = "new-access-token-url")
- }
+ override def running() = {
+ val s: CustomFacebookProvider = provider.withSettings { s =>
+ s.copy(accessTokenURL = "new-access-token-url")
+ }
- s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ }
}
}
"The `authenticate` method" should {
"fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 401
- wsResponse.body returns "Unauthorized"
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
+ override def running() = {
+ val wsRequest = mock(classOf[MockWSRequest])
+ val wsResponse = mock(classOf[MockWSRequest#Response])
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.body).thenReturn("Unauthorized")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ }
}
}
"fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns Json.obj()
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
+ override def running() = {
+ val wsRequest = mock(classOf[MockWSRequest])
+ val wsResponse = mock(classOf[MockWSRequest#Response])
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Json.obj())
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
+ override def running() = {
+ val wsRequest = mock(classOf[MockWSRequest])
+ val wsResponse = mock(classOf[MockWSRequest#Response])
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
- authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ }
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 400
- wsResponse.json returns Helper.loadJson("providers/custom/facebook.error.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
+ override def running() = {
+ val wsRequest = mock(classOf[MockWSRequest])
+ val wsResponse = mock(classOf[MockWSRequest#Response])
+ when(wsResponse.status).thenReturn(400)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/custom/facebook.error.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- "An active access token must be used to query information about the current user.",
- "OAuthException",
- 2500))
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ "An active access token must be used to query information about the current user.",
+ "OAuthException",
+ 2500))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 500
- wsResponse.json throws new RuntimeException("")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
+ override def running() = {
+ val wsRequest = mock(classOf[MockWSRequest])
+ val wsResponse = mock(classOf[MockWSRequest#Response])
+ when(wsResponse.status).thenReturn(500)
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"return the social profile" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/custom/facebook.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
+ override def running() = {
+ val wsRequest = mock(classOf[MockWSRequest])
+ val wsResponse = mock(classOf[MockWSRequest#Response])
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/custom/facebook.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CustomSocialProfile(
- loginInfo = LoginInfo(provider.id, "134405962728980"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- fullName = Some("Apollonia Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-ash2/t1/36245_155530314499277_2350717_n.jpg?lvh=1"),
- gender = Some("male"))
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CustomSocialProfile(
+ loginInfo = LoginInfo(provider.id, "134405962728980"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ fullName = Some("Apollonia Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-ash2/t1/36245_155530314499277_2350717_n.jpg?lvh=1"),
+ gender = Some("male"))
+ }
}
}
}
@@ -182,7 +199,7 @@ class FacebookProviderSpec extends OAuth2ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new CustomFacebookProvider(httpLayer, stateProvider, oAuthSettings)
+ lazy val provider: CustomFacebookProvider = new CustomFacebookProvider(httpLayer, stateProvider, oAuthSettings)
}
/**
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala
index 583f5169..6f1a0a9a 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala
@@ -22,7 +22,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialProfil
import io.github.honeycombcheesecake.play.silhouette.impl.providers._
import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.LinkedInProvider._
import play.api.test.WithApplication
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
import test.Helper
+import test.Helper.mock
import scala.concurrent.Future
@@ -33,80 +36,90 @@ class LinkedInProviderSpec extends OAuth1ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s =>
- s.copy("new-request-token-url")
+ override def running() = {
+ val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s =>
+ s.copy("new-request-token-url")
+ }
+ val s: LinkedInProvider = provider.withSettings(overrideSettingsFunction)
+
+ s.settings.requestTokenURL must be equalTo "new-request-token-url"
+ verify(oAuthService).withSettings(overrideSettingsFunction)
}
- val s = provider.withSettings(overrideSettingsFunction)
-
- s.settings.requestTokenURL must be equalTo "new-request-token-url"
- there was one(oAuthService).withSettings(overrideSettingsFunction)
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json returns Helper.loadJson("providers/oauth1/linkedin.error.json")
- httpLayer.url(API) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- 0,
- Some("Unknown authentication scheme"),
- Some("LY860UAC5U"),
- Some(401),
- Some(1390421660154L)))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.error.json"))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ 0,
+ Some("Unknown authentication scheme"),
+ Some("LY860UAC5U"),
+ Some(401),
+ Some(1390421660154L)))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json throws new RuntimeException("")
- httpLayer.url(API) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url"
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json returns Helper.loadJson("providers/oauth1/linkedin.success.json")
- httpLayer.url(url) returns wsRequest
-
- await(provider.retrieveProfile(oAuthInfo))
-
- there was one(httpLayer).url(url)
+ override def running() = {
+ val url = "https://custom.api.url"
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.success.json"))
+ when(httpLayer.url(url)).thenReturn(wsRequest)
+
+ await(provider.retrieveProfile(oAuthInfo))
+
+ verify(httpLayer).url(url)
+ }
}
"return the social profile" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json returns Helper.loadJson("providers/oauth1/linkedin.success.json")
- httpLayer.url(API) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo)) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "NhZXBl_O6f"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- fullName = Some("Apollonia Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("http://media.linkedin.com/mpr/mprx/0_fsPnURNRhLhk_Ue2fjKLUZkB2FL6TOe2S4bdUZz61GA9Ysxu_y_sz4THGW5JGJWhaMleN0F61-Dg"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.success.json"))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo)) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "NhZXBl_O6f"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ fullName = Some("Apollonia Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("http://media.linkedin.com/mpr/mprx/0_fsPnURNRhLhk_Ue2fjKLUZkB2FL6TOe2S4bdUZz61GA9Ysxu_y_sz4THGW5JGJWhaMleN0F61-Dg"))
+ }
}
}
}
@@ -137,6 +150,6 @@ class LinkedInProviderSpec extends OAuth1ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new LinkedInProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings)
+ lazy val provider: LinkedInProvider = new LinkedInProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala
index a7dcf7fc..7fef246b 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala
@@ -22,7 +22,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialProfil
import io.github.honeycombcheesecake.play.silhouette.impl.providers._
import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.TwitterProvider._
import play.api.test.WithApplication
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
import test.Helper
+import test.Helper.mock
import scala.concurrent.Future
@@ -33,91 +36,103 @@ class TwitterProviderSpec extends OAuth1ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s =>
- s.copy("new-request-token-url")
+ override def running() = {
+ val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s =>
+ s.copy("new-request-token-url")
+ }
+ val s: TwitterProvider = provider.withSettings(overrideSettingsFunction)
+
+ s.settings.requestTokenURL must be equalTo "new-request-token-url"
+ verify(oAuthService).withSettings(overrideSettingsFunction)
}
- val s = provider.withSettings(overrideSettingsFunction)
-
- s.settings.requestTokenURL must be equalTo "new-request-token-url"
- there was one(oAuthService).withSettings(overrideSettingsFunction)
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json returns Helper.loadJson("providers/oauth1/twitter.error.json")
- httpLayer.url(API) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- 215,
- Some("Bad Authentication data")))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.error.json"))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ 215,
+ Some("Bad Authentication data")))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json throws new RuntimeException("")
- httpLayer.url(API) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url"
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json returns Helper.loadJson("providers/oauth1/twitter.with.email.json")
- httpLayer.url(url) returns wsRequest
-
- await(provider.retrieveProfile(oAuthInfo))
-
- there was one(httpLayer).url(url)
+ override def running() = {
+ val url = "https://custom.api.url"
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.with.email.json"))
+ when(httpLayer.url(url)).thenReturn(wsRequest)
+
+ await(provider.retrieveProfile(oAuthInfo))
+
+ verify(httpLayer).url(url)
+ }
}
"return the social profile" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json returns Helper.loadJson("providers/oauth1/twitter.success.json")
- httpLayer.url(API) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo)) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "6253282"),
- fullName = Some("Apollonia Vanova"),
- avatarURL = Some("https://pbs.twimg.com/profile_images/1209905677/appolonia_.jpg"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.success.json"))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo)) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "6253282"),
+ fullName = Some("Apollonia Vanova"),
+ avatarURL = Some("https://pbs.twimg.com/profile_images/1209905677/appolonia_.jpg"))
+ }
}
}
"return the social profile with email" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json returns Helper.loadJson("providers/oauth1/twitter.with.email.json")
- httpLayer.url(API) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo)) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "6253282"),
- fullName = Some("Apollonia Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://pbs.twimg.com/profile_images/1209905677/appolonia_.jpg"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.with.email.json"))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo)) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "6253282"),
+ fullName = Some("Apollonia Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://pbs.twimg.com/profile_images/1209905677/appolonia_.jpg"))
+ }
}
}
}
@@ -137,7 +152,7 @@ class TwitterProviderSpec extends OAuth1ProviderSpec {
/**
* The OAuth1 settings.
*/
- override lazy val oAuthSettings = spy(OAuth1Settings(
+ override lazy val oAuthSettings = org.mockito.Mockito.spy(OAuth1Settings(
requestTokenURL = "https://twitter.com/oauth/request_token",
accessTokenURL = "https://twitter.com/oauth/access_token",
authorizationURL = "https://twitter.com/oauth/authenticate",
@@ -148,6 +163,6 @@ class TwitterProviderSpec extends OAuth1ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new TwitterProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings)
+ lazy val provider: TwitterProvider = new TwitterProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala
index bfb7ce56..b82c6430 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala
@@ -22,7 +22,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialProfil
import io.github.honeycombcheesecake.play.silhouette.impl.providers._
import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.XingProvider._
import play.api.test.WithApplication
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
import test.Helper
+import test.Helper.mock
import scala.concurrent.Future
@@ -33,77 +36,87 @@ class XingProviderSpec extends OAuth1ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s =>
- s.copy("new-request-token-url")
+ override def running() = {
+ val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s =>
+ s.copy("new-request-token-url")
+ }
+ val s: XingProvider = provider.withSettings(overrideSettingsFunction)
+
+ s.settings.requestTokenURL must be equalTo "new-request-token-url"
+ verify(oAuthService).withSettings(overrideSettingsFunction)
}
- val s = provider.withSettings(overrideSettingsFunction)
-
- s.settings.requestTokenURL must be equalTo "new-request-token-url"
- there was one(oAuthService).withSettings(overrideSettingsFunction)
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json returns Helper.loadJson("providers/oauth1/xing.error.json")
- httpLayer.url(API) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- "INVALID_PARAMETERS",
- "Invalid parameters (Limit must be a non-negative number.)"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.error.json"))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ "INVALID_PARAMETERS",
+ "Invalid parameters (Limit must be a non-negative number.)"))
+ }
}
}
"throw ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json throws new RuntimeException("")
- httpLayer.url(API) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url"
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json returns Helper.loadJson("providers/oauth1/xing.success.json")
- httpLayer.url(url) returns wsRequest
-
- await(provider.retrieveProfile(oAuthInfo))
-
- there was one(httpLayer).url(url)
+ override def running() = {
+ val url = "https://custom.api.url"
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.success.json"))
+ when(httpLayer.url(url)).thenReturn(wsRequest)
+
+ await(provider.retrieveProfile(oAuthInfo))
+
+ verify(httpLayer).url(url)
+ }
}
"return the social profile" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsRequest.sign(any) returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- wsResponse.json returns Helper.loadJson("providers/oauth1/xing.success.json")
- httpLayer.url(API) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo)) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "1235468792"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- fullName = Some("Apollonia Vanova"),
- avatarURL = Some("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.140x185.jpg"),
- email = Some("apollonia.vanova@watchmen.com"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsRequest.sign(any)).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.success.json"))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo)) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "1235468792"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ fullName = Some("Apollonia Vanova"),
+ avatarURL = Some("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.140x185.jpg"),
+ email = Some("apollonia.vanova@watchmen.com"))
+ }
}
}
}
@@ -134,6 +147,6 @@ class XingProviderSpec extends OAuth1ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new XingProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings)
+ lazy val provider: XingProvider = new XingProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala
index a00dfe85..b41e9788 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala
@@ -23,12 +23,13 @@ import io.github.honeycombcheesecake.play.silhouette.impl.exceptions.OAuth1Token
import io.github.honeycombcheesecake.play.silhouette.impl.providers.OAuth1Info
import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.secrets.CookieSecret._
import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.secrets.CookieSecretProvider._
-import org.specs2.control.NoLanguageFeatures
import org.specs2.matcher.JsonMatchers
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.mvc.{ AnyContentAsEmpty, Cookie, Results }
import play.api.test.{ FakeRequest, PlaySpecification, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
+import test.Helper.mockSmart
import java.time.{ ZoneId, ZonedDateTime }
import scala.concurrent.ExecutionContext.Implicits.global
@@ -40,7 +41,7 @@ import scala.util.{ Failure, Success }
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.secrets.CookieSecret]] class.
*/
-class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers with NoLanguageFeatures {
+class CookieSecretSpec extends PlaySpecification with JsonMatchers {
"The `isExpired` method of the secret" should {
"return true if the secret is expired" in new Context {
@@ -54,62 +55,76 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers
"The `serialize` method of the secret" should {
"sign the cookie" in new WithApplication with Context {
- serialize(secret, signer, crypter)
+ override def running() = {
+ serialize(secret, signer, crypter)
- there was one(signer).sign(any())
+ verify(signer).sign(any())
+ }
}
"encrypt the cookie" in new WithApplication with Context {
- serialize(secret, signer, crypter)
+ override def running() = {
+ serialize(secret, signer, crypter)
- there was one(crypter).encrypt(any())
+ verify(crypter).encrypt(any())
+ }
}
}
"The `unserialize` method of the secret" should {
"throw an OAuth1TokenSecretException if a secret contains invalid json" in new WithApplication with Context {
- val value = "invalid"
- val msg = Pattern.quote(InvalidJson.format(value))
+ override def running() = {
+ val value = "invalid"
+ val msg = Pattern.quote(InvalidJson.format(value))
- unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg)
+ unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg)
+ }
}
"throw an OAuth1TokenSecretException if a secret contains valid json but invalid secret" in new WithApplication with Context {
- val value = "{ \"test\": \"test\" }"
- val msg = "^" + Pattern.quote(InvalidSecretFormat.format("")) + ".*"
+ override def running() = {
+ val value = "{ \"test\": \"test\" }"
+ val msg = "^" + Pattern.quote(InvalidSecretFormat.format("")) + ".*"
- unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg)
+ unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg)
+ }
}
"throw an OAuth1TokenSecretException if a secret is badly signed" in new WithApplication with Context {
- signer.extract(any()) returns Failure(new Exception("Bad signature"))
+ override def running() = {
+ when(signer.extract(any())).thenReturn(Failure(new Exception("Bad signature")))
- val value = serialize(secret, signer, crypter)
- val msg = Pattern.quote(InvalidCookieSignature)
+ val value = serialize(secret, signer, crypter)
+ val msg = Pattern.quote(InvalidCookieSignature)
- unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg)
+ unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg)
+ }
}
}
"The `serialize/unserialize` method of the secret" should {
"serialize/unserialize a secret" in new WithApplication with Context {
- val serialized = serialize(secret, signer, crypter)
+ override def running() = {
+ val serialized = serialize(secret, signer, crypter)
- unserialize(serialized, signer, crypter) must beSuccessfulTry.withValue(secret)
+ unserialize(serialized, signer, crypter) must beSuccessfulTry.withValue(secret)
+ }
}
}
"The `build` method of the provider" should {
"return a new secret" in new WithApplication with Context {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
- val dateTime = ZonedDateTime.of(2014, 8, 8, 0, 0, 0, 0, ZoneId.systemDefault)
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
+ val dateTime = ZonedDateTime.of(2014, 8, 8, 0, 0, 0, 0, ZoneId.systemDefault)
- clock.now returns dateTime
+ when(clock.now).thenReturn(dateTime)
- val s = await(provider.build(oAuthInfo))
+ val s = await(provider.build(oAuthInfo))
- s.expirationDate must be equalTo dateTime.plusSeconds(settings.expirationTime.toSeconds.toInt)
- s.value must be equalTo oAuthInfo.secret
+ s.expirationDate must be equalTo dateTime.plusSeconds(settings.expirationTime.toSeconds.toInt)
+ s.value must be equalTo oAuthInfo.secret
+ }
}
}
@@ -123,60 +138,72 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers
}
"throw an OAuth1TokenSecretException if secret is expired" in new WithApplication with Context {
- val expiredSecret = secret.copy(expirationDate = ZonedDateTime.now.minusHours(1))
+ override def running() = {
+ val expiredSecret = secret.copy(expirationDate = ZonedDateTime.now.minusHours(1))
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(expiredSecret, signer, crypter)))
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(expiredSecret, signer, crypter)))
- await(provider.retrieve) must throwA[OAuth1TokenSecretException].like {
- case e => e.getMessage must startWith(SecretIsExpired.format())
+ await(provider.retrieve) must throwA[OAuth1TokenSecretException].like {
+ case e => e.getMessage must startWith(SecretIsExpired.format())
+ }
}
}
"throw an OAuth1TokenSecretException if client secret contains invalid json" in new WithApplication with Context {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, crypter.encrypt("{")))
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, crypter.encrypt("{")))
- await(provider.retrieve) must throwA[OAuth1TokenSecretException].like {
- case e => e.getMessage must startWith(InvalidJson.format("{"))
+ await(provider.retrieve) must throwA[OAuth1TokenSecretException].like {
+ case e => e.getMessage must startWith(InvalidJson.format("{"))
+ }
}
}
"throw an OAuth1TokenSecretException if client secret contains valid json but invalid secret" in new WithApplication with Context {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, crypter.encrypt("{ \"test\": \"test\" }")))
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, crypter.encrypt("{ \"test\": \"test\" }")))
- await(provider.retrieve) must throwA[OAuth1TokenSecretException].like {
- case e => e.getMessage must startWith(InvalidSecretFormat.format(""))
+ await(provider.retrieve) must throwA[OAuth1TokenSecretException].like {
+ case e => e.getMessage must startWith(InvalidSecretFormat.format(""))
+ }
}
}
"throw an OAuth1TokenSecretException if client secret is badly signed" in new WithApplication with Context {
- signer.extract(any()) returns Failure(new Exception("Bad signature"))
+ override def running() = {
+ when(signer.extract(any())).thenReturn(Failure(new Exception("Bad signature")))
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(secret, signer, crypter)))
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(secret, signer, crypter)))
- await(provider.retrieve) must throwA[OAuth1TokenSecretException].like {
- case e => e.getMessage must startWith(InvalidCookieSignature)
+ await(provider.retrieve) must throwA[OAuth1TokenSecretException].like {
+ case e => e.getMessage must startWith(InvalidCookieSignature)
+ }
}
}
"return the secret if it's valid" in new WithApplication with Context {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(secret, signer, crypter)))
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(secret, signer, crypter)))
- await(provider.retrieve) must be equalTo secret
+ await(provider.retrieve) must be equalTo secret
+ }
}
}
"The `publish` method of the provider" should {
"add the secret to the cookie" in new WithApplication with Context {
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/")
- val result = Future.successful(provider.publish(Results.Ok, secret))
-
- cookies(result).get(settings.cookieName) should beSome[Cookie].which { c =>
- c.name must be equalTo settings.cookieName
- unserialize(c.value, signer, crypter).get must be equalTo secret
- c.maxAge must beSome(settings.expirationTime.toSeconds.toInt)
- c.path must be equalTo settings.cookiePath
- c.domain must be equalTo settings.cookieDomain
- c.secure must be equalTo settings.secureCookie
+ override def running() = {
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/")
+ val result = Future.successful(provider.publish(Results.Ok, secret))
+
+ cookies(result).get(settings.cookieName) should beSome[Cookie].which { c =>
+ c.name must be equalTo settings.cookieName
+ unserialize(c.value, signer, crypter).get must be equalTo secret
+ c.maxAge must beSome(settings.expirationTime.toSeconds.toInt)
+ c.path must be equalTo settings.cookiePath
+ c.domain must be equalTo settings.cookieDomain
+ c.secure must be equalTo settings.secureCookie
+ }
}
}
}
@@ -189,7 +216,7 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers
/**
* The clock implementation.
*/
- lazy val clock: Clock = mock[Clock].smart
+ lazy val clock: Clock = mockSmart[Clock]
/**
* The settings.
@@ -205,9 +232,9 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers
* none cookie values in a cookie.
*/
lazy val crypter = {
- val c = mock[Crypter].smart
- c.encrypt(any()) answers { p: Any => Base64.encode(p.asInstanceOf[String]) }
- c.decrypt(any()) answers { p: Any => Base64.decode(p.asInstanceOf[String]) }
+ val c = mockSmart[Crypter]
+ when(c.encrypt(any())).thenAnswer(p => Base64.encode(p.getArgument(0).asInstanceOf[String]))
+ when(c.decrypt(any())).thenAnswer(p => Base64.decode(p.getArgument(0).asInstanceOf[String]))
c
}
@@ -217,9 +244,9 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers
* The signer returns the same value as passed to the methods. This is enough for testing.
*/
lazy val signer = {
- val c = mock[Signer].smart
- c.sign(any()) answers { p: Any => p.asInstanceOf[String] }
- c.extract(any()) answers { p: Any => Success(p.asInstanceOf[String]) }
+ val c = mockSmart[Signer]
+ when(c.sign(any())).thenAnswer(_.getArgument(0).asInstanceOf[String])
+ when(c.extract(any())).thenAnswer(p => Success(p.getArgument(0).asInstanceOf[String]))
c
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala
index 16233d04..4d655d9a 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala
@@ -16,27 +16,29 @@
package io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.services
import io.github.honeycombcheesecake.play.silhouette.impl.providers.{ OAuth1Info, OAuth1Settings }
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.libs.oauth.{ OAuth, RequestToken }
import play.api.libs.ws.WSSignatureCalculator
import play.api.test.{ PlaySpecification, WithApplication }
import play.shaded.oauth.oauth.signpost.exception.{ OAuthException, OAuthMessageSignerException }
+import org.mockito.Mockito._
import scala.concurrent.ExecutionContext.Implicits.global
/**
* Test case for the [[PlayOAuth1Service]] class.
*/
-class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito {
+class PlayOAuth1ServiceSpec extends PlaySpecification {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = service.withSettings { s =>
- s.copy("new-request-token-url")
- }
+ override def running() = {
+ val s = service.withSettings { s =>
+ s.copy("new-request-token-url")
+ }
- s.settings.requestTokenURL must be equalTo "new-request-token-url"
+ s.settings.requestTokenURL must be equalTo "new-request-token-url"
+ }
}
}
@@ -48,13 +50,13 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito {
"The `use10a` method" should {
"return true if the safer 1.0a specification will be used" in new Context {
- oauth.use10a returns true
+ when(oauth.use10a).thenReturn(true)
service.use10a must beTrue
}
"return false if the unsafer 1.0 specification will be used" in new Context {
- oauth.use10a returns false
+ when(oauth.use10a).thenReturn(false)
service.use10a must beFalse
}
@@ -62,13 +64,13 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito {
"The `retrieveRequestToken` method" should {
"throw exception if the token couldn't be retrieved" in new Context {
- oauth.retrieveRequestToken(settings.callbackURL) returns Left(new OAuthMessageSignerException(""))
+ when(oauth.retrieveRequestToken(settings.callbackURL)).thenReturn(Left(new OAuthMessageSignerException("")))
await(service.retrieveRequestToken(settings.callbackURL)) must throwA[OAuthException]
}
"return request token" in new Context {
- oauth.retrieveRequestToken(settings.callbackURL) returns Right(token)
+ when(oauth.retrieveRequestToken(settings.callbackURL)).thenReturn(Right(token))
await(service.retrieveRequestToken(settings.callbackURL)) must be equalTo info
}
@@ -76,13 +78,13 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito {
"The `retrieveAccessToken` method" should {
"throw Exception if the token couldn't be retrieved" in new Context {
- oauth.retrieveAccessToken(token, "") returns Left(new OAuthMessageSignerException(""))
+ when(oauth.retrieveAccessToken(token, "")).thenReturn(Left(new OAuthMessageSignerException("")))
await(service.retrieveAccessToken(info, "")) must throwA[OAuthException]
}
"return access token" in new Context {
- oauth.retrieveAccessToken(token, "") returns Right(token)
+ when(oauth.retrieveAccessToken(token, "")).thenReturn(Right(token))
await(service.retrieveAccessToken(info, "")) must be equalTo info
}
@@ -90,7 +92,7 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito {
"The `redirectUrl` method" should {
"return the redirect Url" in new Context {
- oauth.redirectUrl("token") returns "http://redirect.url"
+ when(oauth.redirectUrl("token")).thenReturn("http://redirect.url")
service.redirectUrl("token") must be equalTo "http://redirect.url"
}
@@ -98,7 +100,7 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito {
"The `sign` method" should {
"return the signature calculator" in new Context {
- oauth.info returns PlayOAuth1Service.serviceInfo(settings)
+ when(oauth.info).thenReturn(PlayOAuth1Service.serviceInfo(settings))
service.sign(info) must beAnInstanceOf[WSSignatureCalculator]
}
@@ -133,7 +135,7 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito {
/**
* A mock of the Play Framework OAuth implementation.
*/
- lazy val oauth: OAuth = mock[OAuth]
+ lazy val oauth: OAuth = mock(classOf[OAuth])
/**
* The service to test.
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala
index 94b1fd7f..f3b2ceab 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala
@@ -24,7 +24,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Auth0
import play.api.mvc.AnyContentAsEmpty
import play.api.libs.json.Json
import play.api.test.{ FakeRequest, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
import test.Helper
+import test.Helper.mock
import scala.concurrent.{ ExecutionContext, Future }
@@ -35,133 +38,149 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = provider.withSettings { s =>
- s.copy(accessTokenURL = "new-access-token-url")
- }
+ override def running() = {
+ val s: Auth0Provider = provider.withSettings { s =>
+ s.copy(accessTokenURL = "new-access-token-url")
+ }
- s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ }
}
}
"The `authenticate` method" should {
"fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 401
- wsResponse.body returns "Unauthorized"
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.body).thenReturn("Unauthorized")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ }
}
}
"fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
-
- wsResponse.status returns 200
- wsResponse.json returns Json.obj()
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Json.obj())
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
-
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ }
}
}
"The `authenticate` method with user state" should {
"return stateful auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
- stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider
- state.items returns Set(userStateItem)
-
- statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider)
+ when(state.items).thenReturn(Set(userStateItem))
+
+ statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ }
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
-
- wsResponse.status returns 400
- wsRequest.get() returns Future.successful(wsResponse)
- wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}")) returns wsRequest
- httpLayer.url(oAuthSettings.apiURL.get) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfoObject)) {
- case e => e.getMessage must equalTo(GenericHttpStatusProfileError.format(provider.id, 400))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+
+ when(wsResponse.status).thenReturn(400)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest)
+ when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfoObject)) {
+ case e => e.getMessage must equalTo(GenericHttpStatusProfileError.format(provider.id, 400))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
-
- wsResponse.status returns 500
- wsResponse.json throws new RuntimeException("")
- wsRequest.get() returns Future.successful(wsResponse)
- wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}")) returns wsRequest
- httpLayer.url(oAuthSettings.apiURL.get) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo("[Silhouette][auth0] error retrieving profile information")
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+
+ when(wsResponse.status).thenReturn(500)
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest)
+ when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo("[Silhouette][auth0] error retrieving profile information")
+ }
}
}
"return the social profile" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- val userProfile = Helper.loadJson(Auth0UserProfileJson)
-
- wsResponse.status returns 200
- wsResponse.json returns userProfile
- wsRequest.get() returns Future.successful(wsResponse)
- wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}")) returns wsRequest
- httpLayer.url(oAuthSettings.apiURL.get) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, (userProfile \ "sub").as[String]),
- fullName = (userProfile \ "name").asOpt[String],
- email = (userProfile \ "email").asOpt[String],
- avatarURL = (userProfile \ "picture").asOpt[String])
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ val userProfile = Helper.loadJson("providers/custom/auth0.profile.json")
+
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(userProfile)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest)
+ when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, (userProfile \ "sub").as[String]),
+ fullName = (userProfile \ "name").asOpt[String],
+ email = (userProfile \ "email").asOpt[String],
+ avatarURL = (userProfile \ "picture").asOpt[String])
+ }
}
}
}
@@ -178,13 +197,6 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec {
*/
trait Context extends OAuth2ProviderSpecContext {
- /**
- * Paths to the Json fixtures.
- */
- val Auth0ErrorJson = "providers/custom/auth0.error.json"
- val Auth0SuccessJson = "providers/custom/auth0.success.json"
- val Auth0UserProfileJson = "providers/custom/auth0.profile.json"
-
/**
* The OAuth2 settings.
*/
@@ -200,7 +212,7 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec {
/**
* The OAuth2 info returned by Auth0.
*/
- override lazy val oAuthInfo = Helper.loadJson(Auth0SuccessJson)
+ override lazy val oAuthInfo = Helper.loadJson("providers/custom/auth0.success.json")
/**
* The OAuth2 info deserialized as case class object
@@ -215,6 +227,6 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new Auth0Provider(httpLayer, stateProvider, oAuthSettings)
+ lazy val provider: Auth0Provider = new Auth0Provider(httpLayer, stateProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala
index 5377e49f..d07a6500 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala
@@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Dropb
import play.api.mvc.AnyContentAsEmpty
import play.api.libs.json.Json
import play.api.test.{ FakeRequest, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
import test.Helper
+import test.Helper.mock
import scala.concurrent.{ ExecutionContext, Future }
@@ -36,151 +39,169 @@ class DropboxProviderSpec extends OAuth2ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = provider.withSettings { s =>
- s.copy(accessTokenURL = "new-access-token-url")
- }
+ override def running() = {
+ val s: DropboxProvider = provider.withSettings { s =>
+ s.copy(accessTokenURL = "new-access-token-url")
+ }
- s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ }
}
}
"The `authenticate` method" should {
"fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 401
- wsResponse.body returns "Unauthorized"
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.body).thenReturn("Unauthorized")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ }
}
}
"fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns Json.obj()
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Json.obj())
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ }
}
}
"The `authenticate` method with user state" should {
"return stateful auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
- stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider
- state.items returns Set(userStateItem)
-
- statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider)
+ when(state.items).thenReturn(Set(userStateItem))
+
+ statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ }
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val authInfo = oAuthInfo.as[OAuth2Info]
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.json returns Helper.loadJson("providers/oauth2/dropbox.error.json")
- wsResponse.status returns 400
- wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- "Invalid OAuth request.",
- 400))
+ override def running() = {
+ val authInfo = oAuthInfo.as[OAuth2Info]
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.error.json"))
+ when(wsResponse.status).thenReturn(400)
+ when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ "Invalid OAuth request.",
+ 400))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val authInfo = oAuthInfo.as[OAuth2Info]
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 500
- wsResponse.json throws new RuntimeException("")
- wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val authInfo = oAuthInfo.as[OAuth2Info]
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(500)
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url"
- val authInfo = oAuthInfo.as[OAuth2Info]
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/dropbox.success.json")
- wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(url) returns wsRequest
-
- await(provider.retrieveProfile(authInfo))
-
- there was one(httpLayer).url(url)
+ override def running() = {
+ val url = "https://custom.api.url"
+ val authInfo = oAuthInfo.as[OAuth2Info]
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.success.json"))
+ when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(url)).thenReturn(wsRequest)
+
+ await(provider.retrieveProfile(authInfo))
+
+ verify(httpLayer).url(url)
+ }
}
"return the social profile" in new WithApplication with Context {
- val authInfo = oAuthInfo.as[OAuth2Info]
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/dropbox.success.json")
- wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile(authInfo)) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "12345678"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- fullName = Some("Apollonia Vanova"))
+ override def running() = {
+ val authInfo = oAuthInfo.as[OAuth2Info]
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.success.json"))
+ when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(authInfo)) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "12345678"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ fullName = Some("Apollonia Vanova"))
+ }
}
}
}
@@ -223,6 +244,6 @@ class DropboxProviderSpec extends OAuth2ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new DropboxProvider(httpLayer, stateProvider, oAuthSettings)
+ lazy val provider: DropboxProvider = new DropboxProvider(httpLayer, stateProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala
index f9ec2591..54ecb8fb 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala
@@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Faceb
import play.api.mvc.AnyContentAsEmpty
import play.api.libs.json.Json
import play.api.test.{ FakeRequest, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
import test.Helper
+import test.Helper.mock
import scala.concurrent.{ ExecutionContext, Future }
@@ -36,146 +39,164 @@ class FacebookProviderSpec extends OAuth2ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = provider.withSettings { s =>
- s.copy(accessTokenURL = "new-access-token-url")
- }
+ override def running() = {
+ val s: FacebookProvider = provider.withSettings { s =>
+ s.copy(accessTokenURL = "new-access-token-url")
+ }
- s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ }
}
}
"The `authenticate` method" should {
"fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 401
- wsResponse.body returns "Unauthorized"
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.body).thenReturn("Unauthorized")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ }
}
}
"fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns Json.obj()
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Json.obj())
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ }
}
}
"The `authenticate` method with user state" should {
"return stateful auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
- stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider
- state.items returns Set(userStateItem)
-
- statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider)
+ when(state.items).thenReturn(Set(userStateItem))
+
+ statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ }
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 400
- wsResponse.json returns Helper.loadJson("providers/oauth2/facebook.error.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- "An active access token must be used to query information about the current user.",
- "OAuthException",
- 2500))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(400)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.error.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ "An active access token must be used to query information about the current user.",
+ "OAuthException",
+ 2500))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 500
- wsResponse.json throws new RuntimeException("")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(500)
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url?access_token=%s"
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/facebook.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(url.format("my.access.token")) returns wsRequest
-
- await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
-
- there was one(httpLayer).url(url.format("my.access.token"))
+ override def running() = {
+ val url = "https://custom.api.url?access_token=%s"
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest)
+
+ await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
+
+ verify(httpLayer).url(url.format("my.access.token"))
+ }
}
"return the social profile" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/facebook.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "134405962728980"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- fullName = Some("Apollonia Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-ash2/t1/36245_155530314499277_2350717_n.jpg?lvh=1"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "134405962728980"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ fullName = Some("Apollonia Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-ash2/t1/36245_155530314499277_2350717_n.jpg?lvh=1"))
+ }
}
}
}
@@ -218,6 +239,6 @@ class FacebookProviderSpec extends OAuth2ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new FacebookProvider(httpLayer, stateProvider, oAuthSettings)
+ lazy val provider: FacebookProvider = new FacebookProvider(httpLayer, stateProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala
index d2fe157a..676b7212 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala
@@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Fours
import play.api.mvc.AnyContentAsEmpty
import play.api.libs.json.Json
import play.api.test.{ FakeRequest, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
import test.Helper
+import test.Helper.mock
import scala.concurrent.{ ExecutionContext, Future }
@@ -36,205 +39,229 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = provider.withSettings { s =>
- s.copy(accessTokenURL = "new-access-token-url")
- }
+ override def running() = {
+ val s: FoursquareProvider = provider.withSettings { s =>
+ s.copy(accessTokenURL = "new-access-token-url")
+ }
- s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ }
}
}
"The `authenticate` method" should {
"fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 401
- wsResponse.body returns "Unauthorized"
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.body).thenReturn("Unauthorized")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ }
}
}
"fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns Json.obj()
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Json.obj())
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ }
}
}
"The `authenticate` method with user state" should {
"return stateful auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
- stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider
- state.items returns Set(userStateItem)
-
- statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider)
+ when(state.items).thenReturn(Set(userStateItem))
+
+ statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ }
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 400
- wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.error.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token", DefaultAPIVersion)) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- 400,
- Some("param_error"),
- Some("Must provide a valid user ID or 'self.'")))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(400)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.error.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ 400,
+ Some("param_error"),
+ Some("Must provide a valid user ID or 'self.'")))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 500
- wsResponse.json throws new RuntimeException("")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token", DefaultAPIVersion)) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(500)
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url?access_token=%s"
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(url.format("my.access.token")) returns wsRequest
-
- await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
-
- there was one(httpLayer).url(url.format("my.access.token"))
+ override def running() = {
+ val url = "https://custom.api.url?access_token=%s"
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest)
+
+ await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
+
+ verify(httpLayer).url(url.format("my.access.token"))
+ }
}
"return the social profile" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token", DefaultAPIVersion)) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "13221052"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "13221052"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png"))
+ }
}
}
"return the social profile if API is deprecated" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.deprecated.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token", DefaultAPIVersion)) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "13221052"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.deprecated.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "13221052"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png"))
+ }
}
}
"handle the custom API version property" in new WithApplication with Context {
- val customProperties = Map(APIVersion -> "20120101")
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
-
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token", "20120101")) returns wsRequest
-
- profile(provider.withSettings(_.copy(customProperties = customProperties))
- .retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "13221052"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png"))
+ override def running() = {
+ val customProperties = Map(APIVersion -> "20120101")
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token", "20120101"))).thenReturn(wsRequest)
+
+ profile(provider.withSettings(_.copy(customProperties = customProperties))
+ .retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "13221052"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png"))
+ }
}
}
"handle the custom avatar resolution property" in new WithApplication with Context {
- val customProperties = Map(AvatarResolution -> "150x150")
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
-
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token", DefaultAPIVersion)) returns wsRequest
-
- profile(provider.withSettings(_.copy(customProperties = customProperties))
- .retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "13221052"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://irs0.4sqi.net/img/user/150x150/blank_girl.png"))
+ override def running() = {
+ val customProperties = Map(AvatarResolution -> "150x150")
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest)
+
+ profile(provider.withSettings(_.copy(customProperties = customProperties))
+ .retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "13221052"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://irs0.4sqi.net/img/user/150x150/blank_girl.png"))
+ }
}
}
}
@@ -276,6 +303,6 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new FoursquareProvider(httpLayer, stateProvider, oAuthSettings)
+ lazy val provider: FoursquareProvider = new FoursquareProvider(httpLayer, stateProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala
index 3da08694..69487856 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala
@@ -26,7 +26,10 @@ import play.api.http.HeaderNames
import play.api.mvc.AnyContentAsEmpty
import play.api.libs.json.Json
import play.api.test.{ FakeRequest, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
import test.Helper
+import test.Helper.mock
import scala.concurrent.{ ExecutionContext, Future }
@@ -37,151 +40,169 @@ class GitHubProviderSpec extends OAuth2ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = provider.withSettings { s =>
- s.copy(accessTokenURL = "new-access-token-url")
- }
+ override def running() = {
+ val s: GitHubProvider = provider.withSettings { s =>
+ s.copy(accessTokenURL = "new-access-token-url")
+ }
- s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ }
}
}
"The `authenticate` method" should {
"fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 401
- wsResponse.body returns "Unauthorized"
- wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json") returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.body).thenReturn("Unauthorized")
+ when(wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json")).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ }
}
}
"fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns Json.obj()
- wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json") returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Json.obj())
+ when(wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json")).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ }
}
}
"The `authenticate` method with user state" should {
"return stateful auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
- stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider
- state.items returns Set(userStateItem)
-
- statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider)
+ when(state.items).thenReturn(Set(userStateItem))
+
+ statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ }
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val authInfo = oAuthInfo.as[OAuth2Info]
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 400
- wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest
- wsResponse.json returns Helper.loadJson("providers/oauth2/github.error.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- "Bad credentials",
- Some("http://developer.github.com/v3")))
+ override def running() = {
+ val authInfo = oAuthInfo.as[OAuth2Info]
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(400)
+ when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.error.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ "Bad credentials",
+ Some("http://developer.github.com/v3")))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val authInfo = oAuthInfo.as[OAuth2Info]
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 500
- wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest
- wsResponse.json throws new RuntimeException("")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val authInfo = oAuthInfo.as[OAuth2Info]
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(500)
+ when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest)
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url"
- val authInfo = oAuthInfo.as[OAuth2Info]
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/github.success.json")
- wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(url) returns wsRequest
-
- await(provider.retrieveProfile(authInfo))
-
- there was one(httpLayer).url(url)
+ override def running() = {
+ val url = "https://custom.api.url"
+ val authInfo = oAuthInfo.as[OAuth2Info]
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.success.json"))
+ when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(url)).thenReturn(wsRequest)
+
+ await(provider.retrieveProfile(authInfo))
+
+ verify(httpLayer).url(url)
+ }
}
"return the social profile" in new WithApplication with Context {
- val authInfo = oAuthInfo.as[OAuth2Info]
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/github.success.json")
- wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API) returns wsRequest
-
- profile(provider.retrieveProfile(authInfo)) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "1"),
- fullName = Some("Apollonia Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://github.com/images/error/apollonia_vanova.gif"))
+ override def running() = {
+ val authInfo = oAuthInfo.as[OAuth2Info]
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.success.json"))
+ when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API)).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(authInfo)) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "1"),
+ fullName = Some("Apollonia Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://github.com/images/error/apollonia_vanova.gif"))
+ }
}
}
}
@@ -224,6 +245,6 @@ class GitHubProviderSpec extends OAuth2ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new GitHubProvider(httpLayer, stateProvider, oAuthSettings)
+ lazy val provider: GitHubProvider = new GitHubProvider(httpLayer, stateProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala
index f6df2584..5923939a 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala
@@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.GitLa
import play.api.libs.json.Json
import play.api.mvc.AnyContentAsEmpty
import play.api.test.{ FakeRequest, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
import test.Helper
+import test.Helper.mock
import scala.concurrent.{ ExecutionContext, Future }
@@ -36,142 +39,160 @@ class GitLabProviderSpec extends OAuth2ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = provider.withSettings { s =>
- s.copy(accessTokenURL = "new-access-token-url")
- }
+ override def running() = {
+ val s: GitLabProvider = provider.withSettings { s =>
+ s.copy(accessTokenURL = "new-access-token-url")
+ }
- s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ }
}
}
"The `authenticate` method" should {
"fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 401
- wsResponse.body returns "Unauthorized"
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.body).thenReturn("Unauthorized")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ }
}
}
"fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns Json.obj()
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Json.obj())
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ }
}
}
"The `authenticate` method with user state" should {
"return stateful auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
- stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider
- state.items returns Set(userStateItem)
-
- statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider)
+ when(state.items).thenReturn(Set(userStateItem))
+
+ statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ }
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 400
- wsResponse.json returns Helper.loadJson("providers/oauth2/gitlab.error.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- "Bad credentials"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(400)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.error.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ "Bad credentials"))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 500
- wsResponse.json throws new RuntimeException("")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(500)
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url?access_token=%s"
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/gitlab.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(url.format("my.access.token")) returns wsRequest
-
- await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
-
- there was one(httpLayer).url(url.format("my.access.token"))
+ override def running() = {
+ val url = "https://custom.api.url?access_token=%s"
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest)
+
+ await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
+
+ verify(httpLayer).url(url.format("my.access.token"))
+ }
}
"return the social profile" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/gitlab.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "1"),
- fullName = Some("John Smith"),
- email = Some("john@example.com"),
- avatarURL = Some("http://gitlab.com/uploads/user/avatar/1/index.jpg"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "1"),
+ fullName = Some("John Smith"),
+ email = Some("john@example.com"),
+ avatarURL = Some("http://gitlab.com/uploads/user/avatar/1/index.jpg"))
+ }
}
}
}
@@ -214,7 +235,7 @@ class GitLabProviderSpec extends OAuth2ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new GitLabProvider(httpLayer, stateProvider, oAuthSettings)
+ lazy val provider: GitLabProvider = new GitLabProvider(httpLayer, stateProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala
index 5b5243bc..973b0b95 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala
@@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Googl
import play.api.libs.json.Json
import play.api.mvc.AnyContentAsEmpty
import play.api.test.{ FakeRequest, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
import test.Helper
+import test.Helper.mock
import scala.concurrent.{ ExecutionContext, Future }
@@ -36,218 +39,244 @@ class GoogleProviderSpec extends OAuth2ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = provider.withSettings { s =>
- s.copy(accessTokenURL = "new-access-token-url")
- }
+ override def running() = {
+ val s: GoogleProvider = provider.withSettings { s =>
+ s.copy(accessTokenURL = "new-access-token-url")
+ }
- s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ }
}
}
"The `authenticate` method" should {
"fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 401
- wsResponse.body returns "Unauthorized"
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.body).thenReturn("Unauthorized")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ }
}
}
"fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns Json.obj()
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Json.obj())
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ }
}
}
"The `authenticate` method with user state" should {
"return stateful auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
- stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider
- state.items returns Set(userStateItem)
-
- statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider)
+ when(state.items).thenReturn(Set(userStateItem))
+
+ statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ }
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 401
- wsResponse.json returns Helper.loadJson("providers/oauth2/google.error.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- 401,
- "Invalid Credentials"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.error.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ 401,
+ "Invalid Credentials"))
+ }
}
}
"fail with ProfileRetrievalException if API returns missing People API config" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 403
- wsResponse.json returns Helper.loadJson("providers/oauth2/google.error.api.missing.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- val apiErrMsg = "People API has not been used in project 1234567890 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/people.googleapis.com/overview?project=1234567890 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- 403,
- apiErrMsg))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(403)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.error.api.missing.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ val apiErrMsg = "People API has not been used in project 1234567890 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/people.googleapis.com/overview?project=1234567890 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ 403,
+ apiErrMsg))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 500
- wsResponse.json throws new RuntimeException("")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(500)
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url?access_token=%s"
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/google.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(url.format("my.access.token")) returns wsRequest
-
- await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
-
- there was one(httpLayer).url(url.format("my.access.token"))
+ override def running() = {
+ val url = "https://custom.api.url?access_token=%s"
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest)
+
+ await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
+
+ verify(httpLayer).url(url.format("my.access.token"))
+ }
}
"return the social profile with an email" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/google.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "109476598527568979481"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- fullName = Some("Apollonia Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "109476598527568979481"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ fullName = Some("Apollonia Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50"))
+ }
}
}
"return the social profile without an email" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/google.without.email.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "109476598527568979481"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- fullName = Some("Apollonia Vanova"),
- email = None,
- avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.without.email.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "109476598527568979481"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ fullName = Some("Apollonia Vanova"),
+ email = None,
+ avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50"))
+ }
}
}
"return the social profile with an avatar url" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/google.img.non-default.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "109476598527568979481"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- fullName = Some("Apollonia Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.img.non-default.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "109476598527568979481"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ fullName = Some("Apollonia Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50"))
+ }
}
}
"return the social profile without an avatar url" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/google.img.default.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "109476598527568979481"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- fullName = Some("Apollonia Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = None)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.img.default.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "109476598527568979481"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ fullName = Some("Apollonia Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = None)
+ }
}
}
}
@@ -290,6 +319,6 @@ class GoogleProviderSpec extends OAuth2ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new GoogleProvider(httpLayer, stateProvider, oAuthSettings)
+ lazy val provider: GoogleProvider = new GoogleProvider(httpLayer, stateProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala
index 31efe11e..468810b1 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala
@@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Insta
import play.api.libs.json.Json
import play.api.mvc.AnyContentAsEmpty
import play.api.test.{ FakeRequest, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
import test.Helper
+import test.Helper.mock
import scala.concurrent.{ ExecutionContext, Future }
@@ -36,143 +39,161 @@ class InstagramProviderSpec extends OAuth2ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = provider.withSettings { s =>
- s.copy(accessTokenURL = "new-access-token-url")
- }
+ override def running() = {
+ val s: InstagramProvider = provider.withSettings { s =>
+ s.copy(accessTokenURL = "new-access-token-url")
+ }
- s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ }
}
}
"The `authenticate` method" should {
"fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 401
- wsResponse.body returns "Unauthorized"
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.body).thenReturn("Unauthorized")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ }
}
}
"fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns Json.obj()
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Json.obj())
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ }
}
}
"The `authenticate` method with user state" should {
"return stateful auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
- stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider
- state.items returns Set(userStateItem)
-
- statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider)
+ when(state.items).thenReturn(Set(userStateItem))
+
+ statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ }
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 400
- wsResponse.json returns Helper.loadJson("providers/oauth2/instagram.error.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- 400,
- Some("OAuthAccessTokenException"),
- Some("The access_token provided is invalid.")))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(400)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.error.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ 400,
+ Some("OAuthAccessTokenException"),
+ Some("The access_token provided is invalid.")))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 500
- wsResponse.json throws new RuntimeException("")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(500)
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url?access_token=%s"
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/instagram.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(url.format("my.access.token")) returns wsRequest
-
- await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
-
- there was one(httpLayer).url(url.format("my.access.token"))
+ override def running() = {
+ val url = "https://custom.api.url?access_token=%s"
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest)
+
+ await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
+
+ verify(httpLayer).url(url.format("my.access.token"))
+ }
}
"return the social profile" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/instagram.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "1574083"),
- fullName = Some("Apollonia Vanova"),
- avatarURL = Some("http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "1574083"),
+ fullName = Some("Apollonia Vanova"),
+ avatarURL = Some("http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg"))
+ }
}
}
}
@@ -215,6 +236,6 @@ class InstagramProviderSpec extends OAuth2ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new InstagramProvider(httpLayer, stateProvider, oAuthSettings)
+ lazy val provider: InstagramProvider = new InstagramProvider(httpLayer, stateProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala
index 07273cb8..44d0b8c7 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala
@@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Linke
import play.api.libs.json.Json
import play.api.mvc.AnyContentAsEmpty
import play.api.test.{ FakeRequest, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
import test.Helper
+import test.Helper.mock
import scala.concurrent.{ ExecutionContext, Future }
@@ -36,169 +39,187 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = provider.withSettings { s =>
- s.copy(accessTokenURL = "new-access-token-url")
- }
+ override def running() = {
+ val s: LinkedInProvider = provider.withSettings { s =>
+ s.copy(accessTokenURL = "new-access-token-url")
+ }
- s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ }
}
}
"The `authenticate` method" should {
"fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 401
- wsResponse.body returns "Unauthorized"
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.body).thenReturn("Unauthorized")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ }
}
}
"fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns Json.obj()
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Json.obj())
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ }
}
}
"The `authenticate` method with user state" should {
"return stateful auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
- stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider
- state.items returns Set(userStateItem)
-
- statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider)
+ when(state.items).thenReturn(Set(userStateItem))
+
+ statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ }
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 401
- wsResponse.json returns Helper.loadJson("providers/oauth2/linkedin.error.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
- mockEmailAndPhoto(httpLayer)
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- 0,
- Some("Unknown authentication scheme"),
- Some("LY860UAC5U"),
- Some(401),
- Some(1390421660154L)))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.error.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+ mockEmailAndPhoto(httpLayer)
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ 0,
+ Some("Unknown authentication scheme"),
+ Some("LY860UAC5U"),
+ Some(401),
+ Some(1390421660154L)))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 500
- wsResponse.json throws new RuntimeException("")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
- mockEmailAndPhoto(httpLayer)
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(500)
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+ mockEmailAndPhoto(httpLayer)
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url?access_token=%s"
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/linkedin.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(url.format("my.access.token")) returns wsRequest
- mockEmailAndPhoto(httpLayer)
- await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
-
- there was one(httpLayer).url(url.format("my.access.token"))
+ override def running() = {
+ val url = "https://custom.api.url?access_token=%s"
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest)
+ mockEmailAndPhoto(httpLayer)
+ await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
+
+ verify(httpLayer).url(url.format("my.access.token"))
+ }
}
def mockEmailAndPhoto(httpLayer: MockHTTPLayer) = {
// Email
val wsRequestEmail = mock[MockWSRequest]
val wsResponseEmail = mock[MockWSRequest#Response]
- wsResponseEmail.status returns 200
- wsResponseEmail.json returns Helper.loadJson("providers/oauth2/linkedin.email.json")
- wsRequestEmail.get() returns Future.successful(wsResponseEmail)
- httpLayer.url(EMAIL.format("my.access.token")) returns wsRequestEmail
+ when(wsResponseEmail.status).thenReturn(200)
+ when(wsResponseEmail.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.email.json"))
+ when(wsRequestEmail.get()).thenReturn(Future.successful(wsResponseEmail))
+ when(httpLayer.url(EMAIL.format("my.access.token"))).thenReturn(wsRequestEmail)
// Photo
val wsRequestPhoto = mock[MockWSRequest]
val wsResponsePhoto = mock[MockWSRequest#Response]
- wsResponsePhoto.status returns 200
- wsResponsePhoto.json returns Helper.loadJson("providers/oauth2/linkedin.photo.json")
- wsRequestPhoto.get() returns Future.successful(wsResponsePhoto)
- httpLayer.url(PHOTO.format("my.access.token")) returns wsRequestPhoto
+ when(wsResponsePhoto.status).thenReturn(200)
+ when(wsResponsePhoto.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.photo.json"))
+ when(wsRequestPhoto.get()).thenReturn(Future.successful(wsResponsePhoto))
+ when(httpLayer.url(PHOTO.format("my.access.token"))).thenReturn(wsRequestPhoto)
}
"return the social profile" in new WithApplication with Context {
- // Basic profile
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/linkedin.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- mockEmailAndPhoto(httpLayer)
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "NhZXBl_O6f"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- fullName = Some("Apollonia Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://media.licdn.com/dms/image/C4E03AQFBprjocrF2iA/profile-displayphoto-shrink_100_100/0?e=1576108800&v=beta&t=Tn7mA43w8qmTuzjSdtuYQMi2kI5At9XOp8X--s5hpRU"))
+ override def running() = {
+ // Basic profile
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ mockEmailAndPhoto(httpLayer)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "NhZXBl_O6f"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ fullName = Some("Apollonia Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://media.licdn.com/dms/image/C4E03AQFBprjocrF2iA/profile-displayphoto-shrink_100_100/0?e=1576108800&v=beta&t=Tn7mA43w8qmTuzjSdtuYQMi2kI5At9XOp8X--s5hpRU"))
+ }
}
}
}
@@ -241,6 +262,6 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new LinkedInProvider(httpLayer, stateProvider, oAuthSettings)
+ lazy val provider: LinkedInProvider = new LinkedInProvider(httpLayer, stateProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala
index e0897735..8ebd238b 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala
@@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.VKPro
import play.api.libs.json.{ JsObject, Json }
import play.api.mvc.AnyContentAsEmpty
import play.api.test.{ FakeRequest, WithApplication }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers._
import test.Helper
+import test.Helper.mock
import scala.concurrent.{ ExecutionContext, Future }
@@ -36,198 +39,222 @@ class VKProviderSpec extends OAuth2ProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = provider.withSettings { s =>
- s.copy(accessTokenURL = "new-access-token-url")
- }
+ override def running() = {
+ val s: VKProvider = provider.withSettings { s =>
+ s.copy(accessTokenURL = "new-access-token-url")
+ }
- s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ s.settings.accessTokenURL must be equalTo "new-access-token-url"
+ }
}
}
"The `authenticate` method" should {
"fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 401
- wsResponse.body returns "Unauthorized"
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(401)
+ when(wsResponse.body).thenReturn("Unauthorized")
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401))
+ }
}
}
"fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns Json.obj()
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- failed[UnexpectedResponseException](provider.authenticate()) {
- case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Json.obj())
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ failed[UnexpectedResponseException](provider.authenticate()) {
+ case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, ""))
+ }
}
}
"return the auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
-
- authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+
+ authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info])
+ }
}
}
"The `authenticate` method with user state" should {
"return stateful auth info" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
- wsResponse.status returns 200
- wsResponse.json returns oAuthInfo
- wsRequest.withHttpHeaders(any) returns wsRequest
- wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse)
- httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest
- stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state)
- stateProvider.state(any[ExecutionContext]) returns Future.successful(state)
- stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider
- state.items returns Set(userStateItem)
-
- statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code")
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(oAuthInfo)
+ when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest)
+ when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest)
+ when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state))
+ when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider)
+ when(state.items).thenReturn(Set(userStateItem))
+
+ statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo)
+ }
}
}
"The `retrieveProfile` method" should {
"fail with ProfileRetrievalException if API returns error" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 400
- wsResponse.json returns Helper.loadJson("providers/oauth2/vk.error.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(SpecifiedProfileError.format(
- provider.id,
- 10,
- "Internal server error: could not get application"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(400)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.error.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(SpecifiedProfileError.format(
+ provider.id,
+ 10,
+ "Internal server error: could not get application"))
+ }
}
}
"fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 500
- wsResponse.json throws new RuntimeException("")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
- case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(500)
+ when(wsResponse.json).thenThrow(new RuntimeException(""))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) {
+ case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id))
+ }
}
}
"use the overridden API URL" in new WithApplication with Context {
- val url = "https://custom.api.url?access_token=%s"
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- oAuthSettings.apiURL returns Some(url)
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/vk.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(url.format("my.access.token")) returns wsRequest
-
- await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
-
- there was one(httpLayer).url(url.format("my.access.token"))
+ override def running() = {
+ val url = "https://custom.api.url?access_token=%s"
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(oAuthSettings.apiURL).thenReturn(Some(url))
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest)
+
+ await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info]))
+
+ verify(httpLayer).url(url.format("my.access.token"))
+ }
}
"return the social profile with email" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/vk.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "66748"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("http://vk.com/images/camera_b.gif"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "66748"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("http://vk.com/images/camera_b.gif"))
+ }
}
}
"return the social profile from response without photo" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/vk.success.without.photo.json").as[JsObject]
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "66748"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = None)
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.without.photo.json").as[JsObject])
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "66748"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = None)
+ }
}
}
"return the social profile from deprecated API response" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/vk.success.deprecated.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "66748"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("http://vk.com/images/camera_b.gif"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.deprecated.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "66748"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("http://vk.com/images/camera_b.gif"))
+ }
}
}
"return the social profile without email" in new WithApplication with Context {
- val wsRequest = mock[MockWSRequest]
- val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsResponse.json returns Helper.loadJson("providers/oauth2/vk.success.json")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(API.format("my.access.token")) returns wsRequest
-
- profile(provider.retrieveProfile((oAuthInfo.as[JsObject] - "email").as[OAuth2Info])) { p =>
- p must be equalTo CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "66748"),
- firstName = Some("Apollonia"),
- lastName = Some("Vanova"),
- email = None,
- avatarURL = Some("http://vk.com/images/camera_b.gif"))
+ override def running() = {
+ val wsRequest = mock[MockWSRequest]
+ val wsResponse = mock[MockWSRequest#Response]
+ when(wsResponse.status).thenReturn(200)
+ when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest)
+
+ profile(provider.retrieveProfile((oAuthInfo.as[JsObject] - "email").as[OAuth2Info])) { p =>
+ p must be equalTo CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "66748"),
+ firstName = Some("Apollonia"),
+ lastName = Some("Vanova"),
+ email = None,
+ avatarURL = Some("http://vk.com/images/camera_b.gif"))
+ }
}
}
}
@@ -270,6 +297,6 @@ class VKProviderSpec extends OAuth2ProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new VKProvider(httpLayer, stateProvider, oAuthSettings)
+ lazy val provider: VKProvider = new VKProvider(httpLayer, stateProvider, oAuthSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala
index ff97f310..cd44acaa 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala
@@ -18,6 +18,7 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers.openid
import io.github.honeycombcheesecake.play.silhouette.api.LoginInfo
import io.github.honeycombcheesecake.play.silhouette.impl.providers._
import play.api.test.WithApplication
+import org.mockito.Mockito._
/**
* Test case for the [[SteamProvider]] class.
@@ -26,21 +27,25 @@ class SteamProviderSpec extends OpenIDProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s =>
- s.copy("new-provider-url")
- }
- val s = provider.withSettings(overrideSettingsFunction)
+ override def running() = {
+ val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s =>
+ s.copy("new-provider-url")
+ }
+ val s: SteamProvider = provider.withSettings(overrideSettingsFunction)
- s.settings.providerURL must be equalTo "new-provider-url"
- there was one(openIDService).withSettings(overrideSettingsFunction)
+ s.settings.providerURL must be equalTo "new-provider-url"
+ verify(openIDService).withSettings(overrideSettingsFunction)
+ }
}
}
"The `retrieveProfile` method" should {
"return the social profile" in new WithApplication with Context {
- profile(provider.retrieveProfile(openIDInfo)) {
- case p => p must be equalTo new CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "http://steamcommunity.com/openid/id/16261495063738643"))
+ override def running() = {
+ profile(provider.retrieveProfile(openIDInfo)) {
+ case p => p must be equalTo new CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "http://steamcommunity.com/openid/id/16261495063738643"))
+ }
}
}
}
@@ -73,6 +78,6 @@ class SteamProviderSpec extends OpenIDProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new SteamProvider(httpLayer, openIDService, openIDSettings)
+ lazy val provider: SteamProvider = new SteamProvider(httpLayer, openIDService, openIDSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala
index 8c5581ed..213e5ba6 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala
@@ -18,6 +18,7 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers.openid
import io.github.honeycombcheesecake.play.silhouette.api.LoginInfo
import io.github.honeycombcheesecake.play.silhouette.impl.providers._
import play.api.test.WithApplication
+import org.mockito.Mockito._
/**
* Test case for the [[YahooProvider]] class.
@@ -26,25 +27,29 @@ class YahooProviderSpec extends OpenIDProviderSpec {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s =>
- s.copy("new-provider-url")
- }
- val s = provider.withSettings(overrideSettingsFunction)
+ override def running() = {
+ val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s =>
+ s.copy("new-provider-url")
+ }
+ val s: YahooProvider = provider.withSettings(overrideSettingsFunction)
- s.settings.providerURL must be equalTo "new-provider-url"
- there was one(openIDService).withSettings(overrideSettingsFunction)
+ s.settings.providerURL must be equalTo "new-provider-url"
+ verify(openIDService).withSettings(overrideSettingsFunction)
+ }
}
}
"The `retrieveProfile` method" should {
"return the social profile" in new WithApplication with Context {
- profile(provider.retrieveProfile(openIDInfo)) {
- case p =>
- p must be equalTo new CommonSocialProfile(
- loginInfo = LoginInfo(provider.id, "https://me.yahoo.com/a/Xs6hPjazdrMvmbn4jhQjkjkhcasdGdsKajq9we"),
- fullName = Some("Apollonia Vanova"),
- email = Some("apollonia.vanova@watchmen.com"),
- avatarURL = Some("https://s.yimg.com/dh/ap/social/profile/profile_b48.png"))
+ override def running() = {
+ profile(provider.retrieveProfile(openIDInfo)) {
+ case p =>
+ p must be equalTo new CommonSocialProfile(
+ loginInfo = LoginInfo(provider.id, "https://me.yahoo.com/a/Xs6hPjazdrMvmbn4jhQjkjkhcasdGdsKajq9we"),
+ fullName = Some("Apollonia Vanova"),
+ email = Some("apollonia.vanova@watchmen.com"),
+ avatarURL = Some("https://s.yimg.com/dh/ap/social/profile/profile_b48.png"))
+ }
}
}
}
@@ -84,6 +89,6 @@ class YahooProviderSpec extends OpenIDProviderSpec {
/**
* The provider to test.
*/
- lazy val provider = new YahooProvider(httpLayer, openIDService, openIDSettings)
+ lazy val provider: YahooProvider = new YahooProvider(httpLayer, openIDService, openIDSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala
index d4c232d3..0993854c 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala
@@ -2,20 +2,22 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers.openid.serv
import io.github.honeycombcheesecake.play.silhouette.impl.providers.OpenIDSettings
import io.github.honeycombcheesecake.play.silhouette.impl.providers.openid.services.PlayOpenIDService
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
+import org.mockito.Mockito.mock
import play.api.libs.openid.OpenIdClient
import play.api.test.{ PlaySpecification, WithApplication }
-class PlayOpenIDServiceSpec extends PlaySpecification with Mockito {
+class PlayOpenIDServiceSpec extends PlaySpecification {
"The `withSettings` method" should {
"create a new instance with customized settings" in new WithApplication with Context {
- val s = service.withSettings { s =>
- s.copy("new-provider-url")
- }
+ override def running() = {
+ val s = service.withSettings { s =>
+ s.copy("new-provider-url")
+ }
- s.settings.providerURL must be equalTo "new-provider-url"
+ s.settings.providerURL must be equalTo "new-provider-url"
+ }
}
}
@@ -35,7 +37,7 @@ class PlayOpenIDServiceSpec extends PlaySpecification with Mockito {
"image" -> "http://axschema.org/media/image/default"),
realm = Some("http://localhost:9000"))
- val service = new PlayOpenIDService(mock[OpenIdClient], openIDSettings)
+ lazy val service = new PlayOpenIDService(mock(classOf[OpenIdClient]), openIDSettings)
}
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/CsrfStateItemHandlerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/CsrfStateItemHandlerSpec.scala
index 06473612..e7d22297 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/CsrfStateItemHandlerSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/CsrfStateItemHandlerSpec.scala
@@ -21,11 +21,13 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialStateI
import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialStateItem.ItemStructure
import io.github.honeycombcheesecake.play.silhouette.impl.providers.state.CsrfStateItemHandler._
import org.specs2.matcher.JsonMatchers
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.libs.json.Json
import play.api.mvc.{ AnyContentAsEmpty, Cookie, Results }
import play.api.test.{ FakeRequest, PlaySpecification }
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
+import test.Helper.mockSmart
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -34,11 +36,11 @@ import scala.util.Success
/**
* Test case for the [[CsrfStateItemHandler]] class.
*/
-class CsrfStateItemHandlerSpec extends PlaySpecification with Mockito with JsonMatchers {
+class CsrfStateItemHandlerSpec extends PlaySpecification with JsonMatchers {
"The `item` method" should {
"return the CSRF state item" in new Context {
- idGenerator.generate returns Future.successful(csrfToken)
+ when(idGenerator.generate).thenReturn(Future.successful(csrfToken))
await(csrfStateItemHandler.item) must be equalTo csrfStateItem
}
@@ -50,7 +52,7 @@ class CsrfStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM
}
"should return `None` if it can't handle the given item" in new Context {
- val nonCsrfState = mock[SocialStateItem].smart
+ val nonCsrfState = mockSmart[SocialStateItem]
csrfStateItemHandler.canHandle(nonCsrfState) must beNone
}
@@ -58,8 +60,8 @@ class CsrfStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM
"The `canHandle` method" should {
"return false if the give item is for another handler" in new Context {
- val nonCsrfItemStructure = mock[ItemStructure].smart
- nonCsrfItemStructure.id returns "non-csrf-item"
+ val nonCsrfItemStructure = mockSmart[ItemStructure]
+ when(nonCsrfItemStructure.id).thenReturn("non-csrf-item")
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
csrfStateItemHandler.canHandle(nonCsrfItemStructure) must beFalse
@@ -107,7 +109,7 @@ class CsrfStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM
/**
* The ID generator implementation.
*/
- val idGenerator = mock[IDGenerator].smart
+ val idGenerator = mockSmart[IDGenerator]
/**
* The settings.
@@ -120,9 +122,9 @@ class CsrfStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM
* The signer returns the same value as passed to the methods. This is enough for testing.
*/
val signer = {
- val c = mock[Signer].smart
- c.sign(any()) answers { p: Any => p.asInstanceOf[String] }
- c.extract(any()) answers { p: Any => Success(p.asInstanceOf[String]) }
+ val c = mockSmart[Signer]
+ when(c.sign(any())).thenAnswer(_.getArgument(0).asInstanceOf[String])
+ when(c.extract(any())).thenAnswer(p => Success(p.getArgument(0).asInstanceOf[String]))
c
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/UserStateItemHandlerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/UserStateItemHandlerSpec.scala
index 6521ca43..9cfb7336 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/UserStateItemHandlerSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/UserStateItemHandlerSpec.scala
@@ -19,18 +19,19 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialStateI
import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialStateItem.ItemStructure
import io.github.honeycombcheesecake.play.silhouette.impl.providers.state.UserStateItemHandler._
import org.specs2.matcher.JsonMatchers
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.libs.json.Json
import play.api.mvc.AnyContentAsEmpty
import play.api.test.{ FakeRequest, PlaySpecification }
+import org.mockito.Mockito._
+import test.Helper.mockSmart
import scala.concurrent.ExecutionContext.Implicits.global
/**
* Test case for the [[UserStateItemHandler]] class.
*/
-class UserStateItemHandlerSpec extends PlaySpecification with Mockito with JsonMatchers {
+class UserStateItemHandlerSpec extends PlaySpecification with JsonMatchers {
"The `item` method" should {
"return the user state item" in new Context {
@@ -44,7 +45,7 @@ class UserStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM
}
"should return `None` if it can't handle the given item" in new Context {
- val nonUserState = mock[SocialStateItem].smart
+ val nonUserState = mockSmart[SocialStateItem]
userStateItemHandler.canHandle(nonUserState) must beNone
}
@@ -52,8 +53,8 @@ class UserStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM
"The `canHandle` method" should {
"return false if the give item is for another handler" in new Context {
- val nonUserItemStructure = mock[ItemStructure].smart
- nonUserItemStructure.id returns "non-user-item"
+ val nonUserItemStructure = mockSmart[ItemStructure]
+ when(nonUserItemStructure.id).thenReturn("non-user-item")
implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest()
userStateItemHandler.canHandle(nonUserItemStructure) must beFalse
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/services/GravatarServiceSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/services/GravatarServiceSpec.scala
index ca5b6a67..11af505a 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/services/GravatarServiceSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/services/GravatarServiceSpec.scala
@@ -17,9 +17,11 @@ package io.github.honeycombcheesecake.play.silhouette.impl.services
import io.github.honeycombcheesecake.play.silhouette.api.util.{ MockHTTPLayer, MockWSRequest }
import io.github.honeycombcheesecake.play.silhouette.impl.services.GravatarService._
-import org.specs2.mock.Mockito
import org.specs2.specification.Scope
import play.api.test.PlaySpecification
+import org.mockito.Mockito._
+import org.mockito.ArgumentMatchers.any
+import test.Helper.mock
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
@@ -27,7 +29,7 @@ import scala.concurrent.Future
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.services.GravatarService]] class.
*/
-class GravatarServiceSpec extends PlaySpecification with Mockito {
+class GravatarServiceSpec extends PlaySpecification {
"The `retrieveURL` method" should {
"return None if email is empty" in new Context {
@@ -38,9 +40,9 @@ class GravatarServiceSpec extends PlaySpecification with Mockito {
val wsRequest = mock[MockWSRequest]
val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 404
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(any) returns wsRequest
+ when(wsResponse.status).thenReturn(404)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(any)).thenReturn(wsRequest)
await(service.retrieveURL(email)) should beNone
}
@@ -49,9 +51,9 @@ class GravatarServiceSpec extends PlaySpecification with Mockito {
val wsRequest = mock[MockWSRequest]
val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 404
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(any) returns wsRequest
+ when(wsResponse.status).thenReturn(404)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(any)).thenReturn(wsRequest)
await(service.retrieveURL(email)) should beNone
}
@@ -60,9 +62,9 @@ class GravatarServiceSpec extends PlaySpecification with Mockito {
val wsRequest = mock[MockWSRequest]
val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status throws new RuntimeException("Timeout error")
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(any) returns wsRequest
+ when(wsResponse.status).thenThrow(new RuntimeException("Timeout error"))
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(any)).thenReturn(wsRequest)
await(service.retrieveURL(email)) should beNone
}
@@ -71,9 +73,9 @@ class GravatarServiceSpec extends PlaySpecification with Mockito {
val wsRequest = mock[MockWSRequest]
val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(any) returns wsRequest
+ when(wsResponse.status).thenReturn(200)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(any)).thenReturn(wsRequest)
await(service.retrieveURL(email)) should beSome(SecureURL.format(hash, "?d=404"))
}
@@ -82,10 +84,10 @@ class GravatarServiceSpec extends PlaySpecification with Mockito {
val wsRequest = mock[MockWSRequest]
val wsResponse = mock[MockWSRequest#Response]
- settings.secure returns false
- wsResponse.status returns 200
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(any) returns wsRequest
+ when(settings.secure).thenReturn(false)
+ when(wsResponse.status).thenReturn(200)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(any)).thenReturn(wsRequest)
await(service.retrieveURL(email)) should beSome(InsecureURL.format(hash, "?d=404"))
}
@@ -94,10 +96,10 @@ class GravatarServiceSpec extends PlaySpecification with Mockito {
val wsRequest = mock[MockWSRequest]
val wsResponse = mock[MockWSRequest#Response]
- settings.params returns Map("d" -> "http://example.com/images/avatar.jpg", "s" -> "400")
- wsResponse.status returns 200
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(any) returns wsRequest
+ when(settings.params).thenReturn(Map("d" -> "http://example.com/images/avatar.jpg", "s" -> "400"))
+ when(wsResponse.status).thenReturn(200)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(any)).thenReturn(wsRequest)
await(service.retrieveURL(email)) should beSome(
SecureURL.format(hash, "?d=http%3A%2F%2Fexample.com%2Fimages%2Favatar.jpg&s=400"))
@@ -107,9 +109,9 @@ class GravatarServiceSpec extends PlaySpecification with Mockito {
val wsRequest = mock[MockWSRequest]
val wsResponse = mock[MockWSRequest#Response]
- wsResponse.status returns 200
- wsRequest.get() returns Future.successful(wsResponse)
- httpLayer.url(any) returns wsRequest
+ when(wsResponse.status).thenReturn(200)
+ when(wsRequest.get()).thenReturn(Future.successful(wsResponse))
+ when(httpLayer.url(any)).thenReturn(wsRequest)
await(service.retrieveURL("123test@test.com")) should beSome(
SecureURL.format("0d77aed6b4c5857473c9a04c2017f8b8", "?d=404"))
@@ -126,7 +128,7 @@ class GravatarServiceSpec extends PlaySpecification with Mockito {
*/
val httpLayer = {
val m = mock[MockHTTPLayer]
- m.executionContext returns global
+ when(m.executionContext).thenReturn(global)
m
}
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/DefaultFingerprintGeneratorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/DefaultFingerprintGeneratorSpec.scala
index 1a463dd8..3ef16ea7 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/DefaultFingerprintGeneratorSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/DefaultFingerprintGeneratorSpec.scala
@@ -27,7 +27,7 @@ class DefaultFingerprintGeneratorSpec extends PlaySpecification {
"return fingerprint including the `User-Agent` header" in {
val userAgent = "test-user-agent"
val generator = new DefaultFingerprintGenerator()
- implicit val request = FakeRequest().withHeaders(USER_AGENT -> userAgent)
+ implicit val request = FakeRequest().withHeaders((USER_AGENT, userAgent))
generator.generate must be equalTo Hash.sha1(userAgent + ":::")
}
@@ -35,7 +35,7 @@ class DefaultFingerprintGeneratorSpec extends PlaySpecification {
"return fingerprint including the `Accept-Language` header" in {
val acceptLanguage = "test-accept-language"
val generator = new DefaultFingerprintGenerator()
- implicit val request = FakeRequest().withHeaders(ACCEPT_LANGUAGE -> acceptLanguage)
+ implicit val request = FakeRequest().withHeaders((ACCEPT_LANGUAGE, acceptLanguage))
generator.generate must be equalTo Hash.sha1(":" + acceptLanguage + "::")
}
@@ -43,7 +43,7 @@ class DefaultFingerprintGeneratorSpec extends PlaySpecification {
"return fingerprint including the `Accept-Charset` header" in {
val acceptCharset = "test-accept-charset"
val generator = new DefaultFingerprintGenerator()
- implicit val request = FakeRequest().withHeaders(ACCEPT_CHARSET -> acceptCharset)
+ implicit val request = FakeRequest().withHeaders((ACCEPT_CHARSET, acceptCharset))
generator.generate must be equalTo Hash.sha1("::" + acceptCharset + ":")
}
@@ -61,9 +61,9 @@ class DefaultFingerprintGeneratorSpec extends PlaySpecification {
val acceptCharset = "test-accept-charset"
val generator = new DefaultFingerprintGenerator(true)
implicit val request = FakeRequest().withHeaders(
- USER_AGENT -> userAgent,
- ACCEPT_LANGUAGE -> acceptLanguage,
- ACCEPT_CHARSET -> acceptCharset)
+ (USER_AGENT, userAgent),
+ (ACCEPT_LANGUAGE, acceptLanguage),
+ (ACCEPT_CHARSET, acceptCharset))
generator.generate must be equalTo Hash.sha1(
userAgent + ":" + acceptLanguage + ":" + acceptCharset + ":127.0.0.1")
diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/PlayCacheLayerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/PlayCacheLayerSpec.scala
index 56510f0f..a5c63671 100644
--- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/PlayCacheLayerSpec.scala
+++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/PlayCacheLayerSpec.scala
@@ -15,7 +15,7 @@
*/
package io.github.honeycombcheesecake.play.silhouette.impl.util
-import org.specs2.mock.Mockito
+import org.mockito.Mockito._
import org.specs2.specification.Scope
import play.api.cache.AsyncCacheApi
import play.api.test.PlaySpecification
@@ -27,15 +27,15 @@ import scala.concurrent.duration.Duration
/**
* Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.util.PlayCacheLayer]] class.
*/
-class PlayCacheLayerSpec extends PlaySpecification with Mockito {
+class PlayCacheLayerSpec extends PlaySpecification {
"The `find` method" should {
"return value from cache" in new Context {
- cacheAPI.get[ZonedDateTime]("id") returns Future.successful(Some(value))
+ when(cacheAPI.get[ZonedDateTime]("id")).thenReturn(Future.successful(Some(value)))
await(layer.find[ZonedDateTime]("id")) should beSome(value)
- there was one(cacheAPI).get[ZonedDateTime]("id")
+ verify(cacheAPI).get[ZonedDateTime]("id")
}
}
@@ -43,7 +43,7 @@ class PlayCacheLayerSpec extends PlaySpecification with Mockito {
"save value in cache" in new Context {
await(layer.save("id", value))
- there was one(cacheAPI).set("id", value, Duration.Inf)
+ verify(cacheAPI).set("id", value, Duration.Inf)
}
}
@@ -51,7 +51,7 @@ class PlayCacheLayerSpec extends PlaySpecification with Mockito {
"removes value from cache" in new Context {
await(layer.remove("id")) must beEqualTo(())
- there was one(cacheAPI).remove("id")
+ verify(cacheAPI).remove("id")
}
}
@@ -63,7 +63,7 @@ class PlayCacheLayerSpec extends PlaySpecification with Mockito {
/**
* The cache API.
*/
- lazy val cacheAPI = mock[AsyncCacheApi]
+ lazy val cacheAPI = mock(classOf[AsyncCacheApi])
/**
* The layer to test.