~jan0sch/smederee

Showing details for patch dbdefe8d234d4eebb2578b39cb77733a07307444.
2024-07-21 (Sun), 7:40 AM - Jens Grassel - dbdefe8d234d4eebb2578b39cb77733a07307444

chore: migrate test to ScalaCheckEffect

Summary of changes
1 files modified with 175 lines added and 178 lines removed
  • modules/hub/src/test/scala/de/smederee/hub/AuthenticationRoutesTest.scala with 175 added and 178 removed lines
diff -rN -u old-smederee/modules/hub/src/test/scala/de/smederee/hub/AuthenticationRoutesTest.scala new-smederee/modules/hub/src/test/scala/de/smederee/hub/AuthenticationRoutesTest.scala
--- old-smederee/modules/hub/src/test/scala/de/smederee/hub/AuthenticationRoutesTest.scala	2025-01-11 03:11:16.720535197 +0000
+++ new-smederee/modules/hub/src/test/scala/de/smederee/hub/AuthenticationRoutesTest.scala	2025-01-11 03:11:16.720535197 +0000
@@ -13,6 +13,7 @@
 import cats.syntax.all.*
 import com.typesafe.config.ConfigFactory
 import de.smederee.hub.Generators.*
+import de.smederee.hub.Generators.given
 import de.smederee.hub.config.*
 import de.smederee.hub.forms.*
 import de.smederee.hub.forms.types.*
@@ -25,7 +26,11 @@
 
 import munit.*
 
-class AuthenticationRoutesTest extends CatsEffectSuite {
+import org.scalacheck.effect.PropF
+
+class AuthenticationRoutesTest extends CatsEffectSuite with ScalaCheckEffectSuite {
+    override def scalaCheckTestParameters = super.scalaCheckTestParameters.withMinSuccessfulTests(1)
+
     val loginPath         = uri"/login"
     private val resetPath = uri"/forgot-password"
 
@@ -152,198 +157,190 @@
     }
 
     test("POST /login must return 400 - Bad Request AND increase failed attempts if the credentials are invalid") {
-        genValidAccount.sample match {
-            case None => fail("Could not generate data samples!")
-            case Some(account) =>
-                val clock                = java.time.Clock.systemUTC
-                val authenticationConfig = configuration.service.authentication
-                val externalConfig       = configuration.service.external
-                val repo                 = new TestAuthenticationRepository[IO](List(account), List.empty)
-                val signAndValidate      = SignAndValidate(configuration.service.authentication.cookieSecret)
-
-                def service: HttpRoutes[IO] =
-                    Router(
-                        "/" -> new AuthenticationRoutes[IO](
-                            clock,
-                            authenticationConfig,
-                            externalConfig,
-                            repo,
-                            signAndValidate
-                        ).routes
-                    )
-
-                val payload = UrlForm.empty
-                    .updateFormField(LoginForm.fieldName.toString, account.name.toString)
-                    .updateFormField(LoginForm.fieldPassword.toString, "doesn't matter")
-                def request = Request[IO](method = Method.POST, uri = loginPath).withEntity(payload)
-
-                val expectedHtml =
-                    views.html.login()(loginPath, None, resetPath, title = "Smederee - Login to your account".some)(
-                        formData = Map(LoginForm.fieldName.toString -> Chain(account.name.toString)),
-                        Map(LoginForm.fieldGlobal -> List(FormFieldError("Invalid credentials!")))
-                    )
-
-                def response: IO[Response[IO]] = service.orNotFound.run(request)
-
-                val test = for {
-                    result <- response
-                    body   <- result.as[String]
-                    failed <- repo.getFailed
-                } yield (result, body, failed)
-
-                test.map { output =>
-                    val (result, body, failed) = output
-                    assertEquals(result.status, Status.BadRequest)
-                    assertEquals(failed.find(_._1 === account.uid), Option((account.uid, 1)))
-                    assertEquals(body, expectedHtml.toString)
-                }
+        PropF.forAllF { (account: Account) =>
+            val clock                = java.time.Clock.systemUTC
+            val authenticationConfig = configuration.service.authentication
+            val externalConfig       = configuration.service.external
+            val repo                 = new TestAuthenticationRepository[IO](List(account), List.empty)
+            val signAndValidate      = SignAndValidate(configuration.service.authentication.cookieSecret)
+
+            def service: HttpRoutes[IO] =
+                Router(
+                    "/" -> new AuthenticationRoutes[IO](
+                        clock,
+                        authenticationConfig,
+                        externalConfig,
+                        repo,
+                        signAndValidate
+                    ).routes
+                )
+
+            val payload = UrlForm.empty
+                .updateFormField(LoginForm.fieldName.toString, account.name.toString)
+                .updateFormField(LoginForm.fieldPassword.toString, "doesn't matter")
+            def request = Request[IO](method = Method.POST, uri = loginPath).withEntity(payload)
+
+            val expectedHtml =
+                views.html.login()(loginPath, None, resetPath, title = "Smederee - Login to your account".some)(
+                    formData = Map(LoginForm.fieldName.toString -> Chain(account.name.toString)),
+                    Map(LoginForm.fieldGlobal -> List(FormFieldError("Invalid credentials!")))
+                )
+
+            def response: IO[Response[IO]] = service.orNotFound.run(request)
+
+            val test = for {
+                result <- response
+                body   <- result.as[String]
+                failed <- repo.getFailed
+            } yield (result, body, failed)
+
+            test.start.flatMap(_.joinWithNever).map { output =>
+                val (result, body, failed) = output
+                assertEquals(result.status, Status.BadRequest)
+                assertEquals(failed.find(_._1 === account.uid), Option((account.uid, 1)))
+                assertEquals(body, expectedHtml.toString)
+            }
         }
     }
 
     test(
         "POST /login must return 400 - Bad Request AND lock the account if the credentials are invalid and failed attempts are too high"
     ) {
-        genValidAccount.sample match {
-            case None => fail("Could not generate data samples!")
-            case Some(account) =>
-                val clock                = java.time.Clock.systemUTC
-                val authenticationConfig = configuration.service.authentication
-                val externalConfig       = configuration.service.external
-                val repo                 = new TestAuthenticationRepository[IO](List(account), List.empty)
-                val signAndValidate      = SignAndValidate(configuration.service.authentication.cookieSecret)
-
-                for (_ <- 0 to authenticationConfig.lockAfter.toInt) yield repo.incrementFailedAttempts(account.uid)
-
-                def service: HttpRoutes[IO] =
-                    Router(
-                        "/" -> new AuthenticationRoutes[IO](
-                            clock,
-                            authenticationConfig,
-                            externalConfig,
-                            repo,
-                            signAndValidate
-                        ).routes
-                    )
-
-                val payload = UrlForm.empty
-                    .updateFormField(LoginForm.fieldName.toString, account.name.toString)
-                    .updateFormField(LoginForm.fieldPassword.toString, "doesn't matter")
-                def request = Request[IO](method = Method.POST, uri = loginPath).withEntity(payload)
-
-                val expectedHtml =
-                    views.html.login()(loginPath, None, resetPath, title = "Smederee - Login to your account".some)(
-                        formData = Map(LoginForm.fieldName.toString -> Chain(account.name.toString)),
-                        Map(LoginForm.fieldGlobal -> List(FormFieldError("Invalid credentials!")))
-                    )
-
-                def response: IO[Response[IO]] = service.orNotFound.run(request)
-
-                val test = for {
-                    result <- response
-                    body   <- result.as[String]
-                    locked <- repo.getLocked
-                } yield (result, body, locked)
-
-                test.map { output =>
-                    val (result, body, locked) = output
-                    assertEquals(result.status, Status.BadRequest)
-                    assert(locked.exists(_ === account.uid), s"Account was not locked! ($locked)")
-                    assertEquals(body, expectedHtml.toString)
-                }
+        PropF.forAllF { (account: Account) =>
+            val clock                = java.time.Clock.systemUTC
+            val authenticationConfig = configuration.service.authentication
+            val externalConfig       = configuration.service.external
+            val repo                 = new TestAuthenticationRepository[IO](List(account), List.empty)
+            val signAndValidate      = SignAndValidate(configuration.service.authentication.cookieSecret)
+
+            for (_ <- 0 to authenticationConfig.lockAfter.toInt) yield repo.incrementFailedAttempts(account.uid)
+
+            def service: HttpRoutes[IO] =
+                Router(
+                    "/" -> new AuthenticationRoutes[IO](
+                        clock,
+                        authenticationConfig,
+                        externalConfig,
+                        repo,
+                        signAndValidate
+                    ).routes
+                )
+
+            val payload = UrlForm.empty
+                .updateFormField(LoginForm.fieldName.toString, account.name.toString)
+                .updateFormField(LoginForm.fieldPassword.toString, "doesn't matter")
+            def request = Request[IO](method = Method.POST, uri = loginPath).withEntity(payload)
+
+            val expectedHtml =
+                views.html.login()(loginPath, None, resetPath, title = "Smederee - Login to your account".some)(
+                    formData = Map(LoginForm.fieldName.toString -> Chain(account.name.toString)),
+                    Map(LoginForm.fieldGlobal -> List(FormFieldError("Invalid credentials!")))
+                )
+
+            def response: IO[Response[IO]] = service.orNotFound.run(request)
+
+            val test = for {
+                result <- response
+                body   <- result.as[String]
+                locked <- repo.getLocked
+            } yield (result, body, locked)
+
+            test.start.flatMap(_.joinWithNever).map { output =>
+                val (result, body, locked) = output
+                assertEquals(result.status, Status.BadRequest)
+                assert(locked.exists(_ === account.uid), s"Account was not locked! ($locked)")
+                assertEquals(body, expectedHtml.toString)
+            }
         }
     }
 
     test("POST /login must return 400 - Bad Request if the credentials are valid but the account is locked") {
-        genValidAccount.sample match {
-            case None => fail("Could not generate data samples!")
-            case Some(account) =>
-                val clock                = java.time.Clock.systemUTC
-                val authenticationConfig = configuration.service.authentication
-                val externalConfig       = configuration.service.external
-                // A locked account is not supposed to be returned by findAccount, so we leave the account list empty.
-                val repo            = new TestAuthenticationRepository[IO](List.empty, List.empty)
-                val signAndValidate = SignAndValidate(configuration.service.authentication.cookieSecret)
-
-                def service: HttpRoutes[IO] =
-                    Router(
-                        "/" -> new AuthenticationRoutes[IO](
-                            clock,
-                            authenticationConfig,
-                            externalConfig,
-                            repo,
-                            signAndValidate
-                        ).routes
-                    )
-
-                val payload = UrlForm.empty
-                    .updateFormField(LoginForm.fieldName.toString, account.name.toString)
-                    .updateFormField(LoginForm.fieldPassword.toString, "doesn't matter")
-                def request = Request[IO](method = Method.POST, uri = loginPath).withEntity(payload)
-
-                val expectedHtml =
-                    views.html.login()(loginPath, None, resetPath, title = "Smederee - Login to your account".some)(
-                        formData = Map(LoginForm.fieldName.toString -> Chain(account.name.toString)),
-                        Map(LoginForm.fieldGlobal -> List(FormFieldError("Invalid credentials!")))
-                    )
-
-                def response: IO[Response[IO]] = service.orNotFound.run(request)
-
-                val test = for {
-                    result <- response
-                    body   <- result.as[String]
-                } yield (result, body)
-
-                test.map { output =>
-                    val (result, body) = output
-                    assertEquals(result.status, Status.BadRequest)
-                    assertEquals(body, expectedHtml.toString)
-                }
+        PropF.forAllF { (account: Account) =>
+            val clock                = java.time.Clock.systemUTC
+            val authenticationConfig = configuration.service.authentication
+            val externalConfig       = configuration.service.external
+            // A locked account is not supposed to be returned by findAccount, so we leave the account list empty.
+            val repo            = new TestAuthenticationRepository[IO](List.empty, List.empty)
+            val signAndValidate = SignAndValidate(configuration.service.authentication.cookieSecret)
+
+            def service: HttpRoutes[IO] =
+                Router(
+                    "/" -> new AuthenticationRoutes[IO](
+                        clock,
+                        authenticationConfig,
+                        externalConfig,
+                        repo,
+                        signAndValidate
+                    ).routes
+                )
+
+            val payload = UrlForm.empty
+                .updateFormField(LoginForm.fieldName.toString, account.name.toString)
+                .updateFormField(LoginForm.fieldPassword.toString, "doesn't matter")
+            def request = Request[IO](method = Method.POST, uri = loginPath).withEntity(payload)
+
+            val expectedHtml =
+                views.html.login()(loginPath, None, resetPath, title = "Smederee - Login to your account".some)(
+                    formData = Map(LoginForm.fieldName.toString -> Chain(account.name.toString)),
+                    Map(LoginForm.fieldGlobal -> List(FormFieldError("Invalid credentials!")))
+                )
+
+            def response: IO[Response[IO]] = service.orNotFound.run(request)
+
+            val test = for {
+                result <- response
+                body   <- result.as[String]
+            } yield (result, body)
+
+            test.start.flatMap(_.joinWithNever).map { output =>
+                val (result, body) = output
+                assertEquals(result.status, Status.BadRequest)
+                assertEquals(body, expectedHtml.toString)
+            }
         }
     }
 
     test("POST /login must return 303 - See Other AND set the authentication cookie if the credentials are valid") {
-        genValidAccount.sample match {
-            case None => fail("Could not generate data samples!")
-            case Some(account) =>
-                val clock                = java.time.Clock.systemUTC
-                val authenticationConfig = configuration.service.authentication
-                val externalConfig       = configuration.service.external
-                val repo                 = new TestAuthenticationRepository[IO](List(account), List.empty)
-                val signAndValidate      = SignAndValidate(configuration.service.authentication.cookieSecret)
-
-                def service: HttpRoutes[IO] =
-                    Router(
-                        "/" -> new AuthenticationRoutes[IO](
-                            clock,
-                            authenticationConfig,
-                            externalConfig,
-                            repo,
-                            signAndValidate
-                        ).routes
-                    )
-
-                val payload = UrlForm.empty
-                    .updateFormField(LoginForm.fieldName.toString, account.name.toString)
-                    .updateFormField(LoginForm.fieldPassword.toString, repo.DefaultPassword)
-                def request = Request[IO](method = Method.POST, uri = loginPath).withEntity(payload)
-
-                def response: IO[Response[IO]] = service.orNotFound.run(request)
-
-                val test = for {
-                    result <- response
-                    body   <- result.as[String]
-                } yield (result, body)
-
-                test.map { output =>
-                    val (result, body) = output
-                    assertEquals(result.status, Status.SeeOther)
-                    assert(body.isEmpty, "Response body must be empty!")
-                    result.cookies.find(_.name === Constants.authenticationCookieName.toString).map(_.content) match {
-                        case None => fail("Authentication cookie not set!")
-                        case Some(content) =>
-                            assert(SignedToken.from(content).flatMap(signAndValidate.validate).nonEmpty)
-                    }
+        PropF.forAllF { (account: Account) =>
+            val clock                = java.time.Clock.systemUTC
+            val authenticationConfig = configuration.service.authentication
+            val externalConfig       = configuration.service.external
+            val repo                 = new TestAuthenticationRepository[IO](List(account), List.empty)
+            val signAndValidate      = SignAndValidate(configuration.service.authentication.cookieSecret)
+
+            def service: HttpRoutes[IO] =
+                Router(
+                    "/" -> new AuthenticationRoutes[IO](
+                        clock,
+                        authenticationConfig,
+                        externalConfig,
+                        repo,
+                        signAndValidate
+                    ).routes
+                )
+
+            val payload = UrlForm.empty
+                .updateFormField(LoginForm.fieldName.toString, account.name.toString)
+                .updateFormField(LoginForm.fieldPassword.toString, repo.DefaultPassword)
+            def request = Request[IO](method = Method.POST, uri = loginPath).withEntity(payload)
+
+            def response: IO[Response[IO]] = service.orNotFound.run(request)
+
+            val test = for {
+                result <- response
+                body   <- result.as[String]
+            } yield (result, body)
+
+            test.start.flatMap(_.joinWithNever).map { output =>
+                val (result, body) = output
+                assertEquals(result.status, Status.SeeOther)
+                assert(body.isEmpty, "Response body must be empty!")
+                result.cookies.find(_.name === Constants.authenticationCookieName.toString).map(_.content) match {
+                    case None => fail("Authentication cookie not set!")
+                    case Some(content) =>
+                        assert(SignedToken.from(content).flatMap(signAndValidate.validate).nonEmpty)
                 }
+            }
         }
     }
 }