I'm implementing a Vault client in Scala using Http4s client.
And I'm now starting to write integration tests. So far I have this:
abstract class Utils extends AsyncWordSpec with Matchers {
implicit override def executionContext = ExecutionContext.global
implicit val timer: Timer[IO] = IO.timer(executionContext)
implicit val cs: ContextShift[IO] = IO.contextShift(executionContext)
val vaultUri = Uri.unsafeFromString(Properties.envOrElse("VAULT_ADDR", throw IllegalArgumentException))
val vaultToken = Properties.envOrElse("VAULT_TOKEN", throw IllegalArgumentException)
val clientResource = BlazeClientBuilder[IO](global)
.withCheckEndpointAuthentication(false)
.resource
def usingClient[T](f: VaultClient[IO] => IO[Assertion]): Future[Assertion] = {
clientResource.use { implicit client =>
f(new VaultClient[IO](vaultUri, vaultToken))
}.unsafeToFuture()
}
}
Then my tests look like this (just showing one test):
class SysSpec extends Utils {
"The auth endpoint" should {
"successfully mount an authentication method" in {
usingClient { client =>
for {
result <- client.sys.auth.create("test", AuthMethod(
"approle", "some nice description", config = TuneOptions(defaultLeaseTtl = 60.minutes)
))
} yield result should be (())
}
}
}
}
This approach works, however it doesn't feel right. For each test I'm opening the connection (clientResource.use
) and recreating the VaultClient
.
Is there a way for me to reuse the same connection and client for all the tests in SysSpec.
Please note these are integration tests and not unit tests.
This is the best I could come up with.
abstract class Utils extends AsyncWordSpec with Matchers with BeforeAndAfterAll {
implicit override def executionContext = ExecutionContext.global
implicit val timer: Timer[IO] = IO.timer(executionContext)
implicit val cs: ContextShift[IO] = IO.contextShift(executionContext)
val (httpClient, finalizer) = BlazeClientBuilder[IO](global)
.withCheckEndpointAuthentication(false)
.resource.allocated.unsafeRunSync()
override protected def afterAll(): Unit = finalizer.unsafeRunSync()
private implicit val c = httpClient
val client = new VaultClient[IO](uri"http://[::1]:8200", "the root token fetched from somewhere")
}
Then the tests just use the client directly:
class SysSpec extends Utils {
"The auth endpoint" should {
"successfully mount an authentication method" in {
client.sys.auth.create("test", AuthMethod(
"approle", "some nice description",
config = TuneOptions(defaultLeaseTtl = 60.minutes))
).map(_ shouldBe ()).unsafeToFuture()
}
}
}
My two main problems with this approach are the two unsafeRunSync
s in the code. The first one is to create the client and the second one to clean the resource. However it is a much better approach then repeatedly creating and destroy the client.
I would also like not to use the unsafeToFuture
but that would require ScalaTest to support Cats-Effect directly.