Skip to content

Commit

Permalink
add option to configure pre- and post-testtasks per test
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Sievert committed Jun 21, 2024
1 parent 471c6ae commit 28d3388
Show file tree
Hide file tree
Showing 25 changed files with 337 additions and 19 deletions.
34 changes: 33 additions & 1 deletion src/main/kotlin/de/smartsquare/squit/config/ConfigExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.typesafe.config.ConfigRenderOptions
import com.typesafe.config.ConfigValueFactory
import de.smartsquare.squit.entity.SquitDatabaseConfiguration
import de.smartsquare.squit.io.FilesUtils
import de.smartsquare.squit.task.SquitPostTestTask
import de.smartsquare.squit.task.SquitPreTestTask
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
Expand All @@ -29,10 +31,12 @@ private const val PRE_PROCESSORS = "preProcessors"
private const val PRE_PROCESSOR_SCRIPTS = "preProcessorScripts"
private const val PRE_RUNNERS = "preRunners"
private const val PRE_RUN_SCRIPTS = "preRunnerScripts"
private const val PRE_TEST_TASKS = "preTestTasks"
private const val POST_PROCESSORS = "postProcessors"
private const val POST_PROCESSOR_SCRIPTS = "postProcessorScripts"
private const val POST_RUNNERS = "postRunners"
private const val POST_RUN_SCRIPTS = "postRunnerScripts"
private const val POST_TEST_TASKS = "postTestTasks"
private const val TAGS = "tags"
private const val DATABASE_CONFIGURATIONS = "databaseConfigurations"
private const val DATABASE_CONFIGURATION_NAME = "name"
Expand Down Expand Up @@ -104,6 +108,20 @@ val Config.preRunners get() = getSafeStringList(PRE_RUNNERS)
*/
val Config.preRunnerScripts get() = getSafePathList(PRE_RUN_SCRIPTS)

/**
* preTestTasks to execute.
* default: PRE_RUNNERS, PRE_RUNNER_SCRIPTS, DATABASE_SCRIPTS
*/
val Config.preTestTasks get() =
when (hasPath(PRE_TEST_TASKS)) {
true -> getEnumList(SquitPreTestTask::class.java, PRE_TEST_TASKS)!!
else -> listOf(
SquitPreTestTask.PRE_RUNNERS,
SquitPreTestTask.PRE_RUNNER_SCRIPTS,
SquitPreTestTask.DATABASE_SCRIPTS
)
}

/**
* List of post-processors to use.
*/
Expand All @@ -124,6 +142,20 @@ val Config.postRunners get() = getSafeStringList(POST_RUNNERS)
*/
val Config.postRunnerScripts get() = getSafePathList(POST_RUN_SCRIPTS)

/**
* postTestTasks to execute.
* default: DATABASE_SCRIPTS, POST_RUNNERS, POST_RUNNER_SCRIPTS
*/
val Config.postTestTasks get() =
when (hasPath(POST_TEST_TASKS)) {
true -> getEnumList(SquitPostTestTask::class.java, POST_TEST_TASKS)!!
else -> listOf(
SquitPostTestTask.DATABASE_SCRIPTS,
SquitPostTestTask.POST_RUNNERS,
SquitPostTestTask.POST_RUNNER_SCRIPTS
)
}

/**
* List of tags associated with the test.
*/
Expand Down Expand Up @@ -174,7 +206,7 @@ fun Config.withTestDir(testDir: Path): Config = withValue(
*/
fun Config.validate() = this.apply {
// Call getters of properties to check existence and correct declaration.
endpoint; mediaType; shouldExclude; shouldIgnore; headers; testDir
endpoint; mediaType; shouldExclude; shouldIgnore; headers; testDir; preTestTasks; postTestTasks

preProcessors.forEach { checkClass(it) }
preProcessorScripts.forEach { FilesUtils.validateExistence(it) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package de.smartsquare.squit.task

enum class SquitPostTestTask {
DATABASE_SCRIPTS, POST_RUNNERS, POST_RUNNER_SCRIPTS
}
5 changes: 5 additions & 0 deletions src/main/kotlin/de/smartsquare/squit/task/SquitPreTestTask.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package de.smartsquare.squit.task

enum class SquitPreTestTask {
DATABASE_SCRIPTS, PRE_RUNNERS, PRE_RUNNER_SCRIPTS
}
67 changes: 49 additions & 18 deletions src/main/kotlin/de/smartsquare/squit/task/SquitRequestTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import de.smartsquare.squit.config.mediaType
import de.smartsquare.squit.config.method
import de.smartsquare.squit.config.postRunnerScripts
import de.smartsquare.squit.config.postRunners
import de.smartsquare.squit.config.postTestTasks
import de.smartsquare.squit.config.preRunnerScripts
import de.smartsquare.squit.config.preRunners
import de.smartsquare.squit.config.preTestTasks
import de.smartsquare.squit.db.ConnectionCollection
import de.smartsquare.squit.db.executeScript
import de.smartsquare.squit.entity.SquitMetaInfo
Expand Down Expand Up @@ -58,6 +60,7 @@ import java.util.concurrent.TimeUnit
* Task for running requests against the given api. Also capable of running existing sql scripts before and after the
* request.
*/
@Suppress("TooManyFunctions")
open class SquitRequestTask : DefaultTask() {

/**
Expand Down Expand Up @@ -246,20 +249,16 @@ open class SquitRequestTask : DefaultTask() {
}

private fun doPreScriptExecutions(config: Config, testDirectoryPath: Path) {
config
.preRunners
.map {
preRunnersCache.getOrPut(it) { Class.forName(it).getConstructor().newInstance() as SquitPreRunner }
config.preTestTasks.forEach { task ->
when (task!!) {
SquitPreTestTask.PRE_RUNNERS -> executePreRunners(config)
SquitPreTestTask.PRE_RUNNER_SCRIPTS -> executePreRunnerScripts(config)
SquitPreTestTask.DATABASE_SCRIPTS -> executePreDatabaseScripts(config, testDirectoryPath)
}
.forEach { it.run(config) }

config.preRunnerScripts.forEach {
GroovyShell(javaClass.classLoader)
.parse(it.toFile())
.apply { binding = Binding(mapOf("config" to config)) }
.run()
}
}

private fun executePreDatabaseScripts(config: Config, testDirectoryPath: Path) {
config.databaseConfigurations.forEach {
executeScriptIfExisting(
testDirectoryPath.resolve("${it.name}_pre.sql"),
Expand All @@ -270,7 +269,35 @@ open class SquitRequestTask : DefaultTask() {
}
}

private fun executePreRunnerScripts(config: Config) {
config.preRunnerScripts.forEach {
GroovyShell(javaClass.classLoader)
.parse(it.toFile())
.apply { binding = Binding(mapOf("config" to config)) }
.run()
}
}

private fun executePreRunners(config: Config) {
config
.preRunners
.map {
preRunnersCache.getOrPut(it) { Class.forName(it).getConstructor().newInstance() as SquitPreRunner }
}
.forEach { it.run(config) }
}

private fun doPostScriptExecutions(config: Config, testDirectoryPath: Path) {
config.postTestTasks.forEach { task ->
when (task!!) {
SquitPostTestTask.POST_RUNNER_SCRIPTS -> executePostRunnerScripts(config)
SquitPostTestTask.POST_RUNNERS -> executePostRunners(config)
SquitPostTestTask.DATABASE_SCRIPTS -> executePostDatabaseScripts(config, testDirectoryPath)
}
}
}

private fun executePostDatabaseScripts(config: Config, testDirectoryPath: Path) {
config.databaseConfigurations.forEach {
executeScriptIfExisting(
testDirectoryPath.resolve("${it.name}_post.sql"),
Expand All @@ -279,14 +306,9 @@ open class SquitRequestTask : DefaultTask() {
it.password
)
}
}

config
.postRunners
.map {
postRunnersCache.getOrPut(it) { Class.forName(it).getConstructor().newInstance() as SquitPostRunner }
}
.forEach { it.run(config) }

private fun executePostRunnerScripts(config: Config) {
config.postRunnerScripts.forEach {
GroovyShell(javaClass.classLoader)
.parse(it.toFile())
Expand All @@ -295,6 +317,15 @@ open class SquitRequestTask : DefaultTask() {
}
}

private fun executePostRunners(config: Config) {
config
.postRunners
.map {
postRunnersCache.getOrPut(it) { Class.forName(it).getConstructor().newInstance() as SquitPostRunner }
}
.forEach { it.run(config) }
}

private fun constructApiCall(requestPath: Path?, config: Config): Call {
val requestBody = requestPath?.toFile()?.asRequestBody(config.mediaType)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.smartsquare.squit.config

import com.typesafe.config.ConfigException.BadValue
import com.typesafe.config.ConfigFactory
import de.smartsquare.squit.TestUtils
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
Expand Down Expand Up @@ -491,4 +492,35 @@ class ConfigExtensionsTest {

call shouldNotThrow AnyException
}

@Test
fun `config object with an invalid preTestTask`() {
val config = ConfigFactory.parseMap(
mapOf(
"endpoint" to "https://example.com",
"preTestTasks" to listOf("[NotExistingTask]"),
)
)

val call = { config.validate() }
@Suppress("MaxLineLength")
call shouldThrow BadValue::class withMessage "hardcoded value: Invalid value at 'preTestTasks': " +
"The enum class SquitPreTestTask has no constant of the name '[NotExistingTask]' " +
"(should be one of [DATABASE_SCRIPTS, PRE_RUNNERS, PRE_RUNNER_SCRIPTS].)"
}

@Test
fun `config object with an invalid postTestTask`() {
val config = ConfigFactory.parseMap(
mapOf(
"endpoint" to "https://example.com",
"postTestTasks" to listOf("[NotExistingTask]"),
)
)

val call = { config.validate() }
call shouldThrow BadValue::class withMessage "hardcoded value: Invalid value at 'postTestTasks': " +
"The enum class SquitPostTestTask has no constant of the name '[NotExistingTask]' " +
"(should be one of [DATABASE_SCRIPTS, POST_RUNNERS, POST_RUNNER_SCRIPTS].)"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package de.smartsquare.squit.task

import de.smartsquare.squit.TestUtils
import de.smartsquare.squit.gradleRunner
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.amshove.kluent.shouldBe
import org.amshove.kluent.shouldBeAfter
import org.amshove.kluent.shouldBeBefore
import org.amshove.kluent.shouldBeEqualTo
import org.amshove.kluent.shouldBeFalse
import org.amshove.kluent.shouldExist
import org.amshove.kluent.shouldNotExist
import org.gradle.testkit.runner.TaskOutcome
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.sql.DriverManager
import java.time.Instant

class SquitRequestTaskConfigurableTasksTest {
private val project = TestUtils.getResourcePath("test-project-task-config")

private val jdbc = "jdbc:h2:$project/testDb;IFEXISTS=TRUE"
private val username = "test"
private val password = "test"
private val preRunFile = project.resolve("build/pre_run.txt").toFile()
private val postRunFile = project.resolve("build/post_run.txt").toFile()

private lateinit var server: MockWebServer

@BeforeEach
fun setUp() {
server = MockWebServer()
}

@AfterEach
fun tearDown() {
server.shutdown()

TestUtils.deleteDatabaseFiles(project)
preRunFile.delete()
postRunFile.delete()
}

@Test
fun `should execute pre and post tasks in default order`() {
server.enqueue(MockResponse().setBody("<cool/>"))

val arguments = listOf(
"squitRunRequests", "-Psquit.endpointPlaceholder=${server.url("/")}",
"-Psquit.rootDir=$project", "-Ptags=default"
)

val result = gradleRunner(project, arguments).build()

result.task(":squitRunRequests")?.outcome shouldBe TaskOutcome.SUCCESS
val preScriptExecution = Instant.ofEpochMilli(preRunFile.readText().toLong())
val postScriptExecution = Instant.ofEpochMilli(postRunFile.readText().toLong())
DriverManager.getConnection(jdbc, username, password).use { connection ->
val resultSet = connection.createStatement().executeQuery("SELECT * FROM TIMESTAMPS")
resultSet.next()
val preDbScriptExecution = resultSet.getTimestamp(2).toInstant()
resultSet.getString(3) shouldBeEqualTo "TEST_PRE.SQL"
resultSet.next()
resultSet.getString(3) shouldBeEqualTo "TEST_POST.SQL"
val postDbScriptExecution = resultSet.getTimestamp(2).toInstant()
preDbScriptExecution shouldBeBefore postDbScriptExecution
preScriptExecution shouldBeBefore postScriptExecution
preScriptExecution shouldBeBefore preDbScriptExecution
postScriptExecution shouldBeAfter postDbScriptExecution
}
}

@Test
fun `should execute scripts in configured order`() {
server.enqueue(MockResponse().setBody("<cool/>"))

val arguments = listOf(
"squitRunRequests", "-Psquit.endpointPlaceholder=${server.url("/")}",
"-Psquit.rootDir=$project", "-Ptags=configured_order"
)

val result = gradleRunner(project, arguments).build()

result.task(":squitRunRequests")?.outcome shouldBe TaskOutcome.SUCCESS

val preScriptExecution = Instant.ofEpochMilli(preRunFile.readText().toLong())
val postScriptExecution = Instant.ofEpochMilli(postRunFile.readText().toLong())
DriverManager.getConnection(jdbc, username, password).use { connection ->
val resultSet = connection.createStatement().executeQuery("SELECT * FROM TIMESTAMPS")
resultSet.next()
val preDbScriptExecution = resultSet.getTimestamp(2).toInstant()
resultSet.getString(3) shouldBeEqualTo "TEST_PRE.SQL"
resultSet.next()
resultSet.getString(3) shouldBeEqualTo "TEST_POST.SQL"
val postDbScriptExecution = resultSet.getTimestamp(2).toInstant()
preDbScriptExecution shouldBeBefore postDbScriptExecution
preScriptExecution shouldBeBefore postScriptExecution
preScriptExecution shouldBeAfter preDbScriptExecution
postScriptExecution shouldBeBefore postDbScriptExecution
}
}

@Test
fun `should only execute pre db script and post script`() {
server.enqueue(MockResponse().setBody("<cool/>"))

val arguments = listOf(
"squitRunRequests", "-Psquit.endpointPlaceholder=${server.url("/")}",
"-Psquit.rootDir=$project", "-Ptags=only_pre_db_script"
)

val result = gradleRunner(project, arguments).build()

result.task(":squitRunRequests")?.outcome shouldBe TaskOutcome.SUCCESS

DriverManager.getConnection(jdbc, username, password).use { connection ->
val resultSet = connection.createStatement().executeQuery("SELECT * FROM TIMESTAMPS")
resultSet.next()
resultSet.getString(3) shouldBeEqualTo "TEST_PRE.SQL"
resultSet.next().shouldBeFalse()
}
preRunFile.shouldNotExist()
postRunFile.shouldExist()
}
}
8 changes: 8 additions & 0 deletions src/test/resources/test-project-task-config/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
plugins {
id 'base'
id 'de.smartsquare.squit'
}

squit {
jdbcDrivers = ["org.h2.Driver"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = "test-project-task-config"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>

<animals>
<animal name="dog"/>
<animal name="cat"/>
</animals>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8" ?>

<cool/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
preTestTasks = [DATABASE_SCRIPTS, PRE_RUNNER_SCRIPTS]
postTestTasks = [POST_RUNNER_SCRIPTS, DATABASE_SCRIPTS]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INSERT INTO TIMESTAMPS (CREATED_DATE, SCRIPT) VALUES (CURRENT_TIMESTAMP, 'TEST_POST.SQL');
Loading

0 comments on commit 28d3388

Please sign in to comment.