package be.simplenotes.persistence import be.simplenotes.persistence.notes.* import be.simplenotes.persistence.repositories.NoteRepository import be.simplenotes.persistence.repositories.UserRepository import be.simplenotes.persistence.users.createFakeUser import be.simplenotes.types.ExportedNote import be.simplenotes.types.PersistedNote import be.simplenotes.types.PersistedUser import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.ktorm.database.Database import org.ktorm.dsl.eq import org.ktorm.entity.filter import org.ktorm.entity.find import org.ktorm.entity.mapColumns import org.ktorm.entity.toList import java.sql.SQLIntegrityConstraintViolationException internal class NoteRepositoryImplTest : DbTest() { private lateinit var noteRepo: NoteRepository private lateinit var userRepo: UserRepository private lateinit var db: Database private lateinit var user1: PersistedUser private lateinit var user2: PersistedUser @BeforeEach fun insertUsers() { noteRepo = beanContext.getBean() userRepo = beanContext.getBean() db = beanContext.getBean() user1 = userRepo.createFakeUser()!! user2 = userRepo.createFakeUser()!! } @Nested @DisplayName("create()") inner class Create { @Test fun `create note for non existing user`() { val note = fakeNote() assertThatThrownBy { noteRepo.create(1000, note) }.isInstanceOf(SQLIntegrityConstraintViolationException::class.java) } @Test fun `create note for existing user`() { val note = fakeNote() assertThat(noteRepo.create(user1.id, note)) .hasNoNullFieldsOrProperties() .usingRecursiveComparison() .ignoringFields("uuid", "updatedAt", "public") .isEqualTo(note) assertThat(db.notes.toList()) .hasSize(1) .first() .usingRecursiveComparison() .ignoringFields("uuid", "updatedAt", "public") .isEqualTo(note) } } @Nested @DisplayName("findAll()") inner class FindAll { @Test fun `find all notes`() { val notes1 = noteRepo.insertFakeNotes(user1, count = 3) val notes2 = listOf(noteRepo.insertFakeNote(user2)) 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 `find all notes with tag`() { with(noteRepo) { insertFakeNote(user1, "1", listOf("a", "b")) insertFakeNote(user1, "2", tags = emptyList()) insertFakeNote(user1, "3", listOf("c")) insertFakeNote(user1, "4", listOf("c")) insertFakeNote(user2, "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`() { val fakeNote = noteRepo.insertFakeNote(user1) val note = db.notes.find { Notes.title eq fakeNote.title }!! .let { entity -> val tags = db.tags.filter { be.simplenotes.persistence.Tags.noteUuid eq entity.uuid }.mapColumns { be.simplenotes.persistence.Tags.name } as List PersistedNote( uuid = entity.uuid, title = entity.title, tags = tags, markdown = entity.markdown, html = entity.html, updatedAt = entity.updatedAt, public = entity.public, ) } 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 = noteRepo.insertFakeNote(user1) assertThat(noteRepo.find(user2.id, note.uuid)).isNull() assertThat(noteRepo.exists(user2.id, note.uuid)).isFalse } @Test fun `find a non existing note`() { noteRepo.insertFakeNote(user1) val uuid = fakeUuid() 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 = noteRepo.insertFakeNote(user1) assertThat(noteRepo.delete(user1.id, note.uuid)).isTrue } @Test fun `delete an existing note for the wrong user`() { val note = noteRepo.insertFakeNote(user1) assertThat(noteRepo.delete(1000, note.uuid)).isFalse } } @Nested @DisplayName("getTags()") inner class Tags { @Test fun getTags() { val notes1 = noteRepo.insertFakeNotes(user1, count = 3) val notes2 = noteRepo.insertFakeNotes(user2, count = 1) val user1Tags = notes1.flatMap { it.tags }.toSet() assertThat(noteRepo.getTags(user1.id)) .containsExactlyInAnyOrderElementsOf(user1Tags) val user2Tags = notes2.flatMap { it.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 = noteRepo.insertFakeNote(user1) val newNote1 = fakeNote() assertThat(noteRepo.update(user1.id, note1.uuid, newNote1)).isNotNull assertThat(noteRepo.find(user1.id, note1.uuid)) .usingRecursiveComparison() .ignoringFields("updatedAt", "uuid", "public") .isEqualTo(newNote1) val note2 = noteRepo.insertFakeNote(user1) val newNote2 = fakeNote().copy(tags = tagGenerator().take(3).toList()) assertThat(noteRepo.update(user1.id, note2.uuid, newNote2)) .isNotNull assertThat(noteRepo.find(user1.id, note2.uuid)) .usingRecursiveComparison() .ignoringFields("updatedAt", "uuid", "public") .isEqualTo(newNote2) } } @Nested inner class Trash { @Test fun `trashed noted should be restored`() { val note1 = noteRepo.insertFakeNote(user1, "1", listOf("a", "b")) assertThat(noteRepo.delete(user1.id, note1.uuid, permanent = false)) .isTrue val isDeleted = db.notes .find { Notes.uuid eq note1.uuid } ?.deleted assertThat(isDeleted).`as`("Check that Notes.deleted is true").isTrue assertThat(noteRepo.restore(user1.id, note1.uuid)).isTrue val isDeleted2 = db.notes .find { Notes.uuid eq note1.uuid } ?.deleted assertThat(isDeleted2).`as`("Check that Notes.deleted is false after restore()").isFalse } @Test fun `permanent delete`() { val note = noteRepo.insertFakeNote(user1) assertThat(noteRepo.delete(user1.id, note.uuid, permanent = true)).isTrue assertThat(noteRepo.restore(user1.id, note.uuid)).isFalse } } @Test fun count() { assertThat(noteRepo.count(user1.id)).isEqualTo(0) noteRepo.insertFakeNotes(user1, count = 10) assertThat(noteRepo.count(user1.id)).isEqualTo(10) } @Test fun countWithTag() { noteRepo.insertFakeNote(user1, tags = listOf("a", "b")) noteRepo.insertFakeNote(user1, tags = emptyList()) noteRepo.insertFakeNote(user1, tags = listOf("a")) noteRepo.insertFakeNote(user1, tags = emptyList()) assertThat(noteRepo.count(user1.id, tag = "a")).isEqualTo(2) } @Test fun export() { val notes = noteRepo.insertFakeNotes(user1, count = 4) noteRepo.delete(user1.id, notes.first().uuid, permanent = false) val export = noteRepo.export(user1.id) val expected = notes.mapIndexed { i, n -> ExportedNote( title = n.title, tags = n.tags, markdown = n.markdown, html = n.html, updatedAt = n.updatedAt, trash = i == 0, ) } assertThat(export) .usingElementComparatorIgnoringFields("updatedAt") .containsExactlyInAnyOrderElementsOf(expected) } @Test fun findAllDetails() { val notes = noteRepo.insertFakeNotes(user1, count = 10) val res = noteRepo.findAllDetails(user1.id) assertThat(res) .usingElementComparatorIgnoringFields("updatedAt") .containsExactlyInAnyOrderElementsOf(notes) } @Test fun access() { val n = noteRepo.insertFakeNote(user1) noteRepo.makePublic(user1.id, n.uuid) assertThat(noteRepo.findPublic(n.uuid)) .usingRecursiveComparison() .ignoringFields("updatedAt") .isEqualTo(n.copy(public = true)) noteRepo.makePrivate(user1.id, n.uuid) assertThat(noteRepo.findPublic(n.uuid)).isNull() } }