All Downloads are FREE. Search and download functionalities are using the official Maven repository.

doobie.munit.analysisspec.scala Maven / Gradle / Ivy

// 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.munit

import cats.effect.{IO, Async}
import doobie.munit.analysisspec.Checker.ErrorItems
import doobie.syntax.connectionio.*
import doobie.util.query.{Query0, Query}
import doobie.util.testing.*
import org.tpolecat.typename.*
import munit.{Assertions, Location}

/** Module with a mix-in trait for specifications that enables checking of doobie `Query` and `Update` values.
  *
  * {{{
  * class ExampleSuite extends FunSuite with IOChecker {
  *
  *   // The transactor to use for the tests.
  *   val transactor = Transactor.fromDriverManager[IO](...)
  *
  *   // Now just mention the queries. Arguments are not used.
  *   test("findByNameAndAge") { check(MyDaoModule.findByNameAndAge(null, 0)) }
  *   test("allWoozles") { check(MyDaoModule.allWoozles) }
  *
  * }
  * }}}
  */
object analysisspec {

  trait Checker[M[_]] extends CheckerBase[M] { this: Assertions =>

    def check[A: Analyzable](a: A)(implicit loc: Location): Unit = checkImpl(Analyzable.unpack(a))

    def checkOutput[A: TypeName](q: Query0[A])(implicit loc: Location): Unit =
      checkImpl(AnalysisArgs(
        s"Query0[${typeName[A]}]",
        q.pos,
        q.sql,
        q.outputAnalysis
      ))

    def checkOutput[A: TypeName, B: TypeName](q: Query[A, B])(implicit loc: Location): Unit =
      checkImpl(AnalysisArgs(
        s"Query[${typeName[A]}, ${typeName[B]}]",
        q.pos,
        q.sql,
        q.outputAnalysis
      ))

    private def checkImpl(args: AnalysisArgs)(implicit loc: Location): Unit = {
      val report = U.unsafeRunSync(analyze(args).transact(transactor))
      if (!report.succeeded) {
        fail(
          message = formatReport(args, report, colors)
            .padLeft("  ")
            .toString,
          cause = ErrorItems(report.items.filter(_.error.isDefined))
        )
      }
    }
  }

  object Checker {
    case class ErrorItems(errors: List[AnalysisReport.Item]) extends Exception
  }

  /** Implementation of Checker[IO] */
  trait IOChecker extends Checker[IO] {
    self: Assertions =>
    import cats.effect.unsafe.implicits.global
    override implicit val M: Async[IO] = IO.asyncForIO
    override implicit val U: UnsafeRun[IO] = new UnsafeRun[IO] {
      def unsafeRunSync[A](ioa: IO[A]) = ioa.unsafeRunSync()
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy