Skip to content

Commit

Permalink
Merge pull request #295 from alejandrohdezma/feature/fixtures
Browse files Browse the repository at this point in the history
BREAKING | Simplify classes
  • Loading branch information
alejandrohdezma authored Apr 23, 2024
2 parents bc1f09f + 8a0a6a5 commit 0bcad2f
Show file tree
Hide file tree
Showing 27 changed files with 926 additions and 534 deletions.
4 changes: 0 additions & 4 deletions .github/auto_assign.yml

This file was deleted.

187 changes: 128 additions & 59 deletions .github/docs/README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .github/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ changelog:
labels:
- ":chart_with_upwards_trend: dependency-update"
authors:
- alejandrohdezma-steward
- dependabot
categories:
- title: "⚠️ Breaking changes"
labels:
Expand Down
11 changes: 6 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
# Runs `sbt ci-test` on the project on differnt JDKs (this task should be added to the project as a command alias
# containing the necessary steps to compile, check formatters, launch tests...).
#
# An example of this `ci-test` alias can be found in https://github.com/alejandrohdezma/sbt-github/blob/main/build.sbt.
# Examples of this `ci-test` alias can be found [here](https://github.com/search?q=org%3Aalejandrohdezma+%22ci-test%22+path%3Abuild.sbt++NOT+is%3Aarchived&type=code).
#
# It will also do the following:
#
# - It will automatically label PRs based on head branch.
# - It will automatically enable auto-merge on `Scala Steward` PRs.
# - It will automatically enable auto-merge on `Scala Steward` PRs. You'll need to add a `STEWARD_BOT` repository or
# organization variable with the name of your scala-steward bot. See https://docs.github.com/en/actions/learn-github-actions/variables.

name: CI

Expand All @@ -34,7 +35,7 @@ jobs:
ci-steward:
if: |
github.event.pull_request.state == 'OPEN' && github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.user.login == 'alejandrohdezma-steward[bot]'
github.event.pull_request.user.login == vars.STEWARD_BOT
name: (Scala Steward) Enable auto-merge
runs-on: ubuntu-latest
steps:
Expand All @@ -58,11 +59,11 @@ jobs:
- 17
steps:
- name: Checkout project
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: ${{ github.head_ref }}

- uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
- uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
distribution: "liberica"
java-version: ${{ matrix.jdk }}
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
# This workflow performs two tasks:
#
# - Creates a release of the project by running `sbt ci-publish` (this task should be added to the project as a command
# alias containing the necessary steps to do a release). An example of the `ci-publish` alias can be found in
# https://github.com/alejandrohdezma/sbt-github/blob/main/build.sbt.
# alias containing the necessary steps to do a release). Examples of this `ci-publish` alias can be found
# [here](https://github.com/search?q=org%3Aalejandrohdezma+%22ci-publish%22+path%3Abuild.sbt++NOT+is%3Aarchived&type=code).
#
# - Runs `sbt ci-docs` on the project and pushes a commit with the changes (the `ci-docs` task should be added to the
# project as a command alias containing the necessary steps to update documentation: re-generate docs files,
# publish websites, update headers...). An example of the `ci-docs` alias can be found in
# https://github.com/alejandrohdezma/sbt-github/blob/main/build.sbt.
# publish websites, update headers...). Examples of this `ci-docs` alias can be found
# [here](https://github.com/search?q=org%3Aalejandrohdezma+%22ci-docs%22+path%3Abuild.sbt++NOT+is%3Aarchived&type=code).
#
# This workflow will launch on pushed tags. Alternatively one can launch it manually using a "workflow dispatch" to
# create a snapshot release (this won't trigger the documentation update).
Expand All @@ -29,15 +29,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout project
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 0

- name: Check latest tag follows semantic versioning
if: github.event_name == 'push'
uses: alejandrohdezma/actions/check-semver-tag@v1

- uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
- uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
distribution: "liberica"
java-version: "11"
Expand All @@ -59,13 +59,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout project
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 0
ref: main
token: ${{ secrets.ADMIN_GITHUB_TOKEN }}

- uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
- uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
with:
distribution: "liberica"
java-version: "17"
Expand Down
43 changes: 19 additions & 24 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,35 +1,30 @@
val Scala3 = "3.3.0" // scala-steward:off
ThisBuild / scalaVersion := "2.13.11"
ThisBuild / crossScalaVersions := Seq("2.12.18", "2.13.11", Scala3)
ThisBuild / scalaVersion := "2.13.13"
ThisBuild / crossScalaVersions := Seq("2.12.19", "2.13.13", "3.3.3")
ThisBuild / versionPolicyIntention := Compatibility.None

ThisBuild / organization := "com.alejandrohdezma"

addCommandAlias("ci-test", "scalafmtCheckAll; mdoc; +test")
addCommandAlias("ci-test", "fix --check; versionPolicyCheck; mdoc; +test")
addCommandAlias("ci-docs", "github; headerCreateAll; mdoc")
addCommandAlias("ci-publish", "github; ci-release")
addCommandAlias("ci-publish", "versionCheck; github; ci-release")

lazy val documentation = project
.enablePlugins(MdocPlugin)
.settings(mdocOut := file("."))
.dependsOn(`http4s-munit` % "compile->test")
.settings(scalacOptions -= "-Wnonunit-statement")
.settings(libraryDependencies += "org.http4s" %% "http4s-blaze-client" % "0.23.15")
.settings(libraryDependencies += "org.http4s" %% "http4s-blaze-client" % "0.23.16")

lazy val `http4s-munit` = module
.settings(Test / fork := true)
.settings(libraryDependencies += "org.scalameta" %% "munit" % "1.0.0-M8")
.settings(libraryDependencies += "org.http4s" %% "http4s-client" % "0.23.23")
.settings(libraryDependencies += "org.http4s" %% "http4s-dsl" % "0.23.23")
.settings(libraryDependencies += "org.http4s" %% "http4s-ember-client" % "0.23.23" % Optional)
.settings(libraryDependencies += "org.typelevel" %% "munit-cats-effect" % "2.0.0-M3")
.settings(libraryDependencies += "io.circe" %% "circe-parser" % "0.14.5")
.settings(libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.4.11" % Test)
.settings(libraryDependencies += "org.http4s" %% "http4s-circe" % "0.23.23" % Test)
.settings(libraryDependencies += "com.dimafeng" %% "testcontainers-scala-munit" % "0.41.0" % Test)
.settings(libraryDependencies += "org.http4s" %% "http4s-ember-client" % "0.23.23" % Test)
.settings(
libraryDependencies ++= CrossVersion
.partialVersion(scalaVersion.value)
.collect { case (2, _) => compilerPlugin("org.typelevel" % "kind-projector" % "0.13.2").cross(CrossVersion.full) }
.toList
)
.settings(libraryDependencies += "org.scalameta" %% "munit" % "0.7.29")
.settings(libraryDependencies += "org.http4s" %% "http4s-client" % "0.23.26")
.settings(libraryDependencies += "org.http4s" %% "http4s-dsl" % "0.23.26")
.settings(libraryDependencies += "org.http4s" %% "http4s-ember-client" % "0.23.26" % Optional)
.settings(libraryDependencies += "org.typelevel" %% "munit-cats-effect-3" % "1.0.7")
.settings(libraryDependencies += "io.circe" %% "circe-parser" % "0.14.6")
.settings(libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.5.5" % Test)
.settings(libraryDependencies += "org.http4s" %% "http4s-circe" % "0.23.26" % Test)
.settings(libraryDependencies += "com.dimafeng" %% "testcontainers-scala-munit" % "0.40.16" % Test)
.settings(libraryDependencies += "org.http4s" %% "http4s-ember-client" % "0.23.26" % Test)
.settings(libraryDependencies ++= scalaVersion.value.on(2)(kindProjector))

def kindProjector = compilerPlugin("org.typelevel" % "kind-projector" % "0.13.3").cross(CrossVersion.full)
3 changes: 2 additions & 1 deletion modules/http4s-munit/src/main/scala/munit/ClientSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import org.http4s.dsl.Http4sDslBinCompat
* @author
* Alejandro Hernández
*/
@deprecated("Extend Http4sMUnitSyntax instead", since = "0.16.0")
trait ClientSuite extends CatsEffectSuite with Http4sDslBinCompat[IO] {

implicit class ClientTypeOps(t: Client.type) {
Expand Down Expand Up @@ -94,7 +95,7 @@ trait ClientSuite extends CatsEffectSuite with Http4sDslBinCompat[IO] {
def fixture[A](
f: Client[IO] => Resource[IO, A]
): PartialFunction[Request[IO], IO[Response[IO]]] => SyncIO[FunFixture[A]] =
pf => ResourceFunFixture(f(from(pf)))
pf => ResourceFixture(f(from(pf)))

}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 Alejandro Hernández <https://github.com/alejandrohdezma>
* Copyright 2020-2022 Alejandro Hernández <https://github.com/alejandrohdezma>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,16 +16,14 @@

package munit

import cats.Show
import cats.effect.IO
import cats.effect.Resource
import cats.effect.SyncIO

import org.http4s.AuthedRequest
import org.http4s.AuthedRoutes
import org.http4s.ContextRequest
import org.http4s.Request
import org.http4s.Response
import org.http4s.client.Client
import org.typelevel.vault.Key

/** Base class for suites testing `AuthedRoutes`.
*
Expand Down Expand Up @@ -53,42 +51,20 @@ import org.http4s.Response
* }
* }}}
*/
abstract class Http4sAuthedRoutesSuite[A: Show] extends Http4sSuite[AuthedRequest[IO, A]] {
@deprecated("Use `Http4sSuite` overriding `http4sMUnitClientFixture` instead", since = "0.16.0")
abstract class Http4sAuthedRoutesSuite[A] extends Http4sSuite {

@SuppressWarnings(Array("scalafix:DisableSyntax.valInAbstract"))
implicit val key: Key[A] = Key.newKey[IO, A].unsafeRunSync()

/** The HTTP routes being tested */
val routes: AuthedRoutes[A, IO]

/** @inheritdoc */
override def http4sMUnitNameCreator(
request: AuthedRequest[IO, A],
followingRequests: List[String],
testOptions: TestOptions,
config: Http4sMUnitConfig
): String = Http4sMUnitDefaults.http4sMUnitNameCreator(
request,
followingRequests,
testOptions,
config,
http4sMUnitNameCreatorReplacements()
)

implicit class Request2AuthedRequest(request: Request[IO]) {

/** Converts an `IO[Request[IO]]` into an `IO[AuthedRequest[IO, A]]` by providing the `A` context. */
def context(context: A): AuthedRequest[IO, A] = AuthedRequest(context, request)

/** Converts an `IO[Request[IO]]` into an `IO[AuthedRequest[IO, A]]` by providing the `A` context. */
def ->(a: A): AuthedRequest[IO, A] = context(a)

}

implicit class Http4sMUnitTestCreatorOps(creator: Http4sMUnitTestCreator) {

/** Allows overriding the routes used when running this test. */
def withRoutes(newRoutes: AuthedRoutes[A, IO]): Http4sMUnitTestCreator = creator.copy(
http4sMUnitFunFixture =
SyncIO.pure(FunFixture(_ => req => newRoutes.orNotFound.run(req).to[Resource[IO, *]], _ => ()))
)
/** Alias for adding a request's context. */
@deprecated("Use `.context` instead", since = "0.16.0")
def ->(a: A): Request[IO] = request.context(a)

}

Expand All @@ -100,8 +76,9 @@ abstract class Http4sAuthedRoutesSuite[A: Show] extends Http4sSuite[AuthedReques

}

def http4sMUnitFunFixture: SyncIO[FunFixture[ContextRequest[IO, A] => Resource[IO, Response[IO]]]] =
SyncIO.pure(FunFixture(_ => routes.orNotFound.run(_).to[Resource[IO, *]], _ => ()))
/** @inheritdoc */
override def http4sMUnitClientFixture: SyncIO[FunFixture[Client[IO]]] =
AuthedRequest.fromContext[A].andThen(routes).orFail.asFixture

/** Declares a test for the provided request. That request will be executed using the routes provided in `routes`.
*
Expand All @@ -126,7 +103,11 @@ abstract class Http4sAuthedRoutesSuite[A: Show] extends Http4sSuite[AuthedReques
* }
* }}}
*/
def test(request: AuthedRequest[IO, A]): Http4sMUnitTestCreator =
Http4sMUnitTestCreator(request, http4sMUnitFunFixture)
override def test(request: Request[IO]): Http4sMUnitTestCreator = {
if (!request.attributes.contains(key))
fail("Auth context not found on request, remember to add one with `.context`", clues(request))

super.test(request)
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020-2023 Alejandro Hernández <https://github.com/alejandrohdezma>
* Copyright 2020-2022 Alejandro Hernández <https://github.com/alejandrohdezma>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,13 +17,10 @@
package munit

import cats.effect.IO
import cats.effect.Resource
import cats.effect.SyncIO

import org.http4s.ContextRequest
import org.http4s.HttpRoutes
import org.http4s.Request
import org.http4s.Response
import org.http4s.client.Client

/** Base class for suites testing `HttpRoutes`.
*
Expand Down Expand Up @@ -53,7 +50,8 @@ import org.http4s.Response
* @author
* José Gutiérrez
*/
trait Http4sHttpRoutesSuite extends Http4sSuite[Request[IO]] {
@deprecated("Use `Http4sSuite` overriding `http4sMUnitClientFixture` instead", since = "0.16.0")
trait Http4sHttpRoutesSuite extends Http4sSuite {

/** The HTTP routes being tested.
*
Expand All @@ -65,64 +63,14 @@ trait Http4sHttpRoutesSuite extends Http4sSuite[Request[IO]] {
*/
val routes: HttpRoutes[IO]

/** @inheritdoc */
override def http4sMUnitNameCreator(
request: Request[IO],
followingRequests: List[String],
testOptions: TestOptions,
config: Http4sMUnitConfig
): String =
Http4sMUnitDefaults.http4sMUnitNameCreator(
ContextRequest((), request),
followingRequests,
testOptions,
config,
http4sMUnitNameCreatorReplacements()
)

implicit class Http4sMUnitTestCreatorOps(creator: Http4sMUnitTestCreator) {

/** Allows overriding the routes used when running this test. */
def withRoutes(newRoutes: HttpRoutes[IO]): Http4sMUnitTestCreator = creator.copy(
http4sMUnitFunFixture =
SyncIO.pure(FunFixture(_ => req => newRoutes.orNotFound.run(req).to[Resource[IO, *]], _ => ()))
)

}

implicit class HttpRoutesCompanionOps(companion: HttpRoutes.type) {

/** An HttpRoutes instance that always fails */
val fail: HttpRoutes[IO] = HttpRoutes(request => Assertions.fail("This should not be called", clues(request)))

}

def http4sMUnitFunFixture: SyncIO[FunFixture[Request[IO] => Resource[IO, Response[IO]]]] =
SyncIO.pure(FunFixture(_ => req => routes.orNotFound.run(req).to[Resource[IO, *]], _ => ()))

/** Declares a test for the provided request. That request will be executed using the routes provided in `routes`.
*
* @example
* {{{
* test(GET(uri"users" / 42)) { response =>
* // test body
* }
* }}}
*
* @example
* {{{
* test(POST(json, uri"users")).alias("Create a new user") { response =>
* // test body
* }
* }}}
*
* @example
* {{{
* test(GET(uri"users" / 42)).flaky { response =>
* // test body
* }
* }}}
*/
def test(request: Request[IO]): Http4sMUnitTestCreator = Http4sMUnitTestCreator(request, http4sMUnitFunFixture)
/** @inheritdoc */
override def http4sMUnitClientFixture: SyncIO[FunFixture[Client[IO]]] = routes.orFail.asFixture

}
Loading

0 comments on commit 0bcad2f

Please sign in to comment.