kotest / kotest-extensions-arrow

Kotest extensions for Arrow
Apache License 2.0
39 stars 13 forks source link

[Proposal] Resource Extension / PerProjectListener #189

Closed nomisRev closed 1 year ago

nomisRev commented 2 years ago

This PR introduces an MountableExtension for Resource and a PerProjectListener for Resource inspired by the Test Containers extensions. It also introduces TestResource which we could expose in other ways.

This all works on top of a combinator allocatedCase that I intend to introduce into Arrow Fx (Utils) as a sort of low-level operator to intro with Resource in ways like this.

This would replace the by resource and the removal of runBlocking used in there.

sksamuel commented 2 years ago

We could also consider adding a suspendable install that's only usable from within a test or callback.

On Wed, Jun 29, 2022, 3:15 AM Simon Vergauwen @.***> wrote:

@.**** commented on this pull request.

In kotest-assertions-arrow-fx-coroutines/src/commonMain/kotlin/io/kotest/assertions/arrow/fx/coroutines/ResourceExtension.kt https://github.com/kotest/kotest-extensions-arrow/pull/189#discussion_r909334682 :

+public class ResourceExtension(

Hey @Kantis https://github.com/Kantis,

Thanks for the review and remarks!

The user will always want to interact with A directly, the problem is that install does not support suspend which means it's only safe to access from within TestScope.

I tried to model it in such a way that it's impossible to access it from a non-suspend context, this would then be within the init block. Otherwise we'd have to implicitly use runBlocking under the hood to initialise the Resource, which are in a lot of cases doing blocking I/O, etc.

IIRC ResourceExtension<A, A> : MountableExtension<A, A> would not have this same guarantees in terms of preventing the users to access the A from outside of its initialised state.

The tests here are not a good representation of how it would actually be used in reality, since to tests this I have to expose an "unsafe resource" so we can verify the interactions/state. Normally domain code would expose something like Resource and then the user would install, and use it like so:

fun database(config: HikariConfig): Resource = resource { val hikari = Resource.fromCloseable { HikariDataSource(config) }.bind() Resource({ Database.connect(hikari) }, { db, _ -> db.close() }).bind() } class MySpec : StringSpec({ val testDb = install(database(config).extension()) { configure() }

// testDb.get() not allowed requires supension // runBlocking { testDb.get() } forcefully initialise anyway, while still respecting the LifecycleMode.

beforeSpec { testDb.get() // allowed since this is supports suspension }

afterTest { testDb.get() // Also fine since it also supports suspension }

"test" { testDb().shouldNotBeNull() } })

— Reply to this email directly, view it on GitHub https://github.com/kotest/kotest-extensions-arrow/pull/189#discussion_r909334682, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFVSGUAP5NB7RMOBETJDRLVRQA23ANCNFSM5ZNRG2EA . You are receiving this because your review was requested.Message ID: @.***>

nomisRev commented 2 years ago

@sksamuel I think that would be really valuable.

Alternatively, can the Spec init block become suspend? I assume the Kotest machinery that creates the classes are not within a suspending context?

sksamuel commented 2 years ago

It's because you can call it from the init constructor which isn't suspendable. So we just need to add a variant that is constrained to only be called inside a kotest callback or test. Should be easy enough to do - we can add a SuspendableExtension which plugins can use.

nomisRev commented 1 year ago

@i-walker @sksamuel @Kantis I updated this PR to use LazyMountableExtension from 5.5.x 🥳

It could use another proper review now that the implementation has changed, I am not 100% sure I wired the XXListener correctly. I wanted to support the same LifecycleMode as Kotest has for TestContainerExtension.

nomisRev commented 1 year ago

Thank you for the review Kantis! ❤️