Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add option to configure pre- and post-testtasks per test #25

Merged
merged 4 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
)
}
RuepInc marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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
)
}
RuepInc marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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")
RuepInc marked this conversation as resolved.
Show resolved Hide resolved
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!!) {
RuepInc marked this conversation as resolved.
Show resolved Hide resolved
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!!) {
RuepInc marked this conversation as resolved.
Show resolved Hide resolved
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
RuepInc marked this conversation as resolved.
Show resolved Hide resolved
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")
RuepInc marked this conversation as resolved.
Show resolved Hide resolved
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].)"
}
}
RuepInc marked this conversation as resolved.
Show resolved Hide resolved
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
Loading