Testing: make the ledger DSL take a ServiceHub rather than individual services.

It defaults to a fresh UnitTestServices(). Also clear up a few other areas.
This commit is contained in:
Mike Hearn 2016-07-29 14:48:25 +02:00
parent ba05b90b8f
commit 1c3379f508
5 changed files with 35 additions and 36 deletions

View File

@ -4,14 +4,12 @@ package com.r3corda.core.testing
import com.google.common.base.Throwables
import com.google.common.net.HostAndPort
import com.r3corda.core.contracts.Attachment
import com.r3corda.core.contracts.StateRef
import com.r3corda.core.contracts.TransactionBuilder
import com.r3corda.core.crypto.*
import com.r3corda.core.node.services.IdentityService
import com.r3corda.core.node.services.StorageService
import com.r3corda.core.node.ServiceHub
import com.r3corda.core.node.services.testing.MockIdentityService
import com.r3corda.core.node.services.testing.MockStorageService
import com.r3corda.core.node.services.testing.UnitTestServices
import java.math.BigInteger
import java.net.ServerSocket
import java.security.KeyPair
@ -95,17 +93,14 @@ fun freeLocalHostAndPort(): HostAndPort {
}
/**
* Creates and tests a ledger built by the passed in dsl.
* @param identityService: The [IdentityService] to be used while building the ledger.
* @param storageService: The [StorageService] to be used for storing e.g. [Attachment]s.
* @param dsl: The dsl building the ledger.
* Creates and tests a ledger built by the passed in dsl. The provided services can be customised, otherwise a default
* of a freshly built [UnitTestServices] is used.
*/
@JvmOverloads fun ledger(
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
storageService: StorageService = MockStorageService(),
services: ServiceHub = UnitTestServices(),
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
): LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(identityService, storageService))
val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(services))
dsl(ledgerDsl)
return ledgerDsl
}

View File

@ -40,11 +40,13 @@ interface Verifies {
val exceptionMessage = exception.message
if (exceptionMessage == null) {
throw AssertionError(
"Expected exception containing '$expectedMessage' but raised exception had no message"
"Expected exception containing '$expectedMessage' but raised exception had no message",
exception
)
} else if (!exceptionMessage.toLowerCase().contains(expectedMessage.toLowerCase())) {
throw AssertionError(
"Expected exception containing '$expectedMessage' but raised exception was '$exception'"
"Expected exception containing '$expectedMessage' but raised exception was '$exception'",
exception
)
}
}

View File

@ -5,8 +5,7 @@ import com.r3corda.core.crypto.DigitalSignature
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.signWithECDSA
import com.r3corda.core.node.services.IdentityService
import com.r3corda.core.node.services.StorageService
import com.r3corda.core.node.ServiceHub
import com.r3corda.core.serialization.serialize
import java.io.InputStream
import java.security.KeyPair
@ -95,6 +94,10 @@ data class TestTransactionDSLInterpreter private constructor(
transactionBuilder: TransactionBuilder
) : this(ledgerInterpreter, transactionBuilder, HashMap())
val services = object : ServiceHub by ledgerInterpreter.services {
override fun loadState(stateRef: StateRef) = ledgerInterpreter.resolveStateRef<ContractState>(stateRef)
}
private fun copy(): TestTransactionDSLInterpreter =
TestTransactionDSLInterpreter(
ledgerInterpreter = ledgerInterpreter,
@ -141,18 +144,15 @@ data class TestTransactionDSLInterpreter private constructor(
}
data class TestLedgerDSLInterpreter private constructor (
private val identityService: IdentityService,
private val storageService: StorageService,
val services: ServiceHub,
internal val labelToOutputStateAndRefs: HashMap<String, StateAndRef<ContractState>> = HashMap(),
private val transactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = HashMap(),
private val transactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = LinkedHashMap(),
private val nonVerifiedTransactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = HashMap()
) : 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
constructor(identityService: IdentityService, storageService: StorageService) : this(
identityService, storageService, labelToOutputStateAndRefs = HashMap()
)
constructor(services: ServiceHub) : this(services, labelToOutputStateAndRefs = HashMap())
companion object {
private fun getCallerLocation(): String? {
@ -179,8 +179,7 @@ data class TestLedgerDSLInterpreter private constructor (
internal fun copy(): TestLedgerDSLInterpreter =
TestLedgerDSLInterpreter(
identityService,
storageService,
services,
labelToOutputStateAndRefs = HashMap(labelToOutputStateAndRefs),
transactionWithLocations = HashMap(transactionWithLocations),
nonVerifiedTransactionWithLocations = HashMap(nonVerifiedTransactionWithLocations)
@ -189,7 +188,7 @@ data class TestLedgerDSLInterpreter private constructor (
internal fun resolveWireTransaction(wireTransaction: WireTransaction): TransactionForVerification {
return wireTransaction.run {
val authenticatedCommands = commands.map {
AuthenticatedObject(it.signers, it.signers.mapNotNull { identityService.partyFromKey(it) }, it.value)
AuthenticatedObject(it.signers, it.signers.mapNotNull { services.identityService.partyFromKey(it) }, it.value)
}
val resolvedInputStates = inputs.map { resolveStateRef<ContractState>(it) }
val resolvedAttachments = attachments.map { resolveAttachment(it) }
@ -220,7 +219,7 @@ data class TestLedgerDSLInterpreter private constructor (
}
internal fun resolveAttachment(attachmentId: SecureHash): Attachment =
storageService.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId)
services.storageService.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId)
private fun <R> interpretTransactionDsl(
transactionBuilder: TransactionBuilder,
@ -233,10 +232,10 @@ data class TestLedgerDSLInterpreter private constructor (
fun toTransactionGroup(): TransactionGroup {
val ledgerTransactions = transactionWithLocations.map {
it.value.transaction.toLedgerTransaction(identityService, storageService.attachments)
it.value.transaction.toLedgerTransaction(services.identityService, services.storageService.attachments)
}
val nonVerifiedLedgerTransactions = nonVerifiedTransactionWithLocations.map {
it.value.transaction.toLedgerTransaction(identityService, storageService.attachments)
it.value.transaction.toLedgerTransaction(services.identityService, services.storageService.attachments)
}
return TransactionGroup(ledgerTransactions.toSet(), nonVerifiedLedgerTransactions.toSet())
}
@ -295,7 +294,7 @@ data class TestLedgerDSLInterpreter private constructor (
dsl(LedgerDSL(copy()))
override fun attachment(attachment: InputStream): SecureHash {
return storageService.attachments.importAttachment(attachment)
return services.storageService.attachments.importAttachment(attachment)
}
override fun verifies(): EnforceVerifyOrFail {
@ -322,6 +321,9 @@ data class TestLedgerDSLInterpreter private constructor (
return stateAndRef as StateAndRef<S>
}
}
val transactionsToVerify: List<WireTransaction> get() = transactionWithLocations.values.map { it.transaction }
val transactionsUnverified: List<WireTransaction> get() = nonVerifiedTransactionWithLocations.values.map { it.transaction }
}
/**
@ -330,7 +332,7 @@ data class TestLedgerDSLInterpreter private constructor (
* @param extraKeys extra keys to sign transactions with.
* @return List of [SignedTransaction]s.
*/
fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: Array<out KeyPair>) = transactionsToSign.map { wtx ->
fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>) = transactionsToSign.map { wtx ->
val allPubKeys = wtx.signers.toMutableSet()
val bits = wtx.serialize()
require(bits == wtx.serialized)
@ -350,4 +352,4 @@ fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: Array<out KeyP
* @return List of [SignedTransaction]s.
*/
fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.signAll(
vararg extraKeys: KeyPair) = signAll(this.interpreter.wireTransactions, extraKeys)
vararg extraKeys: KeyPair) = signAll(this.interpreter.wireTransactions, extraKeys.toList())

View File

@ -246,7 +246,7 @@ class TwoPartyTradeProtocolTests {
val aliceNode = makeNodeWithTracking(notaryNode.info, ALICE.name, ALICE_KEY)
val bobNode = makeNodeWithTracking(notaryNode.info, BOB.name, BOB_KEY)
ledger(storageService = aliceNode.storage) {
ledger(aliceNode.services) {
// Insert a prospectus type attachment into the commercial paper transaction.
val stream = ByteArrayOutputStream()
@ -413,7 +413,7 @@ class TwoPartyTradeProtocolTests {
wtxToSign: List<WireTransaction>,
services: ServiceHub,
vararg extraKeys: KeyPair): Map<SecureHash, SignedTransaction> {
val signed: List<SignedTransaction> = signAll(wtxToSign, extraKeys)
val signed: List<SignedTransaction> = signAll(wtxToSign, extraKeys.toList())
services.recordTransactions(signed)
val validatedTransactions = services.storageService.validatedTransactions
if (validatedTransactions is RecordingTransactionStorage) {

View File

@ -17,13 +17,13 @@ class GraphVisualiser(val dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedg
}
fun convert(): SingleGraph {
val tg = dsl.interpreter.toTransactionGroup()
val testLedger: TestLedgerDSLInterpreter = dsl.interpreter
val graph = createGraph("Transaction group", css)
// Map all the transactions, including the bogus non-verified ones (with no inputs) to graph nodes.
for ((txIndex, tx) in (tg.transactions + tg.nonVerifiedRoots).withIndex()) {
for ((txIndex, tx) in (testLedger.transactionsToVerify + testLedger.transactionsUnverified).withIndex()) {
val txNode = graph.addNode<Node>("tx$txIndex")
if (tx !in tg.nonVerifiedRoots)
if (tx !in testLedger.transactionsUnverified)
txNode.label = dsl.interpreter.transactionName(tx.id).let { it ?: "TX[${tx.id.prefixChars()}]" }
txNode.styleClass = "tx"
@ -48,7 +48,7 @@ class GraphVisualiser(val dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedg
}
}
// And now all states and transactions were mapped to graph nodes, hook up the input edges.
for ((txIndex, tx) in tg.transactions.withIndex()) {
for ((txIndex, tx) in testLedger.transactionsToVerify.withIndex()) {
for ((inputIndex, ref) in tx.inputs.withIndex()) {
val edge = graph.addEdge<Edge>("tx$txIndex-in$inputIndex", ref.toString(), "tx$txIndex", true)
edge.weight = 1.2