diff --git a/api/pom.xml b/api/pom.xml
index 098b0a5..4cc60f5 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -161,6 +161,24 @@
1.0.2
test
+
+ io.ktor
+ ktor-server-tests
+ ${ktor_version}
+ test
+
+
+ org.testcontainers
+ mariadb
+ 1.14.3
+ test
+
+
+ org.amshove.kluent
+ kluent
+ 1.61
+ test
+
${project.basedir}/src
@@ -174,12 +192,7 @@
org.apache.maven.plugins
maven-surefire-plugin
-
-
- Test*
- *Test
-
-
+ 3.0.0-M4
maven-compiler-plugin
@@ -210,7 +223,7 @@
enable
- ${project.basedir}/src/test
+ ${project.basedir}/test
diff --git a/api/resources/db/migration/V7__Update_constraints_cascade_delete.sql b/api/resources/db/migration/V7__Update_constraints_cascade_delete.sql
index 48bb1f5..ed821bf 100644
--- a/api/resources/db/migration/V7__Update_constraints_cascade_delete.sql
+++ b/api/resources/db/migration/V7__Update_constraints_cascade_delete.sql
@@ -1,19 +1,19 @@
-- ON DELETE -> CASCADE
ALTER TABLE `Notes`
- DROP CONSTRAINT `Notes_ibfk_1`;
+ DROP CONSTRAINT IF EXISTS `Notes_ibfk_1`;
ALTER TABLE `Notes`
ADD FOREIGN KEY (`user_id`) REFERENCES `Users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT;
ALTER TABLE `Chapters`
- DROP CONSTRAINT `Chapters_ibfk_1`;
+ DROP CONSTRAINT IF EXISTS `Chapters_ibfk_1`;
ALTER TABLE `Chapters`
ADD FOREIGN KEY (`note_id`) REFERENCES `Notes` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT;
ALTER TABLE `Tags`
- DROP CONSTRAINT `Tags_ibfk_1`;
+ DROP CONSTRAINT IF EXISTS `Tags_ibfk_1`;
ALTER TABLE `Tags`
- ADD FOREIGN KEY (`note_id`) REFERENCES `Notes` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT;
\ No newline at end of file
+ ADD FOREIGN KEY (`note_id`) REFERENCES `Notes` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT;
diff --git a/api/resources/logback.xml b/api/resources/logback.xml
index d38ae26..b9c2bba 100644
--- a/api/resources/logback.xml
+++ b/api/resources/logback.xml
@@ -12,4 +12,6 @@
+
+
diff --git a/api/src/routing/UserController.kt b/api/src/routing/UserController.kt
index f8f94e3..05245ae 100644
--- a/api/src/routing/UserController.kt
+++ b/api/src/routing/UserController.kt
@@ -2,7 +2,6 @@ package be.vandewalleh.routing
import be.vandewalleh.extensions.respondStatus
import be.vandewalleh.extensions.userId
-import be.vandewalleh.services.UserDto
import be.vandewalleh.services.UserService
import io.ktor.application.*
import io.ktor.auth.*
@@ -26,9 +25,7 @@ fun Routing.user(kodein: Kodein) {
val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt())
- userService.createUser(
- UserDto(user.username, user.email, hashedPassword)
- )
+ userService.createUser(user.username, user.email, hashedPassword)
call.respondStatus(HttpStatusCode.Created)
}
@@ -42,10 +39,7 @@ fun Routing.user(kodein: Kodein) {
val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt())
- userService.updateUser(
- call.userId(),
- UserDto(user.username, user.email, hashedPassword)
- )
+ userService.updateUser(call.userId(), user.username, user.email, hashedPassword)
call.respondStatus(HttpStatusCode.OK)
}
@@ -57,5 +51,6 @@ fun Routing.user(kodein: Kodein) {
}
}
+}
-}
\ No newline at end of file
+private data class UserDto(val username: String, val email: String, val password: String)
diff --git a/api/src/services/UserService.kt b/api/src/services/UserService.kt
index 0ed10f9..8a5f05a 100644
--- a/api/src/services/UserService.kt
+++ b/api/src/services/UserService.kt
@@ -8,6 +8,7 @@ import me.liuwj.ktorm.entity.*
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware
import org.kodein.di.generic.instance
+import java.sql.SQLIntegrityConstraintViolationException
import java.time.LocalDateTime
/**
@@ -28,19 +29,14 @@ class UserService(override val kodein: Kodein) : KodeinAware {
}
/**
- * returns a user email and password from it's email if found or null
+ * returns a user email and password from it's username if found or null
*/
- fun getFromUsername(username: String): UserSchema? {
+ fun getFromUsername(username: String): User? {
return db.from(Users)
.select(Users.email, Users.password, Users.id)
.where { Users.username eq username }
.map { row ->
- UserSchema(
- row[Users.id]!!,
- username,
- row[Users.email]!!,
- row[Users.password]!!
- )
+ Users.createEntity(row)
}
.firstOrNull()
}
@@ -59,11 +55,11 @@ class UserService(override val kodein: Kodein) : KodeinAware {
.firstOrNull() != null
}
- fun getUserInfo(id: Int): UserInfoDto? {
+ fun getUserInfo(id: Int): User? {
return db.from(Users)
.select(Users.email, Users.username)
.where { Users.id eq id }
- .map { UserInfoDto(it[Users.username]!!, it[Users.email]!!) }
+ .map { Users.createEntity(it) }
.firstOrNull()
}
@@ -71,25 +67,30 @@ class UserService(override val kodein: Kodein) : KodeinAware {
* create a new user
* password should already be hashed
*/
- fun createUser(user: UserDto) {
- db.useTransaction {
- val newUser = User {
- this.username = user.username
- this.email = user.email
- this.password = user.password
- this.createdAt = LocalDateTime.now()
- }
+ 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()
+ }
- db.sequenceOf(Users).add(newUser)
+ db.sequenceOf(Users).add(newUser)
+ return newUser
+ }
+ } catch (e: SQLIntegrityConstraintViolationException) {
+ return null
}
}
- fun updateUser(userId: Int, user: UserDto) {
+ fun updateUser(userId: Int, username: String, email: String, hashedPassword: String) {
db.useTransaction {
db.update(Users) {
- it.username to user.username
- it.email to user.email
- it.password to user.password
+ it.username to username
+ it.email to email
+ it.password to hashedPassword
where {
it.id eq userId
}
@@ -104,6 +105,3 @@ class UserService(override val kodein: Kodein) : KodeinAware {
}
}
-data class UserSchema(val id: Int, val username: String, val email: String, val password: String)
-data class UserDto(val username: String, val email: String, val password: String)
-data class UserInfoDto(val username: String, val email: String)
diff --git a/api/test/FakeDataTest.kt b/api/test/FakeDataTest.kt
deleted file mode 100644
index 3d8f6a4..0000000
--- a/api/test/FakeDataTest.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-import be.vandewalleh.services.ChapterDTO
-import be.vandewalleh.services.FullNoteCreateDTO
-import be.vandewalleh.services.NotesService
-import com.github.javafaker.Faker
-import org.junit.jupiter.api.Test
-import org.kodein.di.generic.instance
-
-class FakeDataTest {
- val notesService by kodein.instance()
-
- @Test
- fun addNotes() {
- val faker = Faker()
- val title = faker.hobbit().quote()
-
- val tags = listOf(
- faker.beer().name(),
- faker.beer().yeast()
- )
-
- val chapters = listOf(
- ChapterDTO(
- faker.animal().name(),
- faker.lorem().paragraph()
- ),
- ChapterDTO(
- faker.animal().name(),
- faker.lorem().paragraph()
- )
- )
-
- val note = FullNoteCreateDTO(title, tags, chapters)
-
- notesService.createNote(1, note)
- }
-
-}
diff --git a/api/test/NotesRetrievePerformanceTest.kt b/api/test/NotesRetrievePerformanceTest.kt
deleted file mode 100644
index 35fe610..0000000
--- a/api/test/NotesRetrievePerformanceTest.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-import be.vandewalleh.services.NotesService
-import be.vandewalleh.services.UserService
-import com.hekeki.huckleberry.Benchmark
-import com.hekeki.huckleberry.BenchmarkRunner
-import com.hekeki.huckleberry.BenchmarkTest
-import com.hekeki.huckleberry.TimeUnit
-import com.zaxxer.hikari.HikariConfig
-import com.zaxxer.hikari.HikariDataSource
-import me.liuwj.ktorm.database.*
-import org.junit.jupiter.api.Assertions.*
-import org.junit.jupiter.api.Test
-import org.kodein.di.Kodein
-import org.kodein.di.generic.bind
-import org.kodein.di.generic.instance
-import org.kodein.di.generic.singleton
-
-
-val hikariConfig = HikariConfig().apply {
- jdbcUrl = "jdbc:mariadb://localhost:3306/notes"
- username = "notes"
- password = "notes"
-}
-
-val dataSource = HikariDataSource(hikariConfig)
-
-val db = Database.Companion.connect(dataSource)
-
-val kodein = Kodein {
- bind() with singleton { db }
- bind() with singleton { UserService(this.kodein) }
- bind() with singleton { NotesService(this.kodein) }
-}
-
-val notesService by kodein.instance()
-
-@Benchmark(threads = 1, iterations = 30, warmup = true, warmupIterations = 1000, timeUnit = TimeUnit.MILLIS)
-class RetrieveNotesBenchmarkTest : BenchmarkTest {
-
- override fun execute() {
- notesService.getNotes(15)
- }
-
- @Test
- fun compute() {
- val benchmarkResult = BenchmarkRunner(RetrieveNotesBenchmarkTest::class.java).run()
- assertTrue(benchmarkResult.median(2, TimeUnit.MILLIS))
- assertTrue(benchmarkResult.maxTime(4, TimeUnit.MILLIS))
- }
-
-}
diff --git a/api/test/services/UserServiceTest.kt b/api/test/services/UserServiceTest.kt
new file mode 100644
index 0000000..3cfc9a4
--- /dev/null
+++ b/api/test/services/UserServiceTest.kt
@@ -0,0 +1,88 @@
+package services
+
+import be.vandewalleh.migrations.Migration
+import be.vandewalleh.services.NotesService
+import be.vandewalleh.services.UserService
+import com.zaxxer.hikari.HikariConfig
+import com.zaxxer.hikari.HikariDataSource
+import me.liuwj.ktorm.database.*
+import org.amshove.kluent.*
+import org.junit.jupiter.api.*
+import org.kodein.di.Kodein
+import org.kodein.di.generic.bind
+import org.kodein.di.generic.instance
+import org.kodein.di.generic.singleton
+import org.testcontainers.containers.MariaDBContainer
+import javax.sql.DataSource
+
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
+class UserServiceTest {
+
+ class KMariadbContainer : MariaDBContainer()
+
+ private val mariadb = KMariadbContainer().apply {
+ this.start()
+ }
+
+ private val hikariConfig = HikariConfig().apply {
+ jdbcUrl = mariadb.jdbcUrl
+ username = mariadb.username
+ password = mariadb.password
+ }
+
+ private val dataSource = HikariDataSource(hikariConfig)
+
+ private val db = Database.Companion.connect(dataSource)
+
+ private val kodein = Kodein {
+ bind() with singleton { dataSource }
+ bind() with singleton { db }
+ bind() with singleton { Migration(this.kodein) }
+ bind() with singleton { UserService(this.kodein) }
+ bind() with singleton { NotesService(this.kodein) }
+ }
+
+ private val migration by kodein.instance()
+
+ init {
+ migration.migrate()
+ }
+
+ private val userService by kodein.instance()
+
+ @Test
+ @Order(1)
+ fun `test create user`() {
+ val username = "hubert"
+ val email = "a@a"
+ val password = "password"
+ println(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
+ }
+ }
+
+ @Test
+ @Order(2)
+ fun `test create same user`() {
+ 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)
+
+ userService.getUserId(email) `should be` null
+ userService.getUserInfo(id) `should be` null
+ }
+}