Created
December 16, 2019 19:03
-
-
Save colomboe/26a480ebd58130decc6ba8bba5b55272 to your computer and use it in GitHub Desktop.
Porting of "Simple example of testing with ZIO environment" to Kotlin with KIO
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Porting of https://gist.github.com/jdegoes/dd66656382247dc5b7228fb0f2cb97c8 | |
import it.msec.kio.* | |
import it.msec.kio.common.tuple.T | |
import it.msec.kio.common.tuple.T2 | |
import it.msec.kio.common.tuple.T3 | |
import it.msec.kio.ref.Ref | |
import it.msec.kio.result.Failure | |
import it.msec.kio.result.Result | |
import it.msec.kio.result.Success | |
import it.msec.kio.runtime.RuntimeSuspended | |
typealias UserID = String | |
data class UserProfile(val name: String) | |
// The database module: | |
interface Database { | |
// The database module contains the database service: | |
interface Service { | |
fun lookup(id: UserID): Task<UserProfile> | |
fun update(id: UserID, profile: UserProfile): Task<Unit> | |
} | |
val database: Service | |
} | |
// The logger module: | |
interface Logger { | |
// The logger module contains the logger service: | |
interface Service { | |
fun info(id: String): Task<Unit> | |
} | |
val logger: Service | |
} | |
// A concurrent-safe test database service, which uses a `Ref` to keep track | |
// of changes to the test database state: | |
class DatabaseTestService(val ref: Ref<State>) : Database.Service { | |
// The database state, which keeps track of the data as well as a log of | |
// database operations performed against the database: | |
data class State(val map: Map<UserID, UserProfile>, val ops: List<String>) { | |
fun log(op: String): State = copy(ops = listOf(op) + ops) | |
fun lookup(id: UserID): T2<State, UserProfile?> = | |
T(log("Lookup(${id})"), map[id]) | |
fun update(id: UserID, profile: UserProfile): State = | |
copy(map = map + (id to profile)).log("Update(${id}, ${profile})") | |
} | |
override fun lookup(id: UserID): Task<UserProfile> = | |
ref.modify { it.lookup(id) }.flatMap { p -> unsafe { p!! } } | |
override fun update(id: UserID, profile: UserProfile): Task<Unit> = | |
ref.update { it.update(id, profile) }.map { Unit } | |
} | |
// A concurrent-safe test logger service, which uses a `Ref` to keep track | |
// of log output: | |
class LoggerTestService(val ref: Ref<List<String>>) : Logger.Service { | |
override fun info(line: String): Task<Unit> = ref.update { it + line } | |
} | |
interface DatabaseWithLogger : Database, Logger | |
// A helper function to run a test scenario, and extract out test data. | |
// This function can be used many times across many unit tests. | |
fun <E, A> testScenario(state: DatabaseTestService.State, eff: KIO<DatabaseWithLogger, E, A>): UIO<T3<Result<E, A>, DatabaseTestService.State, List<String>>> { | |
val databaseRef = Ref(state) | |
val loggerRef = Ref(emptyList<String>()) | |
val env = object : DatabaseWithLogger { | |
override val database: Database.Service = DatabaseTestService(databaseRef) | |
override val logger: Logger.Service = LoggerTestService(loggerRef) | |
} | |
return eff.provide(env) | |
.fold(::Failure, ::Success) | |
.flatMapT { databaseRef.get() } | |
.flatMapT { loggerRef.get() } | |
} | |
// An example program that uses database and logger modules: | |
fun <R> lookedUpProfile(): RIO<R, UserProfile> where R : Database, R : Logger = ask { | |
with(it) { | |
database.lookup("abc") | |
.peek { profile -> logger.info(profile.name) } | |
} | |
} | |
// Running a test scenario and unsafely executing it to see what happens: | |
fun main() { | |
val initialState = DatabaseTestService.State(mapOf("abc" to UserProfile("testName")), emptyList()) | |
val v = testScenario(initialState, lookedUpProfile()) | |
val result = RuntimeSuspended.unsafeRunSync(v) | |
println(result) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment