SimpleNotes/persistance/src/test/kotlin/notes/NoteRepositoryImplTest.kt

293 lines
9.2 KiB
Kotlin

package be.simplenotes.persistance.notes
import be.simplenotes.domain.model.*
import be.simplenotes.domain.usecases.repositories.NoteRepository
import be.simplenotes.domain.usecases.repositories.UserRepository
import be.simplenotes.persistance.DbMigrations
import be.simplenotes.persistance.persistanceModule
import be.simplenotes.shared.config.DataSourceConfig
import me.liuwj.ktorm.database.*
import me.liuwj.ktorm.dsl.*
import me.liuwj.ktorm.entity.*
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.flywaydb.core.Flyway
import org.junit.jupiter.api.*
import org.junit.jupiter.api.parallel.ResourceLock
import org.koin.dsl.koinApplication
import org.koin.dsl.module
import java.sql.SQLIntegrityConstraintViolationException
import java.util.*
import javax.sql.DataSource
@ResourceLock("h2")
internal class NoteRepositoryImplTest {
private val testModule = module {
single { dataSourceConfig() }
}
private val koinApp = koinApplication {
modules(persistanceModule, testModule)
}
private fun dataSourceConfig() = DataSourceConfig(
jdbcUrl = "jdbc:h2:mem:regular;DB_CLOSE_DELAY=-1;",
driverClassName = "org.h2.Driver",
username = "h2",
password = "",
maximumPoolSize = 2,
connectionTimeout = 3000
)
private val koin = koinApp.koin
@AfterAll
fun afterAll() = koinApp.close()
private val migration = koin.get<DbMigrations>()
private val dataSource = koin.get<DataSource>()
private val noteRepo = koin.get<NoteRepository>()
private val userRepo = koin.get<UserRepository>()
private val db = koin.get<Database>()
private lateinit var user1: PersistedUser
private lateinit var user2: PersistedUser
@BeforeEach
fun beforeEach() {
Flyway.configure()
.dataSource(dataSource)
.load()
.clean()
migration.migrate()
user1 = userRepo.create(User("1", "1"))!!
user2 = userRepo.create(User("2", "2"))!!
}
private fun createNote(
userId: Int,
title: String,
tags: List<String> = emptyList(),
md: String = "md",
html: String = "html",
): PersistedNote = noteRepo.create(userId, Note(NoteMetadata(title, tags), md, html))
private fun PersistedNote.toPersistedMeta() = PersistedNoteMetadata(meta.title, meta.tags, updatedAt, uuid)
@Nested
@DisplayName("create()")
inner class Create {
@Test
fun `create note for non existing user`() {
val note = Note(NoteMetadata("title", emptyList()), "md", "html")
assertThatThrownBy {
noteRepo.create(1000, note)
}.isInstanceOf(SQLIntegrityConstraintViolationException::class.java)
}
@Test
fun `create note for existing user`() {
val note = Note(NoteMetadata("title", emptyList()), "md", "html")
assertThat(noteRepo.create(user1.id, note))
.isEqualToIgnoringGivenFields(note, "uuid", "updatedAt")
.hasNoNullFieldsOrProperties()
assertThat(db.notes.toList())
.hasSize(1)
.first()
.isEqualToIgnoringGivenFields(note, "uuid", "updatedAt")
}
}
@Nested
@DisplayName("findAll()")
inner class FindAll {
@Test
fun `find all notes`() {
val notes1 = listOf(
createNote(user1.id, "1", listOf("a", "b")),
createNote(user1.id, "2"),
createNote(user1.id, "3", listOf("c"))
)
val notes2 = listOf(
createNote(user2.id, "4")
)
assertThat(noteRepo.findAll(user1.id))
.hasSize(3)
.usingElementComparatorIgnoringFields("updatedAt")
.containsExactlyInAnyOrderElementsOf(
notes1.map { it.toPersistedMeta() }
)
assertThat(noteRepo.findAll(user2.id))
.hasSize(1)
.usingElementComparatorIgnoringFields("updatedAt")
.containsExactlyInAnyOrderElementsOf(
notes2.map { it.toPersistedMeta() }
)
assertThat(noteRepo.findAll(1000)).isEmpty()
}
@Test
fun pagination() {
(50 downTo 1).forEach {
createNote(user1.id, "$it")
}
assertThat(noteRepo.findAll(user1.id, limit = 20, offset = 0))
.hasSize(20)
.allMatch { it.title.toInt() in 1..20 }
assertThat(noteRepo.findAll(user1.id, limit = 20, offset = 20))
.hasSize(20)
.allMatch { it.title.toInt() in 21..40 }
assertThat(noteRepo.findAll(user1.id, limit = 20, offset = 40))
.hasSize(10)
.allMatch { it.title.toInt() in 41..50 }
}
@Test
fun `find all notes with tag`() {
createNote(user1.id, "1", listOf("a", "b"))
createNote(user1.id, "2")
createNote(user1.id, "3", listOf("c"))
createNote(user1.id, "4", listOf("c"))
createNote(user2.id, "5", listOf("c"))
assertThat(noteRepo.findAll(user1.id, tag = "a"))
.hasSize(1)
.first()
.hasFieldOrPropertyWithValue("title", "1")
assertThat(noteRepo.findAll(user1.id, tag = "c"))
.hasSize(2)
assertThat(noteRepo.findAll(user2.id, tag = "c"))
.hasSize(1)
}
}
@Nested
@DisplayName("find() | exists()")
inner class FindExists {
@Test
@Suppress("UNCHECKED_CAST")
fun `find an existing note`() {
createNote(user1.id, "1", listOf("a", "b"))
val note = db.notes.find { it.title eq "1" }!!
.let { entity ->
val tags = db.tags.filter { it.noteUuid eq entity.uuid }.mapColumns { it.name } as List<String>
entity.toPersistedNote(tags)
}
assertThat(noteRepo.find(user1.id, note.uuid))
.isEqualTo(note)
assertThat(noteRepo.exists(user1.id, note.uuid))
.isTrue
}
@Test
fun `find an existing note from the wrong user`() {
val note = createNote(user1.id, "1", listOf("a", "b"))
assertThat(noteRepo.find(user2.id, note.uuid)).isNull()
assertThat(noteRepo.exists(user2.id, note.uuid)).isFalse
}
@Test
fun `find a non existing note`() {
createNote(user1.id, "1", listOf("a", "b"))
val uuid = UUID.randomUUID()
assertThat(noteRepo.find(user1.id, uuid)).isNull()
assertThat(noteRepo.exists(user2.id, uuid)).isFalse
}
}
@Nested
@DisplayName("delete()")
inner class Delete {
@Test
fun `delete an existing note for a user should succeed and then fail`() {
val note = createNote(user1.id, "1", listOf("a", "b"))
assertThat(noteRepo.delete(user1.id, note.uuid))
.isTrue
assertThat(noteRepo.delete(user1.id, note.uuid))
.isFalse
}
@Test
fun `delete an existing note for the wrong user`() {
val note = createNote(user1.id, "1", listOf("a", "b"))
assertThat(noteRepo.delete(1000, note.uuid))
.isFalse
}
}
@Nested
@DisplayName("getTags()")
inner class Tags {
@Test
fun getTags() {
val notes1 = listOf(
createNote(user1.id, "1", listOf("a", "b")),
createNote(user1.id, "2"),
createNote(user1.id, "3", listOf("c", "a"))
)
val notes2 = listOf(
createNote(user2.id, "4", listOf("a"))
)
val user1Tags = notes1.flatMap { it.meta.tags }.toSet()
assertThat(noteRepo.getTags(user1.id))
.containsExactlyInAnyOrderElementsOf(user1Tags)
val user2Tags = notes2.flatMap { it.meta.tags }.toSet()
assertThat(noteRepo.getTags(user2.id))
.containsExactlyInAnyOrderElementsOf(user2Tags)
assertThat(noteRepo.getTags(1000))
.isEmpty()
}
}
@Nested
@DisplayName("update()")
inner class Update {
@Test
fun getTags() {
val note1 = createNote(user1.id, "1", listOf("a", "b"))
val newNote1 = Note(meta = note1.meta, markdown = "new", "new")
assertThat(noteRepo.update(user1.id, note1.uuid, newNote1))
.isNotNull
assertThat(noteRepo.find(user1.id, note1.uuid))
.isEqualToComparingOnlyGivenFields(newNote1, "meta", "markdown", "html")
val note2 = createNote(user1.id, "2")
val newNote2 = Note(meta = note1.meta.copy(tags = listOf("a")), markdown = "new", "new")
assertThat(noteRepo.update(user1.id, note2.uuid, newNote2))
.isNotNull
assertThat(noteRepo.find(user1.id, note2.uuid))
.isEqualToComparingOnlyGivenFields(newNote2, "meta", "markdown", "html")
}
}
}