Add ContentImporter tests; drop redundant elvis; document lint/test commands

This commit is contained in:
2026-09-27 22:17:59 +03:00
parent a021bdb904
commit 2e99fd926c
3 changed files with 128 additions and 18 deletions

View File

@@ -0,0 +1,96 @@
package ru.ysu.lms.academic.content
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import ru.ysu.lms.academic.domain.ContentArticle
import ru.ysu.lms.academic.domain.ContentChapter
import ru.ysu.lms.academic.domain.ContentSection
import ru.ysu.lms.academic.repo.ContentArticleRepository
import ru.ysu.lms.academic.repo.ContentChapterRepository
import ru.ysu.lms.academic.repo.ContentSectionRepository
import java.io.ByteArrayOutputStream
import java.nio.file.Path
import java.util.UUID
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
class ContentImporterTest {
@TempDir
lateinit var tempDir: Path
private val chapters = mockk<ContentChapterRepository>()
private val sections = mockk<ContentSectionRepository>()
private val articles = mockk<ContentArticleRepository>()
private fun importer(): ContentImporter {
every { chapters.findByTitleIgnoreCase(any()) } returns null
every { chapters.findBySlug(any()) } returns null
every { chapters.findAllByOrderByPositionAscIdAsc() } returns emptyList()
every { chapters.save(any()) } answers { firstArg() }
every { sections.findByChapterIdAndTitleIgnoreCase(any(), any()) } returns null
every { sections.findByChapterIdAndSlug(any(), any()) } returns null
every { sections.findByChapterIdOrderByPositionAscIdAsc(any()) } returns emptyList()
every { sections.save(any()) } answers { firstArg() }
every { articles.findBySectionIdAndTitleIgnoreCase(any(), any()) } returns null
every { articles.findBySectionIdAndSlug(any(), any()) } returns null
every { articles.save(any()) } answers { firstArg() }
return ContentImporter(chapters, sections, articles, AssetStorage(tempDir.toString()))
}
private fun articleZip(
title: String,
slug: String,
): ByteArray {
val out = ByteArrayOutputStream()
ZipOutputStream(out).use { zos ->
zos.putNextEntry(ZipEntry("chapters/01-Глава/01-Раздел/01-Статья.md"))
zos.write("---\ntitle: \"$title\"\nslug: \"$slug\"\n---\nТекст статьи".toByteArray())
zos.closeEntry()
}
return out.toByteArray()
}
@Test
fun `creates chapter, section and article with title-based slug`() {
val importer = importer()
val savedChapter = slot<ContentChapter>()
val savedArticle = slot<ContentArticle>()
every { chapters.save(capture(savedChapter)) } answers { firstArg() }
every { articles.save(capture(savedArticle)) } answers { firstArg() }
val result = importer.importZip("content.zip", articleZip("Ввод-вывод", "c-input"), sync = false)
assertThat(result.chapters).isEqualTo(1)
assertThat(result.sections).isEqualTo(1)
assertThat(result.articles).isEqualTo(1)
assertThat(savedChapter.captured.title).isEqualTo("Глава")
assertThat(savedChapter.captured.slug).isEqualTo("glava")
assertThat(savedArticle.captured.title).isEqualTo("Ввод-вывод")
assertThat(savedArticle.captured.slug).isEqualTo("c-input")
}
@Test
fun `reuses existing chapter and section by title instead of duplicating`() {
val importer = importer()
val chapter = ContentChapter(title = "Глава", slug = "glava", position = 1)
val section = ContentSection(chapterId = chapter.id, title = "Раздел", slug = "razdel", position = 1)
every { chapters.findByTitleIgnoreCase("Глава") } returns chapter
every { sections.findByChapterIdAndTitleIgnoreCase(chapter.id, "Раздел") } returns section
val chapterSaves = mutableListOf<UUID>()
every { chapters.save(any()) } answers {
firstArg<ContentChapter>().also { chapterSaves.add(it.id) }
}
val result = importer.importZip("content.zip", articleZip("Тема", "c-topic"), sync = false)
assertThat(result.chapters).isEqualTo(1)
assertThat(chapterSaves).containsExactly(chapter.id)
}
}