diff --git a/other/di-kodein-advanced/src/KodeinAdvancedApplication.kt b/other/di-kodein-advanced/src/KodeinAdvancedApplication.kt index 020964a1..bd587f53 100644 --- a/other/di-kodein-advanced/src/KodeinAdvancedApplication.kt +++ b/other/di-kodein-advanced/src/KodeinAdvancedApplication.kt @@ -10,7 +10,6 @@ import io.ktor.server.netty.* import kotlinx.html.* import org.kodein.di.* import org.kodein.di.generic.* -import java.util.* /** * Entry point of the embedded-server sample program: @@ -27,18 +26,22 @@ import java.util.* fun main(args: Array) { embeddedServer(Netty, port = 8080) { kodeinApplication { application -> - application.apply { - // This adds automatically Date and Server headers to each response, and would allow you to configure - // additional headers served to each response. - install(DefaultHeaders) - } - - bindSingleton { Users.Repository() } - bindSingleton { Users.Controller(it) } + advancedApplication(application) } }.start(wait = true) } +internal fun Kodein.MainBuilder.advancedApplication(application: Application) { + application.apply { + // This adds automatically Date and Server headers to each response, and would allow you to configure + // additional headers served to each response. + install(DefaultHeaders) + } + + bind() with singleton { Users.Repository() } + bind() with singleton { Users.Controller(kodein) } +} + /** * Users Controller, Router and Model. Can move to several files and packages if required. */ @@ -52,7 +55,7 @@ object Users { /** * [Repository] instance provided by [Kodein] */ - val repository: Repository by instance() + private val repository: IRepository by instance() /** * Registers the routes related to [Users]. @@ -94,16 +97,23 @@ object Users { data class User(val name: String) /** - * [Users.Repository] that will handle operations related to the users on the system. + * Repository that will handle operations related to the users on the system. + */ + interface IRepository { + fun list() : List + } + + /** + * Fake in-memory implementation of [Users.IRepository] for demo purposes. */ - class Repository { + class Repository : IRepository { private val initialUsers = listOf(User("test"), User("demo")) - private val usersByName = LinkedHashMap(initialUsers.associateBy { it.name }) + private val usersByName = initialUsers.associateBy { it.name } /** * Lists the available [Users.User] in this repository. */ - fun list() = usersByName.values.toList() + override fun list() = usersByName.values.toList() } /** @@ -174,7 +184,7 @@ abstract class KodeinController(override val kodein: Kodein) : KodeinAware { /** * Injected dependency with the current [Application]. */ - val application: Application by instance() + private val application: Application by instance() /** * Shortcut to get the url of a [TypedRoute]. @@ -187,13 +197,6 @@ abstract class KodeinController(override val kodein: Kodein) : KodeinAware { abstract fun Routing.registerRoutes() } -/** - * Shortcut for binding singletons to the same type. - */ -inline fun Kodein.MainBuilder.bindSingleton(crossinline callback: (Kodein) -> T) { - bind() with singleton { callback(this@singleton.kodein) } -} - /** * Interface used for identify typed routes annotated with [Location]. */ diff --git a/other/di-kodein-advanced/test/KodeinAdvancedApplicationTest.kt b/other/di-kodein-advanced/test/KodeinAdvancedApplicationTest.kt new file mode 100644 index 00000000..344e0089 --- /dev/null +++ b/other/di-kodein-advanced/test/KodeinAdvancedApplicationTest.kt @@ -0,0 +1,93 @@ +package io.ktor.samples.kodein + +import io.ktor.http.HttpMethod +import io.ktor.http.HttpStatusCode +import io.ktor.server.testing.withTestApplication +import org.kodein.di.generic.bind +import org.kodein.di.generic.singleton +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Integration tests for the [advancedApplication] module from KodeinAdvancedApplication. + */ +class KodeinAdvancedApplicationTest { + + @Test + fun `get user`() = withTestApplication( + { + kodeinApplication { advancedApplication(it) } + } + ) { + handleRequest { method = HttpMethod.Get; uri = "/users/fake" }.apply { + assertEquals(HttpStatusCode.OK, response.status()) + assertEquals( + """ + + + +

fake

+ + + """.trimIndent() + "\n", + response.content + ) + } + } + + @Test + fun `get default users`() = withTestApplication( + { + kodeinApplication { advancedApplication(it) } + } + ) { + handleRequest { method = HttpMethod.Get; uri = "/users/" }.apply { + assertEquals(HttpStatusCode.OK, response.status()) + assertEquals( + """ + + + + + + + """.trimIndent() + "\n", + response.content + ) + } + } + + // Note: a JVM bug prevents us from using `nice test names` when there's a local class defined in it. + @Test + fun testGetFakeUsers() = withTestApplication( + { + class FakeRepository : Users.IRepository { + override fun list() = listOf(Users.User("fake")) + } + kodeinApplication { + advancedApplication(it) + bind(overrides = true) with singleton { FakeRepository() } + } + } + ) { + handleRequest { method = HttpMethod.Get; uri = "/users/" }.apply { + assertEquals(HttpStatusCode.OK, response.status()) + assertEquals( + """ + + + + + + + """.trimIndent() + "\n", + response.content + ) + } + } +}