Test: Testcontainers integration tests for distribution and content import

This commit is contained in:
2026-09-27 22:22:54 +03:00
parent 47ec9884a3
commit 6e891e4326
2 changed files with 169 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
package ru.ysu.lms.academic.content
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
import org.testcontainers.postgresql.PostgreSQLContainer
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.Files
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
/**
* Интеграционный тест импорта материалов: реальная PostgreSQL (Testcontainers) + Flyway.
* Проверяет, что повторный импорт не плодит дубли (сопоставление по названию).
*/
@Testcontainers
@SpringBootTest
class ContentImportIntegrationTest {
companion object {
@Container
@ServiceConnection
@JvmStatic
val postgres = PostgreSQLContainer("postgres:18")
@JvmStatic
@DynamicPropertySource
fun assetsDir(registry: DynamicPropertyRegistry) {
registry.add("lms.content.assets-dir") { Files.createTempDirectory("lms-assets").toString() }
}
private fun articleZip(): ByteArray {
val out = ByteArrayOutputStream()
ZipOutputStream(out).use { zos ->
zos.putNextEntry(ZipEntry("chapters/05-Тестовая/01-Раздел/01-Статья.md"))
zos.write(
"---\ntitle: \"Тестовая статья\"\nslug: \"test-article\"\n---\nТело статьи".toByteArray(),
)
zos.closeEntry()
}
return out.toByteArray()
}
}
@Autowired
private lateinit var importer: ContentImporter
@Autowired
private lateinit var chapters: ContentChapterRepository
@Autowired
private lateinit var sections: ContentSectionRepository
@Autowired
private lateinit var articles: ContentArticleRepository
@Test
fun `re-import does not duplicate chapter, section or article`() {
val zip = articleZip()
val first = importer.importZip("content.zip", zip, sync = false)
assertThat(first.chapters).isEqualTo(1)
val chapter = chapters.findByTitleIgnoreCase("Тестовая")
assertThat(chapter).isNotNull
val section = sections.findByChapterIdAndTitleIgnoreCase(chapter!!.id, "Раздел")
assertThat(section).isNotNull
val article = articles.findBySectionIdAndTitleIgnoreCase(section!!.id, "Тестовая статья")
assertThat(article).isNotNull
assertThat(article!!.slug).isEqualTo("test-article")
// повторный импорт того же архива обновляет существующие записи, а не создаёт новые
importer.importZip("content.zip", zip, sync = false)
assertThat(chapters.findByTitleIgnoreCase("Тестовая")!!.id).isEqualTo(chapter.id)
assertThat(sections.findByChapterIdOrderByPositionAscIdAsc(chapter.id)).hasSize(1)
assertThat(articles.findBySectionIdOrderByPositionAscIdAsc(section.id)).hasSize(1)
}
}