|
@@ -3,50 +3,86 @@ package com.hexagonkt
|
|
|
import com.hexagonkt.serialization.parse
|
|
|
import com.hexagonkt.http.client.Client
|
|
|
import com.hexagonkt.serialization.Json
|
|
|
-import com.hexagonkt.serialization.parseObjects
|
|
|
import com.hexagonkt.http.Method.GET
|
|
|
+import com.hexagonkt.http.client.Response
|
|
|
+import com.hexagonkt.http.client.ahc.AhcAdapter
|
|
|
import com.hexagonkt.http.server.jetty.JettyServletAdapter
|
|
|
-import org.asynchttpclient.Response
|
|
|
-import org.testng.annotations.AfterClass
|
|
|
-import org.testng.annotations.BeforeClass
|
|
|
-import org.testng.annotations.Test
|
|
|
+import com.hexagonkt.serialization.parseObjects
|
|
|
+import io.kotest.assertions.throwables.shouldThrow
|
|
|
+import io.kotest.core.spec.style.StringSpec
|
|
|
import java.lang.IllegalStateException
|
|
|
import java.lang.System.setProperty
|
|
|
|
|
|
-@Test class BenchmarkJettyMongoDbTest : BenchmarkTestBase("jetty", "mongodb")
|
|
|
+class BenchmarkJettyMongoDbTest : BenchmarkTestBase("jetty", "mongodb")
|
|
|
|
|
|
-@Test class BenchmarkJettyPostgreSqlTest : BenchmarkTestBase("jetty", "postgresql")
|
|
|
+class BenchmarkJettyPostgreSqlTest : BenchmarkTestBase("jetty", "postgresql")
|
|
|
|
|
|
-@Test abstract class BenchmarkTestBase(
|
|
|
+abstract class BenchmarkTestBase(
|
|
|
private val webEngine: String,
|
|
|
private val databaseEngine: String,
|
|
|
private val templateEngine: String = "pebble"
|
|
|
-) {
|
|
|
- private val client by lazy { Client("http://localhost:${benchmarkServer.runtimePort}") }
|
|
|
+): StringSpec({
|
|
|
+
|
|
|
+ val endpoint = System.getProperty("verify.endpoint")
|
|
|
+ val users = (System.getProperty("users") ?: "8").toInt()
|
|
|
+ val count = (System.getProperty("count") ?: "2").toInt() * users
|
|
|
+
|
|
|
+ lateinit var client: Client
|
|
|
+
|
|
|
+ fun checkResponse(res: Response, contentType: String) {
|
|
|
+ assert(res.headers["Date"] != null)
|
|
|
+ assert(res.headers["Server"] != null)
|
|
|
+ assert(res.headers["Transfer-Encoding"] != null)
|
|
|
+ assert(res.headers["Content-Type"]?.first() == contentType)
|
|
|
+ }
|
|
|
+
|
|
|
+ fun checkDbRequest(path: String, itemsCount: Int) {
|
|
|
+ val response = client.get(path)
|
|
|
+ val content = response.body
|
|
|
+
|
|
|
+ checkResponse(response, Json.contentType)
|
|
|
+
|
|
|
+ val resultsList = content?.parse(List::class) ?: error("")
|
|
|
+ assert(itemsCount == resultsList.size)
|
|
|
+
|
|
|
+ (1..itemsCount).forEach {
|
|
|
+ val r = resultsList[it - 1] as Map<*, *>
|
|
|
+ assert(r.containsKey(World::id.name) && r.containsKey(World::randomNumber.name))
|
|
|
+ assert(!r.containsKey(World::_id.name))
|
|
|
+ assert((r[World::id.name] as Int) in 1..10000)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- @BeforeClass fun startUp() {
|
|
|
+ beforeSpec {
|
|
|
setProperty("WEBENGINE", webEngine)
|
|
|
- main()
|
|
|
+ client = if (endpoint == null) {
|
|
|
+ main()
|
|
|
+ Client(AhcAdapter(), "http://localhost:${benchmarkServer.runtimePort}")
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ Client(AhcAdapter(), endpoint)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- @AfterClass fun shutDown() {
|
|
|
+ afterSpec {
|
|
|
benchmarkStores[databaseEngine]?.close()
|
|
|
benchmarkServer.stop()
|
|
|
}
|
|
|
|
|
|
- @Test fun `Empty server code creates a Jetty Servlet Adapter`() {
|
|
|
+ "Empty server code creates a Jetty Servlet Adapter" {
|
|
|
System.clearProperty("WEBENGINE")
|
|
|
createEngine()
|
|
|
assert(engine is JettyServletAdapter)
|
|
|
}
|
|
|
|
|
|
- @Test(expectedExceptions = [ IllegalStateException::class ])
|
|
|
- fun `Invalid server code throws an exception`() {
|
|
|
- setProperty("WEBENGINE", "invalid")
|
|
|
- createEngine()
|
|
|
+ "Invalid server code throws an exception" {
|
|
|
+ shouldThrow<IllegalStateException> {
|
|
|
+ setProperty("WEBENGINE", "invalid")
|
|
|
+ createEngine()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- @Test fun web() {
|
|
|
+ "Web" {
|
|
|
val web = Web()
|
|
|
|
|
|
val webRoutes = web.serverRouter.requestHandlers
|
|
@@ -64,25 +100,25 @@ import java.lang.System.setProperty
|
|
|
assert(webRoutes.containsAll(benchmarkRoutes))
|
|
|
}
|
|
|
|
|
|
- @Test fun json() {
|
|
|
+ "JSON".config(invocations = count, threads = users) {
|
|
|
val response = client.get("/json")
|
|
|
- val content = response.responseBody
|
|
|
+ val content = response.body
|
|
|
|
|
|
checkResponse(response, Json.contentType)
|
|
|
- assert("Hello, World!" == content.parse<Message>().message)
|
|
|
+ assert("Hello, World!" == content?.parse<Message>()?.message)
|
|
|
}
|
|
|
|
|
|
- @Test fun plaintext() {
|
|
|
+ "Plaintext".config(invocations = count, threads = users) {
|
|
|
val response = client.get("/plaintext")
|
|
|
- val content = response.responseBody
|
|
|
+ val content = response.body
|
|
|
|
|
|
checkResponse(response, "text/plain")
|
|
|
assert("Hello, World!" == content)
|
|
|
}
|
|
|
|
|
|
- @Test fun fortunes() {
|
|
|
+ "Fortunes".config(invocations = count, threads = users) {
|
|
|
val response = client.get("/$databaseEngine/$templateEngine/fortunes")
|
|
|
- val content = response.responseBody
|
|
|
+ val content = response.body ?: error("body is required")
|
|
|
|
|
|
checkResponse(response, "text/html;charset=utf-8")
|
|
|
assert(content.contains("<td><script>alert("This should not be"))
|
|
@@ -90,9 +126,9 @@ import java.lang.System.setProperty
|
|
|
assert(content.contains("<td>フレームワークのベンチマーク</td>"))
|
|
|
}
|
|
|
|
|
|
- @Test fun `no query parameter`() {
|
|
|
+ "No query parameter".config(invocations = count, threads = users) {
|
|
|
val response = client.get("/$databaseEngine/db")
|
|
|
- val body = response.responseBody
|
|
|
+ val body = response.body ?: error("body is required")
|
|
|
|
|
|
checkResponse(response, Json.contentType)
|
|
|
val bodyMap = body.parse(Map::class)
|
|
@@ -100,9 +136,9 @@ import java.lang.System.setProperty
|
|
|
assert(bodyMap.containsKey(World::randomNumber.name))
|
|
|
}
|
|
|
|
|
|
- @Test fun `no updates parameter`() {
|
|
|
+ "No updates parameter".config(invocations = count, threads = users) {
|
|
|
val response = client.get("/$databaseEngine/update")
|
|
|
- val body = response.responseBody
|
|
|
+ val body = response.body ?: error("body is required")
|
|
|
|
|
|
checkResponse(response, Json.contentType)
|
|
|
val bodyMap = body.parseObjects(Map::class).first()
|
|
@@ -110,45 +146,27 @@ import java.lang.System.setProperty
|
|
|
assert(bodyMap.containsKey(World::randomNumber.name))
|
|
|
}
|
|
|
|
|
|
- @Test fun `empty query parameter`() = checkDbRequest("/$databaseEngine/query?queries", 1)
|
|
|
- @Test fun `text query parameter`() = checkDbRequest("/$databaseEngine/query?queries=text", 1)
|
|
|
- @Test fun `zero queries`() = checkDbRequest("/$databaseEngine/query?queries=0", 1)
|
|
|
- @Test fun `one thousand queries`() = checkDbRequest("/$databaseEngine/query?queries=1000", 500)
|
|
|
- @Test fun `one query`() = checkDbRequest("/$databaseEngine/query?queries=1", 1)
|
|
|
- @Test fun `ten queries`() = checkDbRequest("/$databaseEngine/query?queries=10", 10)
|
|
|
- @Test fun `one hundred queries`() = checkDbRequest("/$databaseEngine/query?queries=100", 100)
|
|
|
- @Test fun `five hundred queries`() = checkDbRequest("/$databaseEngine/query?queries=500", 500)
|
|
|
-
|
|
|
- @Test fun `empty updates parameter`() = checkDbRequest("/$databaseEngine/update?queries", 1)
|
|
|
- @Test fun `text updates parameter`() = checkDbRequest("/$databaseEngine/update?queries=text", 1)
|
|
|
- @Test fun `zero updates`() = checkDbRequest("/$databaseEngine/update?queries=0", 1)
|
|
|
- @Test fun `one thousand updates`() = checkDbRequest("/$databaseEngine/update?queries=1000", 500)
|
|
|
- @Test fun `one update`() = checkDbRequest("/$databaseEngine/update?queries=1", 1)
|
|
|
- @Test fun `ten updates`() = checkDbRequest("/$databaseEngine/update?queries=10", 10)
|
|
|
- @Test fun `one hundred updates`() = checkDbRequest("/$databaseEngine/update?queries=100", 100)
|
|
|
- @Test fun `five hundred updates`() = checkDbRequest("/$databaseEngine/update?queries=500", 500)
|
|
|
-
|
|
|
- private fun checkDbRequest(path: String, itemsCount: Int) {
|
|
|
- val response = client.get(path)
|
|
|
- val content = response.responseBody
|
|
|
-
|
|
|
- checkResponse(response, Json.contentType)
|
|
|
-
|
|
|
- val resultsList = content.parse(List::class)
|
|
|
- assert(itemsCount == resultsList.size)
|
|
|
-
|
|
|
- (1..itemsCount).forEach {
|
|
|
- val r = resultsList[it - 1] as Map<*, *>
|
|
|
- assert(r.containsKey(World::id.name) && r.containsKey(World::randomNumber.name))
|
|
|
- assert(!r.containsKey(World::_id.name))
|
|
|
- assert((r[World::id.name] as Int) in 1..10000)
|
|
|
+ fun testDb(test: String, path: String, itemsCount: Int) {
|
|
|
+ test.config(invocations = count, threads = users) {
|
|
|
+ checkDbRequest("/$databaseEngine/$path", itemsCount)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private fun checkResponse(res: Response, contentType: String) {
|
|
|
- assert(res.headers ["Date"] != null)
|
|
|
- assert(res.headers ["Server"] != null)
|
|
|
- assert(res.headers ["Transfer-Encoding"] != null)
|
|
|
- assert(res.headers ["Content-Type"] == contentType)
|
|
|
- }
|
|
|
-}
|
|
|
+ testDb("Empty query parameter", "query?queries", 1)
|
|
|
+ testDb("Text query parameter", "query?queries=text", 1)
|
|
|
+ testDb("Zero queries", "query?queries=0", 1)
|
|
|
+ testDb("One thousand queries", "query?queries=1000", 500)
|
|
|
+ testDb("One query", "query?queries=1", 1)
|
|
|
+ testDb("Ten queries", "query?queries=10", 10)
|
|
|
+ testDb("One hundred queries", "query?queries=100", 100)
|
|
|
+ testDb("Five hundred queries", "query?queries=500", 500)
|
|
|
+
|
|
|
+ testDb("Empty updates parameter", "update?queries", 1)
|
|
|
+ testDb("Text updates parameter", "update?queries=text", 1)
|
|
|
+ testDb("Zero updates", "update?queries=0", 1)
|
|
|
+ testDb("One thousand updates", "update?queries=1000", 500)
|
|
|
+ testDb("One update", "update?queries=1", 1)
|
|
|
+ testDb("Ten updates", "update?queries=10", 10)
|
|
|
+ testDb("One hundred updates", "update?queries=100", 100)
|
|
|
+ testDb("Five hundred updates", "update?queries=500", 500)
|
|
|
+})
|