Skip to content

Commit

Permalink
Add tests for kotlet.tracing
Browse files Browse the repository at this point in the history
  • Loading branch information
turchenkoalex committed Jul 17, 2024
1 parent 99db6e1 commit 5adff5b
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 28 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ subprojects {
reports {
verify {
rule("Minimal line coverage rate in percents") {
minBound(50)
minBound(40)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/kotlin/kotlet/HttpCall.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface HttpCall {
val httpMethod: HttpMethod

/**
* Route path of the request configured in the [routing].
* Route path of the request configured in the [Kotlet.routing].
*/
val routePath: String

Expand Down
6 changes: 4 additions & 2 deletions mocks/src/main/kotlin/kotlet/mocks/Mocks.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ object Mocks {
method: HttpMethod,
routePath: String = "/",
headers: Map<String, String> = emptyMap(),
data: ByteArray = ByteArray(0)
data: ByteArray = ByteArray(0),
async: Boolean = false,
): MockHttpCall {
return MockHttpCall(
httpMethod = method,
routePath = routePath,
headers = headers,
requestData = data
requestData = data,
async = async,
)
}

Expand Down
70 changes: 52 additions & 18 deletions mocks/src/main/kotlin/kotlet/mocks/http/MockHttpCall.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import jakarta.servlet.http.HttpServletResponse
import kotlet.HttpCall
import kotlet.HttpMethod
import java.io.ByteArrayOutputStream
import java.util.Collections
import java.util.Enumeration

/**
* A mock implementation of [HttpCall].
Expand All @@ -16,6 +18,7 @@ class MockHttpCall(
override val routePath: String,
headers: Map<String, String>,
requestData: ByteArray,
async: Boolean
) : HttpCall {
private var contentTypeField: String = ""
private var statusField: Int = 200
Expand All @@ -33,25 +36,14 @@ class MockHttpCall(
get() = responseStream.toByteArray()

init {
val attributes = mutableMapOf<String, Any>()

rawRequest = mockk {
every { inputStream } returns ByteArrayServletInputStream(requestData)
every { getHeader(any()) } answers {
headers[this.firstArg()]
}
every { getAttribute(any()) } answers {
attributes[this.firstArg()]
}
every { setAttribute(any(), any()) } answers {
attributes[this.firstArg()] = this.secondArg()
}
every { removeAttribute(any()) } answers {
attributes.remove(this.firstArg())
}
every { isAsyncStarted } returns false
}
rawRequest = createHttpRequestMock(
methodName = httpMethod.name,
async = async,
headers = headers,
requestData = requestData
)

val responseHeaders = mutableMapOf<String, String>()
rawResponse = mockk {
every { outputStream } returns ByteArrayServletOutputStream(responseStream)

Expand All @@ -62,6 +54,48 @@ class MockHttpCall(
// statusField is a private field, so we need to use a setter to set it
every { status = any() } answers { statusField = this.firstArg() }
every { status } answers { statusField }

every { addHeader(any(), any()) } answers {
responseHeaders[this.firstArg()] = this.secondArg()
}
every { getHeader(any()) } answers {
responseHeaders[this.firstArg()]
}
every { getHeaders(any()) } answers {
val header = responseHeaders[this.firstArg()] ?: ""
header.split(",").map(String::trim)
}
}
}
}

private fun createHttpRequestMock(
methodName: String,
async: Boolean,
headers: Map<String, String>,
requestData: ByteArray,
): HttpServletRequest {
val attributes = mutableMapOf<String, Any>()

return mockk {
every { inputStream } returns ByteArrayServletInputStream(requestData)
every { getHeader(any()) } answers {
headers[this.firstArg()]
}
every { getHeaders(any()) } answers {
val header = headers[this.firstArg()] ?: ""
Collections.enumeration(header.split(",").map(String::trim))
}
every { getAttribute(any()) } answers {
attributes[this.firstArg()]
}
every { setAttribute(any(), any()) } answers {
attributes[this.firstArg()] = this.secondArg()
}
every { removeAttribute(any()) } answers {
attributes.remove(this.firstArg())
}
every { isAsyncStarted } returns async
every { method } returns methodName
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package kotlet.tracing

import jakarta.servlet.http.HttpServletResponse
import kotlet.HttpCall

/**
Expand All @@ -13,6 +14,12 @@ object DefaultHttpCallSanitizer : HttpCallSanitizer {
.toMutableList()
}

override fun getHttpResponseHeader(response: HttpServletResponse, name: String): MutableList<String> {
return response.getHeaders(name)
.toList()
.toMutableList()
}

override fun getUrlPath(call: HttpCall): String? {
return call.rawRequest.requestURI
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ internal fun buildServerInstrumenter(
.buildServerInstrumenter(textMapGetter)
}

private class ServletHttpServerAttributesGetter(
internal class ServletHttpServerAttributesGetter(
private val sanitizer: HttpCallSanitizer,
) : HttpServerAttributesGetter<HttpCall, HttpServletResponse> {
override fun getHttpRequestMethod(call: HttpCall): String? {
return call.rawRequest.method
}

override fun getHttpRoute(call: HttpCall): String? {
override fun getHttpRoute(call: HttpCall): String {
return call.routePath
}

Expand All @@ -51,7 +51,7 @@ private class ServletHttpServerAttributesGetter(
response: HttpServletResponse,
name: String
): MutableList<String> {
return response.getHeaders(name).toMutableList()
return sanitizer.getHttpResponseHeader(response, name)
}

override fun getUrlScheme(call: HttpCall): String? {
Expand Down
6 changes: 6 additions & 0 deletions tracing/src/main/kotlin/kotlet/tracing/HttpCallSanitizer.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package kotlet.tracing

import jakarta.servlet.http.HttpServletResponse
import kotlet.HttpCall

/**
Expand All @@ -11,6 +12,11 @@ interface HttpCallSanitizer {
*/
fun getHttpRequestHeader(call: HttpCall, name: String): MutableList<String>

/**
* Get the HTTP response header with the given name.
*/
fun getHttpResponseHeader(response: HttpServletResponse, name: String): MutableList<String>

/**
* Get the URL path of the HTTP call.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package kotlet.tracing

import io.mockk.every
import kotlet.HttpMethod
import kotlet.mocks.Mocks
import org.junit.jupiter.api.Assertions.*

Check warning on line 6 in tracing/src/test/kotlin/kotlet/tracing/DefaultHttpCallSanitizerUnitTest.kt

View workflow job for this annotation

GitHub Actions / Checkstyle

detekt.WildcardImport

org.junit.jupiter.api.Assertions.* is a wildcard import. Replace it with fully qualified imports.
import kotlin.test.Test

class DefaultHttpCallSanitizerUnitTest {
@Test
fun `default sanitizer just return values`() {
val call = Mocks.httpCall(
method = HttpMethod.GET,
headers = mapOf("key" to "value, value2")
)

call.rawResponse.addHeader("Content-Type", "application/json")

every { call.rawRequest.requestURI } returns "/"
every { call.rawRequest.queryString } returns "query=1"

assertEquals(mutableListOf("value", "value2"), DefaultHttpCallSanitizer.getHttpRequestHeader(call, "key"))
assertEquals(mutableListOf("application/json"), DefaultHttpCallSanitizer.getHttpResponseHeader(call.rawResponse, "Content-Type"))

Check warning on line 23 in tracing/src/test/kotlin/kotlet/tracing/DefaultHttpCallSanitizerUnitTest.kt

View workflow job for this annotation

GitHub Actions / Checkstyle

detekt.MaxLineLength

Line detected, which is longer than the defined maximum line length in the code style.
assertEquals("/", DefaultHttpCallSanitizer.getUrlPath(call))
assertEquals("query=1", DefaultHttpCallSanitizer.getUrlQuery(call))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package kotlet.tracing

import io.mockk.every
import io.mockk.mockk
import jakarta.servlet.http.HttpServletResponse
import kotlet.HttpMethod
import kotlet.mocks.Mocks
import org.junit.jupiter.api.Assertions.assertEquals
import kotlin.test.Test

class ServletHttpServerAttributesGetterUnitTest {
private val sanitizer = mockk<HttpCallSanitizer>()
private val getter = ServletHttpServerAttributesGetter(sanitizer)

@Test
fun `getHttpRequestMethod returns method`() {
val call = Mocks.httpCall(
method = HttpMethod.GET
)

assertEquals("GET", getter.getHttpRequestMethod(call))
}

@Test
fun `getHttpRoute returns route`() {
val call = Mocks.httpCall(
method = HttpMethod.GET,
routePath = "/test",
)

assertEquals("/test", getter.getHttpRoute(call))
}

@Test
fun `getHttpRequestHeader returns header through sanitizer`() {
val call = Mocks.httpCall(
method = HttpMethod.GET,
headers = mapOf("key" to "value")
)

every { sanitizer.getHttpRequestHeader(call, "key") } returns mutableListOf("sanitized value")

assertEquals(mutableListOf("sanitized value"), getter.getHttpRequestHeader(call, "key"))
}

@Test
fun `getHttpResponseStatusCode returns status code`() {
val call = Mocks.httpCall(
method = HttpMethod.GET
)

val response = mockk<HttpServletResponse>()
every { response.status } returns 202

assertEquals(202, getter.getHttpResponseStatusCode(call, response, null))
}

@Test
fun `getHttpResponseHeader returns header through sanitizer`() {
val call = Mocks.httpCall(
method = HttpMethod.GET
)

val response = mockk<HttpServletResponse>()
every { response.getHeaders("key") } returns listOf("value")
every { sanitizer.getHttpResponseHeader(response, "key") } returns mutableListOf("sanitized value")

assertEquals(mutableListOf("sanitized value"), getter.getHttpResponseHeader(call, response, "key"))
}

@Test
fun `getUrlScheme returns scheme`() {
val call = Mocks.httpCall(
method = HttpMethod.GET
)

every { call.rawRequest.scheme } returns "http"

assertEquals("http", getter.getUrlScheme(call))
}

@Test
fun `getUrlPath returns path through sanitizer`() {
val call = Mocks.httpCall(
method = HttpMethod.GET,
)

every { sanitizer.getUrlPath(call) } returns "/sanitized_test"

assertEquals("/sanitized_test", getter.getUrlPath(call))
}

@Test
fun `getUrlQuery returns query through sanitizer`() {
val call = Mocks.httpCall(
method = HttpMethod.GET,
)

every { sanitizer.getUrlQuery(call) } returns "query=sanitized"

assertEquals("query=sanitized", getter.getUrlQuery(call))
}

@Test
fun `getNetworkTransport returns TCP`() {
val call = Mocks.httpCall(
method = HttpMethod.GET,
)

assertEquals("tcp", getter.getNetworkTransport(call, null))
}
}
Loading

0 comments on commit 5adff5b

Please sign in to comment.