Test: Testcontainers integration tests for distribution and content import
This commit is contained in:
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package ru.ysu.lms.progress
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.Mockito.`when`
|
||||||
|
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.bean.override.mockito.MockitoBean
|
||||||
|
import org.testcontainers.junit.jupiter.Container
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers
|
||||||
|
import org.testcontainers.postgresql.PostgreSQLContainer
|
||||||
|
import ru.ysu.lms.progress.repo.VariantAssignmentRepository
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Интеграционный тест распределения вариантов: реальная PostgreSQL (Testcontainers) + Flyway,
|
||||||
|
* внешние сервисы (identity/academic) подменены заглушками.
|
||||||
|
*/
|
||||||
|
@Testcontainers
|
||||||
|
@SpringBootTest
|
||||||
|
class ProgressDistributionIntegrationTest {
|
||||||
|
companion object {
|
||||||
|
@Container
|
||||||
|
@ServiceConnection
|
||||||
|
@JvmStatic
|
||||||
|
val postgres = PostgreSQLContainer("postgres:18")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private lateinit var service: ProgressService
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private lateinit var assignments: VariantAssignmentRepository
|
||||||
|
|
||||||
|
@MockitoBean
|
||||||
|
private lateinit var academic: AcademicClient
|
||||||
|
|
||||||
|
@MockitoBean
|
||||||
|
private lateinit var identity: IdentityClient
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `distributes variants to every student of the selected group`() {
|
||||||
|
val labId = UUID.randomUUID()
|
||||||
|
val variantA = UUID.randomUUID().toString()
|
||||||
|
val variantB = UUID.randomUUID().toString()
|
||||||
|
val student1 = UUID.randomUUID().toString()
|
||||||
|
val student2 = UUID.randomUUID().toString()
|
||||||
|
|
||||||
|
`when`(identity.listGroups("t")).thenReturn(listOf(mapOf("id" to "g1")))
|
||||||
|
`when`(identity.getGroup("g1", "t")).thenReturn(
|
||||||
|
GroupInfo(id = "g1", name = "Группа", students = listOf(StudentInfo(student1), StudentInfo(student2))),
|
||||||
|
)
|
||||||
|
`when`(academic.getLab(labId.toString(), "t")).thenReturn(
|
||||||
|
LabInfo(
|
||||||
|
id = labId.toString(),
|
||||||
|
levels =
|
||||||
|
listOf(
|
||||||
|
LevelInfo(
|
||||||
|
id = "lvl",
|
||||||
|
code = "basic",
|
||||||
|
title = "Основная",
|
||||||
|
variants =
|
||||||
|
listOf(
|
||||||
|
VariantInfo(id = variantA, number = "1"),
|
||||||
|
VariantInfo(id = variantB, number = "2"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = service.distribute(DistributeRequest(labIds = listOf(labId.toString()), groupIds = listOf("g1")), "t")
|
||||||
|
|
||||||
|
assertThat(result.assigned).isEqualTo(2)
|
||||||
|
assertThat(result.labs).isEqualTo(1)
|
||||||
|
val saved = assignments.findByLabId(labId)
|
||||||
|
assertThat(saved).hasSize(2)
|
||||||
|
assertThat(saved.map { it.studentId.toString() }).containsExactlyInAnyOrder(student1, student2)
|
||||||
|
assertThat(saved.map { it.variantId.toString() }).containsAnyOf(variantA, variantB)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user