diff --git a/api/src/extensions/Coroutines.kt b/api/src/extensions/Coroutines.kt index 3985325..6218cd4 100644 --- a/api/src/extensions/Coroutines.kt +++ b/api/src/extensions/Coroutines.kt @@ -6,3 +6,8 @@ import kotlinx.coroutines.* fun ioAsync(block: suspend CoroutineScope.() -> T): Deferred { return CoroutineScope(Dispatchers.IO).async(block = block) } + +suspend inline fun launchIo(crossinline block: () -> T): T = + withContext(Dispatchers.IO) { + block() + } diff --git a/api/src/services/UserService.kt b/api/src/services/UserService.kt index fc93555..3e00482 100644 --- a/api/src/services/UserService.kt +++ b/api/src/services/UserService.kt @@ -1,7 +1,10 @@ package be.vandewalleh.services import be.vandewalleh.entities.User +import be.vandewalleh.extensions.ioAsync +import be.vandewalleh.extensions.launchIo import be.vandewalleh.tables.Users +import kotlinx.coroutines.Deferred import me.liuwj.ktorm.database.* import me.liuwj.ktorm.dsl.* import me.liuwj.ktorm.entity.* @@ -20,8 +23,8 @@ class UserService(override val kodein: Kodein) : KodeinAware { /** * returns a user ID if present or null */ - fun getUserId(userEmail: String): Int? { - return db.from(Users) + suspend fun getUserId(userEmail: String): Int? = launchIo { + db.from(Users) .select(Users.id) .where { Users.email eq userEmail } .map { it[Users.id] } @@ -31,8 +34,8 @@ class UserService(override val kodein: Kodein) : KodeinAware { /** * returns a user email and password from it's username if found or null */ - fun getFromUsername(username: String): User? { - return db.from(Users) + suspend fun getFromUsername(username: String): User? = launchIo { + db.from(Users) .select(Users.email, Users.password, Users.id) .where { Users.username eq username } .map { row -> @@ -41,22 +44,22 @@ class UserService(override val kodein: Kodein) : KodeinAware { .firstOrNull() } - fun userExists(username: String, email: String): Boolean { - return db.from(Users) + suspend fun userExists(username: String, email: String): Boolean = launchIo { + db.from(Users) .select(Users.id) .where { (Users.username eq username) or (Users.email eq email) } .firstOrNull() != null } - fun userExists(userId: Int): Boolean { - return db.from(Users) + suspend fun userExists(userId: Int): Boolean = launchIo { + db.from(Users) .select(Users.id) .where { Users.id eq userId } .firstOrNull() != null } - fun getUserInfo(id: Int): User? { - return db.from(Users) + suspend fun getUserInfo(id: Int): User? = launchIo { + db.from(Users) .select(Users.email, Users.username) .where { Users.id eq id } .map { Users.createEntity(it) } @@ -67,25 +70,27 @@ class UserService(override val kodein: Kodein) : KodeinAware { * create a new user * password should already be hashed */ - fun createUser(username: String, email: String, hashedPassword: String): User? { - try { - db.useTransaction { - val newUser = User { - this.username = username - this.email = email - this.password = hashedPassword - this.createdAt = LocalDateTime.now() - } + suspend fun createUser(username: String, email: String, hashedPassword: String): User? { + val newUser = User { + this.username = username + this.email = email + password = hashedPassword + createdAt = LocalDateTime.now() + } - db.sequenceOf(Users).add(newUser) - return newUser + return try { + launchIo { + db.useTransaction { + db.sequenceOf(Users).add(newUser) + newUser + } } } catch (e: SQLIntegrityConstraintViolationException) { - return null + null } } - fun updateUser(userId: Int, username: String, email: String, hashedPassword: String) { + suspend fun updateUser(userId: Int, username: String, email: String, hashedPassword: String): Unit = launchIo { db.useTransaction { db.update(Users) { it.username to username @@ -98,14 +103,17 @@ class UserService(override val kodein: Kodein) : KodeinAware { } } - fun deleteUser(userId: Int): Boolean { + suspend fun deleteUser(userId: Int): Boolean = launchIo { db.useTransaction { - return when (db.delete(Users) { it.id eq userId }) { - 1 -> true - 0 -> false - else -> error("??") - } + db.delete(Users) { it.id eq userId } + } + }.let { + when (it) { + 1 -> true + 0 -> false + else -> error("??") } } + } diff --git a/api/test/integration/routing/AuthControllerKtTest.kt b/api/test/integration/routing/AuthControllerKtTest.kt index 338f933..11eb503 100644 --- a/api/test/integration/routing/AuthControllerKtTest.kt +++ b/api/test/integration/routing/AuthControllerKtTest.kt @@ -10,7 +10,8 @@ import com.auth0.jwt.JWT import com.auth0.jwt.algorithms.Algorithm import io.ktor.http.* import io.ktor.server.testing.* -import io.mockk.every +import io.mockk.coEvery +import io.mockk.coVerify import io.mockk.mockk import io.mockk.verify import org.amshove.kluent.* @@ -36,9 +37,9 @@ class AuthControllerKtTest { } user["id"] = 1 - every { userService.getFromUsername("existing") } returns user - every { userService.userExists(1) } returns true - every { userService.getUserInfo(1) } returns User { + coEvery { userService.getFromUsername("existing") } returns user + coEvery { userService.userExists(1) } returns true + coEvery { userService.getUserInfo(1) } returns User { username = "existing" email = "existing@mail.com" } @@ -48,12 +49,12 @@ class AuthControllerKtTest { username = "wrong" } user["id"] = 2 - every { userService.getFromUsername("wrong") } returns user2 + coEvery { userService.getFromUsername("wrong") } returns user2 - every { userService.getFromUsername("notExisting") } returns null + coEvery { userService.getFromUsername("notExisting") } returns null - every { userService.userExists(3) } returns false - every { userService.getUserInfo(3) } returns null + coEvery { userService.userExists(3) } returns false + coEvery { userService.getUserInfo(3) } returns null } @@ -78,7 +79,7 @@ class AuthControllerKtTest { } } - verify { userService.getFromUsername("existing") } + coVerify { userService.getFromUsername("existing") } res.status() `should be equal to` HttpStatusCode.OK val jsonObject = JSONObject(res.content) @@ -106,7 +107,7 @@ class AuthControllerKtTest { } } - verify { userService.getFromUsername("wrong") } + coVerify { userService.getFromUsername("wrong") } res.status() `should be equal to` HttpStatusCode.Unauthorized res.content `should strictly be equal to json` """{msg: "Unauthorized"}""" @@ -121,7 +122,7 @@ class AuthControllerKtTest { } } - verify { userService.getFromUsername("notExisting") } + coVerify { userService.getFromUsername("notExisting") } res.status() `should be equal to` HttpStatusCode.Unauthorized res.content `should strictly be equal to json` """{msg: "Unauthorized"}""" @@ -154,7 +155,7 @@ class AuthControllerKtTest { val jsonObject = JSONObject(res.content) jsonObject.keyList() `should be equal to` listOf("token", "refreshToken") - verify { userService.userExists(1) } + coVerify { userService.userExists(1) } res.status() `should be equal to` HttpStatusCode.OK } @@ -169,7 +170,7 @@ class AuthControllerKtTest { } } - verify { userService.userExists(3) } + coVerify { userService.userExists(3) } res.status() `should be equal to` HttpStatusCode.Unauthorized res.content `should strictly be equal to json` """{msg: "Unauthorized"}""" } diff --git a/api/test/integration/routing/UserControllerKtTest.kt b/api/test/integration/routing/UserControllerKtTest.kt index 04fe9d1..b2e1955 100644 --- a/api/test/integration/routing/UserControllerKtTest.kt +++ b/api/test/integration/routing/UserControllerKtTest.kt @@ -7,7 +7,7 @@ import be.vandewalleh.module import be.vandewalleh.services.UserService import io.ktor.http.* import io.ktor.server.testing.* -import io.mockk.every +import io.mockk.coEvery import io.mockk.mockk import org.amshove.kluent.* import org.junit.jupiter.api.* @@ -24,31 +24,31 @@ class UserControllerKtTest { init { // new user - every { userService.userExists("new", "new@test.com") } returns false - every { userService.createUser("new", "new@test.com", any()) } returns User { + coEvery { userService.userExists("new", "new@test.com") } returns false + coEvery { userService.createUser("new", "new@test.com", any()) } returns User { this.createdAt = LocalDateTime.now() this.username = "new" this.email = "new@test.com" } // existing user - every { userService.userExists("existing", "existing@test.com") } returns true - every { userService.createUser("existing", "existing@test.com", any()) } returns null - every { userService.getUserId("existing@test.com") } returns 1 - every { userService.deleteUser(1) } returns true andThen false + coEvery { userService.userExists("existing", "existing@test.com") } returns true + coEvery { userService.createUser("existing", "existing@test.com", any()) } returns null + coEvery { userService.getUserId("existing@test.com") } returns 1 + coEvery { userService.deleteUser(1) } returns true andThen false // modified user - every { userService.userExists("modified", "modified@test.com") } returns true - every { + coEvery { userService.userExists("modified", "modified@test.com") } returns true + coEvery { userService.userExists( and(not("modified"), not("existing")), and(not("modified@test.com"), not("existing@test.com")) ) } returns false - every { userService.userExists(1) } returns true - every { userService.createUser("modified", "modified@test.com", any()) } returns null - every { userService.getUserId("modified@test.com") } returns 1 - every { userService.updateUser(1, "ThisIsMyNewName", "ThisIsMyNewName@mail.com", any()) } returns Unit + coEvery { userService.userExists(1) } returns true + coEvery { userService.createUser("modified", "modified@test.com", any()) } returns null + coEvery { userService.getUserId("modified@test.com") } returns 1 + coEvery { userService.updateUser(1, "ThisIsMyNewName", "ThisIsMyNewName@mail.com", any()) } returns Unit } diff --git a/api/test/integration/services/UserServiceTest.kt b/api/test/integration/services/UserServiceTest.kt index 4cb9b4c..c758707 100644 --- a/api/test/integration/services/UserServiceTest.kt +++ b/api/test/integration/services/UserServiceTest.kt @@ -3,6 +3,7 @@ package integration.services import be.vandewalleh.mainModule import be.vandewalleh.migrations.Migration import be.vandewalleh.services.UserService +import kotlinx.coroutines.runBlocking import org.amshove.kluent.* import org.junit.jupiter.api.* import org.kodein.di.Kodein @@ -36,34 +37,41 @@ class UserServiceTest { @Test @Order(1) fun `test create user`() { - val username = "hubert" - val email = "a@a" - val password = "password" - println(userService.createUser(username, email, password)) + runBlocking { + val username = "hubert" + val email = "a@a" + val password = "password" - val id = userService.getUserId(email) - id `should not be` null + userService.createUser(username, email, password) + val id = userService.getUserId(email) + id `should not be` null - userService.getUserInfo(id!!)!!.let { - it.username `should be equal to` username - it.email `should be equal to` email + userService.getUserInfo(id!!)!!.let { + it.username `should be equal to` username + it.email `should be equal to` email + } } } @Test @Order(2) fun `test create same user`() { - userService.createUser(username = "hubert", hashedPassword = "password", email = "a@a") `should be` null + runBlocking { + userService.createUser(username = "hubert", hashedPassword = "password", email = "a@a") `should be` null + } } @Test @Order(3) fun `test delete user`() { - val email = "a@a" - val id = userService.getUserId(email)!! - userService.deleteUser(id) + runBlocking { + val email = "a@a" + val id = userService.getUserId(email)!! + userService.deleteUser(id) - userService.getUserId(email) `should be` null - userService.getUserInfo(id) `should be` null + userService.getUserId(email) `should be` null + userService.getUserInfo(id) `should be` null + } } + }