~jan0sch/smederee

Showing details for patch 41fc72b7bdf917cf1f31cf3cacbeb03afed88830.
2024-07-22 (Mon), 8:54 AM - Jens Grassel - 41fc72b7bdf917cf1f31cf3cacbeb03afed88830

chore: migrate test to ScalaCheckEffect

Summary of changes
2 files modified with 220 lines added and 199 lines removed
  • modules/hub/src/test/scala/de/smederee/hub/DoobieAccountManagementRepositoryTest.scala with 218 added and 199 removed lines
  • modules/hub/src/test/scala/de/smederee/hub/Generators.scala with 2 added and 0 removed lines
diff -rN -u old-smederee/modules/hub/src/test/scala/de/smederee/hub/DoobieAccountManagementRepositoryTest.scala new-smederee/modules/hub/src/test/scala/de/smederee/hub/DoobieAccountManagementRepositoryTest.scala
--- old-smederee/modules/hub/src/test/scala/de/smederee/hub/DoobieAccountManagementRepositoryTest.scala	2025-01-11 02:47:17.870494389 +0000
+++ new-smederee/modules/hub/src/test/scala/de/smederee/hub/DoobieAccountManagementRepositoryTest.scala	2025-01-11 02:47:17.870494389 +0000
@@ -14,11 +14,17 @@
 import cats.syntax.all.*
 import de.smederee.TestTags.*
 import de.smederee.hub.Generators.*
+import de.smederee.hub.Generators.given
+import de.smederee.i18n.LanguageCode
 import de.smederee.security.*
 import de.smederee.ssh.*
 import doobie.*
 
+import org.scalacheck.effect.PropF
+
 final class DoobieAccountManagementRepositoryTest extends BaseSpec {
+    override def scalaCheckTestParameters = super.scalaCheckTestParameters.withMinSuccessfulTests(1)
+
     val sshKeyWithComment = ResourceSuiteLocalFixture(
         "ssh-key-with-comment",
         Resource.make(IO {
@@ -30,7 +36,7 @@
                 .getLines()
                 .mkString
             val keyString = SshPublicKeyString(input)
-            PublicSshKey.from(UUID.randomUUID())(UserId.randomUserId)(OffsetDateTime.now(ZoneOffset.UTC))(keyString)
+            PublicSshKey.from(UUID.randomUUID())(UserId.randomUserId)(OffsetDateTime.now(ZoneOffset.UTC))(keyString).get
         })(_ => IO.unit)
     )
 
@@ -45,242 +51,255 @@
                 .getLines()
                 .mkString
             val keyString = SshPublicKeyString(input)
-            PublicSshKey.from(UUID.randomUUID())(UserId.randomUserId)(OffsetDateTime.now(ZoneOffset.UTC))(keyString)
+            PublicSshKey.from(UUID.randomUUID())(UserId.randomUserId)(OffsetDateTime.now(ZoneOffset.UTC))(keyString).get
         })(_ => IO.unit)
     )
 
     override def munitFixtures = List(sshKeyWithComment, sshKeyWithoutComment)
 
     test("addSshKey must save the key to the database".tag(NeedsDatabase)) {
-        (genValidAccount.sample, sshKeyWithComment()) match {
-            case (Some(account), Some(sshKey)) =>
-                val dbConfig = configuration.database
-                val tx = Transactor.fromDriverManager[IO](
-                    driver = dbConfig.driver,
-                    url = dbConfig.url,
-                    user = dbConfig.user,
-                    password = dbConfig.pass,
-                    logHandler = None
-                )
-                val repo     = new DoobieAccountManagementRepository[IO](tx)
-                val attempts = scala.util.Random.nextInt(128)
-                val hash     = PasswordHash("Yet another weak password!")
-                val test = for {
-                    _    <- createAccount(account, hash, None, Option(attempts))
-                    w    <- repo.addSshKey(sshKey.copy(ownerId = account.uid))
-                    keys <- repo.listSshKeys(account.uid).compile.toList
-                } yield (w, keys)
-                test.map { result =>
-                    val (written, keys) = result
-                    assert(written === 1, "No database rows written!")
-                    assert(keys.exists(_.id === sshKey.id), "Key must be in the key list of the user!")
-                }
-            case _ => fail("Could not generate data samples!")
+        PropF.forAllF { (account: Account) =>
+            val sshKey   = sshKeyWithComment()
+            val dbConfig = configuration.database
+            val tx = Transactor.fromDriverManager[IO](
+                driver = dbConfig.driver,
+                url = dbConfig.url,
+                user = dbConfig.user,
+                password = dbConfig.pass,
+                logHandler = None
+            )
+            val repo     = new DoobieAccountManagementRepository[IO](tx)
+            val attempts = scala.util.Random.nextInt(128)
+            val hash     = PasswordHash("Yet another weak password!")
+            val test = for {
+                _    <- createAccount(account, hash, None, Option(attempts))
+                w    <- repo.addSshKey(sshKey.copy(ownerId = account.uid))
+                keys <- repo.listSshKeys(account.uid).compile.toList
+            } yield (w, keys)
+            test.start.flatMap(_.joinWithNever).map { result =>
+                val (written, keys) = result
+                assert(written === 1, "No database rows written!")
+                assert(keys.exists(_.id === sshKey.id), "Key must be in the key list of the user!")
+            }
         }
     }
 
     test("addSshKey must fail if a key with the same fingerprint already exists".tag(NeedsDatabase)) {
-        (genValidAccount.sample, sshKeyWithoutComment()) match {
-            case (Some(account), Some(sshKey)) =>
-                val dbConfig = configuration.database
-                val tx = Transactor.fromDriverManager[IO](
-                    driver = dbConfig.driver,
-                    url = dbConfig.url,
-                    user = dbConfig.user,
-                    password = dbConfig.pass,
-                    logHandler = None
-                )
-                val repo     = new DoobieAccountManagementRepository[IO](tx)
-                val attempts = scala.util.Random.nextInt(128)
-                val hash     = PasswordHash("Yet another weak password!")
-                val test = for {
-                    _ <- createAccount(account, hash, None, Option(attempts))
-                    _ <- repo.addSshKey(sshKey.copy(ownerId = account.uid))
-                    _ <- repo.addSshKey(sshKey.copy(ownerId = account.uid))
-                } yield ()
-                test.attempt.map { result =>
-                    assert(result.isLeft, "Writing a key with a duplicate fingerprint must fail!")
-                }
-            case _ => fail("Could not generate data samples!")
+        PropF.forAllF { (account: Account) =>
+            val sshKey   = sshKeyWithoutComment()
+            val dbConfig = configuration.database
+            val tx = Transactor.fromDriverManager[IO](
+                driver = dbConfig.driver,
+                url = dbConfig.url,
+                user = dbConfig.user,
+                password = dbConfig.pass,
+                logHandler = None
+            )
+            val repo     = new DoobieAccountManagementRepository[IO](tx)
+            val attempts = scala.util.Random.nextInt(128)
+            val hash     = PasswordHash("Yet another weak password!")
+            val test = for {
+                _ <- createAccount(account, hash, None, Option(attempts))
+                _ <- repo.addSshKey(sshKey.copy(ownerId = account.uid))
+                _ <- repo.addSshKey(sshKey.copy(ownerId = account.uid))
+            } yield ()
+            test.start.flatMap(_.joinWithNever).attempt.map { result =>
+                assert(result.isLeft, "Writing a key with a duplicate fingerprint must fail!")
+            }
         }
     }
 
     test("deleteAccount must remove the account from the database".tag(NeedsDatabase)) {
-        genValidAccount.sample match {
-            case None => fail("Could not generate data samples!")
-            case Some(account) =>
-                val dbConfig = configuration.database
-                val tx = Transactor.fromDriverManager[IO](
-                    driver = dbConfig.driver,
-                    url = dbConfig.url,
-                    user = dbConfig.user,
-                    password = dbConfig.pass,
-                    logHandler = None
-                )
-                val repo     = new DoobieAccountManagementRepository[IO](tx)
-                val attempts = scala.util.Random.nextInt(128)
-                val hash     = PasswordHash("Yet another weak password!")
-                val test = for {
-                    _ <- createAccount(account, hash, None, Option(attempts))
-                    _ <- repo.deleteAccount(account.uid)
-                    o <- loadAccount(account.uid)
-                } yield o
-                test.map(result => assert(result === None, "Account not deleted from database!"))
+        PropF.forAllF { (account: Account) =>
+            val dbConfig = configuration.database
+            val tx = Transactor.fromDriverManager[IO](
+                driver = dbConfig.driver,
+                url = dbConfig.url,
+                user = dbConfig.user,
+                password = dbConfig.pass,
+                logHandler = None
+            )
+            val repo     = new DoobieAccountManagementRepository[IO](tx)
+            val attempts = scala.util.Random.nextInt(128)
+            val hash     = PasswordHash("Yet another weak password!")
+            val test = for {
+                _ <- createAccount(account, hash, None, Option(attempts))
+                _ <- repo.deleteAccount(account.uid)
+                o <- loadAccount(account.uid)
+            } yield o
+            test.start
+                .flatMap(_.joinWithNever)
+                .map(result => assert(result === None, "Account not deleted from database!"))
         }
     }
 
     test("findByValidationToken must return the matching account".tag(NeedsDatabase)) {
-        genValidAccount.sample match {
-            case None => fail("Could not generate data samples!")
-            case Some(ac) =>
-                val account  = ac.copy(validatedEmail = true)
-                val token    = ValidationToken.generate
-                val dbConfig = configuration.database
-                val tx = Transactor.fromDriverManager[IO](
-                    driver = dbConfig.driver,
-                    url = dbConfig.url,
-                    user = dbConfig.user,
-                    password = dbConfig.pass,
-                    logHandler = None
-                )
-                val repo = new DoobieAccountManagementRepository[IO](tx)
-                val hash = PasswordHash("Yet another weak password!")
-                val test = for {
-                    _ <- createAccount(account, hash, validationToken = token.some)
-                    o <- repo.findByValidationToken(token)
-                } yield o
-                test.map(result => assert(result === Some(account)))
+        PropF.forAllF { (ac: Account) =>
+            val account  = ac.copy(validatedEmail = true)
+            val token    = ValidationToken.generate
+            val dbConfig = configuration.database
+            val tx = Transactor.fromDriverManager[IO](
+                driver = dbConfig.driver,
+                url = dbConfig.url,
+                user = dbConfig.user,
+                password = dbConfig.pass,
+                logHandler = None
+            )
+            val repo = new DoobieAccountManagementRepository[IO](tx)
+            val hash = PasswordHash("Yet another weak password!")
+            val test = for {
+                _ <- createAccount(account, hash, validationToken = token.some)
+                o <- repo.findByValidationToken(token)
+            } yield o
+            test.start.flatMap(_.joinWithNever).map(result => assert(result === Some(account)))
         }
     }
 
     test("findPasswordHash must return correct hash".tag(NeedsDatabase)) {
-        genValidAccount.sample match {
-            case None => fail("Could not generate data samples!")
-            case Some(account) =>
-                val dbConfig = configuration.database
-                val tx = Transactor.fromDriverManager[IO](
-                    driver = dbConfig.driver,
-                    url = dbConfig.url,
-                    user = dbConfig.user,
-                    password = dbConfig.pass,
-                    logHandler = None
-                )
-                val repo     = new DoobieAccountManagementRepository[IO](tx)
-                val attempts = scala.util.Random.nextInt(128)
-                val hash     = PasswordHash("Yet another weak password!")
-                val test = for {
-                    _ <- createAccount(account, hash, None, Option(attempts))
-                    o <- repo.findPasswordHash(account.uid)
-                } yield o
-                test.map(result => assert(result.exists(_ === hash), "Unexpected result from database!"))
+        PropF.forAllF { (account: Account) =>
+            val dbConfig = configuration.database
+            val tx = Transactor.fromDriverManager[IO](
+                driver = dbConfig.driver,
+                url = dbConfig.url,
+                user = dbConfig.user,
+                password = dbConfig.pass,
+                logHandler = None
+            )
+            val repo     = new DoobieAccountManagementRepository[IO](tx)
+            val attempts = scala.util.Random.nextInt(128)
+            val hash     = PasswordHash("Yet another weak password!")
+            val test = for {
+                _ <- createAccount(account, hash, None, Option(attempts))
+                o <- repo.findPasswordHash(account.uid)
+            } yield o
+            test.start
+                .flatMap(_.joinWithNever)
+                .map(result => assert(result.exists(_ === hash), "Unexpected result from database!"))
         }
     }
 
     test("listSshKeys must return all keys for the user".tag(NeedsDatabase)) {
-        (genValidAccount.sample, sshKeyWithComment(), sshKeyWithoutComment()) match {
-            case (Some(account), Some(sshKeyA), Some(sshKeyB)) =>
-                val dbConfig = configuration.database
-                val tx = Transactor.fromDriverManager[IO](
-                    driver = dbConfig.driver,
-                    url = dbConfig.url,
-                    user = dbConfig.user,
-                    password = dbConfig.pass,
-                    logHandler = None
-                )
-                val repo     = new DoobieAccountManagementRepository[IO](tx)
-                val attempts = scala.util.Random.nextInt(128)
-                val hash     = PasswordHash("Yet another weak password!")
-                val test = for {
-                    _    <- createAccount(account, hash, None, Option(attempts))
-                    _    <- repo.addSshKey(sshKeyA.copy(ownerId = account.uid))
-                    _    <- repo.addSshKey(sshKeyB.copy(ownerId = account.uid))
-                    keys <- repo.listSshKeys(account.uid).compile.toList
-                } yield keys
-                test.map { keys =>
-                    assertEquals(keys.length, 2, "Expected 2 keys in the key list!")
-                    assert(keys.exists(_.id === sshKeyA.id), "Key A must be in the key list of the user!")
-                    assert(keys.exists(_.id === sshKeyB.id), "Key B must be in the key list of the user!")
-                }
-            case _ => fail("Could not generate data samples!")
+        PropF.forAllF { (account: Account) =>
+            val sshKeyA  = sshKeyWithComment()
+            val sshKeyB  = sshKeyWithoutComment()
+            val dbConfig = configuration.database
+            val tx = Transactor.fromDriverManager[IO](
+                driver = dbConfig.driver,
+                url = dbConfig.url,
+                user = dbConfig.user,
+                password = dbConfig.pass,
+                logHandler = None
+            )
+            val repo     = new DoobieAccountManagementRepository[IO](tx)
+            val attempts = scala.util.Random.nextInt(128)
+            val hash     = PasswordHash("Yet another weak password!")
+            val test = for {
+                _    <- createAccount(account, hash, None, Option(attempts))
+                _    <- repo.addSshKey(sshKeyA.copy(ownerId = account.uid))
+                _    <- repo.addSshKey(sshKeyB.copy(ownerId = account.uid))
+                keys <- repo.listSshKeys(account.uid).compile.toList
+            } yield keys
+            test.start.flatMap(_.joinWithNever).map { keys =>
+                assertEquals(keys.length, 2, "Expected 2 keys in the key list!")
+                assert(keys.exists(_.id === sshKeyA.id), "Key A must be in the key list of the user!")
+                assert(keys.exists(_.id === sshKeyB.id), "Key B must be in the key list of the user!")
+            }
         }
     }
 
     test("markAsValidated must clear the validation token and set the validated column to true".tag(NeedsDatabase)) {
-        genValidAccount.sample match {
-            case None => fail("Could not generate data samples!")
-            case Some(ac) =>
-                val account  = ac.copy(validatedEmail = true)
-                val token    = ValidationToken.generate
-                val dbConfig = configuration.database
-                val tx = Transactor.fromDriverManager[IO](
-                    driver = dbConfig.driver,
-                    url = dbConfig.url,
-                    user = dbConfig.user,
-                    password = dbConfig.pass,
-                    logHandler = None
-                )
-                val repo = new DoobieAccountManagementRepository[IO](tx)
-                val hash = PasswordHash("Yet another weak password!")
-                val test = for {
-                    _    <- createAccount(account, hash, validationToken = token.some)
-                    _    <- repo.markAsValidated(account.uid)
-                    cols <- loadValidationColumns(account.uid)
-                } yield cols
-                test.map { result =>
-                    assert(result === Some((true, None)), "Unexpected result from database!")
-                }
+        PropF.forAllF { (ac: Account) =>
+            val account  = ac.copy(validatedEmail = true)
+            val token    = ValidationToken.generate
+            val dbConfig = configuration.database
+            val tx = Transactor.fromDriverManager[IO](
+                driver = dbConfig.driver,
+                url = dbConfig.url,
+                user = dbConfig.user,
+                password = dbConfig.pass,
+                logHandler = None
+            )
+            val repo = new DoobieAccountManagementRepository[IO](tx)
+            val hash = PasswordHash("Yet another weak password!")
+            val test = for {
+                _    <- createAccount(account, hash, validationToken = token.some)
+                _    <- repo.markAsValidated(account.uid)
+                cols <- loadValidationColumns(account.uid)
+            } yield cols
+            test.start.flatMap(_.joinWithNever).map { result =>
+                assert(result === Some((true, None)), "Unexpected result from database!")
+            }
         }
     }
 
     test("setLanguage must set the language".tag(NeedsDatabase)) {
-        genValidAccount.sample match {
-            case None => fail("Could not generate data samples!")
-            case Some(account) =>
-                val language = genLanguageCode.sample
-                val dbConfig = configuration.database
-                val tx = Transactor.fromDriverManager[IO](
-                    driver = dbConfig.driver,
-                    url = dbConfig.url,
-                    user = dbConfig.user,
-                    password = dbConfig.pass,
-                    logHandler = None
-                )
-                val repo = new DoobieAccountManagementRepository[IO](tx)
-                val hash = PasswordHash("Yet another weak password!")
-                val test = for {
-                    _               <- createAccount(account, hash)
-                    _               <- repo.setLanguage(account.uid, language)
-                    modifiedAccount <- loadAccount(account.uid)
-                } yield modifiedAccount
-                test.map { modifiedAccount =>
-                    assert(modifiedAccount.exists(_.language === language), "Written language field does not match!")
-                }
+        PropF.forAllF { (account: Account, language: LanguageCode) =>
+            val dbConfig = configuration.database
+            val tx = Transactor.fromDriverManager[IO](
+                driver = dbConfig.driver,
+                url = dbConfig.url,
+                user = dbConfig.user,
+                password = dbConfig.pass,
+                logHandler = None
+            )
+            val repo = new DoobieAccountManagementRepository[IO](tx)
+            val hash = PasswordHash("Yet another weak password!")
+            val test = for {
+                _               <- createAccount(account, hash)
+                _               <- repo.setLanguage(account.uid, language.some)
+                modifiedAccount <- loadAccount(account.uid)
+            } yield modifiedAccount
+            test.start.flatMap(_.joinWithNever).map { modifiedAccount =>
+                assert(modifiedAccount.exists(_.language === language.some), "Written language field does not match!")
+            }
+        }
+    }
+
+    test("setLanguage must delete the language".tag(NeedsDatabase)) {
+        PropF.forAllF { (account: Account, language: LanguageCode) =>
+            val dbConfig = configuration.database
+            val tx = Transactor.fromDriverManager[IO](
+                driver = dbConfig.driver,
+                url = dbConfig.url,
+                user = dbConfig.user,
+                password = dbConfig.pass,
+                logHandler = None
+            )
+            val repo = new DoobieAccountManagementRepository[IO](tx)
+            val hash = PasswordHash("Yet another weak password!")
+            val test = for {
+                _               <- createAccount(account, hash)
+                _               <- repo.setLanguage(account.uid, language.some)
+                _               <- repo.setLanguage(account.uid, None)
+                modifiedAccount <- loadAccount(account.uid)
+            } yield modifiedAccount
+            test.start.flatMap(_.joinWithNever).map { modifiedAccount =>
+                assert(modifiedAccount.exists(_.language === None), "Written language field does not match!")
+            }
         }
     }
 
     test("setValidationToken must set the validation token".tag(NeedsDatabase)) {
-        genValidAccount.sample match {
-            case None => fail("Could not generate data samples!")
-            case Some(account) =>
-                val token    = ValidationToken.generate
-                val dbConfig = configuration.database
-                val tx = Transactor.fromDriverManager[IO](
-                    driver = dbConfig.driver,
-                    url = dbConfig.url,
-                    user = dbConfig.user,
-                    password = dbConfig.pass,
-                    logHandler = None
-                )
-                val repo = new DoobieAccountManagementRepository[IO](tx)
-                val hash = PasswordHash("Yet another weak password!")
-                val test = for {
-                    _    <- createAccount(account, hash)
-                    _    <- repo.setValidationToken(account.uid, token)
-                    cols <- loadValidationColumns(account.uid)
-                } yield cols
-                test.map { result =>
-                    assert(result === Some((account.validatedEmail, Some(token))), "Unexpected result from database!")
-                }
+        PropF.forAllF { (account: Account) =>
+            val token    = ValidationToken.generate
+            val dbConfig = configuration.database
+            val tx = Transactor.fromDriverManager[IO](
+                driver = dbConfig.driver,
+                url = dbConfig.url,
+                user = dbConfig.user,
+                password = dbConfig.pass,
+                logHandler = None
+            )
+            val repo = new DoobieAccountManagementRepository[IO](tx)
+            val hash = PasswordHash("Yet another weak password!")
+            val test = for {
+                _    <- createAccount(account, hash)
+                _    <- repo.setValidationToken(account.uid, token)
+                cols <- loadValidationColumns(account.uid)
+            } yield cols
+            test.start.flatMap(_.joinWithNever).map { result =>
+                assert(result === Some((account.validatedEmail, Some(token))), "Unexpected result from database!")
+            }
         }
     }
 }
diff -rN -u old-smederee/modules/hub/src/test/scala/de/smederee/hub/Generators.scala new-smederee/modules/hub/src/test/scala/de/smederee/hub/Generators.scala
--- old-smederee/modules/hub/src/test/scala/de/smederee/hub/Generators.scala	2025-01-11 02:47:17.870494389 +0000
+++ new-smederee/modules/hub/src/test/scala/de/smederee/hub/Generators.scala	2025-01-11 02:47:17.874494392 +0000
@@ -31,6 +31,8 @@
 
     val genLanguageCode: Gen[LanguageCode] = genLocale.map(_.getISO3Language).filter(_.nonEmpty).map(LanguageCode.apply)
 
+    given Arbitrary[LanguageCode] = Arbitrary(genLanguageCode)
+
     val genFiniteDuration: Gen[FiniteDuration] =
         Gen.choose(0, Int.MaxValue).map(seconds => FiniteDuration(seconds, SECONDS))