diff --git a/delta/kernel/src/main/scala/ch/epfl/bluebrain/nexus/delta/kernel/effect/migration/MigrateEffectSyntax.scala b/delta/kernel/src/main/scala/ch/epfl/bluebrain/nexus/delta/kernel/effect/migration/MigrateEffectSyntax.scala index 064a04f1c2..6cb620917b 100644 --- a/delta/kernel/src/main/scala/ch/epfl/bluebrain/nexus/delta/kernel/effect/migration/MigrateEffectSyntax.scala +++ b/delta/kernel/src/main/scala/ch/epfl/bluebrain/nexus/delta/kernel/effect/migration/MigrateEffectSyntax.scala @@ -1,7 +1,8 @@ package ch.epfl.bluebrain.nexus.delta.kernel.effect.migration import cats.effect.IO -import monix.bio.{IO => BIO, UIO} +import cats.~> +import monix.bio.{IO => BIO, Task, UIO} import monix.execution.Scheduler.Implicits.global import scala.reflect.ClassTag @@ -12,6 +13,8 @@ trait MigrateEffectSyntax { implicit def toMonixBIOOps[A](io: IO[A]): CatsIOToBioOps[A] = new CatsIOToBioOps(io) + val taskToIoK: Task ~> IO = λ[Task ~> IO](toCatsIO(_)) + } final class CatsIOToBioOps[A](private val io: IO[A]) extends AnyVal { diff --git a/delta/plugins/composite-views/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/indexing/CompositeProjectionLifeCycleSuite.scala b/delta/plugins/composite-views/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/indexing/CompositeProjectionLifeCycleSuite.scala index 0c55502e27..eac8f3d376 100644 --- a/delta/plugins/composite-views/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/indexing/CompositeProjectionLifeCycleSuite.scala +++ b/delta/plugins/composite-views/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/compositeviews/indexing/CompositeProjectionLifeCycleSuite.scala @@ -2,22 +2,23 @@ package ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.indexing import cats.data.NonEmptyMapImpl import cats.effect.concurrent.Ref +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.CompositeViewsFixture import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.indexing.CompositeProjectionLifeCycle.Hook import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.indexing.CompositeProjectionLifeCycleSuite.DestroyResult +import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.indexing.CompositeProjectionLifeCycleSuite.DestroyResult._ import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.indexing.CompositeViewDef.{ActiveViewDef, DeprecatedViewDef} import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode.Iri import ch.epfl.bluebrain.nexus.delta.rdf.Vocabulary.nxv import ch.epfl.bluebrain.nexus.delta.sdk.views.{IndexingRev, IndexingViewRef, ViewRef} import ch.epfl.bluebrain.nexus.delta.sourcing.config.BatchConfig -import ch.epfl.bluebrain.nexus.delta.sourcing.stream.{CompiledProjection, ExecutionStatus, ExecutionStrategy, Projection, ProjectionMetadata} +import ch.epfl.bluebrain.nexus.delta.sourcing.stream._ import ch.epfl.bluebrain.nexus.testkit.bio.{BioSuite, PatienceConfig} -import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.indexing.CompositeProjectionLifeCycleSuite.DestroyResult._ import monix.bio.{Task, UIO} import munit.Location -import concurrent.duration._ import java.util.UUID +import scala.concurrent.duration._ class CompositeProjectionLifeCycleSuite extends BioSuite with CompositeViewsFixture { @@ -34,7 +35,7 @@ class CompositeProjectionLifeCycleSuite extends BioSuite with CompositeViewsFixt private def createHook(name: String, test: ViewRef => Boolean, ref: Ref[Task, Set[String]]): Hook = (view: CompositeViewDef.ActiveViewDef) => { Option.when(test(view.ref)) { - ref.update { + ref.mapK(taskToIoK).update { _ + name } } diff --git a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchConfigHookSuite.scala b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchConfigHookSuite.scala index b7f6e87c79..f06c5696cf 100644 --- a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchConfigHookSuite.scala +++ b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchConfigHookSuite.scala @@ -1,6 +1,8 @@ package ch.epfl.bluebrain.nexus.delta.plugins.search +import cats.effect.IO import cats.effect.concurrent.Ref +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.indexing.CompositeViewDef.ActiveViewDef import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.model.CompositeView import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.{CompositeViewFactory, CompositeViewsFixture} @@ -13,14 +15,13 @@ import ch.epfl.bluebrain.nexus.delta.sdk.Defaults import ch.epfl.bluebrain.nexus.delta.sdk.projects.model.ProjectBase import ch.epfl.bluebrain.nexus.delta.sdk.views.ViewRef import ch.epfl.bluebrain.nexus.delta.sourcing.model.ProjectRef -import ch.epfl.bluebrain.nexus.testkit.bio.BioSuite +import ch.epfl.bluebrain.nexus.testkit.ce.CatsEffectSuite import io.circe.{Json, JsonObject} -import monix.bio.{Task, UIO} import java.util.UUID import scala.concurrent.duration._ -class SearchConfigHookSuite extends BioSuite with CompositeViewsFixture { +class SearchConfigHookSuite extends CatsEffectSuite with CompositeViewsFixture { implicit private val projectBase: ProjectBase = ProjectBase.unsafe(nxv.base) @@ -39,8 +40,9 @@ class SearchConfigHookSuite extends BioSuite with CompositeViewsFixture { // Create a search view from the provided config private def searchView(defaults: Defaults, config: IndexingConfig) = { - val fields = SearchViewFactory(defaults, config) - CompositeViewFactory.create(fields).map { value => + val fields = SearchViewFactory(defaults, config) + val compositeViewValue = toCatsIO(CompositeViewFactory.create(fields)) + compositeViewValue.map { value => ActiveViewDef(ViewRef(proj, defaultViewId), UUID.randomUUID(), 1, value) } } @@ -48,22 +50,22 @@ class SearchConfigHookSuite extends BioSuite with CompositeViewsFixture { // Create the hook with a current config private def execSearchHook(view: ActiveViewDef) = Ref - .of[Task, Option[ViewRef]](None) + .of[IO, Option[ViewRef]](None) .map { r => - new SearchConfigHook(defaults, currentConfig, (v, _) => r.set(Some(v.ref)).hideErrors) -> r.get.hideErrors + new SearchConfigHook(defaults, currentConfig, (v, _) => r.set(Some(v.ref))) -> r.get } .flatMap { case (hook, updatedRef) => - hook(view).getOrElse(Task.unit).as(updatedRef) + hook(view).getOrElse(IO.unit).as(updatedRef) } - private def assertViewUpdated(view: UIO[ActiveViewDef]) = + private def assertViewUpdated(view: IO[ActiveViewDef]) = for { v <- view updated <- execSearchHook(v) _ <- updated.assertSome(v.ref) } yield () - private def assertViewNotUpdated(view: UIO[ActiveViewDef]) = + private def assertViewNotUpdated(view: IO[ActiveViewDef]) = for { v <- view updated <- execSearchHook(v) diff --git a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchRoutesSpec.scala b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchRoutesSpec.scala index 510b6e80ed..8480e99bd7 100644 --- a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchRoutesSpec.scala +++ b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchRoutesSpec.scala @@ -2,7 +2,7 @@ package ch.epfl.bluebrain.nexus.delta.plugins.search import akka.http.scaladsl.model.{StatusCodes, Uri} import akka.http.scaladsl.server.Route -import ch.epfl.bluebrain.nexus.delta.plugins.search.model.SearchRejection +import cats.effect.IO import ch.epfl.bluebrain.nexus.delta.sdk.acls.AclSimpleCheck import ch.epfl.bluebrain.nexus.delta.sdk.identities.IdentitiesDummy import ch.epfl.bluebrain.nexus.delta.sdk.identities.model.Caller @@ -10,18 +10,17 @@ import ch.epfl.bluebrain.nexus.delta.sdk.utils.BaseRouteSpec import ch.epfl.bluebrain.nexus.delta.sourcing.model.Label import io.circe.syntax._ import io.circe.{Json, JsonObject} -import monix.bio.IO class SearchRoutesSpec extends BaseRouteSpec { // Dummy implementation of search which just returns the payload private val search = new Search { - override def query(payload: JsonObject, qp: Uri.Query)(implicit caller: Caller): IO[SearchRejection, Json] = + override def query(payload: JsonObject, qp: Uri.Query)(implicit caller: Caller): IO[Json] = IO.pure(payload.asJson) override def query(suite: Label, payload: JsonObject, qp: Uri.Query)(implicit caller: Caller - ): IO[SearchRejection, Json] = + ): IO[Json] = IO.pure(Json.obj(suite.value -> payload.asJson)) } diff --git a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchScopeInitializationSpec.scala b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchScopeInitializationSpec.scala index 1ab8afcb6b..1cec422b1b 100644 --- a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchScopeInitializationSpec.scala +++ b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchScopeInitializationSpec.scala @@ -16,6 +16,7 @@ import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Subject, User} import ch.epfl.bluebrain.nexus.delta.sourcing.model.Label import ch.epfl.bluebrain.nexus.delta.sourcing.postgres.DoobieScalaTestFixture import ch.epfl.bluebrain.nexus.testkit.IOValues +import ch.epfl.bluebrain.nexus.testkit.ce.CatsIOValues import io.circe.JsonObject import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike @@ -25,6 +26,7 @@ class SearchScopeInitializationSpec with AnyWordSpecLike with CompositeViewsFixture with Matchers + with CatsIOValues with IOValues with Fixtures { diff --git a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSparqlQuerySpec.scala b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSparqlQuerySpec.scala index 354b7b58e9..6b0252771e 100644 --- a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSparqlQuerySpec.scala +++ b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSparqlQuerySpec.scala @@ -2,6 +2,8 @@ package ch.epfl.bluebrain.nexus.delta.plugins.search import akka.actor.ActorSystem import akka.testkit.TestKit +import cats.effect.IO +import ch.epfl.bluebrain.nexus.delta.kernel.effect.migration._ import ch.epfl.bluebrain.nexus.delta.plugins.blazegraph.client.BlazegraphClient import ch.epfl.bluebrain.nexus.delta.plugins.blazegraph.client.SparqlQueryResponseType.SparqlNTriples import ch.epfl.bluebrain.nexus.delta.rdf.IriOrBNode @@ -15,9 +17,9 @@ import ch.epfl.bluebrain.nexus.delta.sdk.ConfigFixtures import ch.epfl.bluebrain.nexus.delta.sdk.http.{HttpClient, HttpClientConfig} import ch.epfl.bluebrain.nexus.delta.sdk.syntax._ import ch.epfl.bluebrain.nexus.testkit.blazegraph.BlazegraphDocker +import ch.epfl.bluebrain.nexus.testkit.ce.CatsIOValues import ch.epfl.bluebrain.nexus.testkit.{EitherValuable, IOValues, TestHelpers, TestMatchers} import io.circe.Json -import monix.bio.IO import monix.execution.Scheduler import org.scalatest.concurrent.Eventually import org.scalatest.matchers.should.Matchers @@ -39,6 +41,7 @@ class SearchSparqlQuerySpec with Inspectors with TestMatchers with IOValues + with CatsIOValues with BlazegraphDocker { implicit override def patienceConfig: PatienceConfig = PatienceConfig(6.seconds, 10.milliseconds) @@ -57,7 +60,7 @@ class SearchSparqlQuerySpec private def toNTriples(json: Json): NTriples = { for { - expanded <- ExpandedJsonLd(json) + expanded <- toCatsIO(ExpandedJsonLd(json)) graph <- IO.fromEither(expanded.toGraph) ntriples <- IO.fromEither(graph.toNTriples) } yield ntriples @@ -89,7 +92,7 @@ class SearchSparqlQuerySpec val q = contentOf("construct-query.sparql").replaceAll(quote("{resource_id}"), traceId.rdfFormat) val query = SparqlConstructQuery(q).rightValue val compacted = for { - ntriples <- client.query(Set(index), query, SparqlNTriples) + ntriples <- toCatsIO(client.query(Set(index), query, SparqlNTriples)) graph <- IO.fromEither(Graph(ntriples.value.copy(rootNode = traceId))) compacted <- graph.toCompactedJsonLd(ctx) } yield compacted diff --git a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSpec.scala b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSpec.scala index cb17174647..6493e7ef77 100644 --- a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSpec.scala +++ b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/SearchSpec.scala @@ -4,6 +4,7 @@ import akka.actor.ActorSystem import akka.http.scaladsl.model.Uri.Query import akka.testkit.TestKit import cats.data.NonEmptyList +import cats.effect.IO import cats.implicits._ import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.indexing._ import ch.epfl.bluebrain.nexus.delta.plugins.compositeviews.model.CompositeView @@ -31,9 +32,9 @@ import ch.epfl.bluebrain.nexus.delta.sdk.views.IndexingRev import ch.epfl.bluebrain.nexus.delta.sourcing.model.Identity.{Group, User} import ch.epfl.bluebrain.nexus.delta.sourcing.model.Label import ch.epfl.bluebrain.nexus.testkit._ +import ch.epfl.bluebrain.nexus.testkit.ce.CatsIOValues import ch.epfl.bluebrain.nexus.testkit.elasticsearch.ElasticSearchDocker import io.circe.{Json, JsonObject} -import monix.bio.UIO import org.scalatest.concurrent.Eventually import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike @@ -55,6 +56,7 @@ class SearchSpec with Inspectors with ScalaTestElasticSearchClientSetup with ConfigFixtures + with CatsIOValues with IOValues with Eventually with Fixtures @@ -115,7 +117,7 @@ class SearchSpec private val projections = Seq(projectionProj1, projectionProj2) - private val listViews: ListProjections = () => UIO.pure(projections) + private val listViews: ListProjections = () => IO.pure(projections) private val allSuite = Label.unsafe("allSuite") private val proj2Suite = Label.unsafe("proj2Suite") diff --git a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/model/SearchConfigSpec.scala b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/model/SearchConfigSpec.scala index 8031f58012..af6e849c46 100644 --- a/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/model/SearchConfigSpec.scala +++ b/delta/plugins/search/src/test/scala/ch/epfl/bluebrain/nexus/delta/plugins/search/model/SearchConfigSpec.scala @@ -5,6 +5,7 @@ import ch.epfl.bluebrain.nexus.delta.plugins.search.model.SearchConfigError.{Inv import ch.epfl.bluebrain.nexus.delta.sdk.Defaults import ch.epfl.bluebrain.nexus.delta.sourcing.model.{Label, ProjectRef} import ch.epfl.bluebrain.nexus.testkit.IOValues +import ch.epfl.bluebrain.nexus.testkit.ce.CatsIOValues import com.typesafe.config.ConfigFactory import org.scalatest.Inspectors import org.scalatest.matchers.should.Matchers @@ -12,7 +13,7 @@ import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.duration.DurationInt -class SearchConfigSpec extends AnyWordSpecLike with Matchers with Inspectors with IOValues { +class SearchConfigSpec extends AnyWordSpecLike with Matchers with Inspectors with CatsIOValues with IOValues { private def getAbsolutePath(path: String) = getClass.getResource(path).getPath diff --git a/delta/testkit/src/main/scala/ch/epfl/bluebrain/nexus/testkit/ce/CatsEffectAssertions.scala b/delta/testkit/src/main/scala/ch/epfl/bluebrain/nexus/testkit/ce/CatsEffectAssertions.scala index 1df15177bd..46e2291f21 100644 --- a/delta/testkit/src/main/scala/ch/epfl/bluebrain/nexus/testkit/ce/CatsEffectAssertions.scala +++ b/delta/testkit/src/main/scala/ch/epfl/bluebrain/nexus/testkit/ce/CatsEffectAssertions.scala @@ -218,6 +218,11 @@ trait CatsEffectAssertions { self: Assertions => def assert(implicit loc: Location): IO[Unit] = assertIOBoolean(io, "value is not true") } + + implicit class MunitCatsAssertionsOptionOps[A](io: IO[Option[A]])(implicit loc: Location) { + def assertSome(expected: A): IO[Unit] = assertIO(io, Some(expected), s"the Option did not contains $expected") + def assertNone: IO[Unit] = assertIO(io, None, "the Option was not empty") + } } object CatsEffectAssertions extends Assertions with CatsEffectAssertions diff --git a/delta/testkit/src/main/scala/ch/epfl/bluebrain/nexus/testkit/ce/CatsIOValues.scala b/delta/testkit/src/main/scala/ch/epfl/bluebrain/nexus/testkit/ce/CatsIOValues.scala new file mode 100644 index 0000000000..ea21b5afe9 --- /dev/null +++ b/delta/testkit/src/main/scala/ch/epfl/bluebrain/nexus/testkit/ce/CatsIOValues.scala @@ -0,0 +1,29 @@ +package ch.epfl.bluebrain.nexus.testkit.ce + +import cats.effect.IO +import org.scalactic.source +import org.scalatest.Assertions.fail + +import scala.reflect.ClassTag + +trait CatsIOValues { + + implicit final class CatsIOValuesOps[A](private val io: IO[A]) { + def accepted: A = io.unsafeRunSync() + + def rejectedWith[E](implicit pos: source.Position, EE: ClassTag[E]): E = { + io.attempt.unsafeRunSync() match { + case Left(EE(value)) => value + case Left(value) => + fail( + s"Wrong raised error type caught, expected: '${EE.runtimeClass.getName}', actual: '${value.getClass.getName}'" + ) + case Right(value) => + fail( + s"Expected raising error, but returned successful response with type '${value.getClass.getName}'" + ) + } + } + } + +}