~jan0sch/smederee

Showing details for patch 6c48d3b0796a44153cedcf634b58a1befeeed3b0.
2024-02-06 (Tue), 11:16 AM - Jens Grassel - 6c48d3b0796a44153cedcf634b58a1befeeed3b0

Add baseline for testing reset password routes.

Summary of changes
3 files added
  • modules/hub/src/test/scala/de/smederee/hub/ResetPasswordRoutesTest.scala
  • modules/hub/src/test/scala/de/smederee/hub/TestEmailMiddleware.scala
  • modules/hub/src/test/scala/de/smederee/hub/TestResetPasswordRepository.scala
diff -rN -u old-smederee/modules/hub/src/test/scala/de/smederee/hub/ResetPasswordRoutesTest.scala new-smederee/modules/hub/src/test/scala/de/smederee/hub/ResetPasswordRoutesTest.scala
--- old-smederee/modules/hub/src/test/scala/de/smederee/hub/ResetPasswordRoutesTest.scala	1970-01-01 00:00:00.000000000 +0000
+++ new-smederee/modules/hub/src/test/scala/de/smederee/hub/ResetPasswordRoutesTest.scala	2025-01-13 01:51:08.496904590 +0000
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022  Contributors as noted in the AUTHORS.md file
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.smederee.hub
+
+import java.nio.charset.StandardCharsets
+
+import cats.effect.*
+import cats.syntax.all.*
+import com.typesafe.config.ConfigFactory
+import de.smederee.hub.Generators.*
+import de.smederee.hub.config.*
+import de.smederee.hub.forms.*
+import de.smederee.hub.forms.types.*
+import de.smederee.security.SignAndValidate
+import de.smederee.security.SignedToken
+import org.http4s.*
+import org.http4s.implicits.*
+import org.http4s.server.*
+import pureconfig.ConfigSource
+
+import munit.*
+
+final class ResetPasswordRoutesTest extends CatsEffectSuite {
+    private val resetChangePasswordPath = uri"/forgot-password/change-password"
+
+    protected final val configuration: SmedereeHubConfig =
+        ConfigSource
+            .fromConfig(ConfigFactory.load(getClass.getClassLoader))
+            .at(SmedereeHubConfig.location)
+            .loadOrThrow[SmedereeHubConfig]
+
+    test("GET forgot-password/change-password/TOKEN must show the change password form") {
+        genValidAccount.sample match {
+            case Some(account) =>
+                val token             = ResetToken.generate
+                val changePasswordUri = resetChangePasswordPath.addSegment(token.toString)
+                val expectedHtml = views.html
+                    .changePassword()(changePasswordUri, None, title = "Smederee - Change your password.".some, token)()
+
+                val authenticationConfig = configuration.service.authentication
+                val authenticationRepo   = new TestAuthenticationRepository[IO](List.empty, List.empty)
+                val emailMiddleware      = new TestEmailMiddleware[IO]
+                val externalConfig       = configuration.service.external
+                val resetPasswordRepo    = new TestResetPasswordRepository[IO](account, token.some)
+
+                def service: HttpRoutes[IO] =
+                    Router(
+                        "/" -> new ResetPasswordRoutes[IO](
+                            authenticationConfig,
+                            authenticationRepo,
+                            emailMiddleware,
+                            externalConfig,
+                            resetPasswordRepo
+                        ).routes
+                    )
+
+                def request                    = Request[IO](method = Method.GET, uri = changePasswordUri)
+                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.Ok)
+                    assertEquals(body, expectedHtml.toString)
+                }
+            case _ => fail("Could not generate data samples!")
+        }
+    }
+}
diff -rN -u old-smederee/modules/hub/src/test/scala/de/smederee/hub/TestEmailMiddleware.scala new-smederee/modules/hub/src/test/scala/de/smederee/hub/TestEmailMiddleware.scala
--- old-smederee/modules/hub/src/test/scala/de/smederee/hub/TestEmailMiddleware.scala	1970-01-01 00:00:00.000000000 +0000
+++ new-smederee/modules/hub/src/test/scala/de/smederee/hub/TestEmailMiddleware.scala	2025-01-13 01:51:08.496904590 +0000
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022  Contributors as noted in the AUTHORS.md file
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.smederee.hub
+
+import cats.effect.*
+import cats.syntax.all.*
+import de.smederee.email.EmailMessage
+import de.smederee.email.EmailMiddleware
+
+/** Test implementation of an [[EmailMiddleware]] that does nothing.
+  */
+class TestEmailMiddleware[F[_]: Sync] extends EmailMiddleware[F] {
+    override def send(email: EmailMessage): F[Either[String, Unit]] = Sync[F].pure(().asRight)
+}
diff -rN -u old-smederee/modules/hub/src/test/scala/de/smederee/hub/TestResetPasswordRepository.scala new-smederee/modules/hub/src/test/scala/de/smederee/hub/TestResetPasswordRepository.scala
--- old-smederee/modules/hub/src/test/scala/de/smederee/hub/TestResetPasswordRepository.scala	1970-01-01 00:00:00.000000000 +0000
+++ new-smederee/modules/hub/src/test/scala/de/smederee/hub/TestResetPasswordRepository.scala	2025-01-13 01:51:08.496904590 +0000
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022  Contributors as noted in the AUTHORS.md file
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.smederee.hub
+
+import java.time.OffsetDateTime
+
+import cats.effect.*
+import cats.syntax.all.*
+import de.smederee.hub.*
+import de.smederee.security.*
+
+/** An implementation of a [[ResetPasswordRepository]] for testing purposes.
+  *
+  * @param account
+  *   An account on which shall be operated.
+  * @param token
+  *   A reset token on which shall be operated.
+  * @tparam F
+  *   A higher kinded type which wraps the actual return values.
+  */
+@SuppressWarnings(Array("DisableSyntax.var"))
+class TestResetPasswordRepository[F[_]: Sync](private val account: Account, token: Option[ResetToken])
+    extends ResetPasswordRepository[F] {
+
+    override def findByNameAndResetPasswordToken(name: Username, token: ResetToken): F[Option[Account]] =
+        Sync[F].delay(
+            this.token.find(_ === token).map(_ => account).filter(_.name === name)
+        ) // Return account if token and name matches.
+
+    override def setResetPasswordToken(uid: UserId)(token: ResetToken, tokenExpiration: OffsetDateTime): F[Int] =
+        Sync[F].pure(1)
+
+    override def findByResetPasswordToken(token: ResetToken): F[Option[Account]] =
+        Sync[F].delay(this.token.find(_ === token).map(_ => account)) // Return account if token matches.
+
+    override def removeResetPasswordExpirationDate(uid: UserId): F[Int] = Sync[F].pure(1)
+
+    override def removeResetPasswordToken(uid: UserId): F[Int] = Sync[F].pure(1)
+
+    override def setPassword(uid: UserId)(hash: PasswordHash): F[Int] = Sync[F].pure(1)
+}