contracts, core, node: Port TransactionForTest tests to new DSL

This commit is contained in:
Andras Slemmer 2016-07-05 12:10:38 +01:00
parent a27f195b4f
commit e3d6f51049
7 changed files with 248 additions and 356 deletions

View File

@ -76,6 +76,7 @@ class CommercialPaperTestsGeneric {
output("paper") { thisTest.getPaper() } output("paper") { thisTest.getPaper() }
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() } command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies()
} }
// The CP is sold to alice for her $900, $100 less than the face value. At 10% interest after only 7 days, // The CP is sold to alice for her $900, $100 less than the face value. At 10% interest after only 7 days,
@ -87,6 +88,7 @@ class CommercialPaperTestsGeneric {
output("alice's paper") { "paper".output<ICommercialPaperState>().data `owned by` ALICE_PUBKEY } output("alice's paper") { "paper".output<ICommercialPaperState>().data `owned by` ALICE_PUBKEY }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() } command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() }
this.verifies()
} }
// Time passes, and Alice redeem's her CP for $1000, netting a $100 profit. MegaCorp has received $1200 // Time passes, and Alice redeem's her CP for $1000, netting a $100 profit. MegaCorp has received $1200
@ -95,7 +97,7 @@ class CommercialPaperTestsGeneric {
input("alice's paper") input("alice's paper")
input("some profits") input("some profits")
fun TransactionDsl<TransactionDslInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) { fun TransactionDsl<LastLineShouldTestForVerifiesOrFails, TransactionDslInterpreter<LastLineShouldTestForVerifiesOrFails>>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE_PUBKEY } output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE_PUBKEY }
output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP_PUBKEY } output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP_PUBKEY }
} }

View File

@ -360,7 +360,7 @@ class IRSTests {
/** /**
* Generates a typical transactional history for an IRS. * Generates a typical transactional history for an IRS.
*/ */
fun trade(): LedgerDsl<TestTransactionDslInterpreter, TestLedgerDslInterpreter> { fun trade(): LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter> {
val ld = LocalDate.of(2016, 3, 8) val ld = LocalDate.of(2016, 3, 8)
val bd = BigDecimal("0.0063518") val bd = BigDecimal("0.0063518")
@ -370,6 +370,7 @@ class IRSTests {
output("irs post agreement") { singleIRS() } output("irs post agreement") { singleIRS() }
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies()
} }
transaction("Fix") { transaction("Fix") {
@ -390,82 +391,93 @@ class IRSTests {
Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd) Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)
} }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies()
} }
} }
} }
@Test @Test
fun `ensure failure occurs when there are inbound states for an agreement command`() { fun `ensure failure occurs when there are inbound states for an agreement command`() {
transaction { ledger {
input() { singleIRS() } transaction {
output("irs post agreement") { singleIRS() } input() { singleIRS() }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } output("irs post agreement") { singleIRS() }
timestamp(TEST_TX_TIME) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
this `fails requirement` "There are no in states for an agreement" timestamp(TEST_TX_TIME)
this `fails with` "There are no in states for an agreement"
}
} }
} }
@Test @Test
fun `ensure failure occurs when no events in fix schedule`() { fun `ensure failure occurs when no events in fix schedule`() {
val irs = singleIRS() ledger {
val emptySchedule = HashMap<LocalDate, FixedRatePaymentEvent>() val irs = singleIRS()
transaction { val emptySchedule = HashMap<LocalDate, FixedRatePaymentEvent>()
output() { transaction {
irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule)) output() {
irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule))
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails with` "There are events in the fix schedule"
} }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails requirement` "There are events in the fix schedule"
} }
} }
@Test @Test
fun `ensure failure occurs when no events in floating schedule`() { fun `ensure failure occurs when no events in floating schedule`() {
val irs = singleIRS() ledger {
val emptySchedule = HashMap<LocalDate, FloatingRatePaymentEvent>() val irs = singleIRS()
transaction { val emptySchedule = HashMap<LocalDate, FloatingRatePaymentEvent>()
output() { transaction {
irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule)) output() {
irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule))
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails with` "There are events in the float schedule"
} }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails requirement` "There are events in the float schedule"
} }
} }
@Test @Test
fun `ensure notionals are non zero`() { fun `ensure notionals are non zero`() {
val irs = singleIRS() ledger {
transaction { val irs = singleIRS()
output() { transaction {
irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0))) output() {
irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0)))
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails with` "All notionals must be non zero"
} }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails requirement` "All notionals must be non zero"
}
transaction { transaction {
output() { output() {
irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0))) irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0)))
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails with` "All notionals must be non zero"
} }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails requirement` "All notionals must be non zero"
} }
} }
@Test @Test
fun `ensure positive rate on fixed leg`() { fun `ensure positive rate on fixed leg`() {
val irs = singleIRS() ledger {
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(fixedRate = FixedRate(PercentageRatioUnit("-0.1")))) val irs = singleIRS()
transaction { val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(fixedRate = FixedRate(PercentageRatioUnit("-0.1"))))
output() { transaction {
modifiedIRS output() {
modifiedIRS
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails with` "The fixed leg rate must be positive"
} }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails requirement` "The fixed leg rate must be positive"
} }
} }
@ -474,173 +486,183 @@ class IRSTests {
*/ */
@Test @Test
fun `ensure same currency notionals`() { fun `ensure same currency notionals`() {
val irs = singleIRS() ledger {
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.fixedLeg.notional.quantity, Currency.getInstance("JPY")))) val irs = singleIRS()
transaction { val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.fixedLeg.notional.quantity, Currency.getInstance("JPY"))))
output() { transaction {
modifiedIRS output() {
modifiedIRS
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails with` "The currency of the notionals must be the same"
} }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails requirement` "The currency of the notionals must be the same"
} }
} }
@Test @Test
fun `ensure notional amounts are equal`() { fun `ensure notional amounts are equal`() {
val irs = singleIRS() ledger {
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.floatingLeg.notional.quantity + 1, irs.floatingLeg.notional.token))) val irs = singleIRS()
transaction { val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.floatingLeg.notional.quantity + 1, irs.floatingLeg.notional.token)))
output() { transaction {
modifiedIRS output() {
modifiedIRS
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails with` "All leg notionals must be the same"
} }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails requirement` "All leg notionals must be the same"
} }
} }
@Test @Test
fun `ensure trade date and termination date checks are done pt1`() { fun `ensure trade date and termination date checks are done pt1`() {
val irs = singleIRS() ledger {
val modifiedIRS1 = irs.copy(fixedLeg = irs.fixedLeg.copy(terminationDate = irs.fixedLeg.effectiveDate.minusDays(1))) val irs = singleIRS()
transaction { val modifiedIRS1 = irs.copy(fixedLeg = irs.fixedLeg.copy(terminationDate = irs.fixedLeg.effectiveDate.minusDays(1)))
output() { transaction {
modifiedIRS1 output() {
modifiedIRS1
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails with` "The effective date is before the termination date for the fixed leg"
} }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails requirement` "The effective date is before the termination date for the fixed leg"
}
val modifiedIRS2 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.floatingLeg.effectiveDate.minusDays(1))) val modifiedIRS2 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.floatingLeg.effectiveDate.minusDays(1)))
transaction { transaction {
output() { output() {
modifiedIRS2 modifiedIRS2
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails with` "The effective date is before the termination date for the floating leg"
} }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails requirement` "The effective date is before the termination date for the floating leg"
} }
} }
@Test @Test
fun `ensure trade date and termination date checks are done pt2`() { fun `ensure trade date and termination date checks are done pt2`() {
val irs = singleIRS() ledger {
val irs = singleIRS()
val modifiedIRS3 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.fixedLeg.terminationDate.minusDays(1))) val modifiedIRS3 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.fixedLeg.terminationDate.minusDays(1)))
transaction { transaction {
output() { output() {
modifiedIRS3 modifiedIRS3
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails with` "The termination dates are aligned"
} }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails requirement` "The termination dates are aligned"
}
val modifiedIRS4 = irs.copy(floatingLeg = irs.floatingLeg.copy(effectiveDate = irs.fixedLeg.effectiveDate.minusDays(1))) val modifiedIRS4 = irs.copy(floatingLeg = irs.floatingLeg.copy(effectiveDate = irs.fixedLeg.effectiveDate.minusDays(1)))
transaction { transaction {
output() { output() {
modifiedIRS4 modifiedIRS4
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails with` "The effective dates are aligned"
} }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this `fails requirement` "The effective dates are aligned"
} }
} }
@Test @Test
fun `various fixing tests`() { fun `various fixing tests`() {
ledger {
val ld = LocalDate.of(2016, 3, 8) val ld = LocalDate.of(2016, 3, 8)
val bd = BigDecimal("0.0063518") val bd = BigDecimal("0.0063518")
transaction {
output("irs post agreement") { singleIRS() }
arg(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this.accepts()
}
val oldIRS = singleIRS(1)
val newIRS = oldIRS.copy(oldIRS.fixedLeg,
oldIRS.floatingLeg,
oldIRS.calculation.applyFixing(ld, FixedRate(RatioUnit(bd))),
oldIRS.common)
transaction {
input() {
oldIRS
transaction {
output("irs post agreement") { singleIRS() }
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME)
this.verifies()
} }
// Templated tweak for reference. A corrent fixing applied should be ok val oldIRS = singleIRS(1)
tweak { val newIRS = oldIRS.copy(oldIRS.fixedLeg,
arg(ORACLE_PUBKEY) { oldIRS.floatingLeg,
InterestRateSwap.Commands.Fix() oldIRS.calculation.applyFixing(ld, FixedRate(RatioUnit(bd))),
} oldIRS.common)
timestamp(TEST_TX_TIME)
arg(ORACLE_PUBKEY) {
Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)
}
output() { newIRS }
this.accepts()
}
// This test makes sure that verify confirms the fixing was applied and there is a difference in the old and new transaction {
tweak { input() {
arg(ORACLE_PUBKEY) { InterestRateSwap.Commands.Fix() } oldIRS
timestamp(TEST_TX_TIME)
arg(ORACLE_PUBKEY) { Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd) }
output() { oldIRS }
this`fails requirement` "There is at least one difference in the IRS floating leg payment schedules"
}
// This tests tries to sneak in a change to another fixing (which may or may not be the latest one)
tweak {
arg(ORACLE_PUBKEY) { InterestRateSwap.Commands.Fix() }
timestamp(TEST_TX_TIME)
arg(ORACLE_PUBKEY) {
Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)
} }
val firstResetKey = newIRS.calculation.floatingLegPaymentSchedule.keys.first() // Templated tweak for reference. A corrent fixing applied should be ok
val firstResetValue = newIRS.calculation.floatingLegPaymentSchedule[firstResetKey] tweak {
val modifiedFirstResetValue = firstResetValue!!.copy(notional = Amount(firstResetValue.notional.quantity, Currency.getInstance("JPY"))) command(ORACLE_PUBKEY) {
InterestRateSwap.Commands.Fix()
output() { }
newIRS.copy( timestamp(TEST_TX_TIME)
newIRS.fixedLeg, command(ORACLE_PUBKEY) {
newIRS.floatingLeg, Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)
newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus( }
Pair(firstResetKey, modifiedFirstResetValue))), output() { newIRS }
newIRS.common this.verifies()
)
} }
this`fails requirement` "There is only one change in the IRS floating leg payment schedule"
}
// This tests modifies the payment currency for the fixing // This test makes sure that verify confirms the fixing was applied and there is a difference in the old and new
tweak { tweak {
arg(ORACLE_PUBKEY) { InterestRateSwap.Commands.Fix() } command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Fix() }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
arg(ORACLE_PUBKEY) { Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd) } command(ORACLE_PUBKEY) { Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd) }
output() { oldIRS }
val latestReset = newIRS.calculation.floatingLegPaymentSchedule.filter { it.value.rate is FixedRate }.maxBy { it.key } this `fails with` "There is at least one difference in the IRS floating leg payment schedules"
val modifiedLatestResetValue = latestReset!!.value.copy(notional = Amount(latestReset.value.notional.quantity, Currency.getInstance("JPY"))) }
output() { // This tests tries to sneak in a change to another fixing (which may or may not be the latest one)
newIRS.copy( tweak {
newIRS.fixedLeg, command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Fix() }
newIRS.floatingLeg, timestamp(TEST_TX_TIME)
newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus( command(ORACLE_PUBKEY) {
Pair(latestReset.key, modifiedLatestResetValue))), Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)
newIRS.common }
)
val firstResetKey = newIRS.calculation.floatingLegPaymentSchedule.keys.first()
val firstResetValue = newIRS.calculation.floatingLegPaymentSchedule[firstResetKey]
val modifiedFirstResetValue = firstResetValue!!.copy(notional = Amount(firstResetValue.notional.quantity, Currency.getInstance("JPY")))
output() {
newIRS.copy(
newIRS.fixedLeg,
newIRS.floatingLeg,
newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus(
Pair(firstResetKey, modifiedFirstResetValue))),
newIRS.common
)
}
this `fails with` "There is only one change in the IRS floating leg payment schedule"
}
// This tests modifies the payment currency for the fixing
tweak {
command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Fix() }
timestamp(TEST_TX_TIME)
command(ORACLE_PUBKEY) { Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd) }
val latestReset = newIRS.calculation.floatingLegPaymentSchedule.filter { it.value.rate is FixedRate }.maxBy { it.key }
val modifiedLatestResetValue = latestReset!!.value.copy(notional = Amount(latestReset.value.notional.quantity, Currency.getInstance("JPY")))
output() {
newIRS.copy(
newIRS.fixedLeg,
newIRS.floatingLeg,
newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus(
Pair(latestReset.key, modifiedLatestResetValue))),
newIRS.common
)
}
this `fails with` "The fix payment has the same currency as the notional"
} }
this`fails requirement` "The fix payment has the same currency as the notional"
} }
} }
} }
@ -652,7 +674,7 @@ class IRSTests {
* result and the grouping won't work either. * result and the grouping won't work either.
* In reality, the only fields that should be in common will be the next fixing date and the reference rate. * In reality, the only fields that should be in common will be the next fixing date and the reference rate.
*/ */
fun tradegroups(): LedgerDsl<TestTransactionDslInterpreter, TestLedgerDslInterpreter> { fun tradegroups(): LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter> {
val ld1 = LocalDate.of(2016, 3, 8) val ld1 = LocalDate.of(2016, 3, 8)
val bd1 = BigDecimal("0.0063518") val bd1 = BigDecimal("0.0063518")
@ -670,6 +692,7 @@ class IRSTests {
} }
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies()
} }
transaction("Agreement") { transaction("Agreement") {
@ -683,6 +706,7 @@ class IRSTests {
} }
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies()
} }
transaction("Fix") { transaction("Fix") {
@ -714,6 +738,7 @@ class IRSTests {
Fix(FixOf("ICE LIBOR", ld1, Tenor("3M")), bd1) Fix(FixOf("ICE LIBOR", ld1, Tenor("3M")), bd1)
} }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies()
} }
} }
} }

View File

@ -33,7 +33,9 @@ class ObligationTests {
) )
val outState = inState.copy(beneficiary = DUMMY_PUBKEY_2) val outState = inState.copy(beneficiary = DUMMY_PUBKEY_2)
private fun obligationTestRoots(group: LedgerDsl<TestTransactionDslInterpreter, TestLedgerDslInterpreter>) = group.apply { private fun obligationTestRoots(
group: LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>
) = group.apply {
nonVerifiedTransaction { nonVerifiedTransaction {
output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY)) output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY))
output("Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION `between` Pair(BOB, ALICE_PUBKEY)) output("Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION `between` Pair(BOB, ALICE_PUBKEY))
@ -341,6 +343,7 @@ class ObligationTests {
// Note we can sign with either key here // Note we can sign with either key here
command(ALICE_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } command(ALICE_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies()
} }
this.verifies() this.verifies()
} }
@ -356,6 +359,7 @@ class ObligationTests {
output("change") { oneMillionDollars.OBLIGATION `between` Pair(MEGA_CORP, BOB_PUBKEY) } output("change") { oneMillionDollars.OBLIGATION `between` Pair(MEGA_CORP, BOB_PUBKEY) }
command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies()
} }
this.verifies() this.verifies()
} }
@ -397,6 +401,7 @@ class ObligationTests {
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
command(ALICE_PUBKEY, BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } command(ALICE_PUBKEY, BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies()
} }
this.verifies() this.verifies()
} }
@ -423,6 +428,7 @@ class ObligationTests {
output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION `between` Pair(MEGA_CORP, ALICE_PUBKEY) } output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION `between` Pair(MEGA_CORP, ALICE_PUBKEY) }
command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies()
} }
this.verifies() this.verifies()
} }
@ -452,6 +458,7 @@ class ObligationTests {
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY } output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Obligation.IssuanceDefinition(ALICE, defaultUsd.OBLIGATION_DEF), Amount(oneMillionDollars.quantity, USD)) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Obligation.IssuanceDefinition(ALICE, defaultUsd.OBLIGATION_DEF), Amount(oneMillionDollars.quantity, USD)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
this.verifies()
} }
this.verifies() this.verifies()
} }
@ -496,6 +503,7 @@ class ObligationTests {
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) } output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Obligation.IssuanceDefinition(ALICE, defaultUsd.OBLIGATION_DEF) `at` pastTestTime, Lifecycle.DEFAULTED) } command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Obligation.IssuanceDefinition(ALICE, defaultUsd.OBLIGATION_DEF) `at` pastTestTime, Lifecycle.DEFAULTED) }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies()
} }
this.verifies() this.verifies()
} }

View File

@ -10,16 +10,10 @@ import com.r3corda.core.node.services.IdentityService
import com.r3corda.core.node.services.StorageService import com.r3corda.core.node.services.StorageService
import com.r3corda.core.node.services.testing.MockIdentityService import com.r3corda.core.node.services.testing.MockIdentityService
import com.r3corda.core.node.services.testing.MockStorageService import com.r3corda.core.node.services.testing.MockStorageService
import com.r3corda.core.seconds
import com.r3corda.core.serialization.serialize
import java.net.ServerSocket import java.net.ServerSocket
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.fail
/** If an exception is thrown by the body, rethrows the root cause exception. */ /** If an exception is thrown by the body, rethrows the root cause exception. */
inline fun <R> rootCauseExceptions(body: () -> R): R { inline fun <R> rootCauseExceptions(body: () -> R): R {
@ -132,27 +126,6 @@ val MOCK_IDENTITY_SERVICE = JavaTestHelpers.MOCK_IDENTITY_SERVICE
fun generateStateRef() = JavaTestHelpers.generateStateRef() fun generateStateRef() = JavaTestHelpers.generateStateRef()
fun transaction(body: TransactionForTest.() -> LastLineShouldTestForAcceptOrFailure) = JavaTestHelpers.transaction(body)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Defines a simple DSL for building pseudo-transactions (not the same as the wire protocol) for testing purposes.
//
// Define a transaction like this:
//
// transaction {
// input { someExpression }
// output { someExpression }
// arg { someExpression }
//
// tweak {
// ... same thing but works with a copy of the parent, can add inputs/outputs/args just within this scope.
// }
//
// contract.accepts() -> should pass
// contract `fails requirement` "some substring of the error message"
// }
//
class LabeledOutput(val label: String?, val state: TransactionState<*>) { class LabeledOutput(val label: String?, val state: TransactionState<*>) {
override fun toString() = state.toString() + (if (label != null) " ($label)" else "") override fun toString() = state.toString() + (if (label != null) " ($label)" else "")
override fun equals(other: Any?) = other is LabeledOutput && state.equals(other.state) override fun equals(other: Any?) = other is LabeledOutput && state.equals(other.state)
@ -161,148 +134,3 @@ class LabeledOutput(val label: String?, val state: TransactionState<*>) {
infix fun TransactionState<*>.label(label: String) = LabeledOutput(label, this) infix fun TransactionState<*>.label(label: String) = LabeledOutput(label, this)
abstract class AbstractTransactionForTest {
protected val attachments = ArrayList<SecureHash>()
protected val outStates = ArrayList<LabeledOutput>()
protected val commands = ArrayList<Command>()
protected val signers = LinkedHashSet<PublicKey>()
protected val type = TransactionType.General()
@JvmOverloads
open fun output(label: String? = null, s: () -> ContractState) = LabeledOutput(label, TransactionState(s(), DUMMY_NOTARY)).apply { outStates.add(this) }
@JvmOverloads
open fun output(label: String? = null, s: ContractState) = output(label) { s }
protected fun commandsToAuthenticatedObjects(): List<AuthenticatedObject<CommandData>> {
return commands.map { AuthenticatedObject(it.signers, it.signers.mapNotNull { MOCK_IDENTITY_SERVICE.partyFromKey(it) }, it.value) }
}
fun attachment(attachmentID: SecureHash) {
attachments.add(attachmentID)
}
fun arg(vararg keys: PublicKey, c: () -> CommandData) {
val keysList = listOf(*keys)
addCommand(Command(c(), keysList))
}
fun arg(key: PublicKey, c: CommandData) = arg(key) { c }
fun timestamp(time: Instant) {
val data = TimestampCommand(time, 30.seconds)
timestamp(data)
}
fun timestamp(data: TimestampCommand) {
addCommand(Command(data, DUMMY_NOTARY.owningKey))
}
fun addCommand(cmd: Command) {
signers.addAll(cmd.signers)
commands.add(cmd)
}
// Forbid patterns like: transaction { ... transaction { ... } }
@Deprecated("Cannot nest transactions, use tweak", level = DeprecationLevel.ERROR)
fun transaction(body: TransactionForTest.() -> LastLineShouldTestForAcceptOrFailure) {
}
}
/** If you jumped here from a compiler error make sure the last line of your test tests for a transaction accept or fail
* This is a dummy type that can only be instantiated by functions in this module. This way we can ensure that all tests
* will have as the last line either an accept or a failure test. The name is deliberately long to help make sense of
* the triggered diagnostic
*/
sealed class LastLineShouldTestForAcceptOrFailure {
internal object Token: LastLineShouldTestForAcceptOrFailure()
}
// Corresponds to the args to Contract.verify
// Note on defaults: try to avoid Kotlin defaults as they don't work from Java. Instead define overloads
open class TransactionForTest : AbstractTransactionForTest() {
private val inStates = arrayListOf<TransactionState<ContractState>>()
fun input(s: () -> ContractState) {
signers.add(DUMMY_NOTARY.owningKey)
inStates.add(TransactionState(s(), DUMMY_NOTARY))
}
fun input(s: ContractState) = input { s }
protected fun runCommandsAndVerify() {
val cmds = commandsToAuthenticatedObjects()
val tx = TransactionForVerification(inStates, outStates.map { it.state }, emptyList(), cmds, SecureHash.Companion.randomSHA256(), signers.toList(), type)
tx.verify()
}
fun accepts(): LastLineShouldTestForAcceptOrFailure {
runCommandsAndVerify()
return LastLineShouldTestForAcceptOrFailure.Token
}
@JvmOverloads
fun rejects(withMessage: String? = null): LastLineShouldTestForAcceptOrFailure {
val r = try {
runCommandsAndVerify()
false
} catch (e: Exception) {
val m = e.message
if (m == null)
fail("Threw exception without a message")
else
if (withMessage != null && !m.toLowerCase().contains(withMessage.toLowerCase())) throw AssertionError("Error was actually: $m", e)
true
}
if (!r) throw AssertionError("Expected exception but didn't get one")
return LastLineShouldTestForAcceptOrFailure.Token
}
/**
* Used to confirm that the test, when (implicitly) run against the .verify() method, fails with the text of the message
*/
infix fun `fails requirement`(msg: String): LastLineShouldTestForAcceptOrFailure = rejects(msg)
fun failsRequirement(msg: String) = this.`fails requirement`(msg)
// Use this to create transactions where the output of this transaction is automatically used as an input of
// the next.
fun chain(vararg outputLabels: String, body: TransactionForTest.() -> LastLineShouldTestForAcceptOrFailure): TransactionForTest {
val states = outStates.mapNotNull {
val l = it.label
if (l != null && outputLabels.contains(l))
it.state
else
null
}
val tx = TransactionForTest()
tx.inStates.addAll(states)
tx.body()
return tx
}
// Allow customisation of partial transactions.
fun tweak(body: TransactionForTest.() -> LastLineShouldTestForAcceptOrFailure): LastLineShouldTestForAcceptOrFailure {
val tx = TransactionForTest()
tx.inStates.addAll(inStates)
tx.outStates.addAll(outStates)
tx.commands.addAll(commands)
tx.signers.addAll(tx.inStates.map { it.notary.owningKey })
tx.signers.addAll(commands.flatMap { it.signers })
return tx.body()
}
override fun toString(): String {
return """transaction {
inputs: $inStates
outputs: $outStates
commands $commands
}"""
}
override fun equals(other: Any?) = this === other || (other is TransactionForTest && inStates == other.inStates && outStates == other.outStates && commands == other.commands)
override fun hashCode(): Int {
var result = inStates.hashCode()
result += 31 * result + outStates.hashCode()
result += 31 * result + commands.hashCode()
return result
}
}

View File

@ -56,15 +56,17 @@ class TransactionGroupTests {
input("£1000") input("£1000")
output("alice's £1000") { A_THOUSAND_POUNDS `owned by` ALICE_PUBKEY } output("alice's £1000") { A_THOUSAND_POUNDS `owned by` ALICE_PUBKEY }
command(MINI_CORP_PUBKEY) { TestCash.Commands.Move() } command(MINI_CORP_PUBKEY) { TestCash.Commands.Move() }
this.verifies()
} }
transaction { transaction {
input("alice's £1000") input("alice's £1000")
command(ALICE_PUBKEY) { TestCash.Commands.Move() } command(ALICE_PUBKEY) { TestCash.Commands.Move() }
command(MINI_CORP_PUBKEY) { TestCash.Commands.Exit(1000.POUNDS) } command(MINI_CORP_PUBKEY) { TestCash.Commands.Exit(1000.POUNDS) }
this.verifies()
} }
verifies() this.verifies()
} }
} }
@ -74,6 +76,7 @@ class TransactionGroupTests {
val t = transaction { val t = transaction {
output("cash") { A_THOUSAND_POUNDS } output("cash") { A_THOUSAND_POUNDS }
command(MINI_CORP_PUBKEY) { TestCash.Commands.Issue() } command(MINI_CORP_PUBKEY) { TestCash.Commands.Issue() }
this.verifies()
} }
val conflict1 = transaction { val conflict1 = transaction {
@ -82,6 +85,7 @@ class TransactionGroupTests {
output { HALF } output { HALF }
output { HALF } output { HALF }
command(MINI_CORP_PUBKEY) { TestCash.Commands.Move() } command(MINI_CORP_PUBKEY) { TestCash.Commands.Move() }
this.verifies()
} }
verifies() verifies()
@ -93,6 +97,7 @@ class TransactionGroupTests {
output { HALF } output { HALF }
output { HALF } output { HALF }
command(MINI_CORP_PUBKEY) { TestCash.Commands.Move() } command(MINI_CORP_PUBKEY) { TestCash.Commands.Move() }
this.verifies()
} }
assertNotEquals(conflict1, conflict2) assertNotEquals(conflict1, conflict2)
@ -112,11 +117,13 @@ class TransactionGroupTests {
transaction { transaction {
output("cash") { A_THOUSAND_POUNDS } output("cash") { A_THOUSAND_POUNDS }
command(MINI_CORP_PUBKEY) { TestCash.Commands.Issue() } command(MINI_CORP_PUBKEY) { TestCash.Commands.Issue() }
this.verifies()
} }
transaction { transaction {
input("cash") input("cash")
output { A_THOUSAND_POUNDS `owned by` BOB_PUBKEY } output { A_THOUSAND_POUNDS `owned by` BOB_PUBKEY }
this.verifies()
} }
} }
@ -126,6 +133,7 @@ class TransactionGroupTests {
assertFailsWith(TransactionResolutionException::class) { assertFailsWith(TransactionResolutionException::class) {
input(input.ref) input(input.ref)
} }
this.verifies()
} }
} }
} }
@ -143,6 +151,7 @@ class TransactionGroupTests {
input("£1000") input("£1000")
output { A_THOUSAND_POUNDS.copy(amount = A_THOUSAND_POUNDS.amount * 2) } output { A_THOUSAND_POUNDS.copy(amount = A_THOUSAND_POUNDS.amount * 2) }
command(MINI_CORP_PUBKEY) { TestCash.Commands.Move() } command(MINI_CORP_PUBKEY) { TestCash.Commands.Move() }
this.verifies()
} }
assertFailsWith(TransactionConflictException::class) { assertFailsWith(TransactionConflictException::class) {
@ -157,18 +166,21 @@ class TransactionGroupTests {
transaction { transaction {
output("£1000") { A_THOUSAND_POUNDS } output("£1000") { A_THOUSAND_POUNDS }
command(MINI_CORP_PUBKEY) { TestCash.Commands.Issue() } command(MINI_CORP_PUBKEY) { TestCash.Commands.Issue() }
this.verifies()
} }
transaction { transaction {
input("£1000") input("£1000")
output("alice's £1000") { A_THOUSAND_POUNDS `owned by` ALICE_PUBKEY } output("alice's £1000") { A_THOUSAND_POUNDS `owned by` ALICE_PUBKEY }
command(MINI_CORP_PUBKEY) { TestCash.Commands.Move() } command(MINI_CORP_PUBKEY) { TestCash.Commands.Move() }
this.verifies()
} }
transaction { transaction {
input("alice's £1000") input("alice's £1000")
command(ALICE_PUBKEY) { TestCash.Commands.Move() } command(ALICE_PUBKEY) { TestCash.Commands.Move() }
command(MINI_CORP_PUBKEY) { TestCash.Commands.Exit(1000.POUNDS) } command(MINI_CORP_PUBKEY) { TestCash.Commands.Exit(1000.POUNDS) }
this.verifies()
} }
}.interpreter.wireTransactions.let { signAll(it) } }.interpreter.wireTransactions.let { signAll(it) }

View File

@ -17,6 +17,7 @@ import com.r3corda.core.node.services.ServiceType
import com.r3corda.core.node.services.TransactionStorage import com.r3corda.core.node.services.TransactionStorage
import com.r3corda.core.node.services.Wallet import com.r3corda.core.node.services.Wallet
import com.r3corda.core.random63BitValue import com.r3corda.core.random63BitValue
import com.r3corda.core.contracts.Attachment
import com.r3corda.core.seconds import com.r3corda.core.seconds
import com.r3corda.core.testing.* import com.r3corda.core.testing.*
import com.r3corda.core.utilities.BriefLogFormatter import com.r3corda.core.utilities.BriefLogFormatter
@ -249,10 +250,11 @@ class TwoPartyTradeProtocolTests {
@Test @Test
fun `check dependencies of sale asset are resolved`() { fun `check dependencies of sale asset are resolved`() {
ledger { val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) val aliceNode = makeNodeWithTracking(notaryNode.info, ALICE.name, ALICE_KEY)
val aliceNode = makeNodeWithTracking(notaryNode.info, ALICE.name, ALICE_KEY) val bobNode = makeNodeWithTracking(notaryNode.info, BOB.name, BOB_KEY)
val bobNode = makeNodeWithTracking(notaryNode.info, BOB.name, BOB_KEY)
ledger(storageService = aliceNode.storage) {
// Insert a prospectus type attachment into the commercial paper transaction. // Insert a prospectus type attachment into the commercial paper transaction.
val stream = ByteArrayOutputStream() val stream = ByteArrayOutputStream()
@ -261,7 +263,7 @@ class TwoPartyTradeProtocolTests {
it.write("Our commercial paper is top notch stuff".toByteArray()) it.write("Our commercial paper is top notch stuff".toByteArray())
it.closeEntry() it.closeEntry()
} }
val attachmentID = aliceNode.storage.attachments.importAttachment(ByteArrayInputStream(stream.toByteArray())) val attachmentID = attachment(ByteArrayInputStream(stream.toByteArray()))
val issuer = MEGA_CORP.ref(1) val issuer = MEGA_CORP.ref(1)
val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public, issuer).second val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public, issuer).second
@ -365,8 +367,11 @@ class TwoPartyTradeProtocolTests {
} }
} }
private fun LedgerDsl<TransactionDslInterpreter, LedgerDslInterpreter<TransactionDslInterpreter>>.runWithError(bobError: Boolean, aliceError: Boolean, private fun LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>.runWithError(
expectedMessageSubstring: String) { bobError: Boolean,
aliceError: Boolean,
expectedMessageSubstring: String
) {
val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
val aliceNode = net.createPartyNode(notaryNode.info, ALICE.name, ALICE_KEY) val aliceNode = net.createPartyNode(notaryNode.info, ALICE.name, ALICE_KEY)
val bobNode = net.createPartyNode(notaryNode.info, BOB.name, BOB_KEY) val bobNode = net.createPartyNode(notaryNode.info, BOB.name, BOB_KEY)
@ -427,7 +432,7 @@ class TwoPartyTradeProtocolTests {
return signed.associateBy { it.id } return signed.associateBy { it.id }
} }
private fun LedgerDsl<TransactionDslInterpreter, LedgerDslInterpreter<TransactionDslInterpreter>>.fillUpForBuyer( private fun LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>.fillUpForBuyer(
withError: Boolean, withError: Boolean,
owner: PublicKey = BOB_PUBKEY, owner: PublicKey = BOB_PUBKEY,
issuer: PartyAndReference = MEGA_CORP.ref(1)): Pair<Wallet, List<WireTransaction>> { issuer: PartyAndReference = MEGA_CORP.ref(1)): Pair<Wallet, List<WireTransaction>> {
@ -441,6 +446,11 @@ class TwoPartyTradeProtocolTests {
if (!withError) if (!withError)
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
if (withError) {
this.fails()
} else {
this.verifies()
}
} }
// Bob gets some cash onto the ledger from BoE // Bob gets some cash onto the ledger from BoE
@ -448,6 +458,7 @@ class TwoPartyTradeProtocolTests {
input("elbonian money 1") input("elbonian money 1")
output("bob cash 1") { 800.DOLLARS.CASH `issued by` issuer `owned by` owner } output("bob cash 1") { 800.DOLLARS.CASH `issued by` issuer `owned by` owner }
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
this.verifies()
} }
val bc2 = transaction { val bc2 = transaction {
@ -455,13 +466,14 @@ class TwoPartyTradeProtocolTests {
output("bob cash 2") { 300.DOLLARS.CASH `issued by` issuer `owned by` owner } output("bob cash 2") { 300.DOLLARS.CASH `issued by` issuer `owned by` owner }
output { 700.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY } // Change output. output { 700.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY } // Change output.
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
this.verifies()
} }
val wallet = Wallet(listOf("bob cash 1".outputStateAndRef(), "bob cash 2".outputStateAndRef())) val wallet = Wallet(listOf("bob cash 1".outputStateAndRef(), "bob cash 2".outputStateAndRef()))
return Pair(wallet, listOf(eb1, bc1, bc2)) return Pair(wallet, listOf(eb1, bc1, bc2))
} }
private fun LedgerDsl<TransactionDslInterpreter, LedgerDslInterpreter<TransactionDslInterpreter>>.fillUpForSeller( private fun LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>.fillUpForSeller(
withError: Boolean, withError: Boolean,
owner: PublicKey, owner: PublicKey,
amount: Amount<Issued<Currency>>, amount: Amount<Issued<Currency>>,
@ -476,6 +488,11 @@ class TwoPartyTradeProtocolTests {
command(notary.owningKey) { TimestampCommand(TEST_TX_TIME, 30.seconds) } command(notary.owningKey) { TimestampCommand(TEST_TX_TIME, 30.seconds) }
if (attachmentID != null) if (attachmentID != null)
attachment(attachmentID) attachment(attachmentID)
if (withError) {
this.fails()
} else {
this.verifies()
}
} }
val wallet = Wallet(listOf("alice's paper".outputStateAndRef())) val wallet = Wallet(listOf("alice's paper".outputStateAndRef()))

View File

@ -9,7 +9,7 @@ import org.graphstream.graph.Node
import org.graphstream.graph.implementations.SingleGraph import org.graphstream.graph.implementations.SingleGraph
import kotlin.reflect.memberProperties import kotlin.reflect.memberProperties
class GraphVisualiser(val dsl: LedgerDsl<TestTransactionDslInterpreter, TestLedgerDslInterpreter>) { class GraphVisualiser(val dsl: LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>) {
companion object { companion object {
val css = GraphVisualiser::class.java.getResourceAsStream("graph.css").bufferedReader().readText() val css = GraphVisualiser::class.java.getResourceAsStream("graph.css").bufferedReader().readText()
} }