mirror of
https://github.com/corda/corda.git
synced 2025-02-01 08:48:09 +00:00
Test DSL: update tests to handle new transaction constraints
This commit is contained in:
parent
f5776d6bd7
commit
73664c3383
@ -289,7 +289,7 @@ abstract class TypeOnlyCommandData : CommandData {
|
||||
data class Command<T : CommandData>(val value: T, val signers: List<PublicKey>) {
|
||||
// TODO Introduce NonEmptyList?
|
||||
init {
|
||||
require(signers.isNotEmpty())
|
||||
require(signers.isNotEmpty()) { "The list of signers cannot be empty" }
|
||||
}
|
||||
|
||||
constructor(data: T, key: PublicKey) : this(data, listOf(key))
|
||||
|
@ -169,8 +169,6 @@ class Cap {
|
||||
output { stateInitial }
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
this `fails with` "transaction has a single command"
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
|
@ -56,8 +56,6 @@ class Caplet {
|
||||
output { stateStart }
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
this `fails with` "transaction has a single command"
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
|
@ -53,8 +53,6 @@ class FXFwdTimeOption
|
||||
output { inState }
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
this `fails with` "transaction has a single command"
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
|
@ -45,8 +45,6 @@ class FXSwap {
|
||||
output { inState }
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
this `fails with` "transaction has a single command"
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
|
@ -136,8 +136,6 @@ class IRS {
|
||||
output { stateInitial }
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
this `fails with` "transaction has a single command"
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
|
@ -145,8 +145,6 @@ class RollOutTests {
|
||||
output { stateStart }
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
this `fails with` "transaction has a single command"
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
|
@ -62,8 +62,6 @@ class Swaption {
|
||||
output { stateInitial }
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
this `fails with` "transaction has a single command"
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
|
@ -48,12 +48,9 @@ class ZeroCouponBond {
|
||||
|
||||
@Test
|
||||
fun `issue - signature`() {
|
||||
|
||||
transaction {
|
||||
output { inState }
|
||||
|
||||
this `fails with` "transaction has a single command"
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
|
@ -4,6 +4,7 @@ import kotlin.Unit;
|
||||
import net.corda.core.contracts.PartyAndReference;
|
||||
import net.corda.core.identity.AnonymousParty;
|
||||
import net.corda.core.utilities.OpaqueBytes;
|
||||
import net.corda.testing.DummyCommandData;
|
||||
import org.junit.Test;
|
||||
|
||||
import static net.corda.finance.CurrencyUtils.DOLLARS;
|
||||
@ -24,16 +25,17 @@ public class CashTestsJava {
|
||||
ledger(lg -> {
|
||||
lg.transaction(tx -> {
|
||||
tx.input(inState);
|
||||
tx.failsWith("the amounts balance");
|
||||
|
||||
tx.tweak(tw -> {
|
||||
tw.output(new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), new AnonymousParty(getMINI_CORP_PUBKEY())));
|
||||
tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
|
||||
return tw.failsWith("the amounts balance");
|
||||
});
|
||||
|
||||
tx.tweak(tw -> {
|
||||
tw.output(outState);
|
||||
// No command arguments
|
||||
tw.command(getMEGA_CORP_PUBKEY(), DummyCommandData.INSTANCE);
|
||||
// Invalid command
|
||||
return tw.failsWith("required net.corda.finance.contracts.asset.Cash.Commands.Move command");
|
||||
});
|
||||
tx.tweak(tw -> {
|
||||
|
@ -85,15 +85,16 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
fun trivial() {
|
||||
transaction {
|
||||
input { inState }
|
||||
this `fails with` "the amounts balance"
|
||||
|
||||
tweak {
|
||||
output { outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
tweak {
|
||||
output { outState }
|
||||
// No command arguments
|
||||
command(ALICE_PUBKEY) { DummyCommandData }
|
||||
// Invalid command
|
||||
this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command"
|
||||
}
|
||||
tweak {
|
||||
@ -279,12 +280,14 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
transaction {
|
||||
input { inState }
|
||||
input { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
this `fails with` "zero sized inputs"
|
||||
}
|
||||
transaction {
|
||||
input { inState }
|
||||
output { inState }
|
||||
output { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
this `fails with` "zero sized outputs"
|
||||
}
|
||||
}
|
||||
@ -295,6 +298,7 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
transaction {
|
||||
input { inState }
|
||||
output { outState `issued by` MINI_CORP }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
// Can't change deposit reference when splitting.
|
||||
@ -302,6 +306,7 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
val splits2 = inState.amount.splitEvenly(2)
|
||||
input { inState }
|
||||
for (i in 0..1) output { outState.copy(amount = splits2[i]).editDepositRef(i.toByte()) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
// Can't mix currencies.
|
||||
@ -309,6 +314,7 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
input { inState }
|
||||
output { outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer) }
|
||||
output { outState.copy(amount = 200.POUNDS `issued by` defaultIssuer) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
transaction {
|
||||
@ -320,6 +326,7 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
)
|
||||
}
|
||||
output { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
// Can't have superfluous input states from different issuers.
|
||||
@ -335,6 +342,7 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
input { inState }
|
||||
input { inState.editDepositRef(3) }
|
||||
output { outState.copy(amount = inState.amount * 2).editDepositRef(3) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
this `fails with` "for reference [01]"
|
||||
}
|
||||
}
|
||||
@ -404,6 +412,7 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
// Gather 2000 dollars from two different issuers.
|
||||
input { inState }
|
||||
input { inState `issued by` MINI_CORP }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
|
||||
// Can't merge them together.
|
||||
tweak {
|
||||
@ -420,7 +429,6 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
// This works.
|
||||
output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) }
|
||||
output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import java.io.InputStream
|
||||
import java.security.KeyPair
|
||||
import java.security.PublicKey
|
||||
@ -247,6 +248,7 @@ data class TestLedgerDSLInterpreter private constructor(
|
||||
): WireTransaction {
|
||||
val transactionLocation = getCallerLocation()
|
||||
val transactionInterpreter = interpretTransactionDsl(transactionBuilder, dsl)
|
||||
makeTxValid(transactionBuilder)
|
||||
// Create the WireTransaction
|
||||
val wireTransaction = transactionInterpreter.toWireTransaction()
|
||||
// Record the output states
|
||||
@ -262,6 +264,20 @@ data class TestLedgerDSLInterpreter private constructor(
|
||||
return wireTransaction
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fills the transaction builder with dummy components to satisfy the base transaction validity rules.
|
||||
*
|
||||
* A common pattern in our tests is using a base transaction and expressing the test cases using [tweak]s.
|
||||
* The base transaction may not be valid, but it still gets recorded to the ledger. This causes a test failure,
|
||||
* even though is not being used for anything afterwards.
|
||||
*/
|
||||
private fun makeTxValid(transactionBuilder: TransactionBuilder) {
|
||||
if (transactionBuilder.commands().isEmpty()) transactionBuilder.addCommand(dummyCommand())
|
||||
if (transactionBuilder.inputStates().isEmpty() && transactionBuilder.outputStates().isEmpty()) {
|
||||
transactionBuilder.addOutputState(DummyContract.SingleOwnerState(owner = ALICE))
|
||||
}
|
||||
}
|
||||
|
||||
override fun _transaction(
|
||||
transactionLabel: String?,
|
||||
transactionBuilder: TransactionBuilder,
|
||||
|
@ -78,9 +78,6 @@ class TransactionDSL<out T : TransactionDSLInterpreter>(val interpreter: T) : Tr
|
||||
fun input(state: ContractState) {
|
||||
val transaction = ledgerInterpreter._unverifiedTransaction(null, TransactionBuilder(notary = DUMMY_NOTARY)) {
|
||||
output { state }
|
||||
// Add a dummy randomised output so that the transaction id differs when issuing the same state multiple times
|
||||
val nonceState = DummyContract.SingleOwnerState(Random().nextInt(), DUMMY_NOTARY)
|
||||
output { nonceState }
|
||||
}
|
||||
input(transaction.outRef<ContractState>(0).ref)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user