Skip to content

Commit

Permalink
Add module for end 2 end tests for verify the the generated client co…
Browse files Browse the repository at this point in the history
…mplies with the spec and is run able
  • Loading branch information
Jens Zettelmeyer authored and cjbooms committed Dec 12, 2023
1 parent eba93e1 commit e6300c9
Show file tree
Hide file tree
Showing 4 changed files with 352 additions and 7 deletions.
16 changes: 9 additions & 7 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ val projectDesc = "Fabricates Kotlin code from OpenApi3 specifications"
val projectLicenseName = "Apache License 2.0"
val projectLicenseUrl = "https://opensource.org/licenses/Apache-2.0"

repositories {
mavenCentral()
allprojects {
repositories {
mavenCentral()
}
}

val jacksonVersion by extra { "2.15.1" }
val junitVersion by extra { "5.9.2" }
dependencies {
val jacksonVersion = "2.15.1"

implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.github.jknack:handlebars:4.3.1")
Expand All @@ -53,9 +55,9 @@ dependencies {
implementation("com.squareup:kotlinpoet:1.14.2") { exclude(module = "kotlin-stdlib-jre7") }
implementation("com.google.flogger:flogger:0.7.4")

testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2")
testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion")
testImplementation("org.junit.jupiter:junit-jupiter-engine:$junitVersion")
testImplementation("org.junit.jupiter:junit-jupiter-params:$junitVersion")
testImplementation("org.assertj:assertj-core:3.24.2")

// Below dependencies are solely present so code examples in the test resources dir compile
Expand Down
72 changes: 72 additions & 0 deletions end2end-tests/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
val fabrikt: Configuration by configurations.creating

val generationDir = "$buildDir/generated"
val apiFile = "$buildDir/../../src/test/resources/examples/okHttpClient/api.yaml"

sourceSets {
main { java.srcDirs("$generationDir/src/main/kotlin") }
test { java.srcDirs("$generationDir/src/test/kotlin") }
}

plugins {
id("org.jetbrains.kotlin.jvm") version "1.8.20" // Apply the Kotlin JVM plugin to add support for Kotlin.
}

java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

val jacksonVersion: String by rootProject.extra
val junitVersion: String by rootProject.extra

dependencies {
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation("io.github.resilience4j:resilience4j-circuitbreaker:2.1.0")
implementation("jakarta.validation:jakarta.validation-api:3.0.2")
implementation("javax.validation:validation-api:2.0.1.Final")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
implementation("com.fasterxml.jackson.core:jackson-core:$jacksonVersion")
implementation("com.fasterxml.jackson.core:jackson-annotations:$jacksonVersion")

testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion")
testImplementation("org.junit.jupiter:junit-jupiter-engine:$junitVersion")
testImplementation("org.junit.jupiter:junit-jupiter-params:$junitVersion")
testImplementation("org.assertj:assertj-core:3.24.2")
testImplementation("org.wiremock:wiremock:3.3.1")
testImplementation("com.marcinziolo:kotlin-wiremock:2.1.1")
}

tasks {

val generateCode by creating(JavaExec::class) {
inputs.files(apiFile)
outputs.dir(generationDir)
outputs.cacheIf { true }
classpath = rootProject.files("./build/libs/fabrikt-${rootProject.version}.jar")
mainClass.set("com.cjbooms.fabrikt.cli.CodeGen")
args = listOf(
"--output-directory", generationDir,
"--base-package", "com.example",
"--api-file", apiFile,
"--targets", "http_models",
"--targets", "client",
"--http-client-opts", "resilience4j"
)
dependsOn(":jar")
dependsOn(":shadowJar")
}

withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.jvmTarget = "17"
dependsOn(generateCode)
}


withType<Test> {
useJUnitPlatform()
jvmArgs = listOf("--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.base/java.util=ALL-UNNAMED")

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
package com.cjbooms.fabrikt.clients.okio3

import com.example.client.*
import com.example.models.Failure
import com.example.models.FirstModel
import com.example.models.QueryResult
import com.fasterxml.jackson.databind.ObjectMapper
import com.github.tomakehurst.wiremock.WireMockServer
import com.github.tomakehurst.wiremock.common.ConsoleNotifier
import com.github.tomakehurst.wiremock.core.WireMockConfiguration.options
import com.marcinziolo.kotlin.wiremock.*
import okhttp3.OkHttpClient
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.*
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import java.net.ServerSocket
import java.util.*
import java.util.stream.Stream


@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class Okio3Test {
private val port: Int = ServerSocket(0).use { socket -> socket.localPort }

private val wiremock: WireMockServer = WireMockServer(options().port(port).notifier(ConsoleNotifier(true)))

private val mapper = ObjectMapper()
private val httpClient = OkHttpClient.Builder().build()
private val examplePath1Client = ExamplePath1Client(mapper, "http://localhost:$port", httpClient)
private val examplePath2Client = ExamplePath2Client(mapper, "http://localhost:$port", httpClient)
private val examplePath3Client = ExamplePath3SubresourceClient(mapper, "http://localhost:$port", httpClient)

private val uuid = UUID.randomUUID()
private val failure = Failure(traceId = uuid,
error = "testError",
errorCode = "testErrorCode")

@Suppress("unused")
private fun path2ErrorCodes(): Stream<Int> = Stream.of(400, 422, 423)

@BeforeEach
fun setUp() {
wiremock.start()
}

@AfterEach
fun afterEach() {
wiremock.resetAll()
wiremock.stop()
}

@Test
fun `throws an exception if 404 is returned`() {
wiremock.get {
url like "/example-path-1"
} returns {
statusCode = 404
}

val result = assertThrows<ApiClientException> {
examplePath1Client.getExamplePath1()
}
assertThat(result.statusCode).isEqualTo(404)
}

@Test
fun `returns data when no query parameters are send`(testInfo: TestInfo) {
wiremock.get {
url like "/example-path-1"
} returns {
statusCode = 200
body = mapper.writeValueAsString(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

val result = examplePath1Client.getExamplePath1()

assertThat(result.data).isEqualTo(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

@Test
fun `adds query_param2 to the query`(testInfo: TestInfo) {
wiremock.get {
urlPath like "/example-path-1"
queryParams contains "query_param2" like "10"
} returns {
statusCode = 200
body = mapper.writeValueAsString(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

val result = examplePath1Client.getExamplePath1(queryParam2 = 10)

assertThat(result.data).isEqualTo(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

@Test
fun `adds explode_list_query_param to the query`(testInfo: TestInfo) {
wiremock.get {
urlPath like "/example-path-1"
queryParams contains "explode_list_query_param" like "list"
queryParams contains "explode_list_query_param" like "of"
queryParams contains "explode_list_query_param" like "parameters"
} returns {
statusCode = 200
body = mapper.writeValueAsString(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

val result = examplePath1Client.getExamplePath1(explodeListQueryParam = listOf("list", "of", "parameters"))

assertThat(result.data).isEqualTo(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

@Test
fun `adds additional headers to the query`(testInfo: TestInfo) {
wiremock.get {
urlPath like "/example-path-1"
headers contains "awesome" like "header"
} returns {
statusCode = 200
body = mapper.writeValueAsString(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

val result = examplePath1Client.getExamplePath1(additionalHeaders = mapOf("awesome" to "header"))

assertThat(result.data).isEqualTo(
QueryResult(
listOf(FirstModel(id = testInfo.displayName))
)
)
}

@Test
fun `send body with post request`(testInfo: TestInfo) {
val content = FirstModel(id = testInfo.displayName)
wiremock.post {
urlPath like "/example-path-1"
body equalTo mapper.writeValueAsString(content)
} returns {
statusCode = 201
}

val result = examplePath1Client.postExamplePath1(content)
assertThat(result.statusCode).isEqualTo(201)
}

@ParameterizedTest
@MethodSource("path2ErrorCodes")
fun `throws an exception if a 4xx http status code is returned`(errorCode: Int) {
wiremock.get {
urlPath like "/example-path-2/$errorCode"
} returns {
statusCode = errorCode
body = mapper.writeValueAsString(failure)
}

val result = assertThrows<ApiClientException> {
examplePath2Client.getExamplePath2PathParam(errorCode.toString(), 10)
}

assertThat(result.statusCode).isEqualTo(errorCode)
assertThat(mapper.readValue(result.message, Failure::class.java)).isEqualTo(failure)
}

@Test
fun `throws an exception if a http status code 500 is returned`() {
wiremock.get {
urlPath like "/example-path-2/500"
} returns {
statusCode = 500
body = mapper.writeValueAsString(failure)
}

val result = assertThrows<ApiServerException> {
examplePath2Client.getExamplePath2PathParam("500", 10)
}

assertThat(result.statusCode).isEqualTo(500)
assertThat(mapper.readValue(result.message, Failure::class.java)).isEqualTo(failure)
}

@Test
fun `throws an exception if a http status code 304 is returned`() {
wiremock.get {
urlPath like "/example-path-2/304"
} returns {
statusCode = 304
}

val result = assertThrows<ApiRedirectException> {
examplePath2Client.getExamplePath2PathParam("304", 10)
}

assertThat(result.statusCode).isEqualTo(304)
}


@Test
fun `head returns 200`() {
wiremock.head {
urlPath like "/example-path-2/head200"
} returns {
statusCode = 200
}

val result = examplePath2Client.headOperationIdExample("head200")

assertThat(result.statusCode).isEqualTo(200)
}

@Test
fun `put returns 204`() {
val model = FirstModel(id = "put", secondAttr = "204")
wiremock.put {
urlPath like "/example-path-2/put204"
body equalTo mapper.writeValueAsString(model)
headers contains "If-Match" like "match"
} returns {
statusCode = 204
}

val result = examplePath2Client.putExamplePath2PathParam(firstModel = model, pathParam = "put204", ifMatch = "match")

assertThat(result.statusCode).isEqualTo(204)
}

@Test
fun `put returns 204 with sub resource`() {
val model = FirstModel(id = "put", secondAttr = "304")
wiremock.put {
urlPath like "/example-path-3/put304/subresource"
body equalTo mapper.writeValueAsString(model)
headers contains "If-Match" like "match"
} returns {
statusCode = 204
}

val result = examplePath3Client.putExamplePath3PathParamSubresource(firstModel = model, pathParam = "put304", ifMatch = "match")

assertThat(result.statusCode).isEqualTo(204)
}
}
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
rootProject.name = "fabrikt"

include("end2end-tests")

0 comments on commit e6300c9

Please sign in to comment.