Skip to content

Commit

Permalink
feat: add keycloak for integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
alarv committed May 29, 2024
1 parent 013b80a commit 03586d8
Show file tree
Hide file tree
Showing 5 changed files with 2,538 additions and 21 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ dependencies {
testImplementation("io.rest-assured:kotlin-extensions:5.4.0")
// testcontainers
testImplementation("org.testcontainers:postgresql:1.19.8")
testImplementation("com.github.dasniko:testcontainers-keycloak:3.3.1")
testImplementation("org.testcontainers:junit-jupiter:1.19.8")
}

Expand Down
19 changes: 19 additions & 0 deletions docker/keycloak/imports/realm-export.json
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,25 @@
},
"notBefore": 0,
"groups": []
},
{
"username": "jaqpot",
"email": "[email protected]",
"firstName": "jaq",
"lastName": "pot",
"enabled": true,
"credentials": [
{
"type": "password",
"value": "jaqpot"
}
],
"clientRoles": {
"account": [
"view-profile",
"manage-account"
]
}
}
],
"scopeMappings": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package org.jaqpot.api.integration

import dasniko.testcontainers.keycloak.KeycloakContainer
import io.restassured.RestAssured
import io.restassured.RestAssured.given
import io.restassured.builder.RequestSpecBuilder
import io.restassured.http.ContentType
import io.restassured.parsing.Parser
import io.restassured.specification.RequestSpecification
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.util.TestPropertyValues
Expand All @@ -8,6 +15,7 @@ import org.springframework.context.ConfigurableApplicationContext
import org.springframework.test.context.ContextConfiguration
import org.testcontainers.containers.PostgreSQLContainer


@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@ContextConfiguration(initializers = [AbstractIntegrationTest.Initializer::class])
@AutoConfigureMockMvc
Expand All @@ -23,20 +31,52 @@ abstract class AbstractIntegrationTest {
withUsername(TEST_DB_USERNAME)
withPassword(TEST_DB_PASSWORD)
}

val keycloakContainer =
KeycloakContainer("quay.io/keycloak/keycloak:24.0.3")
.withRealmImportFile("/test-realm-export.json")
}


internal class Initializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
override fun initialize(configurableApplicationContext: ConfigurableApplicationContext) {
postgreSQLContainer.start()
keycloakContainer.start()

val keycloakUrl = "http://${keycloakContainer.host}:${keycloakContainer.firstMappedPort}"
val dbUrl =
"jdbc:postgresql://${postgreSQLContainer.host}:${postgreSQLContainer.firstMappedPort}/${TEST_DB_NAME}"

TestPropertyValues.of(
"spring.datasource.url= $dbUrl",
"spring.datasource.username=${postgreSQLContainer.username}",
"spring.datasource.password=${postgreSQLContainer.password}",
"spring.security.oauth2.resourceserver.jwt.issuer-uri=${keycloakUrl}/realms/jaqpot-local"
).applyTo(configurableApplicationContext.environment)
}
}

fun getJaqpotUserAccessToken(): String {
val keycloakUrl = "http://${keycloakContainer.host}:${keycloakContainer.firstMappedPort}"

RestAssured.defaultParser = Parser.JSON
val spec: RequestSpecification = RequestSpecBuilder()
.setBaseUri(keycloakUrl).build()

val accessToken = given()
.spec(spec)
.contentType(ContentType.URLENC)
.formParam("client_id", "jaqpot-frontend")
.formParam("grant_type", "password")
.formParam("username", "jaqpot")
.formParam("password", "jaqpot")
.post("/realms/jaqpot-local/protocol/openid-connect/token")
.then()
.statusCode(200)
.extract().path<String>("access_token")

return accessToken
}


}
60 changes: 39 additions & 21 deletions src/test/kotlin/org/jaqpot/api/integration/ModelApiTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ class ModelApiTest : AbstractIntegrationTest() {

@Test
fun testGetModelsEmpty() {
val accessToken = getJaqpotUserAccessToken()

given().contentType(ContentType.JSON)
.`when`()
.header("Authorization", "Bearer $accessToken")
.get("/v1/models")
.then()
.statusCode(200)
Expand All @@ -22,6 +24,8 @@ class ModelApiTest : AbstractIntegrationTest() {

@Test
fun testPostModelWithoutLibrariesAndFeatures() {
val accessToken = getJaqpotUserAccessToken()

given()
.contentType(ContentType.JSON)
.body(
Expand All @@ -42,22 +46,31 @@ class ModelApiTest : AbstractIntegrationTest() {
"actualModel": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="
}
""".trimIndent()
).post("/v1/models")
)
.header("Authorization", "Bearer $accessToken")
.post("/v1/models")
.then()
.statusCode(201)
.header("Location", matchesRegex(".*/v1/models/[0-1]+$"))
.header("Location", matchesRegex(".*/v1/models/[0-9]+$"))
.body(`is`(emptyString()))

given().contentType(ContentType.JSON)
.`when`()
.get("/v1/models")
.then()
.assertThat()
.statusCode(200)
.body(".", hasSize<ModelDto>(1));
// TODO figure order of execution of rest assured tests
// given().contentType(ContentType.JSON)
// .header("Authorization", "Bearer $accessToken")
// .get("/v1/models")
// .then()
// .assertThat()
// .statusCode(200)
// .body("", hasSize<ModelDto>(1))
// .body("[0].libraries", hasSize<ModelDto>(0))
// .body("[0].independentFeatures", hasSize<ModelDto>(0))
// .body("[0].dependentFeatures", hasSize<ModelDto>(0));
}

@Test
fun testPostModelWithLibrariesAndFeatures() {
val accessToken = getJaqpotUserAccessToken()

given()
.contentType(ContentType.JSON)
.body(
Expand Down Expand Up @@ -93,21 +106,26 @@ class ModelApiTest : AbstractIntegrationTest() {
"actualModel": "ewoJImluZm8iOiB7CgkJIl9wb3N0bWFuX2lkIjogIjU0MTM0N2Y1LTMyZDYtNDNkYy1iZjc5LTA4ODVkYzYzZWYyMCIsCgkJIm5hbWUiOiAiamFxcG90IGFwaSB2MiIsCgkJInNjaGVtYSI6ICJodHRwczovL3NjaGVtYS5nZXRwb3N0bWFuLmNvbS9qc29uL2NvbGxlY3Rpb24vdjIuMS4wL2NvbGxlY3Rpb24uanNvbiIsCgkJIl9leHBvcnRlcl9pZCI6ICIzNDUwMzcyNyIKCX0sCgkiaXRlbSI6IFsKCQl7CgkJCSJuYW1lIjogIm1vZGVsIiwKCQkJIml0ZW0iOiBbCgkJCQl7CgkJCQkJIm5hbWUiOiAiZ2V0QWxsIiwKCQkJCQkicmVxdWVzdCI6IHsKCQkJCQkJImF1dGgiOiB7CgkJCQkJCQkidHlwZSI6ICJub2F1dGgiCgkJCQkJCX0sCgkJCQkJCSJtZXRob2QiOiAiR0VUIiwKCQkJCQkJImhlYWRlciI6IFtdLAoJCQkJCQkidXJsIjogewoJCQkJCQkJInJhdyI6ICJ7e2Jhc2VVcmx9fS9tb2RlbHMvIiwKCQkJCQkJCSJob3N0IjogWwoJCQkJCQkJCSJ7e2Jhc2VVcmx9fSIKCQkJCQkJCV0sCgkJCQkJCQkicGF0aCI6IFsKCQkJCQkJCQkibW9kZWxzIiwKCQkJCQkJCQkiIgoJCQkJCQkJXQoJCQkJCQl9CgkJCQkJfSwKCQkJCQkicmVzcG9uc2UiOiBbXQoJCQkJfQoJCQldCgkJfSwKCQl7CgkJCSJuYW1lIjogImluZmVyZW5jZSIsCgkJCSJpdGVtIjogWwoJCQkJewoJCQkJCSJuYW1lIjogIk5ldyBGb2xkZXIiLAoJCQkJCSJpdGVtIjogW10KCQkJCX0KCQkJXQoJCX0sCgkJewoJCQkibmFtZSI6ICJyb290IiwKCQkJInJlcXVlc3QiOiB7CgkJCQkiYXV0aCI6IHsKCQkJCQkidHlwZSI6ICJiZWFyZXIiLAoJCQkJCSJiZWFyZXIiOiBbCgkJCQkJCXsKCQkJCQkJCSJrZXkiOiAidG9rZW4iLAoJCQkJCQkJInZhbHVlIjogInt7dG9rZW59fSIsCgkJCQkJCQkidHlwZSI6ICJzdHJpbmciCgkJCQkJCX0KCQkJCQldCgkJCQl9LAoJCQkJIm1ldGhvZCI6ICJHRVQiLAoJCQkJImhlYWRlciI6IFtdLAoJCQkJInVybCI6IHsKCQkJCQkicmF3IjogInt7YmFzZVVybH19LyIsCgkJCQkJImhvc3QiOiBbCgkJCQkJCSJ7e2Jhc2VVcmx9fSIKCQkJCQldLAoJCQkJCSJwYXRoIjogWwoJCQkJCQkiIgoJCQkJCV0KCQkJCX0KCQkJfSwKCQkJInJlc3BvbnNlIjogW10KCQl9LAoJCXsKCQkJIm5hbWUiOiAiY3JlYXRlIiwKCQkJInJlcXVlc3QiOiB7CgkJCQkiYXV0aCI6IHsKCQkJCQkidHlwZSI6ICJiZWFyZXIiLAoJCQkJCSJiZWFyZXIiOiBbCgkJCQkJCXsKCQkJCQkJCSJrZXkiOiAidG9rZW4iLAoJCQkJCQkJInZhbHVlIjogInt7dG9rZW59fSIsCgkJCQkJCQkidHlwZSI6ICJzdHJpbmciCgkJCQkJCX0KCQkJCQldCgkJCQl9LAoJCQkJIm1ldGhvZCI6ICJQT1NUIiwKCQkJCSJoZWFkZXIiOiBbXSwKCQkJCSJib2R5IjogewoJCQkJCSJtb2RlIjogInJhdyIsCgkJCQkJInJhdyI6ICJ7XG4gICAgXCJwdWJsaWNcIjogdHJ1ZVxufSIsCgkJCQkJIm9wdGlvbnMiOiB7CgkJCQkJCSJyYXciOiB7CgkJCQkJCQkibGFuZ3VhZ2UiOiAianNvbiIKCQkJCQkJfQoJCQkJCX0KCQkJCX0sCgkJCQkidXJsIjogewoJCQkJCSJyYXciOiAie3tiYXNlVXJsfX0vbW9kZWxzIiwKCQkJCQkiaG9zdCI6IFsKCQkJCQkJInt7YmFzZVVybH19IgoJCQkJCV0sCgkJCQkJInBhdGgiOiBbCgkJCQkJCSJtb2RlbHMiCgkJCQkJXQoJCQkJfQoJCQl9LAoJCQkicmVzcG9uc2UiOiBbXQoJCX0sCgkJewoJCQkibmFtZSI6ICJrZXljbG9hayB0b2tlbiIsCgkJCSJyZXF1ZXN0IjogewoJCQkJIm1ldGhvZCI6ICJQT1NUIiwKCQkJCSJoZWFkZXIiOiBbXSwKCQkJCSJib2R5IjogewoJCQkJCSJtb2RlIjogInVybGVuY29kZWQiLAoJCQkJCSJ1cmxlbmNvZGVkIjogWwoJCQkJCQl7CgkJCQkJCQkia2V5IjogImNsaWVudF9pZCIsCgkJCQkJCQkidmFsdWUiOiAiYWRtaW4tY2xpIiwKCQkJCQkJCSJ0eXBlIjogInRleHQiCgkJCQkJCX0sCgkJCQkJCXsKCQkJCQkJCSJrZXkiOiAiY2xpZW50X3NlY3JldCIsCgkJCQkJCQkidmFsdWUiOiAiNjJyOFFtNzlnand0QTZ3Rk1ZdDQxSW5VSzcxOG53ekoiLAoJCQkJCQkJInR5cGUiOiAidGV4dCIKCQkJCQkJfSwKCQkJCQkJewoJCQkJCQkJImtleSI6ICJncmFudF90eXBlIiwKCQkJCQkJCSJ2YWx1ZSI6ICJjbGllbnRfY3JlZGVudGlhbHMiLAoJCQkJCQkJInR5cGUiOiAidGV4dCIKCQkJCQkJfSwKCQkJCQkJewoJCQkJCQkJImtleSI6ICJyZWFsbSIsCgkJCQkJCQkidmFsdWUiOiAiIiwKCQkJCQkJCSJ0eXBlIjogInRleHQiLAoJCQkJCQkJImRpc2FibGVkIjogdHJ1ZQoJCQkJCQl9CgkJCQkJXQoJCQkJfSwKCQkJCSJ1cmwiOiB7CgkJCQkJInJhdyI6ICJodHRwOi8vbG9jYWxob3N0OjgwNzAvcmVhbG1zL2phcXBvdC1sb2NhbC9wcm90b2NvbC9vcGVuaWQtY29ubmVjdC90b2tlbiIsCgkJCQkJInByb3RvY29sIjogImh0dHAiLAoJCQkJCSJob3N0IjogWwoJCQkJCQkibG9jYWxob3N0IgoJCQkJCV0sCgkJCQkJInBvcnQiOiAiODA3MCIsCgkJCQkJInBhdGgiOiBbCgkJCQkJCSJyZWFsbXMiLAoJCQkJCQkiamFxcG90LWxvY2FsIiwKCQkJCQkJInByb3RvY29sIiwKCQkJCQkJIm9wZW5pZC1jb25uZWN0IiwKCQkJCQkJInRva2VuIgoJCQkJCV0KCQkJCX0KCQkJfSwKCQkJInJlc3BvbnNlIjogW10KCQl9CgldLAoJImV2ZW50IjogWwoJCXsKCQkJImxpc3RlbiI6ICJwcmVyZXF1ZXN0IiwKCQkJInNjcmlwdCI6IHsKCQkJCSJ0eXBlIjogInRleHQvamF2YXNjcmlwdCIsCgkJCQkicGFja2FnZXMiOiB7fSwKCQkJCSJleGVjIjogWwoJCQkJCSIiCgkJCQldCgkJCX0KCQl9LAoJCXsKCQkJImxpc3RlbiI6ICJ0ZXN0IiwKCQkJInNjcmlwdCI6IHsKCQkJCSJ0eXBlIjogInRleHQvamF2YXNjcmlwdCIsCgkJCQkicGFja2FnZXMiOiB7fSwKCQkJCSJleGVjIjogWwoJCQkJCSIiCgkJCQldCgkJCX0KCQl9CgldCn0="
}
""".trimIndent()
).post("/v1/models")
)
.header("Authorization", "Bearer $accessToken")
.post("/v1/models")
.then()
.statusCode(201)
.header("Location", matchesRegex(".*/v1/models/[0-1]+$"))
.header("Location", matchesRegex(".*/v1/models/[0-9]+$"))
.body(`is`(emptyString()))

given().contentType(ContentType.JSON)
.`when`()
.get("/v1/models")
.then()
.assertThat()
.statusCode(200)
.body("$.libraries", hasSize<ModelDto>(1))
.body("$.independentFeatures", hasSize<ModelDto>(2))
.body("$.dependentFeatures", hasSize<ModelDto>(1));

// TODO figure order of execution of rest assured tests
// given().contentType(ContentType.JSON)
// .header("Authorization", "Bearer $accessToken")
// .get("/v1/models")
// .then()
// .assertThat()
// .statusCode(200)
// .body("", hasSize<ModelDto>(1))
// .body("[0].libraries", hasSize<ModelDto>(1))
// .body("[0].independentFeatures", hasSize<ModelDto>(2))
// .body("[0].dependentFeatures", hasSize<ModelDto>(1));
}

}
Loading

0 comments on commit 03586d8

Please sign in to comment.