Add tests for user registration routes

This commit is contained in:
William Brawner 2023-10-15 18:43:48 -06:00
parent c3a4e94bd4
commit 589bc18069
4 changed files with 116 additions and 31 deletions

View file

@ -66,7 +66,7 @@ fun Application.userRoutes(
User( User(
name = request.username, name = request.username,
password = request.password.hash(), password = request.password.hash(),
email = if (request.email.isNullOrBlank()) null else request.email email = if (request.email.isNullOrBlank()) "" else request.email
) )
).asResponse() ).asResponse()
) )
@ -96,27 +96,6 @@ fun Application.userRoutes(
?: errorResponse(HttpStatusCode.NotFound) ?: errorResponse(HttpStatusCode.NotFound)
} }
post {
val request = call.receive<UserRequest>()
if (request.username.isNullOrBlank()) {
errorResponse(HttpStatusCode.BadRequest, "Username must not be null or blank")
return@post
}
if (request.password.isNullOrBlank()) {
errorResponse(HttpStatusCode.BadRequest, "Password must not be null or blank")
return@post
}
call.respond(
userRepository.save(
User(
name = request.username,
password = request.password,
email = request.email
)
).asResponse()
)
}
put("/{id}") { put("/{id}") {
val session = call.principal<Session>()!! val session = call.principal<Session>()!!
val request = call.receive<UserRequest>() val request = call.receive<UserRequest>()

View file

@ -1,8 +1,6 @@
package com.wbrawner.twigs.server.api package com.wbrawner.twigs.server.api
import com.wbrawner.twigs.ErrorResponse import com.wbrawner.twigs.*
import com.wbrawner.twigs.LoginRequest
import com.wbrawner.twigs.SessionResponse
import com.wbrawner.twigs.model.User import com.wbrawner.twigs.model.User
import io.ktor.client.call.* import io.ktor.client.call.*
import io.ktor.client.request.* import io.ktor.client.request.*
@ -68,7 +66,7 @@ class UserRouteTest : ApiTest() {
} }
@Test @Test
fun `login with valid credentials returns 200`() = apiTest { client -> fun `login with valid username and password returns 200`() = apiTest { client ->
val users = listOf( val users = listOf(
User(name = "testuser", password = "\$2a\$10\$bETxbFPja1PyXVLybETxb.CWBYzyYdZpmCcA7NSIN8dkdzidt1Xv2"), User(name = "testuser", password = "\$2a\$10\$bETxbFPja1PyXVLybETxb.CWBYzyYdZpmCcA7NSIN8dkdzidt1Xv2"),
User(name = "otheruser", password = "\$2a\$10\$bETxbFPja1PyXVLybETxb..rhfIeOkP4qil1Drj29LDUhBxVkm6fS"), User(name = "otheruser", password = "\$2a\$10\$bETxbFPja1PyXVLybETxb..rhfIeOkP4qil1Drj29LDUhBxVkm6fS"),
@ -86,7 +84,7 @@ class UserRouteTest : ApiTest() {
} }
@Test @Test
fun `login with valid email returns 200`() = apiTest { client -> fun `login with valid email and password returns 200`() = apiTest { client ->
val users = listOf( val users = listOf(
User( User(
name = "testuser", name = "testuser",
@ -106,4 +104,112 @@ class UserRouteTest : ApiTest() {
assertEquals(users.first().id, session.userId) assertEquals(users.first().id, session.userId)
assert(session.token.isNotBlank()) assert(session.token.isNotBlank())
} }
@Test
fun `register with null username returns 400`() = apiTest { client ->
val request = UserRequest(password = "")
val response = client.post("/api/users/register") {
header("Content-Type", "application/json")
setBody(request)
}
assertEquals(HttpStatusCode.BadRequest, response.status)
val errorBody = response.body<ErrorResponse>()
assertEquals("Username must not be null or blank", errorBody.message)
}
@Test
fun `register with empty username returns 400`() = apiTest { client ->
val request = UserRequest(username = "", password = "")
val response = client.post("/api/users/register") {
header("Content-Type", "application/json")
setBody(request)
}
assertEquals(HttpStatusCode.BadRequest, response.status)
val errorBody = response.body<ErrorResponse>()
assertEquals("Username must not be null or blank", errorBody.message)
}
@Test
fun `register with null password returns 400`() = apiTest { client ->
val request = UserRequest(username = "test@example.com")
val response = client.post("/api/users/register") {
header("Content-Type", "application/json")
setBody(request)
}
assertEquals(HttpStatusCode.BadRequest, response.status)
val errorBody = response.body<ErrorResponse>()
assertEquals("Password must not be null or blank", errorBody.message)
}
@Test
fun `register with empty password returns 400`() = apiTest { client ->
val request = UserRequest(username = "test@example.com", password = "")
val response = client.post("/api/users/register") {
header("Content-Type", "application/json")
setBody(request)
}
assertEquals(HttpStatusCode.BadRequest, response.status)
val errorBody = response.body<ErrorResponse>()
assertEquals("Password must not be null or blank", errorBody.message)
}
@Test
fun `register with existing username returns 400`() = apiTest { client ->
val users = listOf(
User(
name = "testuser",
email = "test@example.com",
password = "\$2a\$10\$bETxbFPja1PyXVLybETxb.CWBYzyYdZpmCcA7NSIN8dkdzidt1Xv2"
),
)
users.forEach { userRepository.save(it) }
val request = UserRequest(username = "testuser", password = "password")
val response = client.post("/api/users/register") {
header("Content-Type", "application/json")
setBody(request)
}
assertEquals(HttpStatusCode.BadRequest, response.status)
val errorBody = response.body<ErrorResponse>()
assertEquals("Username or email already taken", errorBody.message)
}
@Test
fun `register with existing email returns 400`() = apiTest { client ->
val users = listOf(
User(
name = "testuser",
email = "test@example.com",
password = "\$2a\$10\$bETxbFPja1PyXVLybETxb.CWBYzyYdZpmCcA7NSIN8dkdzidt1Xv2"
),
)
users.forEach { userRepository.save(it) }
val request = UserRequest(username = "testuser2", email = "test@example.com", password = "password")
val response = client.post("/api/users/register") {
header("Content-Type", "application/json")
setBody(request)
}
assertEquals(HttpStatusCode.BadRequest, response.status)
val errorBody = response.body<ErrorResponse>()
assertEquals("Username or email already taken", errorBody.message)
}
@Test
fun `register with valid username and password returns 200`() = apiTest { client ->
val request = UserRequest("testuser", "testpassword")
val response = client.post("/api/users/register") {
header("Content-Type", "application/json")
setBody(request)
}
assertEquals(HttpStatusCode.OK, response.status)
val userResponse = response.body<UserResponse>()
assert(userResponse.id.isNotBlank())
assertEquals(request.username, userResponse.username)
assertEquals("", userResponse.email)
assertEquals(1, userRepository.entities.size)
val savedUser: User = userRepository.entities.first()
assertEquals(userResponse.id, savedUser.id)
assertEquals(request.username, savedUser.name)
assertEquals("", savedUser.email)
assertEquals("\$2a\$10\$bETxbFPja1PyXVLybETxb.CWBYzyYdZpmCcA7NSIN8dkdzidt1Xv2", savedUser.password)
}
} }

View file

@ -8,7 +8,7 @@ data class User(
override val id: String = randomString(), override val id: String = randomString(),
val name: String = "", val name: String = "",
val password: String = "", val password: String = "",
val email: String? = null val email: String = ""
) : Principal, Identifiable ) : Principal, Identifiable
enum class Permission { enum class Permission {

View file

@ -5,11 +5,11 @@ import com.wbrawner.twigs.storage.UserRepository
class FakeUserRepository : FakeRepository<User>(), UserRepository { class FakeUserRepository : FakeRepository<User>(), UserRepository {
override fun findAll(nameOrEmail: String, password: String?): List<User> { override fun findAll(nameOrEmail: String, password: String?): List<User> {
return entities.filter { return entities.filter { user ->
(it.name.equals(nameOrEmail, ignoreCase = true) || it.email.equals( (user.name.equals(nameOrEmail, ignoreCase = true) || user.email.equals(
nameOrEmail, nameOrEmail,
ignoreCase = true ignoreCase = true
)) && it.password == password )) && password?.let { user.password == it } ?: true
} }
} }