~jan0sch/smederee
Showing details for patch 3b17a69c78562b980024d43506690f740d948287.
diff -rN -u old-smederee/modules/hub/src/test/scala/de/smederee/tickets/BaseSpec.scala new-smederee/modules/hub/src/test/scala/de/smederee/tickets/BaseSpec.scala --- old-smederee/modules/hub/src/test/scala/de/smederee/tickets/BaseSpec.scala 2025-01-10 23:57:48.741013515 +0000 +++ new-smederee/modules/hub/src/test/scala/de/smederee/tickets/BaseSpec.scala 2025-01-10 23:57:48.741013515 +0000 @@ -31,7 +31,7 @@ * does initialise the test database for each suite. The latter means a possibly existing database with the name * configured **will be deleted**! */ -abstract class BaseSpec extends CatsEffectSuite with ScalaCheckCats { +abstract class BaseSpec extends CatsEffectSuite with ScalaCheckEffectSuite with ScalaCheckCats { protected final val configuration: SmedereeTicketsConfiguration = ConfigSource diff -rN -u old-smederee/modules/hub/src/test/scala/de/smederee/tickets/DoobieLabelRepositoryTest.scala new-smederee/modules/hub/src/test/scala/de/smederee/tickets/DoobieLabelRepositoryTest.scala --- old-smederee/modules/hub/src/test/scala/de/smederee/tickets/DoobieLabelRepositoryTest.scala 2025-01-10 23:57:48.741013515 +0000 +++ new-smederee/modules/hub/src/test/scala/de/smederee/tickets/DoobieLabelRepositoryTest.scala 2025-01-10 23:57:48.741013515 +0000 @@ -6,13 +6,17 @@ package de.smederee.tickets +import cats.data.NonEmptyList import cats.effect.* import cats.syntax.all.* import de.smederee.TestTags.* -import de.smederee.tickets.Generators.* +import de.smederee.tickets.Generators.given import doobie.* +import org.scalacheck.effect.PropF + final class DoobieLabelRepositoryTest extends BaseSpec { + override def scalaCheckTestParameters = super.scalaCheckTestParameters.withMinSuccessfulTests(1) /** Find the label ID for the given project and label name. * @@ -56,246 +60,234 @@ } test("allLabels must return all labels".tag(NeedsDatabase)) { - (genProjectOwner.sample, genProject.sample, genLabels.sample) match { - case (Some(owner), Some(generatedProject), Some(labels)) => - val project = generatedProject.copy(owner = owner) - val dbConfig = configuration.database - val tx = Transactor.fromDriverManager[IO]( - driver = dbConfig.driver, - url = dbConfig.url, - user = dbConfig.user, - password = dbConfig.pass, - logHandler = None - ) - val labelRepo = new DoobieLabelRepository[IO](tx) - val test = for { - _ <- createProjectOwner(owner) - _ <- createTicketsProject(project) - projectId <- loadProjectId(owner.uid, project.name) - createdLabels <- projectId match { - case None => IO.pure(List.empty) - case Some(projectId) => labels.traverse(label => labelRepo.createLabel(projectId)(label)) - } - foundLabels <- projectId match { - case None => IO.pure(List.empty) - case Some(projectId) => labelRepo.allLabels(projectId).compile.toList - } - } yield foundLabels - test.map { foundLabels => - assert(foundLabels.size === labels.size, "Different number of labels!") - foundLabels.sortBy(_.name).zip(labels.sortBy(_.name)).map { (found, expected) => - assertEquals(found.copy(id = expected.id), expected) - } + PropF.forAllF { (owner: ProjectOwner, generatedProject: Project, labels: NonEmptyList[Label]) => + val project = generatedProject.copy(owner = owner) + val dbConfig = configuration.database + val tx = Transactor.fromDriverManager[IO]( + driver = dbConfig.driver, + url = dbConfig.url, + user = dbConfig.user, + password = dbConfig.pass, + logHandler = None + ) + val labelRepo = new DoobieLabelRepository[IO](tx) + val test = for { + _ <- createProjectOwner(owner) + _ <- createTicketsProject(project) + projectId <- loadProjectId(owner.uid, project.name) + createdLabels <- projectId match { + case None => IO.pure(List.empty) + case Some(projectId) => labels.toList.traverse(label => labelRepo.createLabel(projectId)(label)) } - case _ => fail("Could not generate data samples!") + foundLabels <- projectId match { + case None => IO.pure(List.empty) + case Some(projectId) => labelRepo.allLabels(projectId).compile.toList + } + } yield foundLabels + test.start.flatMap(_.joinWithNever).map { foundLabels => + assert(foundLabels.size === labels.size, "Different number of labels!") + val cleanedFound = + foundLabels.sortBy(_.name).zip(labels.toList.sortBy(_.name)).map { (found, expected) => + found.copy(id = expected.id) + } + assertEquals(cleanedFound.sortBy(_.name), labels.toList.sortBy(_.name)) + } } } test("createLabel must create the label".tag(NeedsDatabase)) { - (genProjectOwner.sample, genProject.sample, genLabel.sample) match { - case (Some(owner), Some(generatedProject), Some(label)) => - val project = generatedProject.copy(owner = owner) - val dbConfig = configuration.database - val tx = Transactor.fromDriverManager[IO]( - driver = dbConfig.driver, - url = dbConfig.url, - user = dbConfig.user, - password = dbConfig.pass, - logHandler = None - ) - val labelRepo = new DoobieLabelRepository[IO](tx) - val test = for { - _ <- createProjectOwner(owner) - createdProjects <- createTicketsProject(project) - projectId <- loadProjectId(owner.uid, project.name) - createdLabels <- projectId.traverse(id => labelRepo.createLabel(id)(label)) - foundLabel <- projectId.traverse(id => labelRepo.findLabel(id)(label.name)) - } yield (createdProjects, projectId, createdLabels, foundLabel) - test.map { tuple => - val (createdProjects, projectId, createdLabels, foundLabel) = tuple - assert(createdProjects === 1, "Test project was not created!") - assert(projectId.nonEmpty, "No project id found!") - assert(createdLabels.exists(_ === 1), "Test label was not created!") - foundLabel.getOrElse(None) match { - case None => fail("Created label not found!") - case Some(foundLabel) => - assert(foundLabel.id.nonEmpty, "Label ID must not be empty!") - assertEquals(foundLabel.name, label.name) - assertEquals(foundLabel.description, label.description) - assertEquals(foundLabel.colour, label.colour) - } + PropF.forAllF { (owner: ProjectOwner, generatedProject: Project, label: Label) => + val project = generatedProject.copy(owner = owner) + val dbConfig = configuration.database + val tx = Transactor.fromDriverManager[IO]( + driver = dbConfig.driver, + url = dbConfig.url, + user = dbConfig.user, + password = dbConfig.pass, + logHandler = None + ) + val labelRepo = new DoobieLabelRepository[IO](tx) + val test = for { + _ <- createProjectOwner(owner) + createdProjects <- createTicketsProject(project) + projectId <- loadProjectId(owner.uid, project.name) + createdLabels <- projectId.traverse(id => labelRepo.createLabel(id)(label)) + foundLabel <- projectId.traverse(id => labelRepo.findLabel(id)(label.name)) + } yield (createdProjects, projectId, createdLabels, foundLabel) + test.start.flatMap(_.joinWithNever).map { tuple => + val (createdProjects, projectId, createdLabels, foundLabel) = tuple + assert(createdProjects === 1, "Test project was not created!") + assert(projectId.nonEmpty, "No project id found!") + assert(createdLabels.exists(_ === 1), "Test label was not created!") + foundLabel.getOrElse(None) match { + case None => fail("Created label not found!") + case Some(foundLabel) => + assert(foundLabel.id.nonEmpty, "Label ID must not be empty!") + assertEquals(foundLabel.name, label.name) + assertEquals(foundLabel.description, label.description) + assertEquals(foundLabel.colour, label.colour) } - case _ => fail("Could not generate data samples!") + } } } test("createLabel must fail if the label name already exists".tag(NeedsDatabase)) { - (genProjectOwner.sample, genProject.sample, genLabel.sample) match { - case (Some(owner), Some(generatedProject), Some(label)) => - val project = generatedProject.copy(owner = owner) - val dbConfig = configuration.database - val tx = Transactor.fromDriverManager[IO]( - driver = dbConfig.driver, - url = dbConfig.url, - user = dbConfig.user, - password = dbConfig.pass, - logHandler = None - ) - val labelRepo = new DoobieLabelRepository[IO](tx) - val test = for { - _ <- createProjectOwner(owner) - createdProjects <- createTicketsProject(project) - projectId <- loadProjectId(owner.uid, project.name) - createdLabels <- projectId.traverse(id => labelRepo.createLabel(id)(label)) - _ <- projectId.traverse(id => labelRepo.createLabel(id)(label)) - } yield (createdProjects, projectId, createdLabels) - test.attempt.map(r => assert(r.isLeft, "Creating a label with a duplicate name must fail!")) - case _ => fail("Could not generate data samples!") + PropF.forAllF { (owner: ProjectOwner, generatedProject: Project, label: Label) => + val project = generatedProject.copy(owner = owner) + val dbConfig = configuration.database + val tx = Transactor.fromDriverManager[IO]( + driver = dbConfig.driver, + url = dbConfig.url, + user = dbConfig.user, + password = dbConfig.pass, + logHandler = None + ) + val labelRepo = new DoobieLabelRepository[IO](tx) + val test = for { + _ <- createProjectOwner(owner) + createdProjects <- createTicketsProject(project) + projectId <- loadProjectId(owner.uid, project.name) + createdLabels <- projectId.traverse(id => labelRepo.createLabel(id)(label)) + _ <- projectId.traverse(id => labelRepo.createLabel(id)(label)) + } yield (createdProjects, projectId, createdLabels) + test.attempt.map(r => assert(r.isLeft, "Creating a label with a duplicate name must fail!")) } } test("deleteLabel must delete an existing label".tag(NeedsDatabase)) { - (genProjectOwner.sample, genProject.sample, genLabel.sample) match { - case (Some(owner), Some(generatedProject), Some(label)) => - val project = generatedProject.copy(owner = owner) - val dbConfig = configuration.database - val tx = Transactor.fromDriverManager[IO]( - driver = dbConfig.driver, - url = dbConfig.url, - user = dbConfig.user, - password = dbConfig.pass, - logHandler = None - ) - val labelRepo = new DoobieLabelRepository[IO](tx) - val test = for { - _ <- createProjectOwner(owner) - createdProjects <- createTicketsProject(project) - projectId <- loadProjectId(owner.uid, project.name) - createdLabels <- projectId.traverse(id => labelRepo.createLabel(id)(label)) - labelId <- findLabelId(owner.uid, project.name, label.name) - deletedLabels <- labelRepo.deleteLabel(label.copy(id = labelId.flatMap(LabelId.from))) - foundLabel <- projectId.traverse(id => labelRepo.findLabel(id)(label.name)) - } yield (createdProjects, projectId, createdLabels, deletedLabels, foundLabel) - test.map { tuple => - val (createdProjects, projectId, createdLabels, deletedLabels, foundLabel) = tuple - assert(createdProjects === 1, "Test vcs project was not created!") - assert(projectId.nonEmpty, "No vcs project id found!") - assert(createdLabels.exists(_ === 1), "Test label was not created!") - assert(deletedLabels === 1, "Test label was not deleted!") - assert(foundLabel.getOrElse(None).isEmpty, "Test label was not deleted!") - } - case _ => fail("Could not generate data samples!") + PropF.forAllF { (owner: ProjectOwner, generatedProject: Project, label: Label) => + val project = generatedProject.copy(owner = owner) + val dbConfig = configuration.database + val tx = Transactor.fromDriverManager[IO]( + driver = dbConfig.driver, + url = dbConfig.url, + user = dbConfig.user, + password = dbConfig.pass, + logHandler = None + ) + val labelRepo = new DoobieLabelRepository[IO](tx) + val test = for { + _ <- createProjectOwner(owner) + createdProjects <- createTicketsProject(project) + projectId <- loadProjectId(owner.uid, project.name) + createdLabels <- projectId.traverse(id => labelRepo.createLabel(id)(label)) + labelId <- findLabelId(owner.uid, project.name, label.name) + deletedLabels <- labelRepo.deleteLabel(label.copy(id = labelId.flatMap(LabelId.from))) + foundLabel <- projectId.traverse(id => labelRepo.findLabel(id)(label.name)) + } yield (createdProjects, projectId, createdLabels, deletedLabels, foundLabel) + test.start.flatMap(_.joinWithNever).map { tuple => + val (createdProjects, projectId, createdLabels, deletedLabels, foundLabel) = tuple + assert(createdProjects === 1, "Test vcs project was not created!") + assert(projectId.nonEmpty, "No vcs project id found!") + assert(createdLabels.exists(_ === 1), "Test label was not created!") + assert(deletedLabels === 1, "Test label was not deleted!") + assert(foundLabel.getOrElse(None).isEmpty, "Test label was not deleted!") + } } } test("findLabel must find existing labels".tag(NeedsDatabase)) { - (genProjectOwner.sample, genProject.sample, genLabels.sample) match { - case (Some(owner), Some(generatedProject), Some(labels)) => - val project = generatedProject.copy(owner = owner) - val dbConfig = configuration.database - val expectedLabel = labels(scala.util.Random.nextInt(labels.size)) - val tx = Transactor.fromDriverManager[IO]( - driver = dbConfig.driver, - url = dbConfig.url, - user = dbConfig.user, - password = dbConfig.pass, - logHandler = None - ) - val labelRepo = new DoobieLabelRepository[IO](tx) - val test = for { - _ <- createProjectOwner(owner) - createdProjects <- createTicketsProject(project) - projectId <- loadProjectId(owner.uid, project.name) - createdLabels <- projectId match { - case None => IO.pure(List.empty) - case Some(projectId) => labels.traverse(label => labelRepo.createLabel(projectId)(label)) - } - foundLabel <- projectId.traverse(id => labelRepo.findLabel(id)(expectedLabel.name)) - } yield foundLabel.flatten - test.map { foundLabel => - assertEquals(foundLabel.map(_.copy(id = expectedLabel.id)), Option(expectedLabel)) + PropF.forAllF { (owner: ProjectOwner, generatedProject: Project, labels: NonEmptyList[Label]) => + val project = generatedProject.copy(owner = owner) + val dbConfig = configuration.database + val expectedLabel = labels.toList(scala.util.Random.nextInt(labels.size)) + val tx = Transactor.fromDriverManager[IO]( + driver = dbConfig.driver, + url = dbConfig.url, + user = dbConfig.user, + password = dbConfig.pass, + logHandler = None + ) + val labelRepo = new DoobieLabelRepository[IO](tx) + val test = for { + _ <- createProjectOwner(owner) + createdProjects <- createTicketsProject(project) + projectId <- loadProjectId(owner.uid, project.name) + createdLabels <- projectId match { + case None => IO.pure(List.empty) + case Some(projectId) => labels.toList.traverse(label => labelRepo.createLabel(projectId)(label)) } - case _ => fail("Could not generate data samples!") + foundLabel <- projectId.traverse(id => labelRepo.findLabel(id)(expectedLabel.name)) + } yield foundLabel.flatten + test.start.flatMap(_.joinWithNever).map { foundLabel => + assertEquals(foundLabel.map(_.copy(id = expectedLabel.id)), Option(expectedLabel)) + } } } test("updateLabel must update an existing label".tag(NeedsDatabase)) { - (genProjectOwner.sample, genProject.sample, genLabel.sample) match { - case (Some(owner), Some(generatedProject), Some(label)) => - val updatedLabel = label.copy( - name = LabelName("updated label"), - description = Option(LabelDescription("I am an updated label description...")), - colour = ColourCode("#abcdef") - ) - val project = generatedProject.copy(owner = owner) - val dbConfig = configuration.database - val tx = Transactor.fromDriverManager[IO]( - driver = dbConfig.driver, - url = dbConfig.url, - user = dbConfig.user, - password = dbConfig.pass, - logHandler = None - ) - val labelRepo = new DoobieLabelRepository[IO](tx) - val test = for { - _ <- createProjectOwner(owner) - createdProjects <- createTicketsProject(project) - projectId <- loadProjectId(owner.uid, project.name) - createdLabels <- projectId.traverse(id => labelRepo.createLabel(id)(label)) - labelId <- findLabelId(owner.uid, project.name, label.name) - updatedLabels <- labelRepo.updateLabel(updatedLabel.copy(id = labelId.map(LabelId.apply))) - foundLabel <- projectId.traverse(id => labelRepo.findLabel(id)(updatedLabel.name)) - } yield (createdProjects, projectId, createdLabels, updatedLabels, foundLabel.flatten) - test.map { tuple => - val (createdProjects, projectId, createdLabels, updatedLabels, foundLabel) = tuple - assert(createdProjects === 1, "Test vcs project was not created!") - assert(projectId.nonEmpty, "No vcs project id found!") - assert(createdLabels.exists(_ === 1), "Test label was not created!") - assert(updatedLabels === 1, "Test label was not updated!") - assert(foundLabel.nonEmpty, "Updated label not found!") - foundLabel.map { label => - assertEquals(label, updatedLabel.copy(id = label.id)) - } + PropF.forAllF { (owner: ProjectOwner, generatedProject: Project, label: Label) => + val updatedLabel = label.copy( + name = LabelName("updated label"), + description = Option(LabelDescription("I am an updated label description...")), + colour = ColourCode("#abcdef") + ) + val project = generatedProject.copy(owner = owner) + val dbConfig = configuration.database + val tx = Transactor.fromDriverManager[IO]( + driver = dbConfig.driver, + url = dbConfig.url, + user = dbConfig.user, + password = dbConfig.pass, + logHandler = None + ) + val labelRepo = new DoobieLabelRepository[IO](tx) + val test = for { + _ <- createProjectOwner(owner) + createdProjects <- createTicketsProject(project) + projectId <- loadProjectId(owner.uid, project.name) + createdLabels <- projectId.traverse(id => labelRepo.createLabel(id)(label)) + labelId <- findLabelId(owner.uid, project.name, label.name) + updatedLabels <- labelRepo.updateLabel(updatedLabel.copy(id = labelId.map(LabelId.apply))) + foundLabel <- projectId.traverse(id => labelRepo.findLabel(id)(updatedLabel.name)) + } yield (createdProjects, projectId, createdLabels, updatedLabels, foundLabel.flatten) + test.start.flatMap(_.joinWithNever).map { tuple => + val (createdProjects, projectId, createdLabels, updatedLabels, foundLabel) = tuple + assert(createdProjects === 1, "Test vcs project was not created!") + assert(projectId.nonEmpty, "No vcs project id found!") + assert(createdLabels.exists(_ === 1), "Test label was not created!") + assert(updatedLabels === 1, "Test label was not updated!") + assert(foundLabel.nonEmpty, "Updated label not found!") + foundLabel.foreach { label => + assertEquals(label, updatedLabel.copy(id = label.id)) } - case _ => fail("Could not generate data samples!") + } } } test("updateLabel must do nothing if id attribute is empty".tag(NeedsDatabase)) { - (genProjectOwner.sample, genProject.sample, genLabel.sample) match { - case (Some(owner), Some(generatedProject), Some(label)) => - val updatedLabel = label.copy( - id = None, - name = LabelName("updated label"), - description = Option(LabelDescription("I am an updated label description...")), - colour = ColourCode("#abcdef") - ) - val project = generatedProject.copy(owner = owner) - val dbConfig = configuration.database - val tx = Transactor.fromDriverManager[IO]( - driver = dbConfig.driver, - url = dbConfig.url, - user = dbConfig.user, - password = dbConfig.pass, - logHandler = None - ) - val labelRepo = new DoobieLabelRepository[IO](tx) - val test = for { - _ <- createProjectOwner(owner) - createdProjects <- createTicketsProject(project) - projectId <- loadProjectId(owner.uid, project.name) - createdLabels <- projectId.traverse(id => labelRepo.createLabel(id)(label)) - labelId <- findLabelId(owner.uid, project.name, label.name) - updatedLabels <- labelRepo.updateLabel(updatedLabel) - } yield (createdProjects, projectId, createdLabels, updatedLabels) - test.map { tuple => - val (createdProjects, projectId, createdLabels, updatedLabels) = tuple - assert(createdProjects === 1, "Test vcs project was not created!") - assert(projectId.nonEmpty, "No vcs project id found!") - assert(createdLabels.exists(_ === 1), "Test label was not created!") - assert(updatedLabels === 0, "Label with empty id must not be updated!") - } - case _ => fail("Could not generate data samples!") + PropF.forAllF { (owner: ProjectOwner, generatedProject: Project, label: Label) => + val updatedLabel = label.copy( + id = None, + name = LabelName("updated label"), + description = Option(LabelDescription("I am an updated label description...")), + colour = ColourCode("#abcdef") + ) + val project = generatedProject.copy(owner = owner) + val dbConfig = configuration.database + val tx = Transactor.fromDriverManager[IO]( + driver = dbConfig.driver, + url = dbConfig.url, + user = dbConfig.user, + password = dbConfig.pass, + logHandler = None + ) + val labelRepo = new DoobieLabelRepository[IO](tx) + val test = for { + _ <- createProjectOwner(owner) + createdProjects <- createTicketsProject(project) + projectId <- loadProjectId(owner.uid, project.name) + createdLabels <- projectId.traverse(id => labelRepo.createLabel(id)(label)) + labelId <- findLabelId(owner.uid, project.name, label.name) + updatedLabels <- labelRepo.updateLabel(updatedLabel) + } yield (createdProjects, projectId, createdLabels, updatedLabels) + test.start.flatMap(_.joinWithNever).map { tuple => + val (createdProjects, projectId, createdLabels, updatedLabels) = tuple + assert(createdProjects === 1, "Test vcs project was not created!") + assert(projectId.nonEmpty, "No vcs project id found!") + assert(createdLabels.exists(_ === 1), "Test label was not created!") + assert(updatedLabels === 0, "Label with empty id must not be updated!") + } } } } diff -rN -u old-smederee/modules/hub/src/test/scala/de/smederee/tickets/Generators.scala new-smederee/modules/hub/src/test/scala/de/smederee/tickets/Generators.scala --- old-smederee/modules/hub/src/test/scala/de/smederee/tickets/Generators.scala 2025-01-10 23:57:48.741013515 +0000 +++ new-smederee/modules/hub/src/test/scala/de/smederee/tickets/Generators.scala 2025-01-10 23:57:48.741013515 +0000 @@ -210,6 +210,8 @@ colour <- genColourCode } yield Label(id, name, description, colour) + given Arbitrary[Label] = Arbitrary(genLabel) + val genLabels: Gen[List[Label]] = Gen.nonEmptyListOf(genLabel).map(_.distinct) val genMilestoneTitle: Gen[MilestoneTitle] = @@ -287,6 +289,8 @@ isPrivate <- Gen.oneOf(List(false, true)) } yield Project(owner, name, description, isPrivate) + given Arbitrary[Project] = Arbitrary(genProject) + val genProjects: Gen[List[Project]] = Gen.nonEmptyListOf(genProject) }