Skip to content

Commit

Permalink
Merge pull request #1879 from digitalfabrik/1864-integration-tests-fo…
Browse files Browse the repository at this point in the history
…r-addeakapplication

1864: Integration tests for Verein360 applications
  • Loading branch information
seluianova authored Jan 24, 2025
2 parents 772d747 + afafb61 commit 1d19749
Show file tree
Hide file tree
Showing 4 changed files with 264 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package app.ehrenamtskarte.backend.application.webservice

import app.ehrenamtskarte.backend.GraphqlApiTest
import app.ehrenamtskarte.backend.application.database.ApplicationEntity
import app.ehrenamtskarte.backend.application.database.ApplicationVerificationEntity
import app.ehrenamtskarte.backend.application.database.ApplicationVerificationExternalSource
import app.ehrenamtskarte.backend.application.database.ApplicationVerifications
import app.ehrenamtskarte.backend.application.database.Applications
import app.ehrenamtskarte.backend.application.webservice.schema.create.Application
import app.ehrenamtskarte.backend.application.webservice.schema.create.ApplicationType
import app.ehrenamtskarte.backend.auth.database.ApiTokenType
import app.ehrenamtskarte.backend.auth.database.ApiTokens
import app.ehrenamtskarte.backend.helper.TestAdministrators
import app.ehrenamtskarte.backend.helper.TestApplicationBuilder
import app.ehrenamtskarte.backend.helper.TestData
import app.ehrenamtskarte.backend.util.GraphQLRequestSerializer
import io.javalin.testtools.JavalinTest
import org.jetbrains.exposed.sql.deleteAll
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull

internal class Verein360ApplicationTest : GraphqlApiTest() {

data class ValidationErrorTestCase(val application: Application, val error: String)

companion object {
@JvmStatic
fun validationErrorTestCases(): List<ValidationErrorTestCase> {
return listOf(
ValidationErrorTestCase(
application = TestApplicationBuilder.build(
isAlreadyVerified = true,
applicationType = ApplicationType.RENEWAL_APPLICATION
),
error = "Application type must be FIRST_APPLICATION if application is already verified"
),
ValidationErrorTestCase(
application = TestApplicationBuilder.build(
isAlreadyVerified = true,
wantsDigitalCard = false,
wantsPhysicalCard = true
),
error = "Digital card must be true if application is already verified"
),
ValidationErrorTestCase(
application = TestApplicationBuilder.build(
isAlreadyVerified = true,
wantsPhysicalCard = true
),
error = "Physical card must be false if application is already verified"
),
ValidationErrorTestCase(
application = TestApplicationBuilder.build(
isAlreadyVerified = true,
category = "Other"
),
error = "All organizations must be of category Sport if application is already verified"
)
)
}
}

private val adminVerein360 = TestAdministrators.BAYERN_VEREIN_360

@BeforeEach
fun cleanUp() {
transaction {
ApplicationVerifications.deleteAll()
Applications.deleteAll()
ApiTokens.deleteAll()
}
}

@ParameterizedTest
@MethodSource("validationErrorTestCases")
fun `should return validation error when the request is not valid`(testCase: ValidationErrorTestCase) = JavalinTest.test(app) { _, client ->
TestData.createApiToken(creatorId = adminVerein360.id, type = ApiTokenType.VERIFIED_APPLICATION)

val mutation = createMutation(application = testCase.application)
val response = post(client, mutation, token = "dummy")

assertEquals(200, response.code)

val jsonResponse = response.json()

assertEquals("Error INVALID_JSON occurred.", jsonResponse.findValue("message").textValue())
assertEquals(testCase.error, jsonResponse.findValue("reason").textValue())
}

@Test
fun `should return an error when region not found`() = JavalinTest.test(app) { _, client ->
val mutation = createMutation(
regionId = 99,
application = TestApplicationBuilder.build(true)
)
val response = post(client, mutation)

assertEquals(200, response.code)

val jsonResponse = response.json()

assertEquals("Error REGION_NOT_FOUND occurred.", jsonResponse.findValue("message").textValue())
}

@Test
fun `should return an error when the application is pre-verified but auth token is missing`() = JavalinTest.test(app) { _, client ->
val mutation = createMutation(
application = TestApplicationBuilder.build(true)
)
val response = post(client, mutation)

assertEquals(401, response.code)
}

@Test
fun `should return an error when api token not found`() = JavalinTest.test(app) { _, client ->
val mutation = createMutation(
application = TestApplicationBuilder.build(true)
)
val response = post(client, mutation, token = "non-existent")

assertEquals(403, response.code)
}

@Test
fun `should return an error when api token has wrong type`() = JavalinTest.test(app) { _, client ->
TestData.createApiToken(creatorId = adminVerein360.id, type = ApiTokenType.USER_IMPORT)

val mutation = createMutation(
application = TestApplicationBuilder.build(true)
)
val response = post(client, mutation, token = "dummy")

assertEquals(403, response.code)
}

@Test
fun `should create an application and approved verification if the request is pre-verified and valid`() = JavalinTest.test(app) { _, client ->
TestData.createApiToken(creatorId = adminVerein360.id, type = ApiTokenType.VERIFIED_APPLICATION)

val mutation = createMutation(
application = TestApplicationBuilder.build(true)
)
val response = post(client, mutation, token = "dummy")

assertEquals(200, response.code)

transaction {
assertEquals(1, Applications.selectAll().count())
assertEquals(1, ApplicationVerifications.selectAll().count())

val application = ApplicationEntity.all().single()

ApplicationVerificationEntity.find { ApplicationVerifications.applicationId eq application.id }.single().let {
assertNotNull(it.verifiedDate)
assertNull(it.rejectedDate)
assertEquals(ApplicationVerificationExternalSource.VEREIN360, it.automaticSource)
}
}
}

@Test
fun `should create an application and pending verification if the request is not pre-verified`() = JavalinTest.test(app) { _, client ->
val mutation = createMutation(
application = TestApplicationBuilder.build(false)
)
val response = post(client, mutation)

assertEquals(200, response.code)

transaction {
assertEquals(1, Applications.selectAll().count())
assertEquals(1, ApplicationVerifications.selectAll().count())

val application = ApplicationEntity.all().single()

ApplicationVerificationEntity.find { ApplicationVerifications.applicationId eq application.id }.single().let {
assertNull(it.verifiedDate)
assertNull(it.rejectedDate)
assertEquals(ApplicationVerificationExternalSource.NONE, it.automaticSource)
}
}
}

private fun createMutation(
project: String = "bayern.ehrenamtskarte.app",
regionId: Int = 1,
application: Application
): String {
val applicationJson = GraphQLRequestSerializer.serializeObject(application)
return """
mutation AddEakApplication {
addEakApplication(
project: "$project"
regionId: $regionId
application: $applicationJson
)
}
""".trimIndent()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ import app.ehrenamtskarte.backend.application.webservice.schema.create.primitive

class TestApplicationBuilder {
companion object {
fun build(isAlreadyVerified: Boolean): Application {
fun build(
isAlreadyVerified: Boolean,
applicationType: ApplicationType = ApplicationType.FIRST_APPLICATION,
cardType: BavariaCardType = BavariaCardType.BLUE,
wantsDigitalCard: Boolean = true,
wantsPhysicalCard: Boolean = false,
category: String = "Sport"
): Application {
return Application(
personalData = PersonalData(
forenames = ShortTextInput("John"),
Expand All @@ -36,13 +43,13 @@ class TestApplicationBuilder {
emailAddress = EmailInput("[email protected]")
),
applicationDetails = ApplicationDetails(
applicationType = ApplicationType.FIRST_APPLICATION,
cardType = BavariaCardType.BLUE,
applicationType = applicationType,
cardType = cardType,
givenInformationIsCorrectAndComplete = true,
hasAcceptedEmailUsage = true,
hasAcceptedPrivacyPolicy = true,
wantsDigitalCard = true,
wantsPhysicalCard = false,
wantsDigitalCard = wantsDigitalCard,
wantsPhysicalCard = wantsPhysicalCard,
blueCardEntitlement = BlueCardEntitlement(
juleicaEntitlement = null,
militaryReserveEntitlement = null,
Expand All @@ -61,7 +68,7 @@ class TestApplicationBuilder {
country = ShortTextInput("Deutschland"),
addressSupplement = null
),
category = ShortTextInput("Sport"),
category = ShortTextInput(category),
contact = OrganizationContact(
name = ShortTextInput("Jane Doe"),
email = EmailInput("[email protected]"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package app.ehrenamtskarte.backend.util

import kotlin.reflect.KProperty1
import kotlin.reflect.KVisibility
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.declaredMembers

object GraphQLRequestSerializer {

/**
* Serialize object into graphql request format
* e.g. { stringProperty: "value", intProperty: 123, objectProperty: { booleanProperty: true } }
*/
fun serializeObject(obj: Any): String {
return obj::class.declaredMemberProperties.filter { it.visibility == KVisibility.PUBLIC }
.joinToString(", ") { property ->
val value = readInstanceProperty<Any>(obj, property.name)
val serializedValue = "${property.name}: ${
when (value) {
is String -> "\"$value\""
is Number, is Boolean -> value.toString()
is Enum<*> -> value.name
is List<*> -> serializeList(value)
null -> null
else -> serializeObject(value)
}
}"
serializedValue
}.let { "{ $it }" }
}

private fun serializeList(list: List<*>): String? {
if (list.isEmpty()) return null
return list.joinToString(", ", "[", "]") { serializeObject(it!!) }
}

@Suppress("UNCHECKED_CAST")
private fun <R> readInstanceProperty(instance: Any, propertyName: String): R? {
val property = instance::class.declaredMembers.first { it.name == propertyName } as KProperty1<Any, *>
return property.get(instance) as R?
}
}
2 changes: 1 addition & 1 deletion backend/src/test/resources/config.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ projects:
- id: bayern.ehrenamtskarte.app
importUrl: dummy
pipelineName: dummy
administrationBaseUrl: dummy
administrationBaseUrl: https://test.app
administrationName: dummy
timezone: "Europe/Berlin"
selfServiceEnabled: false
Expand Down

0 comments on commit 1d19749

Please sign in to comment.