Repository: 7mind/izumi
Pull request #1367 (2021-01-15 13:38:18)

Make `IntegrationCheck[F]` covariant in F to allow effect subtypes same as in roles and resources


Patch:
diff --git a/distage/distage-framework-api/src/main/scala/izumi/distage/framework/model/IntegrationCheck.scala b/distage/distage-framework-api/src/main/scala/izumi/distage/framework/model/IntegrationCheck.scala
index acbffecdb..bb61c3fe2 100644
--- a/distage/distage-framework-api/src/main/scala/izumi/distage/framework/model/IntegrationCheck.scala
+++ b/distage/distage-framework-api/src/main/scala/izumi/distage/framework/model/IntegrationCheck.scala
@@ -8,7 +8,7 @@ import izumi.fundamentals.platform.integration.ResourceCheck
   * startup should stop. When used with [[izumi.distage.testkit.scalatest.DistageSpecScalatest]],
   * if `resourcesAvailable()` method returns a [[izumi.fundamentals.platform.integration.ResourceCheck.Failure]] the test will be skipped.
   */
-trait IntegrationCheck[F[_]] {
+trait IntegrationCheck[+F[_]] {
   /** This method must never throw, on error return [[izumi.fundamentals.platform.integration.ResourceCheck.Failure]] instead * */
   def resourcesAvailable(): F[ResourceCheck]
 }


Test patch:
diff --git a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala
index 0a3d7142e..faefbc1e9 100644
--- a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala
+++ b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/DistageTestExample.scala
@@ -22,13 +22,12 @@ import zio.{Has, Task, ZIO}
 
 trait DistageMemoizeExample[F[_]] extends DistageAbstractScalatestSpec[F] {
   override protected def config: TestConfig = {
-    super
-      .config.copy(
-        memoizationRoots = Map(
-          1 -> Set(DIKey.get[MockCache[F]]),
-          2 -> Set(DIKey.get[Set[SetElement]], DIKey.get[SetCounter]),
-        )
+    super.config.copy(
+      memoizationRoots = Map(
+        1 -> Set(DIKey.get[MockCache[F]]),
+        2 -> Set(DIKey.get[Set[SetElement]], DIKey.get[SetCounter]),
       )
+    )
   }
 }
 
@@ -94,31 +93,30 @@ object DistageTestExampleBase {
 
 abstract class DistageTestExampleBase[F[_]: TagK: DefaultModule](implicit F: QuasiIO[F]) extends Spec1[F] with DistageMemoizeExample[F] {
 
-  override protected def config: TestConfig = super
-    .config.copy(
-      pluginConfig = super.config.pluginConfig.enablePackage("xxx") ++ new PluginDef {
-        make[SetCounter]
-
-        make[SetElement1]
-        make[SetElement2]
-        make[SetElement3]
-        make[SetElement4]
-        make[SetElementRetainer]
-
-        many[SetElement]
-          .weak[SetElement1]
-          .weak[SetElement2]
-          .weak[SetElement3]
-          .weak[SetElement4]
-
-        many[SetElement]
-          .named("unmemoized-set")
-          .weak[SetElement1]
-          .weak[SetElement2]
-          .weak[SetElement3]
-          .weak[SetElement4]
-      }
-    )
+  override protected def config: TestConfig = super.config.copy(
+    pluginConfig = super.config.pluginConfig.enablePackage("xxx") ++ new PluginDef {
+      make[SetCounter]
+
+      make[SetElement1]
+      make[SetElement2]
+      make[SetElement3]
+      make[SetElement4]
+      make[SetElementRetainer]
+
+      many[SetElement]
+        .weak[SetElement1]
+        .weak[SetElement2]
+        .weak[SetElement3]
+        .weak[SetElement4]
+
+      many[SetElement]
+        .named("unmemoized-set")
+        .weak[SetElement1]
+        .weak[SetElement2]
+        .weak[SetElement3]
+        .weak[SetElement4]
+    }
+  )
 
   val XXX_Whitebox_memoizedMockCache = new AtomicReference[MockCache[F]]
 
@@ -215,7 +213,7 @@ abstract class DistageTestExampleBase[F[_]: TagK: DefaultModule](implicit F: Qua
         assert(false)
     }
 
-    "test 6 (should be ignored)" in {
+    "test 6 (should be ignored due to `assume`)" in {
       _: MockCachedUserService[F] =>
         assume(false, "xxx")
     }
@@ -300,14 +298,13 @@ final class ActivationTestTask extends ActivationTest[Task]
 final class ActivationTestIdentity extends ActivationTest[Identity]
 
 abstract class ForcedRootTest[F[_]: QuasiIO: TagK: DefaultModule] extends Spec1[F] {
-  override protected def config: TestConfig = super
-    .config.copy(
-      moduleOverrides = new ModuleDef {
-        make[ForcedRootResource[F]].fromResource[ForcedRootResource[F]]
-        make[ForcedRootProbe]
-      },
-      forcedRoots = Set(DIKey.get[ForcedRootResource[F]]),
-    )
+  override protected def config: TestConfig = super.config.copy(
+    moduleOverrides = new ModuleDef {
+      make[ForcedRootResource[F]].fromResource[ForcedRootResource[F]]
+      make[ForcedRootProbe]
+    },
+    forcedRoots = Set(DIKey.get[ForcedRootResource[F]]),
+  )
 
   "forced root was attached and the acquire effect has been executed" in {
     locatorRef: LocatorRef =>
diff --git a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/IdentityCompatTest.scala b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/IdentityCompatTest.scala
index 1d1d5f22d..84a4e2deb 100644
--- a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/IdentityCompatTest.scala
+++ b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/IdentityCompatTest.scala
@@ -13,7 +13,7 @@ final class IdentityCompatTest extends Spec1[Identity] {
         assert(true)
     }
 
-    "skip (should be ignored)" in {
+    "skip (should be ignored due to `assume`)" in {
       _: QuasiIO[Identity] =>
         assume(false)
     }
diff --git a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/integration/IntegrationTest1Test.scala b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/integration/IntegrationTest1Test.scala
new file mode 100644
index 000000000..eda7e4566
--- /dev/null
+++ b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/integration/IntegrationTest1Test.scala
@@ -0,0 +1,91 @@
+package izumi.distage.testkit.distagesuite.integration
+
+import cats.Applicative
+import distage.{TagK, TagKK}
+import izumi.distage.framework.model.IntegrationCheck
+import izumi.distage.model.definition.{Lifecycle, ModuleDef}
+import izumi.distage.model.effect.QuasiIO
+import izumi.distage.modules.{DefaultModule, DefaultModule2}
+import izumi.distage.testkit.TestConfig
+import izumi.distage.testkit.scalatest.{Spec1, Spec2}
+import izumi.functional.bio.catz._
+import izumi.functional.bio.{Applicative2, ApplicativeError2, F}
+import izumi.fundamentals.platform.integration.ResourceCheck
+import zio.{Task, UIO}
+
+case class TestEnableDisable()
+
+class DisabledTestZIO extends Lifecycle.Simple[TestEnableDisable] with IntegrationCheck[UIO] {
+  override def resourcesAvailable(): UIO[ResourceCheck] =
+    UIO.succeed(ResourceCheck.ResourceUnavailable("This test is intentionally disabled.", None))
+
+  override def acquire: TestEnableDisable = TestEnableDisable()
+  override def release(resource: TestEnableDisable): Unit = ()
+}
+
+class MyDisabledTestZIO extends Spec1[Task] {
+  override def config: TestConfig = super.config.copy(
+    moduleOverrides = new ModuleDef {
+      make[TestEnableDisable].fromResource[DisabledTestZIO]
+    }
+  )
+
+  "My component" should {
+    "this test should be skipped" in { _: TestEnableDisable => Task.fail(new Throwable("Test was not skipped!")).unit }
+  }
+}
+
+class DisabledTestF[F[_]](implicit F: Applicative[F]) extends Lifecycle.Basic[F, TestEnableDisable] with IntegrationCheck[F] {
+  override def resourcesAvailable(): F[ResourceCheck] =
+    F.pure(ResourceCheck.ResourceUnavailable("This test is intentionally disabled.", None))
+
+  override def acquire: F[TestEnableDisable] = F.pure(TestEnableDisable())
+  override def release(resource: TestEnableDisable): F[Unit] = F.unit
+}
+
+abstract class MyDisabledTestF[F0[_]: QuasiIO: TagK: DefaultModule, F[x] <: F0[x]: TagK](implicit F: Applicative[F]) extends Spec1[F0] {
+  override def config: TestConfig = {
+    super.config.copy(
+      moduleOverrides = new ModuleDef {
+        make[TestEnableDisable].fromResource[DisabledTestF[F]]
+        addImplicit[Applicative[F]]
+      }
+    )
+  }
+
+  "My component" should {
+    "this test should be skipped" in { _: TestEnableDisable => F.map[Unit, Unit](F.unit)(_ => throw new Throwable("Test was not skipped!")) }
+  }
+}
+
+final class MyDisabledTestFCats extends MyDisabledTestF[cats.effect.IO, cats.effect.IO]
+final class MyDisabledTestFMonixTask extends MyDisabledTestF[monix.eval.Task, monix.eval.Task]
+final class MyDisabledTestFMonixBIOUIO extends MyDisabledTestF[monix.bio.Task, monix.bio.UIO]
+final class MyDisabledTestFMonixBIOTask extends MyDisabledTestF[monix.bio.Task, monix.bio.Task]
+final class MyDisabledTestFZioUIO extends MyDisabledTestF[zio.Task, zio.UIO]
+final class MyDisabledTestFZioTask extends MyDisabledTestF[zio.Task, zio.Task]
+
+class DisabledTestF2[F[+_, +_]: Applicative2] extends Lifecycle.Basic[F[Nothing, +?], TestEnableDisable] with IntegrationCheck[F[Nothing, ?]] {
+  override def resourcesAvailable(): F[Nothing, ResourceCheck] =
+    F.pure(ResourceCheck.ResourceUnavailable("This test is intentionally disabled.", None))
+  override def acquire: F[Nothing, TestEnableDisable] = F.pure(TestEnableDisable())
+  override def release(resource: TestEnableDisable): F[Nothing, Unit] = F.unit
+}
+
+abstract class MyDisabledTestF2[F[+_, +_]: DefaultModule2: TagKK](implicit F: ApplicativeError2[F]) extends Spec2[F] {
+  override def config: TestConfig = {
+    super.config.copy(
+      moduleOverrides = new ModuleDef {
+        make[TestEnableDisable].fromResource[DisabledTestF2[F]]
+      }
+    )
+  }
+
+  "My component" should {
+    "this test should be skipped" in { _: TestEnableDisable => F.fail(new Throwable("Test was not skipped!")).void }
+  }
+}
+
+final class MyDisabledTestF2MonixBIO extends MyDisabledTestF2[monix.bio.IO]
+final class MyDisabledTestF2ZioIO extends MyDisabledTestF2[zio.IO]
+//final class MyDisabledTestF2ZIOZIOZEnv extends MyDisabledTestF2[zio.ZIO[zio.ZEnv, +?, +?]]