testdsl: Removed R type parameter, unify verifies() interface

This commit is contained in:
Andras Slemmer
2016-07-11 11:32:57 +01:00
parent fb55ceeb79
commit 4324e33fea
10 changed files with 98 additions and 139 deletions

View File

@ -18,12 +18,64 @@ interface OutputStateLookup {
fun <S : ContractState> retrieveOutputStateAndRef(clazz: Class<S>, label: String): StateAndRef<S>
}
/**
* This interface asserts that the DSL at hand is capable of verifying it's underlying construct(ledger/transaction)
*/
interface Verifies {
/**
* Verifies the ledger/transaction, throws if the verification fails.
*/
fun verifies(): EnforceVerifyOrFail
/**
* Asserts that verifies() throws
* @param expectedMessage: An optional string to be searched for in the raised exception.
*/
fun failsWith(expectedMessage: String?): EnforceVerifyOrFail {
val exceptionThrown = try {
verifies()
false
} catch (exception: Exception) {
if (expectedMessage != null) {
val exceptionMessage = exception.message
if (exceptionMessage == null) {
throw AssertionError(
"Expected exception containing '$expectedMessage' but raised exception had no message"
)
} else if (!exceptionMessage.toLowerCase().contains(expectedMessage.toLowerCase())) {
throw AssertionError(
"Expected exception containing '$expectedMessage' but raised exception was '$exception'"
)
}
}
true
}
if (!exceptionThrown) {
throw AssertionError("Expected exception but didn't get one")
}
return EnforceVerifyOrFail.Token
}
/**
* Asserts that [verifies] throws, with no condition on the exception message
*/
fun fails() = failsWith(null)
/**
* @see failsWith
*/
infix fun `fails with`(msg: String) = failsWith(msg)
}
/**
* This interface defines the bare bone functionality that a Ledger DSL interpreter should implement.
*
* TODO (Kotlin 1.1): Use type synonyms to make the type params less unwieldy
*/
interface LedgerDSLInterpreter<R, out T : TransactionDSLInterpreter<R>> : OutputStateLookup {
interface LedgerDSLInterpreter<out T : TransactionDSLInterpreter> : Verifies, OutputStateLookup {
/**
* Creates and adds a transaction to the ledger.
* @param transactionLabel: Optional label of the transaction, to be used in diagnostic messages.
@ -32,7 +84,7 @@ interface LedgerDSLInterpreter<R, out T : TransactionDSLInterpreter<R>> : Output
* @return: The final [WireTransaction] of the built transaction.
*/
fun _transaction(transactionLabel: String?, transactionBuilder: TransactionBuilder,
dsl: TransactionDSL<R, T>.() -> R): WireTransaction
dsl: TransactionDSL<T>.() -> EnforceVerifyOrFail): WireTransaction
/**
* Creates and adds a transaction to the ledger that will not be verified by [verifies].
@ -42,13 +94,13 @@ interface LedgerDSLInterpreter<R, out T : TransactionDSLInterpreter<R>> : Output
* @return: The final [WireTransaction] of the built transaction.
*/
fun _unverifiedTransaction(transactionLabel: String?, transactionBuilder: TransactionBuilder,
dsl: TransactionDSL<R, T>.() -> Unit): WireTransaction
dsl: TransactionDSL<T>.() -> Unit): WireTransaction
/**
* Creates a local scoped copy of the ledger.
* @param dsl: The ledger DSL to be interpreted using the copy.
*/
fun tweak(dsl: LedgerDSL<R, T, LedgerDSLInterpreter<R, T>>.() -> Unit)
fun tweak(dsl: LedgerDSL<T, LedgerDSLInterpreter<T>>.() -> Unit)
/**
* Adds an attachment to the ledger.
@ -57,16 +109,6 @@ interface LedgerDSLInterpreter<R, out T : TransactionDSLInterpreter<R>> : Output
*/
fun attachment(attachment: InputStream): SecureHash
/**
* Verifies the ledger using [TransactionGroup.verify], throws if the verification fails.
*/
fun verifies()
/**
* Verifies the ledger, expecting an exception to be thrown.
* @param expectedMessage: An optional string to be searched for in the raised exception.
*/
fun failsWith(expectedMessage: String?)
}
/**
@ -75,22 +117,22 @@ interface LedgerDSLInterpreter<R, out T : TransactionDSLInterpreter<R>> : Output
* functionality then first add your primitive to [LedgerDSLInterpreter] and then add the convenience defaults/extension
* methods here.
*/
class LedgerDSL<R, out T : TransactionDSLInterpreter<R>, out L : LedgerDSLInterpreter<R, T>> (val interpreter: L) :
LedgerDSLInterpreter<R, TransactionDSLInterpreter<R>> by interpreter {
class LedgerDSL<out T : TransactionDSLInterpreter, out L : LedgerDSLInterpreter<T>> (val interpreter: L) :
LedgerDSLInterpreter<TransactionDSLInterpreter> by interpreter {
/**
* @see LedgerDSLInterpreter._transaction
*/
@JvmOverloads
fun transaction(label: String? = null, transactionBuilder: TransactionBuilder = TransactionBuilder(),
dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> R) =
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail) =
_transaction(label, transactionBuilder, dsl)
/**
* @see LedgerDSLInterpreter._unverifiedTransaction
*/
@JvmOverloads
fun unverifiedTransaction(label: String? = null, transactionBuilder: TransactionBuilder = TransactionBuilder(),
dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> Unit) =
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> Unit) =
_unverifiedTransaction(label, transactionBuilder, dsl)
/**
@ -117,14 +159,4 @@ class LedgerDSL<R, out T : TransactionDSLInterpreter<R>, out L : LedgerDSLInterp
*/
fun <S : ContractState> retrieveOutput(clazz: Class<S>, label: String) =
retrieveOutputStateAndRef(clazz, label).state.data
/**
* Asserts that the transaction will fail verification
*/
fun fails() = failsWith(null)
/**
* @see TransactionDSLInterpreter.failsWith
*/
infix fun `fails with`(msg: String) = failsWith(msg)
}

View File

@ -47,10 +47,7 @@ import java.util.*
fun transaction(
transactionLabel: String? = null,
transactionBuilder: TransactionBuilder = TransactionBuilder(),
dsl: TransactionDSL<
EnforceVerifyOrFail,
TransactionDSLInterpreter<EnforceVerifyOrFail>
>.() -> EnforceVerifyOrFail
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail
) = JavaTestHelpers.transaction(transactionLabel, transactionBuilder, dsl)
/**
@ -59,7 +56,7 @@ fun transaction(
fun ledger(
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
storageService: StorageService = MockStorageService(),
dsl: LedgerDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
) = JavaTestHelpers.ledger(identityService, storageService, dsl)
@Deprecated(
@ -67,8 +64,8 @@ fun ledger(
replaceWith = ReplaceWith("tweak"),
level = DeprecationLevel.ERROR)
@Suppress("UNUSED_PARAMETER")
fun TransactionDSLInterpreter<EnforceVerifyOrFail>.ledger(
dsl: LedgerDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit) {
fun TransactionDSLInterpreter.ledger(
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit) {
}
@Deprecated(
@ -76,11 +73,8 @@ fun TransactionDSLInterpreter<EnforceVerifyOrFail>.ledger(
replaceWith = ReplaceWith("tweak"),
level = DeprecationLevel.ERROR)
@Suppress("UNUSED_PARAMETER")
fun TransactionDSLInterpreter<EnforceVerifyOrFail>.transaction(
dsl: TransactionDSL<
EnforceVerifyOrFail,
TransactionDSLInterpreter<EnforceVerifyOrFail>
>.() -> EnforceVerifyOrFail) {
fun TransactionDSLInterpreter.transaction(
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail) {
}
@Deprecated(
@ -88,8 +82,8 @@ fun TransactionDSLInterpreter<EnforceVerifyOrFail>.transaction(
replaceWith = ReplaceWith("tweak"),
level = DeprecationLevel.ERROR)
@Suppress("UNUSED_PARAMETER")
fun LedgerDSLInterpreter<EnforceVerifyOrFail, TransactionDSLInterpreter<EnforceVerifyOrFail>>.ledger(
dsl: LedgerDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit) {
fun LedgerDSLInterpreter<TransactionDSLInterpreter>.ledger(
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit) {
}
/**
@ -113,7 +107,7 @@ data class TestTransactionDSLInterpreter private constructor(
override val ledgerInterpreter: TestLedgerDSLInterpreter,
val transactionBuilder: TransactionBuilder,
internal val labelToIndexMap: HashMap<String, Int>
) : TransactionDSLInterpreter<EnforceVerifyOrFail>, OutputStateLookup by ledgerInterpreter {
) : TransactionDSLInterpreter, OutputStateLookup by ledgerInterpreter {
constructor(
ledgerInterpreter: TestLedgerDSLInterpreter,
@ -160,19 +154,8 @@ data class TestTransactionDSLInterpreter private constructor(
return EnforceVerifyOrFail.Token
}
override fun failsWith(expectedMessage: String?): EnforceVerifyOrFail {
expectExceptionContainingString(expectedMessage) {
this.verifies()
}
return EnforceVerifyOrFail.Token
}
override fun tweak(
dsl: TransactionDSL<
EnforceVerifyOrFail,
TransactionDSLInterpreter<EnforceVerifyOrFail>
>.() -> EnforceVerifyOrFail
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail
) = dsl(TransactionDSL(copy()))
}
@ -182,7 +165,7 @@ data class TestLedgerDSLInterpreter private constructor (
internal val labelToOutputStateAndRefs: HashMap<String, StateAndRef<ContractState>> = HashMap(),
private val transactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = HashMap(),
private val nonVerifiedTransactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = HashMap()
) : LedgerDSLInterpreter<EnforceVerifyOrFail, TestTransactionDSLInterpreter> {
) : LedgerDSLInterpreter<TestTransactionDSLInterpreter> {
val wireTransactions: List<WireTransaction> get() = transactionWithLocations.values.map { it.transaction }
// We specify [labelToOutputStateAndRefs] just so that Kotlin picks the primary constructor instead of cycling
@ -260,7 +243,7 @@ data class TestLedgerDSLInterpreter private constructor (
private fun <R> interpretTransactionDsl(
transactionBuilder: TransactionBuilder,
dsl: TransactionDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter>.() -> R
dsl: TransactionDSL<TestTransactionDSLInterpreter>.() -> R
): TestTransactionDSLInterpreter {
val transactionInterpreter = TestTransactionDSLInterpreter(this, transactionBuilder)
dsl(TransactionDSL(transactionInterpreter))
@ -292,7 +275,7 @@ data class TestLedgerDSLInterpreter private constructor (
private fun <R> recordTransactionWithTransactionMap(
transactionLabel: String?,
transactionBuilder: TransactionBuilder,
dsl: TransactionDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter>.() -> R,
dsl: TransactionDSL<TestTransactionDSLInterpreter>.() -> R,
transactionMap: HashMap<SecureHash, WireTransactionWithLocation> = HashMap()
): WireTransaction {
val transactionLocation = getCallerLocation()
@ -316,25 +299,25 @@ data class TestLedgerDSLInterpreter private constructor (
override fun _transaction(
transactionLabel: String?,
transactionBuilder: TransactionBuilder,
dsl: TransactionDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter>.() -> EnforceVerifyOrFail
dsl: TransactionDSL<TestTransactionDSLInterpreter>.() -> EnforceVerifyOrFail
) = recordTransactionWithTransactionMap(transactionLabel, transactionBuilder, dsl, transactionWithLocations)
override fun _unverifiedTransaction(
transactionLabel: String?,
transactionBuilder: TransactionBuilder,
dsl: TransactionDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter>.() -> Unit
dsl: TransactionDSL<TestTransactionDSLInterpreter>.() -> Unit
) = recordTransactionWithTransactionMap(transactionLabel, transactionBuilder, dsl, nonVerifiedTransactionWithLocations)
override fun tweak(
dsl: LedgerDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter,
LedgerDSLInterpreter<EnforceVerifyOrFail, TestTransactionDSLInterpreter>>.() -> Unit) =
dsl: LedgerDSL<TestTransactionDSLInterpreter,
LedgerDSLInterpreter<TestTransactionDSLInterpreter>>.() -> Unit) =
dsl(LedgerDSL(copy()))
override fun attachment(attachment: InputStream): SecureHash {
return storageService.attachments.importAttachment(attachment)
}
override fun verifies() {
override fun verifies(): EnforceVerifyOrFail {
val transactionGroup = toTransactionGroup()
try {
transactionGroup.verify()
@ -343,12 +326,8 @@ data class TestLedgerDSLInterpreter private constructor (
val transactionName = transactionWithLocation?.label ?: transactionWithLocation?.location ?: "<unknown>"
throw VerifiesFailed(transactionName, exception)
}
}
override fun failsWith(expectedMessage: String?) {
expectExceptionContainingString(expectedMessage) {
this.verifies()
}
return EnforceVerifyOrFail.Token
}
override fun <S : ContractState> retrieveOutputStateAndRef(clazz: Class<S>, label: String): StateAndRef<S> {
@ -389,30 +368,5 @@ fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: Array<out KeyP
* @param extraKeys: extra keys to sign transactions with.
* @return: List of [SignedTransaction]s.
*/
fun LedgerDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.signAll(
fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.signAll(
vararg extraKeys: KeyPair) = signAll(this.interpreter.wireTransactions, extraKeys)
internal inline fun expectExceptionContainingString(string: String?, body:() -> Unit) {
val exceptionThrown = try {
body()
false
} catch (exception: Exception) {
if (string != null) {
val exceptionMessage = exception.message
if (exceptionMessage == null) {
throw AssertionError(
"Expected exception containing '$string' but raised exception had no message"
)
} else if (!exceptionMessage.toLowerCase().contains(string.toLowerCase())) {
throw AssertionError(
"Expected exception containing '$string' but raised exception was '$exception'"
)
}
}
true
}
if (!exceptionThrown) {
throw AssertionError("Expected exception but didn't get one")
}
}

View File

@ -97,8 +97,8 @@ object JavaTestHelpers {
@JvmStatic @JvmOverloads fun ledger(
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
storageService: StorageService = MockStorageService(),
dsl: LedgerDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
): LedgerDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
): LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(identityService, storageService))
dsl(ledgerDsl)
return ledgerDsl
@ -111,10 +111,7 @@ object JavaTestHelpers {
@JvmStatic @JvmOverloads fun transaction(
transactionLabel: String? = null,
transactionBuilder: TransactionBuilder = TransactionBuilder(),
dsl: TransactionDSL<
EnforceVerifyOrFail,
TransactionDSLInterpreter<EnforceVerifyOrFail>
>.() -> EnforceVerifyOrFail
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail
) = ledger { this.transaction(transactionLabel, transactionBuilder, dsl) }
}

View File

@ -13,11 +13,11 @@ import java.time.Instant
* @param <R>: The return type of [verifies]/[failsWith] and the like. It is generic so that we have control over whether
* we want to enforce users to call these methods (@see [EnforceVerifyOrFail]) or not.
*/
interface TransactionDSLInterpreter<R> : OutputStateLookup {
interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
/**
* A reference to the enclosing ledger{..}'s interpreter.
*/
val ledgerInterpreter: LedgerDSLInterpreter<R, TransactionDSLInterpreter<R>>
val ledgerInterpreter: LedgerDSLInterpreter<TransactionDSLInterpreter>
/**
* Adds an input reference to the transaction. Note that [verifies] will resolve this reference.
@ -46,31 +46,19 @@ interface TransactionDSLInterpreter<R> : OutputStateLookup {
*/
fun _command(signers: List<PublicKey>, commandData: CommandData)
/**
* Verifies the transaction.
* @return: Possibly a token confirming that [verifies] has been called.
*/
fun verifies(): R
/**
* Verifies the transaction, expecting an exception to be thrown.
* @param expectedMessage: An optional string to be searched for in the raised exception.
*/
fun failsWith(expectedMessage: String?): R
/**
* Creates a local scoped copy of the transaction.
* @param dsl: The transaction DSL to be interpreted using the copy.
*/
fun tweak(dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> R): R
fun tweak(dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail): EnforceVerifyOrFail
}
class TransactionDSL<R, out T : TransactionDSLInterpreter<R>> (val interpreter: T) :
TransactionDSLInterpreter<R> by interpreter {
class TransactionDSL<out T : TransactionDSLInterpreter> (val interpreter: T) :
TransactionDSLInterpreter by interpreter {
/**
* Looks up the output label and adds the found state as an input.
* @param stateLabel: The label of the output state specified when calling [LedgerDSLInterpreter._output] and friends.
* @param stateLabel: The label of the output state specified when calling [TransactionDSLInterpreter._output] and friends.
*/
fun input(stateLabel: String) = input(retrieveOutputStateAndRef(ContractState::class.java, stateLabel).ref)
@ -128,14 +116,4 @@ class TransactionDSL<R, out T : TransactionDSLInterpreter<R>> (val interpreter:
*/
@JvmOverloads
fun timestamp(data: TimestampCommand, notary: PublicKey = DUMMY_NOTARY.owningKey) = command(notary, data)
/**
* Asserts that the transaction will fail verification
*/
fun fails() = failsWith(null)
/**
* @see TransactionDSLInterpreter.failsWith
*/
infix fun `fails with`(msg: String) = failsWith(msg)
}