Testing migrations
Testing a migration
A NPL migration is made up of one or more changes as described in the migration descriptor file (migration.yml
). The
testing tool, EngineMigrationTester
, provides a way to run all the changes in one step, or each change individually.
To get started,
- Specify the directory where the migration artifacts reside. To see what artifacts are necessary, see Migrations - getting started.
- Use a classpath resolver to determine the actual system location of the migration artifact directory.
- Provide various configuration details using the
Configuration
object. Use the configuration inner class,DbConfig
, to set the connection details for the test database. - Instantiate the tester
val migrationDir = "migration-files/"
val migrationLocation = File(javaClass.getResource(migrationDir)!!.path)
val dbConfig = DBConfig(dbUrl = url, dbSchema = schema, dbUser = user, dbPass = password)
val tester = EngineMigrationTester(migrationLocation, Configuration(dbConfig = dbConfig))
Verify a change
To validate a change, the typical assertions methods will suffice.
The following functions can be used to retrieve and modify protocols and the associated states.
Create a new protocol state
val createdId = tester.createProtocol(
"/ENGINE_IT-1.0.0?/testpkg/Iou",
listOf(NumberValue(1000)),
listOf(issuerParty, payeeParty)
)
Execute a protocol permission
To execute a permission, e.g. on the IOU protocol, would be like this:
selectAction(createdId, "pay", listOf(NumberValue(100)))
or like this if the action returns a value
val amount = selectAction(createdId, "getAmountOwed") as NumberValue
Retrieve protocol state(s)
There are several ways to retrieve protocol states.
To retrieve a specific protocol state by its id, use getProtocolStateById
val protocolState = tester.getProtocolStateById(createdId)
To retrieve all states for a given protocol prototype id, use getCurrentProtocolStates
val allContract100States = tester.getCurrentProtocolStates(CONTRACT_1_0_0).toList()
To retrieve the states for all protocol instances, use getAllProtocolStates
val allStates = tester.getAllProtocolStates().toList()
Run migration in one step
To run all changes and then verify, use the run
function.
val tester = EngineMigrationTester(migrationHome, Configuration(getDbConfig()))
tester.run()
Examples
An example of running all changes in a migration and asserting success
fun `Run full migration and assert`() {
val migrationDir = "migration-files/"
val migrationLocation = File(javaClass.getResource(migrationDir)!!.path)
val tester = EngineMigrationTester(migrationLocation, Configuration(getDbConfig()))
tester.run()
val states = tester.getCurrentProtocolStates("/ENGINE_IT-1.0.2?/testpkg/Foo")
assertEquals(2, states.count())
tester.close()
}
Run a migration step by step
To run multiple changes independently, use the runTo
function. The runTo
function will apply the changes in order,
determined by the descriptor file, up to and including the change with the name specified. All previously migrated steps
will be skipped.
val tester = EngineMigrationTester(migrationLocation, Configuration(getDbConfig()))
// run to specific change
tester.runTo("1.0.2")
// ... assertions verifying specified change was migrated successful ...
tester.runTo("1.0.3")
Examples
An example of running each change separately and asserting success after each
fun `Migrate to each version and assert`() {
val migrationDir = "migration-files/"
val migrationLocation = File(javaClass.getResource(migrationDir)!!.path)
val tester = EngineMigrationTester(migrationLocation, Configuration(getDbConfig()))
tester.runTo("1.0.0") // Apply only the first change
// Assert first change completed successfully
val createdId =
tester.createProtocol("/ENGINE_IT-1.0.0?/testpkg/Iou", listOf(NumberValue(1000)), listOf(p1, p2))
tester.apply {
val originalState = getProtocolStateById(createdId)
val stateAmount = (originalState?.getFrameValue("forAmount") as NumberValue).value
val selectAmount = (selectAction(createdId, "getAmountOwed") as NumberValue).value
assertNotNull(originalState)
assertEquals(1000, stateAmount.intValueExact())
assertEquals(1000, selectAmount.intValueExact())
selectAction(createdId, "pay", listOf(NumberValue(100)))
selectAction(createdId, "pay", listOf(NumberValue(0)))
selectAction(createdId, "pay", listOf(NumberValue(50)))
val afterPayments = getProtocolStateById(createdId)
val payments = (afterPayments?.getFrameValue("payments") as ListValue).value.map {
((it as StructValue).slots.getValue("amount") as NumberValue).value.intValueExact()
}
assertContentEquals(listOf(100, 0, 50), payments)
}
// Apply and assert second change
tester.runTo("1.0.1")
tester.apply {
val state = getProtocolStateById(createdId)
val payments = (state?.getFrameValue("payments") as ListValue).value.map {
((it as StructValue).slots.getValue("amount") as NumberValue).value.intValueExact()
}
assertContentEquals(listOf(100, 50), payments)
}
// Apply and assert third change
tester.runTo("1.0.2")
tester.apply {
val state = getProtocolStateById(createdId)
val lateFee = (state?.getFrameValue("lateFee") as NumberValue).value.intValueExact()
val paymentDeadline = (state.getFrameValue("paymentDeadline") as DateTimeValue).value
val expectedPaymentDate = ZonedDateTime.now().plusMonths(3).truncatedTo(ChronoUnit.DAYS)
assertEquals(300, lateFee)
assertEquals(expectedPaymentDate, paymentDeadline)
}
// Assert logs
val logs = tester.getLog()
assertEquals(3, logs.size)
assertTrue(logs.all { it.executionResult == NPLMigrationResult.SUCCESSFUL })
logs.filter { it.changeId.startsWith("1.0.0") }.apply {
assertEquals(1, size)
assertEquals(1, filter { it.changeId.endsWith("migrate") }.size)
}
logs.filter { it.changeId.startsWith("1.0.1") }.apply {
single { it.changeId.endsWith("migrate") }
}
logs.filter { it.changeId.startsWith("1.0.2") }.apply {
single { it.changeId.endsWith("migrate") }
}
tester.close()
}