From 1fd8166cc3d283e556c022531371b64e64cad5b8 Mon Sep 17 00:00:00 2001 From: Daniel Karch Date: Tue, 22 Mar 2022 15:09:49 +0100 Subject: [PATCH 1/7] Added weaver module This PR adds the module `doobie-weaver` which allows testing queries etc. in the weaver testing framework. --- build.sbt | 17 +++++ .../main/scala/doobie/weaver/Checker.scala | 62 +++++++++++++++++++ .../main/scala/doobie/weaver/package.scala | 11 ++++ .../scala/doobie/weaver/CheckerTests.scala | 62 +++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 modules/weaver/src/main/scala/doobie/weaver/Checker.scala create mode 100644 modules/weaver/src/main/scala/doobie/weaver/package.scala create mode 100644 modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala diff --git a/build.sbt b/build.sbt index eb7b0f1a5..09ed15063 100644 --- a/build.sbt +++ b/build.sbt @@ -22,6 +22,7 @@ lazy val scala212Version = "2.12.15" lazy val scala213Version = "2.13.8" lazy val scala30Version = "3.1.1" lazy val slf4jVersion = "1.7.36" +lazy val weaverVersion = "0.7.11" // Basic versioning and publishing stuff ThisBuild / tlBaseVersion := "1.0" @@ -169,6 +170,7 @@ lazy val doobie = project.in(file(".")) scalatest, munit, specs2, + weaver ) lazy val free = project @@ -418,6 +420,21 @@ lazy val munit = project ) ) +lazy val weaver = project + .in(file("modules/weaver")) + .enablePlugins(AutomateHeaderPlugin) + .dependsOn(core) + .settings(doobieSettings) + .settings( + name := s"doobie-weaver", + description := "Weaver support for doobie.", + testFrameworks += new TestFramework("weaver.framework.CatsEffect"), + libraryDependencies ++= Seq( + "com.disneystreaming" %% "weaver-cats" % weaverVersion, + "com.h2database" % "h2" % h2Version % "test" + ) + ) + lazy val bench = project .in(file("modules/bench")) .enablePlugins(NoPublishPlugin) diff --git a/modules/weaver/src/main/scala/doobie/weaver/Checker.scala b/modules/weaver/src/main/scala/doobie/weaver/Checker.scala new file mode 100644 index 000000000..32b042c4f --- /dev/null +++ b/modules/weaver/src/main/scala/doobie/weaver/Checker.scala @@ -0,0 +1,62 @@ +// Copyright (c) 2013-2020 Rob Norris and Contributors +// This software is licensed under the MIT License (MIT). +// For more information see LICENSE or https://opensource.org/licenses/MIT + +package doobie.weaver + +import weaver._ +import weaver.Expectations.Helpers._ +import doobie.util.testing._ +import doobie.syntax.connectionio._ +import cats.effect.kernel.Sync +import cats.syntax.all._ +import doobie.util.Colors +import doobie._ +import org.tpolecat.typename._ + +/** + * Module with a mix-in trait for specifications that enables checking of doobie `Query` and `Update` values. + * + * {{{ + * object ExampleSuite extends IOSuite with IOChecker { + * + * override type Res = Transactor[IO] + * override def sharedResource: Resource[IO, Res] = + * // The transactor to use for the tests. + * Resource.pure(Transactor.fromDriverManager[IO]( + * "org.postgresql.Driver", + * "jdbc:postgresql:world", + * "postgres", "" + * )) + * + * // Now just mention the queries. Arguments are not used. + * test("findByNameAndAge") { implicit transactor => check(MyDaoModule.findByNameAndAge(null, 0)) } + * test("allWoozles") { implicit transactor => check(MyDaoModule.allWoozles) } + * + * } + * }}} + */ +trait Checker[M[_]] { + def check[A: Analyzable](a: A)(implicit M: Sync[M], pos: SourceLocation, transactor: Transactor[M]): M[Expectations] = + checkImpl(Analyzable.unpack(a)) + + def checkOutput[A: TypeName](q: Query0[A])(implicit M: Sync[M], pos: SourceLocation, transactor: Transactor[M]): M[Expectations] = + checkImpl(AnalysisArgs( + s"Query0[${typeName[A]}]", q.pos, q.sql, q.outputAnalysis + )) + + def checkOutput[A: TypeName, B: TypeName](q: Query[A, B])(implicit M: Sync[M], pos: SourceLocation, transactor: Transactor[M]): M[Expectations] = + checkImpl(AnalysisArgs( + s"Query[${typeName[A]}, ${typeName[B]}]", q.pos, q.sql, q.outputAnalysis + )) + + private def checkImpl(args: AnalysisArgs)(implicit M: Sync[M], pos: SourceLocation, transactor: Transactor[M]) = { + analyze(args).transact(transactor).map { report => + if (!report.succeeded) + failure(formatReport(args, report, Colors.Ansi) + .padLeft(" ") + .toString) + else success + } + } +} diff --git a/modules/weaver/src/main/scala/doobie/weaver/package.scala b/modules/weaver/src/main/scala/doobie/weaver/package.scala new file mode 100644 index 000000000..3d8925c0a --- /dev/null +++ b/modules/weaver/src/main/scala/doobie/weaver/package.scala @@ -0,0 +1,11 @@ +// Copyright (c) 2013-2020 Rob Norris and Contributors +// This software is licensed under the MIT License (MIT). +// For more information see LICENSE or https://opensource.org/licenses/MIT + +package doobie + +import cats.effect.IO + +package object weaver { + type IOChecker = Checker[IO] +} diff --git a/modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala b/modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala new file mode 100644 index 000000000..ffe099f87 --- /dev/null +++ b/modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala @@ -0,0 +1,62 @@ +// Copyright (c) 2013-2020 Rob Norris and Contributors +// This software is licensed under the MIT License (MIT). +// For more information see LICENSE or https://opensource.org/licenses/MIT + +package doobie.weaver + +import cats.effect.IO +import doobie.syntax.string._ +import doobie.util.Read +import doobie.util.transactor.Transactor +import weaver._ +import cats.effect.kernel.Resource + +object CheckerChecks extends IOSuite with IOChecker { + + override type Res = Transactor[IO] + override def sharedResource: Resource[IO,Res] = + Resource.pure(Transactor.fromDriverManager[IO]( + "org.h2.Driver", + "jdbc:h2:mem:queryspec;DB_CLOSE_DELAY=-1", + "sa", "" + )) + + test("trivial") { implicit transactor => + check(sql"select 1".query[Int]) + } + + test("fail") { implicit transactor => + check(sql"select 1".query[String]) + } + + final case class Foo[F[_]](x: Int) + + test ("trivial case-class") { implicit transactor => + check(sql"select 1".query[Foo[cats.Id]]) + } + + test("Read should select correct columns when combined with `product`") { implicit transactor => + import cats.syntax.all._ + import doobie.implicits._ + + val ri = Read[Int] + val rs = Read[String] + + // tupled use product under the hood + val combined: Read[(Int, String)] = (ri, rs).tupled + + check(sql"SELECT 1, '2'".query(combined)) + } + + test("Read should select correct columns for checking when combined with `ap`") { implicit transactor => + val readInt = Read[(Int, Int)] + val readIntToInt: Read[Tuple2[Int, Int] => String] = + Read[(String, String)].map(i => k => s"$i,$k") + + val combined: Read[String] = readInt.ap(readIntToInt) + + check(sql"SELECT '1', '2', 3, 4".query(combined)) + } + +} + From 4dd683da94b664fdd3c4eb7abe897bfae21052d1 Mon Sep 17 00:00:00 2001 From: Daniel Karch Date: Thu, 24 Mar 2022 13:27:09 +0100 Subject: [PATCH 2/7] Added documentation for weaver-test integration. --- .../src/main/mdoc/docs/13-Unit-Testing.md | 28 ++++++++++++++++++- .../scala/doobie/weaver/CheckerTests.scala | 3 +- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/modules/docs/src/main/mdoc/docs/13-Unit-Testing.md b/modules/docs/src/main/mdoc/docs/13-Unit-Testing.md index b1db77658..b5771970c 100644 --- a/modules/docs/src/main/mdoc/docs/13-Unit-Testing.md +++ b/modules/docs/src/main/mdoc/docs/13-Unit-Testing.md @@ -1,6 +1,6 @@ ## Unit Testing -The YOLO-mode query checking feature demonstated in an earlier chapter is also available as a trait you can mix into your [Specs2](http://etorreborre.github.io/specs2/), [ScalaTest](http://www.scalatest.org/) or [MUnit](https://scalameta.org/munit) unit tests. +The YOLO-mode query checking feature demonstated in an earlier chapter is also available as a trait you can mix into your [Specs2](http://etorreborre.github.io/specs2/), [ScalaTest](http://www.scalatest.org/), [MUnit](https://scalameta.org/munit) or [Weaver](https://disneystreaming.github.io/weaver-test/) unit tests. ### Setting Up @@ -150,3 +150,29 @@ class AnalysisTestSuite extends FunSuite with doobie.munit.IOChecker { } ``` + +### The Weaver Package + +The `doobie-weaver` add-on provides a mix-in trait what we can add to any effectful test Suite. +The `check` function takes an implicit `Transactor[F]` parameter. Since Weaver has its own way +to manage shared resources, it is convenient to use that to allocate the transcator. + +```scala mdoc:silent +import weaver._ + +object AnalysisTestSuite extends IOSuite with IOChecker { + + override val colors = doobie.util.Colors.None // just for docs + + override type Res = Transactor[IO] + override def sharedResource: Resource[IO,Res] = + Resource.pure(Transactor.fromDriverManager[IO]( + "org.postgresql.Driver", "jdbc:postgresql:world", "postgres", "" + )) + + test("trivial") { implicit transactor => check(trivial) } + test("biggerThan") { implicit transactor => check(biggerThan(0)) } + test("update") { implicit transactor => check(update("", "")) } + +} +``` diff --git a/modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala b/modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala index ffe099f87..8d937a6ce 100644 --- a/modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala +++ b/modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala @@ -11,7 +11,7 @@ import doobie.util.transactor.Transactor import weaver._ import cats.effect.kernel.Resource -object CheckerChecks extends IOSuite with IOChecker { +object CheckerTests extends IOSuite with IOChecker { override type Res = Transactor[IO] override def sharedResource: Resource[IO,Res] = @@ -59,4 +59,3 @@ object CheckerChecks extends IOSuite with IOChecker { } } - From ed5bdacf316d43a291ffe80c86f008f4cc65e29c Mon Sep 17 00:00:00 2001 From: Jacob Wang Date: Mon, 8 Aug 2022 23:01:14 +0100 Subject: [PATCH 3/7] update weaver to 0.7.14 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 6eb1c4d0c..4c4cff512 100644 --- a/build.sbt +++ b/build.sbt @@ -22,7 +22,7 @@ lazy val scala212Version = "2.12.15" lazy val scala213Version = "2.13.8" lazy val scala30Version = "3.1.1" lazy val slf4jVersion = "1.7.36" -lazy val weaverVersion = "0.7.11" +lazy val weaverVersion = "0.7.14" // Basic versioning and publishing stuff ThisBuild / tlBaseVersion := "1.0" From 6a96f0b4a556de098d9aefe7b3381340259c4065 Mon Sep 17 00:00:00 2001 From: Jacob Wang Date: Mon, 8 Aug 2022 23:38:33 +0100 Subject: [PATCH 4/7] update ci.yml --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3192227f..b5e40203a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,11 +91,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p modules/scalatest/target modules/refined/target target modules/postgres/target modules/docs/target modules/postgres-circe/target modules/h2/target modules/hikari/target modules/munit/target modules/h2-circe/target modules/core/target modules/example/target modules/specs2/target modules/free/target modules/bench/target project/target + run: mkdir -p modules/weaver/target modules/scalatest/target modules/refined/target target modules/postgres/target modules/docs/target modules/postgres-circe/target modules/h2/target modules/hikari/target modules/munit/target modules/h2-circe/target modules/core/target modules/example/target modules/specs2/target modules/free/target modules/bench/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar modules/scalatest/target modules/refined/target target modules/postgres/target modules/docs/target modules/postgres-circe/target modules/h2/target modules/hikari/target modules/munit/target modules/h2-circe/target modules/core/target modules/example/target modules/specs2/target modules/free/target modules/bench/target project/target + run: tar cf targets.tar modules/weaver/target modules/scalatest/target modules/refined/target target modules/postgres/target modules/docs/target modules/postgres-circe/target modules/h2/target modules/hikari/target modules/munit/target modules/h2-circe/target modules/core/target modules/example/target modules/specs2/target modules/free/target modules/bench/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') From 72c8c66d3e176180619534f91c7de209f32018ed Mon Sep 17 00:00:00 2001 From: Jacob Wang Date: Mon, 8 Aug 2022 23:58:07 +0100 Subject: [PATCH 5/7] update ci.yml & fix tests --- .../weaver/src/test/scala/doobie/weaver/CheckerTests.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala b/modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala index 8d937a6ce..06a20c4c7 100644 --- a/modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala +++ b/modules/weaver/src/test/scala/doobie/weaver/CheckerTests.scala @@ -26,7 +26,9 @@ object CheckerTests extends IOSuite with IOChecker { } test("fail") { implicit transactor => - check(sql"select 1".query[String]) + check(sql"select 1".query[String]).map(expectation => + expectation.xor(success) + ) } final case class Foo[F[_]](x: Int) From b9c0362bf854f3bc69bd60baf7414dda1d01ab52 Mon Sep 17 00:00:00 2001 From: Jacob Wang Date: Tue, 9 Aug 2022 22:10:29 +0100 Subject: [PATCH 6/7] fix doc compile --- modules/docs/src/main/mdoc/docs/13-Unit-Testing.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/docs/src/main/mdoc/docs/13-Unit-Testing.md b/modules/docs/src/main/mdoc/docs/13-Unit-Testing.md index b5771970c..4ad3c97be 100644 --- a/modules/docs/src/main/mdoc/docs/13-Unit-Testing.md +++ b/modules/docs/src/main/mdoc/docs/13-Unit-Testing.md @@ -158,11 +158,10 @@ The `check` function takes an implicit `Transactor[F]` parameter. Since Weaver h to manage shared resources, it is convenient to use that to allocate the transcator. ```scala mdoc:silent -import weaver._ +import _root_.weaver._ +import doobie.weaver._ object AnalysisTestSuite extends IOSuite with IOChecker { - - override val colors = doobie.util.Colors.None // just for docs override type Res = Transactor[IO] override def sharedResource: Resource[IO,Res] = From 5b12a7499d570f3e680d50b2d424f3100b090b21 Mon Sep 17 00:00:00 2001 From: Jacob Wang Date: Tue, 9 Aug 2022 22:44:22 +0100 Subject: [PATCH 7/7] add weaver to docs dependency --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 4c4cff512..2f973c135 100644 --- a/build.sbt +++ b/build.sbt @@ -445,7 +445,7 @@ lazy val bench = project lazy val docs = project .in(file("modules/docs")) - .dependsOn(core, postgres, specs2, munit, hikari, h2, scalatest) + .dependsOn(core, postgres, specs2, munit, hikari, h2, scalatest, weaver) .enablePlugins(NoPublishPlugin) .enablePlugins(ParadoxPlugin) .enablePlugins(ParadoxSitePlugin)