~jan0sch/smederee
Showing details for patch f84160f508d04211cfafcca776f98da63f76ace1.
diff -rN -u old-smederee/modules/hub/src/test/scala/de/smederee/hub/DoobieAuthenticationRepositoryTest.scala new-smederee/modules/hub/src/test/scala/de/smederee/hub/DoobieAuthenticationRepositoryTest.scala --- old-smederee/modules/hub/src/test/scala/de/smederee/hub/DoobieAuthenticationRepositoryTest.scala 2025-01-11 03:19:29.853485109 +0000 +++ new-smederee/modules/hub/src/test/scala/de/smederee/hub/DoobieAuthenticationRepositoryTest.scala 2025-01-11 03:19:29.857485118 +0000 @@ -6,15 +6,20 @@ package de.smederee.hub +import cats.data.NonEmptyList import cats.effect.* import cats.syntax.all.* import de.smederee.TestTags.* -import de.smederee.hub.Generators.* +import de.smederee.hub.Generators.given import de.smederee.security.* import doobie.* import org.flywaydb.core.Flyway +import org.scalacheck.effect.PropF + final class DoobieAuthenticationRepositoryTest extends BaseSpec { + override def scalaCheckTestParameters = super.scalaCheckTestParameters.withMinSuccessfulTests(1) + override def beforeEach(context: BeforeEach): Unit = { val dbConfig = configuration.database val flyway: Flyway = @@ -33,480 +38,443 @@ } test("allAccounts must return all accounts from the database".tag(NeedsDatabase)) { - genValidAccounts.sample match { - case Some(accounts) => - val expected = accounts.map(_.copy(language = None)) - 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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- accounts.traverse(account => - createAccount(account, PasswordHash("I am not a password hash!"), None, None) - ) - result <- repo.allAccounts().compile.toList - } yield result - test.map { result => - assertEquals(result.size, expected.size) - assertEquals(result, expected.sortBy(_.name)) - } - case _ => fail("Could not generate data samples!") + PropF.forAllF { (accounts: NonEmptyList[Account]) => + val expected = accounts.toList.map(_.copy(language = None)) + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- accounts.traverse(account => + createAccount(account, PasswordHash("I am not a password hash!"), None, None) + ) + result <- repo.allAccounts().compile.toList + } yield result + test.start.flatMap(_.joinWithNever).map { result => + assertEquals(result.size, expected.size) + assertEquals(result, expected.sortBy(_.name)) + } } } test("createUserSession must create the user session".tag(NeedsDatabase)) { - (genValidSession.sample, genValidAccount.sample) match { - case (Some(s), Some(account)) => - val session = s.copy(uid = account.uid) - 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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) - w <- repo.createUserSession(session) - o <- repo.findUserSession(session.id) - } yield (w, o) - test.map { result => - val (written, maybeSession) = result - assert(written === 1, "Creating user session must modify one database row!") - assert(clue(maybeSession) === clue(Option(session))) - } - case _ => fail("Could not generate data samples!") + PropF.forAllF { (s: Session, account: Account) => + val session = s.copy(uid = account.uid) + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) + w <- repo.createUserSession(session) + o <- repo.findUserSession(session.id) + } yield (w, o) + test.start.flatMap(_.joinWithNever).map { result => + val (written, maybeSession) = result + assert(written === 1, "Creating user session must modify one database row!") + assert(clue(maybeSession) === clue(Option(session))) + } } } test("createUserSession must fail if the user does not exist".tag(NeedsDatabase)) { - genValidSession.sample match { - case Some(session) => - 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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- repo.createUserSession(session) - _ <- repo.findUserSession(session.id) - } yield () - test.attempt.map(result => assert(result.isLeft)) - case _ => fail("Could not generate data samples!") + PropF.forAllF { (session: Session) => + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- repo.createUserSession(session) + _ <- repo.findUserSession(session.id) + } yield () + test.attempt.map(result => assert(result.isLeft)) } } test("deleteAllUserSessions must delete all sessions of the user".tag(NeedsDatabase)) { - (genValidSessions.sample, genValidAccount.sample) match { - case (Some(generatedSessions), Some(account)) => - val sessions = generatedSessions.map(_.copy(uid = account.uid)) - 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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) - _ <- sessions.traverse(createUserSession) - deleted <- repo.deleteAllUserSessions(account.uid) - foundSessions <- sessions.traverse(s => repo.findUserSession(s.id)) - } yield (deleted, foundSessions) - test.map { result => - val (deleted, foundSessions) = result - assertEquals(deleted, sessions.size, "Number of deleted sessions differs from number of sessions!") - assert(foundSessions.flatten.isEmpty, "Not all sessions were deleted!") - } - case _ => fail("Could not generate data samples!") + PropF.forAllF { (generatedSessions: NonEmptyList[Session], account: Account) => + val sessions = generatedSessions.toList.map(_.copy(uid = account.uid)) + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) + _ <- sessions.traverse(createUserSession) + deleted <- repo.deleteAllUserSessions(account.uid) + foundSessions <- sessions.traverse(s => repo.findUserSession(s.id)) + } yield (deleted, foundSessions) + test.start.flatMap(_.joinWithNever).map { result => + val (deleted, foundSessions) = result + assertEquals(deleted, sessions.size, "Number of deleted sessions differs from number of sessions!") + assert(foundSessions.flatten.isEmpty, "Not all sessions were deleted!") + } } } test("deleteUserSession must delete the session".tag(NeedsDatabase)) { - (genValidSession.sample, genValidAccount.sample) match { - case (Some(s), Some(account)) => - val session = s.copy(uid = account.uid) - 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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) - _ <- createUserSession(session) - before <- repo.findUserSession(session.id) - deleted <- repo.deleteUserSession(session.id) - after <- repo.findUserSession(session.id) - } yield (before, deleted, after) - test.map { result => - val (before, deleted, after) = result - assert(before.nonEmpty, "Session must exist before deleting it!") - assert(deleted === 1, "Deletion must affect one database row!") - assert(after.isEmpty, "Session must not exist after deletion!") - } - case _ => fail("Could not generate data samples!") + PropF.forAllF { (s: Session, account: Account) => + val session = s.copy(uid = account.uid) + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) + _ <- createUserSession(session) + before <- repo.findUserSession(session.id) + deleted <- repo.deleteUserSession(session.id) + after <- repo.findUserSession(session.id) + } yield (before, deleted, after) + test.start.flatMap(_.joinWithNever).map { result => + val (before, deleted, after) = result + assert(before.nonEmpty, "Session must exist before deleting it!") + assert(deleted === 1, "Deletion must affect one database row!") + assert(after.isEmpty, "Session must not exist after deletion!") + } } } test("findAccount must return an existing account".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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) - o <- repo.findAccount(account.uid) - } yield o - test.map { result => - assert(result === Option(account)) - } + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) + o <- repo.findAccount(account.uid) + } yield o + test.start.flatMap(_.joinWithNever).map { result => + assert(result === Option(account)) + } } } test("findAccount must not return a locked account".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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- createAccount( - account, - PasswordHash("I am not a password hash!"), - Option(UnlockToken.generate), - None - ) - o <- repo.findAccount(account.uid) - } yield o - test.map { result => - assert(result.isEmpty, "The function must not return locked accounts!") - } + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- createAccount( + account, + PasswordHash("I am not a password hash!"), + Option(UnlockToken.generate), + None + ) + o <- repo.findAccount(account.uid) + } yield o + test.start.flatMap(_.joinWithNever).map { result => + assert(result.isEmpty, "The function must not return locked accounts!") + } } } test("findAccountByEmail must return an existing account".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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) - o <- repo.findAccountByEmail(account.email) - } yield o - test.map { result => - assert(result === Option(account)) - } + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) + o <- repo.findAccountByEmail(account.email) + } yield o + test.start.flatMap(_.joinWithNever).map { result => + assert(result === Option(account)) + } } } test("findAccountByEmail must not return a locked account".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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- createAccount( - account, - PasswordHash("I am not a password hash!"), - Option(UnlockToken.generate), - None - ) - o <- repo.findAccountByEmail(account.email) - } yield o - test.map { result => - assert(result.isEmpty, "The function must not return locked accounts!") - } + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- createAccount( + account, + PasswordHash("I am not a password hash!"), + Option(UnlockToken.generate), + None + ) + o <- repo.findAccountByEmail(account.email) + } yield o + test.start.flatMap(_.joinWithNever).map { result => + assert(result.isEmpty, "The function must not return locked accounts!") + } } } test("findAccountByName must return an existing account".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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) - o <- repo.findAccountByName(account.name) - } yield o - test.map { result => - assert(result === Option(account)) - } + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) + o <- repo.findAccountByName(account.name) + } yield o + test.start.flatMap(_.joinWithNever).map { result => + assert(result === Option(account)) + } } } test("findAccountByName must not return a locked account".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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- createAccount( - account, - PasswordHash("I am not a password hash!"), - Option(UnlockToken.generate), - None - ) - o <- repo.findAccountByName(account.name) - } yield o - test.map { result => - assert(result.isEmpty, "The function must not return locked accounts!") - } + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- createAccount( + account, + PasswordHash("I am not a password hash!"), + Option(UnlockToken.generate), + None + ) + o <- repo.findAccountByName(account.name) + } yield o + test.start.flatMap(_.joinWithNever).map { result => + assert(result.isEmpty, "The function must not return locked accounts!") + } } } test("findLockedAccount must return a locked account".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 DoobieAuthenticationRepository[IO](tx) - val token = UnlockToken.generate - val test = for { - _ <- createAccount(account, PasswordHash("I am not a password hash!"), Option(token), None) - o <- repo.findLockedAccount(account.name)(token.some) - } yield o - test.map { result => - assert(result === Option(account)) - } + 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 DoobieAuthenticationRepository[IO](tx) + val token = UnlockToken.generate + val test = for { + _ <- createAccount(account, PasswordHash("I am not a password hash!"), Option(token), None) + o <- repo.findLockedAccount(account.name)(token.some) + } yield o + test.start.flatMap(_.joinWithNever).map { result => + assert(result === Option(account)) + } } } test("findLockedAccount must return a locked account if no token is given".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 DoobieAuthenticationRepository[IO](tx) - val token = UnlockToken.generate - val test = for { - _ <- createAccount(account, PasswordHash("I am not a password hash!"), Option(token), None) - o <- repo.findLockedAccount(account.name)(None) - } yield o - test.map { result => - assert(result === Option(account)) - } + 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 DoobieAuthenticationRepository[IO](tx) + val token = UnlockToken.generate + val test = for { + _ <- createAccount(account, PasswordHash("I am not a password hash!"), Option(token), None) + o <- repo.findLockedAccount(account.name)(None) + } yield o + test.start.flatMap(_.joinWithNever).map { result => + assert(result === Option(account)) + } } } test("findPasswordHashAndAttempts must return correct values".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 DoobieAuthenticationRepository[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.findPasswordHashAndAttempts(account.uid) - } yield o - test.map { result => - result match { - case Some((readHash, readAttempts)) => - assert(readHash === hash) - assert(readAttempts === attempts) - case _ => fail("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 DoobieAuthenticationRepository[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.findPasswordHashAndAttempts(account.uid) + } yield o + test.start.flatMap(_.joinWithNever).map { result => + result match { + case Some((readHash, readAttempts)) => + assert(readHash === hash) + assert(readAttempts === attempts) + case _ => fail("Unexpected result from database!") } + } } } test("findPasswordHashAndAttempts must not return values for locked accounts".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 DoobieAuthenticationRepository[IO](tx) - val attempts = scala.util.Random.nextInt(128) - val hash = PasswordHash("Yet another weak password!") - val test = for { - _ <- createAccount(account, hash, Option(UnlockToken.generate), Option(attempts)) - o <- repo.findPasswordHashAndAttempts(account.uid) - } yield o - test.map { result => - assert(result.isEmpty, "The function must not return locked accounts!") - } + 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 DoobieAuthenticationRepository[IO](tx) + val attempts = scala.util.Random.nextInt(128) + val hash = PasswordHash("Yet another weak password!") + val test = for { + _ <- createAccount(account, hash, Option(UnlockToken.generate), Option(attempts)) + o <- repo.findPasswordHashAndAttempts(account.uid) + } yield o + test.start.flatMap(_.joinWithNever).map { result => + assert(result.isEmpty, "The function must not return locked accounts!") + } } } test("findUserSession must find an existing session".tag(NeedsDatabase)) { - (genValidSession.sample, genValidAccount.sample) match { - case (Some(s), Some(account)) => - val session = s.copy(uid = account.uid) - 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 DoobieAuthenticationRepository[IO](tx) - val test = for { - _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) - _ <- createUserSession(session) - o <- repo.findUserSession(session.id) - } yield o - test.map { maybeSession => - assert(clue(maybeSession) === clue(Option(session))) - } - case _ => fail("Could not generate data samples!") + PropF.forAllF { (s: Session, account: Account) => + val session = s.copy(uid = account.uid) + 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 DoobieAuthenticationRepository[IO](tx) + val test = for { + _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) + _ <- createUserSession(session) + o <- repo.findUserSession(session.id) + } yield o + test.start.flatMap(_.joinWithNever).map { maybeSession => + assert(clue(maybeSession) === clue(Option(session))) + } } } test("incrementFailedAttempts must increment failed attempts by 1".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 DoobieAuthenticationRepository[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)) - before <- repo.findPasswordHashAndAttempts(account.uid) - _ <- repo.incrementFailedAttempts(account.uid) - after <- repo.findPasswordHashAndAttempts(account.uid) - } yield (before, after) - test.map { result => - result match { - case (Some((_, before)), Some((_, after))) => - assert(after - before === 1, "Attempts must be incremented by one!") - case _ => fail("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 DoobieAuthenticationRepository[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)) + before <- repo.findPasswordHashAndAttempts(account.uid) + _ <- repo.incrementFailedAttempts(account.uid) + after <- repo.findPasswordHashAndAttempts(account.uid) + } yield (before, after) + test.start.flatMap(_.joinWithNever).map { result => + result match { + case (Some((_, before)), Some((_, after))) => + assert(after - before === 1, "Attempts must be incremented by one!") + case _ => fail("Unexpected result from database!") } + } } } test("lockAccount must lock an account".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 DoobieAuthenticationRepository[IO](tx) - val token = UnlockToken.generate - val test = for { - _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) - before <- repo.findAccount(account.uid) - _ <- repo.lockAccount(account.uid)(Option(token)) - after <- repo.findAccount(account.uid) - locked <- repo.findLockedAccount(account.name)(token.some) - } yield (before, after, locked) - test.map { result => - result match { - case (Some(before), after, Some(locked)) => - assert(after.isEmpty) - assert(before === locked) - case _ => fail("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 DoobieAuthenticationRepository[IO](tx) + val token = UnlockToken.generate + val test = for { + _ <- createAccount(account, PasswordHash("I am not a password hash!"), None, None) + before <- repo.findAccount(account.uid) + _ <- repo.lockAccount(account.uid)(Option(token)) + after <- repo.findAccount(account.uid) + locked <- repo.findLockedAccount(account.name)(token.some) + } yield (before, after, locked) + test.start.flatMap(_.joinWithNever).map { result => + result match { + case (Some(before), after, Some(locked)) => + assert(after.isEmpty) + assert(before === locked) + case _ => fail("Unexpected result from database!") } + } } } - }