package be.simplenotes.persistance.notes import be.simplenotes.persistance.DbTest import be.simplenotes.persistance.converters.NoteConverter import be.simplenotes.persistance.repositories.NoteRepository import be.simplenotes.persistance.repositories.UserRepository import be.simplenotes.persistance.users.createFakeUser import be.simplenotes.types.ExportedNote import be.simplenotes.types.PersistedUser import me.liuwj.ktorm.database.Database import me.liuwj.ktorm.dsl.eq import me.liuwj.ktorm.entity.filter import me.liuwj.ktorm.entity.find import me.liuwj.ktorm.entity.mapColumns import me.liuwj.ktorm.entity.toList import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.* import java.sql.SQLIntegrityConstraintViolationException internal abstract class BaseNoteRepositoryImplTest : 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)) .isEqualToIgnoringGivenFields(note, "uuid", "updatedAt", "public") .hasNoNullFieldsOrProperties() assertThat(db.notes.toList()) .hasSize(1) .first() .isEqualToIgnoringGivenFields(note, "uuid", "updatedAt", "public") } } @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() } // TODO: datetime -> timestamp migration @Disabled("Not working with mariadb, inserts are too fast so the updated_at is the same") @Test fun pagination() { (50 downTo 1).forEach { i -> noteRepo.insertFakeNote(user1, "$i") } assertThat(noteRepo.findAll(user1.id, limit = 20, offset = 0).onEach { println(it) }) .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`() { 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 converter = beanContext.getBean(NoteConverter::class.java) val note = db.notes.find { it.title eq fakeNote.meta.title }!! .let { entity -> val tags = db.tags.filter { it.noteUuid eq entity.uuid }.mapColumns { it.name } as List converter.toPersistedNote(entity, 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 = 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.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 = noteRepo.insertFakeNote(user1) val newNote1 = fakeNote() assertThat(noteRepo.update(user1.id, note1.uuid, newNote1)).isNotNull assertThat(noteRepo.find(user1.id, note1.uuid)) .isEqualToComparingOnlyGivenFields(newNote1, "meta", "markdown", "html") val note2 = noteRepo.insertFakeNote(user1) val newNote2 = fakeNote().let { it.copy(meta = it.meta.copy(tags = tagGenerator().take(3).toList())) } assertThat(noteRepo.update(user1.id, note2.uuid, newNote2)) .isNotNull assertThat(noteRepo.find(user1.id, note2.uuid)) .isEqualToComparingOnlyGivenFields(newNote2, "meta", "markdown", "html") } } @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 { it.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 { it.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.meta.title, tags = n.meta.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)) .isEqualToIgnoringGivenFields(n.copy(public = true), "updatedAt") noteRepo.makePrivate(user1.id, n.uuid) assertThat(noteRepo.findPublic(n.uuid)).isNull() } }