Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#65 Made tests consistently use api clients #67

Merged
merged 6 commits into from
Apr 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import ru.vityaman.lms.botalka.api.http.server.GeneralErrorMessage
import ru.vityaman.lms.botalka.domain.exception.DomainException
import ru.vityaman.lms.botalka.domain.exception.InvalidValueException
import ru.vityaman.lms.botalka.domain.exception.NotFoundException
import ru.vityaman.lms.botalka.domain.exception.PromotionRequestResolvedException

val DomainException.httpCode: HttpStatus
get() = when (this) {
is InvalidValueException -> HttpStatus.NOT_FOUND
is InvalidValueException -> HttpStatus.BAD_REQUEST
is NotFoundException -> HttpStatus.NOT_FOUND
is PromotionRequestResolvedException -> HttpStatus.CONFLICT
else -> TODO()
}

Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,65 +5,59 @@ import io.kotest.matchers.date.shouldBeAfter
import io.kotest.matchers.date.shouldHaveSameInstantAs
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.async
import kotlinx.coroutines.reactor.awaitSingle
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import ru.vityaman.lms.botalka.BotalkaTestSuite
import ru.vityaman.lms.botalka.api.http.message.toMessage
import ru.vityaman.lms.botalka.api.http.server.apis.HomeworkApi
import ru.vityaman.lms.botalka.domain.model.Homework
import ru.vityaman.lms.botalka.api.http.client.HomeworkDraftMessage
import ru.vityaman.lms.botalka.api.http.client.apis.HomeworkApi
import java.time.OffsetDateTime

@SpringBootTest
class HomeworkApiTest(
@Autowired val api: HomeworkApi,
) : BotalkaTestSuite() {
private val drafts = listOf(
Homework.Draft(
Homework.Title("Test Homework 1"),
Homework.Description("Test Homework 1 Description"),
Homework.Score(100),
OffsetDateTime.parse("2024-04-01T10:15:30+03:00"),
HomeworkDraftMessage(
title = "Test Homework 1",
description = "Test Homework 1 Description",
maxScore = 100,
publicationMoment = time("2024-04-01T10:15:30+03:00"),
),
Homework.Draft(
Homework.Title("Test Homework 2"),
Homework.Description("Test Homework 2 Description"),
Homework.Score(250),
OffsetDateTime.parse("2024-05-01T12:00:30+03:00"),
HomeworkDraftMessage(
title = "Test Homework 2",
description = "Test Homework 2 Description",
maxScore = 250,
publicationMoment = time("2024-05-01T12:00:30+03:00"),
),
Homework.Draft(
Homework.Title("Test Homework 3"),
Homework.Description("Test Homework 3 Description"),
Homework.Score(100),
OffsetDateTime.parse("2024-05-02T12:00:30+03:00"),
HomeworkDraftMessage(
title = "Test Homework 3",
description = "Test Homework 3 Description",
maxScore = 100,
publicationMoment = time("2024-05-02T12:00:30+03:00"),
),
Homework.Draft(
Homework.Title("Test Homework 4"),
Homework.Description("Test Homework 4 Description"),
Homework.Score(300),
OffsetDateTime.parse("2024-05-02T12:00:30+03:00"),
HomeworkDraftMessage(
title = "Test Homework 4",
description = "Test Homework 4 Description",
maxScore = 300,
publicationMoment = time("2024-05-02T12:00:30+03:00"),
),
)

@Test
fun createHomework() {
fun createHomework(): Unit = runBlocking {
val startingPoint = OffsetDateTime.now()

val draftToResultList = runBlocking {
drafts.map { draft ->
val message = draft.toMessage()
val result = async { api.homeworkPost(message).body }
Pair(draft, result)
}.map { (draft, result) ->
Pair(draft, result.await() ?: throw NullPointerException())
}
val draftToResultList = drafts.map { draft ->
Pair(draft, async { api.homeworkPost(draft).awaitSingle() })
}.map { (draft, result) ->
Pair(draft, result.await())
}

draftToResultList.forEach { (l, r) ->
l.title.text shouldBe r.title
l.description.text shouldBe r.description
l.maxScore.value shouldBe r.maxScore
l.title shouldBe r.title
l.description shouldBe r.description
l.maxScore shouldBe r.maxScore
l.publicationMoment shouldHaveSameInstantAs r.publicationMoment
}

Expand All @@ -75,4 +69,7 @@ class HomeworkApiTest(
val ids = results.map { it.id }
ids.shouldBeUnique()
}

private fun time(format: String): OffsetDateTime =
OffsetDateTime.parse(format)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,53 @@ import io.kotest.common.runBlocking
import io.kotest.matchers.collections.shouldContainExactly
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.reactor.awaitSingle
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.web.reactive.function.client.WebClientResponseException
import ru.vityaman.lms.botalka.BotalkaTestSuite
import ru.vityaman.lms.botalka.api.http.message.toMessage
import ru.vityaman.lms.botalka.api.http.message.toModel
import ru.vityaman.lms.botalka.api.http.server.PromotionRequestDraftMessage
import ru.vityaman.lms.botalka.api.http.server.PromotionRequestPatchMessage
import ru.vityaman.lms.botalka.api.http.server.PromotionRequestStatusMessage
import ru.vityaman.lms.botalka.api.http.server.apis.PromotionApi
import ru.vityaman.lms.botalka.api.http.server.apis.UserApi
import ru.vityaman.lms.botalka.domain.exception.PromotionRequestResolvedException
import ru.vityaman.lms.botalka.domain.model.PromotionRequest
import ru.vityaman.lms.botalka.domain.model.User
import ru.vityaman.lms.botalka.api.http.client.PromotionRequestDraftMessage
import ru.vityaman.lms.botalka.api.http.client.PromotionRequestPatchMessage
import ru.vityaman.lms.botalka.api.http.client.PromotionRequestStatusMessage
import ru.vityaman.lms.botalka.api.http.client.UserDraftMessage
import ru.vityaman.lms.botalka.api.http.client.UserMessage
import ru.vityaman.lms.botalka.api.http.client.UserRoleMessage
import ru.vityaman.lms.botalka.api.http.client.apis.UserApi

@SpringBootTest
class PromoteApiTest(
@Autowired private val user: UserApi,
@Autowired private val promotion: PromotionApi,
@Autowired private val api: UserApi,
) : BotalkaTestSuite() {
private val student = UserRoleMessage.student
private val teacher = UserRoleMessage.teacher

@Test
fun fromNothingToAll(): Unit = runBlocking {
val userId = create(User.Alias("tester"))
get(userId).roles shouldContainExactly setOf()
approve(promote(userId, User.Role.STUDENT))
get(userId).roles shouldContainExactly setOf(User.Role.STUDENT)
approve(promote(userId, User.Role.TEACHER))
get(userId).roles shouldContainExactly setOf(
User.Role.STUDENT, User.Role.TEACHER,
)
val userId = create("tester")
get(userId).roles.toSet() shouldContainExactly setOf()
approve(promote(userId, student))
get(userId).roles.toSet() shouldContainExactly setOf(student)
approve(promote(userId, teacher))
get(userId).roles.toSet() shouldContainExactly setOf(student, teacher)
}

@Test
fun withoutApprovalHaveNoEffect(): Unit = runBlocking {
val userId = create(User.Alias("tester"))
get(userId).roles shouldContainExactly setOf()
val promotionId = promote(userId, User.Role.STUDENT)
get(userId).roles shouldContainExactly setOf()
val userId = create("tester")
get(userId).roles.toSet() shouldContainExactly setOf()
val promotionId = promote(userId, student)
get(userId).roles.toSet() shouldContainExactly setOf()
approve(promotionId)
get(userId).roles shouldContainExactly setOf(User.Role.STUDENT)
get(userId).roles.toSet() shouldContainExactly setOf(student)
}

@Test
fun promotionCanBeResolvedOnlyOnce(): Unit = runBlocking {
val userId = create(User.Alias("tester"))
val promotionId = promote(userId, User.Role.STUDENT)
val userId = create("tester")
val promotionId = promote(userId, student)
approve(promotionId)
shouldThrow<PromotionRequestResolvedException> { approve(promotionId) }
shouldThrow<WebClientResponseException.Conflict> {
approve(promotionId)
}
}

@Test
Expand All @@ -61,9 +60,9 @@ class PromoteApiTest(
val attempts = 32
for (attempt in 0 until attempts) {
launch {
shouldThrow<PromotionRequestResolvedException> {
val userId = create(User.Alias("tester$attempt"))
val promotionId = promote(userId, User.Role.STUDENT)
shouldThrow<WebClientResponseException.Conflict> {
val userId = create("tester$attempt")
val promotionId = promote(userId, student)
coroutineScope {
launch { approve(promotionId) }
launch { reject(promotionId) }
Expand All @@ -74,35 +73,21 @@ class PromoteApiTest(
}
}

private suspend fun create(alias: User.Alias): User.Id =
User.Draft(alias)
.toMessage()
.let { user.userPost(it) }
.body!!
.toModel()
.id
private suspend fun create(alias: String): Int =
api.userPost(UserDraftMessage(alias)).awaitSingle().id

private suspend fun get(id: User.Id): User =
user.userIdGet(id.number).body!!.toModel()
private suspend fun get(id: Int): UserMessage =
api.userIdGet(id).awaitSingle()

private suspend fun promote(
user: User.Id,
role: User.Role,
): PromotionRequest.Id {
val request = PromotionRequestDraftMessage(role.toMessage())
val id = promotion.promotionRequestPost(user.number, request).body!!.id
return PromotionRequest.Id(id)
}
private suspend fun promote(user: Int, role: UserRoleMessage): Int =
PromotionRequestDraftMessage(role)
.let { api.promotionRequestPost(user, it).awaitSingle().id }

private suspend fun approve(id: PromotionRequest.Id) {
val status = PromotionRequestStatusMessage.APPROVED
val patch = PromotionRequestPatchMessage(status)
promotion.promotionRequestIdPatch(id.number, patch)
}
private suspend fun approve(id: Int) =
PromotionRequestPatchMessage(PromotionRequestStatusMessage.approved)
.let { api.promotionRequestIdPatch(id, it).awaitSingle() }

private suspend fun reject(id: PromotionRequest.Id) {
val status = PromotionRequestStatusMessage.CANCELED
val patch = PromotionRequestPatchMessage(status)
promotion.promotionRequestIdPatch(id.number, patch)
}
private suspend fun reject(id: Int) =
PromotionRequestPatchMessage(PromotionRequestStatusMessage.canceled)
.let { api.promotionRequestIdPatch(id, it).awaitSingle() }
}
Loading
Loading