migratedb.v1.integrationtest.util.dsl.Dsl.kt Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2022-2024 The MigrateDB contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package migratedb.v1.integrationtest.util.dsl
import io.kotest.matchers.booleans.shouldBeTrue
import migratedb.v1.core.api.Checksum
import migratedb.v1.core.api.MigrationInfo
import migratedb.v1.core.api.MigrationInfoService
import migratedb.v1.core.api.Version
import migratedb.v1.core.api.internal.schemahistory.AppliedMigration
import migratedb.v1.core.api.output.BaselineResult
import migratedb.v1.core.api.output.LiberateResult
import migratedb.v1.core.api.output.MigrateResult
import migratedb.v1.core.api.output.RepairResult
import migratedb.v1.integrationtest.database.DbSystem
import migratedb.v1.integrationtest.database.mutation.IndependentDatabaseMutation
import migratedb.v1.integrationtest.util.base.SafeIdentifier
import migratedb.v1.integrationtest.util.base.SafeIdentifier.Companion.asSafeIdentifier
import migratedb.v1.integrationtest.util.container.SharedResources
import migratedb.v1.integrationtest.util.dsl.internal.DatabaseContext
import migratedb.v1.integrationtest.util.dsl.internal.GivenStepImpl
import migratedb.v1.integrationtest.util.dsl.internal.ThenStepImpl
import migratedb.v1.integrationtest.util.dsl.internal.WhenStepImpl
import org.springframework.jdbc.core.JdbcTemplate
class Dsl(dbSystem: DbSystem, sharedResources: SharedResources) : AutoCloseable {
companion object {
/**
* Auto-completes a shortened migration name like "V1" to a valid migration name like "V1__V1".
*/
fun String.toMigrationName() = when {
contains("__") -> this
else -> "${this}__V${this.drop(1)}"
}
/**
* Reconstructs the migration name in the default format, e.g., "V1__My_Cool_Migration".
*/
fun MigrationInfo.migrationName() = when {
isRepeatable -> "R__" + description.spaceToUnderscore()
else -> (if (type.isBaselineMigration) "B" else "V") +
version?.dotToUnderscore() + "__" + description.spaceToUnderscore()
}
/**
* Returns a checksum that depends on the string value and the [delta] value. If, and only if, [delta] is zero,
* the checksum will be equal to `Checksum.builder().addString(this).build()`.
*/
fun String.checksum(delta: Int = 0): Checksum = Checksum.builder()
.addString(this).apply {
delta.takeUnless { it == 0 }?.let {
addNumber(it.toBigInteger())
}
}
.build()
private fun Version.dotToUnderscore() = toString().replace('.', '_')
private fun String.spaceToUnderscore() = replace(' ', '_')
/**
* Applies [toMigrationName] to every element in a list of strings, returning `null` if this list is `null`.
*/
fun List?.toMigrationNames() = this?.map { it.toMigrationName() }
}
private val databaseInstance = lazy { dbSystem.get(sharedResources) }
private val givenStep = GivenStepImpl(databaseInstance::value)
override fun close() {
databaseInstance.value.use {
givenStep.use {
}
}
}
/**
* Normalizes the case of a table name.
*/
fun normalize(s: SafeIdentifier) = normalize(s.toString()).asSafeIdentifier()
/**
* Normalizes the case of a table name.
*/
fun normalize(s: CharSequence): String = databaseInstance.value.normalizeCase(s)
fun given(block: (GivenStep).() -> G): GivenStepResult {
val g = givenStep.block()
return object : GivenStepResult {
override fun `when`(block: WhenStep.() -> W): WhenStepResult {
givenStep.executeActions().let { givenInfo ->
WhenStepImpl(g, givenInfo).let { whenStep ->
val w = whenStep.block()
return object : WhenStepResult {
override fun then(block: (ThenStep).(W) -> Unit) {
val thenStep = ThenStepImpl(g, givenInfo)
thenStep.block(w)
}
}
}
}
}
}
}
interface GivenStep {
fun database(block: DatabaseSpec.() -> Unit)
fun independentDbMutation(): IndependentDatabaseMutation
/**
* For context-specific extensions, not meant to be called by tests.
*
* ```
* fun GivenStep.myPrecondition() = extend { databaseContext ->
* // establish precondition
* }
* ```
*/
fun extend(extension: (DatabaseContext) -> Unit)
}
interface GivenStepResult {
fun `when`(block: (WhenStep).() -> W): WhenStepResult
}
interface AfterGiven {
val given: G
val schemaName: SafeIdentifier?
}
interface WhenStep : AfterGiven {
fun justRun(block: JustRun.() -> T): T
fun baseline(block: RunBaselineSpec.() -> Unit): BaselineResult
fun migrate(block: RunMigrateSpec.() -> Unit): MigrateResult
fun info(block: RunInfoSpec.() -> Unit): MigrationInfoService
fun repair(block: RunRepairSpec.() -> Unit): RepairResult
fun liberate(block: RunLiberateSpec.() -> Unit): LiberateResult
fun arbitraryMutation(): IndependentDatabaseMutation
}
interface WhenStepResult {
fun then(block: (ThenStep).(W) -> Unit)
}
interface ThenStep : AfterGiven {
fun withConnection(block: (JdbcTemplate) -> Unit)
fun schemaHistory(table: String? = null, block: (List).() -> Unit)
fun IndependentDatabaseMutation.shouldBeApplied() {
withConnection {
it.dataSource!!.connection.apply {
isApplied(this).shouldBeTrue()
}
}
}
}
}