Skip to content

Commit

Permalink
ENT-5430: Increase test coverage when serializing Optional fields. (c…
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisr3 authored Jun 22, 2020
1 parent 1a7e302 commit 6485a02
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,31 @@ package net.corda.contracts.serialization.generics
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.requireThat
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
import java.util.Optional

@Suppress("unused")
class GenericTypeContract : Contract {
override fun verify(tx: LedgerTransaction) {
val state = tx.outputsOfType<State>()
require(state.isNotEmpty()) {
"Requires at least one data state"
val states = tx.outputsOfType<State>()
requireThat {
"Requires at least one data state" using states.isNotEmpty()
}
val purchases = tx.commandsOfType<Purchase>()
requireThat {
"Requires at least one purchase" using purchases.isNotEmpty()
}
for (purchase in purchases) {
requireThat {
"Purchase has a price" using purchase.value.price.isPresent
}
}
}

@Suppress("CanBeParameter", "MemberVisibilityCanBePrivate")
class State(val owner: AbstractParty, val data: DataObject) : ContractState {
class State(val owner: AbstractParty, val data: DataObject?) : ContractState {
override val participants: List<AbstractParty> = listOf(owner)

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import net.corda.core.transactions.TransactionBuilder
import java.util.Optional

@StartableByRPC
class GenericTypeFlow(private val purchase: DataObject) : FlowLogic<SecureHash>() {
class GenericTypeFlow(private val purchase: DataObject?) : FlowLogic<SecureHash>() {
@Suspendable
override fun call(): SecureHash {
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val stx = serviceHub.signInitialTransaction(
TransactionBuilder(notary)
.addOutputState(State(ourIdentity, purchase))
.addCommand(Command(Purchase(Optional.of(purchase)), ourIdentity.owningKey))
.addCommand(Command(Purchase(Optional.ofNullable(purchase)), ourIdentity.owningKey))
)
stx.verify(serviceHub, checkSufficientSignatures = false)
return stx.id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package net.corda.node

import net.corda.client.rpc.CordaRPCClient
import net.corda.contracts.serialization.generics.DataObject
import net.corda.core.contracts.TransactionVerificationException.ContractRejection
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor
Expand All @@ -15,7 +16,9 @@ import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.testing.node.NotarySpec
import net.corda.testing.node.User
import net.corda.testing.node.internal.cordappWithPackages
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.jupiter.api.assertThrows

@Suppress("FunctionName")
class ContractWithGenericTypeTest {
Expand All @@ -24,29 +27,52 @@ class ContractWithGenericTypeTest {

@JvmField
val logger = loggerFor<ContractWithGenericTypeTest>()
}

@Test(timeout=300_000)
fun `flow with generic type`() {
@JvmField
val user = User("u", "p", setOf(Permissions.all()))
driver(DriverParameters(
portAllocation = incrementalPortAllocation(),
startNodesInProcess = false,
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
cordappsForAllNodes = listOf(
cordappWithPackages("net.corda.flows.serialization.generics").signed(),
cordappWithPackages("net.corda.contracts.serialization.generics").signed()

fun parameters(): DriverParameters {
return DriverParameters(
portAllocation = incrementalPortAllocation(),
startNodesInProcess = false,
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
cordappsForAllNodes = listOf(
cordappWithPackages("net.corda.flows.serialization.generics").signed(),
cordappWithPackages("net.corda.contracts.serialization.generics").signed()
)
)
)) {
}
}

@Test(timeout = 300_000)
fun `flow with value of generic type`() {
driver(parameters()) {
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
val txID = CordaRPCClient(hostAndPort = alice.rpcAddress)
.start(user.username, user.password)
.use { client ->
client.proxy.startFlow(::GenericTypeFlow, DataObject(DATA_VALUE))
.returnValue
.getOrThrow()
.returnValue
.getOrThrow()
}
logger.info("TX-ID=$txID")
}
}

@Test(timeout = 300_000)
fun `flow without value of generic type`() {
driver(parameters()) {
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
val ex = assertThrows<ContractRejection> {
CordaRPCClient(hostAndPort = alice.rpcAddress)
.start(user.username, user.password)
.use { client ->
client.proxy.startFlow(::GenericTypeFlow, null)
.returnValue
.getOrThrow()
}
}
assertThat(ex).hasMessageContaining("Contract verification failed: Failed requirement: Purchase has a price,")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor
import net.corda.flows.serialization.generics.GenericTypeFlow
import net.corda.node.DeterministicSourcesRule
import net.corda.node.internal.djvm.DeterministicVerificationException
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.driver.DriverParameters
Expand All @@ -15,8 +16,10 @@ import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.testing.node.NotarySpec
import net.corda.testing.node.User
import net.corda.testing.node.internal.cordappWithPackages
import org.assertj.core.api.Assertions.assertThat
import org.junit.ClassRule
import org.junit.Test
import org.junit.jupiter.api.assertThrows

@Suppress("FunctionName")
class DeterministicContractWithGenericTypeTest {
Expand All @@ -26,25 +29,31 @@ class DeterministicContractWithGenericTypeTest {
@JvmField
val logger = loggerFor<DeterministicContractWithGenericTypeTest>()

@JvmField
val user = User("u", "p", setOf(Permissions.all()))

@ClassRule
@JvmField
val djvmSources = DeterministicSourcesRule()

fun parameters(): DriverParameters {
return DriverParameters(
portAllocation = incrementalPortAllocation(),
startNodesInProcess = false,
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
cordappsForAllNodes = listOf(
cordappWithPackages("net.corda.flows.serialization.generics").signed(),
cordappWithPackages("net.corda.contracts.serialization.generics").signed()
),
djvmBootstrapSource = djvmSources.bootstrap,
djvmCordaSource = djvmSources.corda
)
}
}

@Test(timeout=300_000)
@Test(timeout = 300_000)
fun `test DJVM can deserialise command with generic type`() {
val user = User("u", "p", setOf(Permissions.all()))
driver(DriverParameters(
portAllocation = incrementalPortAllocation(),
startNodesInProcess = false,
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
cordappsForAllNodes = listOf(
cordappWithPackages("net.corda.flows.serialization.generics").signed(),
cordappWithPackages("net.corda.contracts.serialization.generics").signed()
),
djvmBootstrapSource = djvmSources.bootstrap,
djvmCordaSource = djvmSources.corda
)) {
driver(parameters()) {
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
val txID = CordaRPCClient(hostAndPort = alice.rpcAddress)
.start(user.username, user.password)
Expand All @@ -56,4 +65,21 @@ class DeterministicContractWithGenericTypeTest {
logger.info("TX-ID=$txID")
}
}

@Test(timeout = 300_000)
fun `test DJVM can deserialise command without value of generic type`() {
driver(parameters()) {
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
val ex = assertThrows<DeterministicVerificationException> {
CordaRPCClient(hostAndPort = alice.rpcAddress)
.start(user.username, user.password)
.use { client ->
client.proxy.startFlow(::GenericTypeFlow, null)
.returnValue
.getOrThrow()
}
}
assertThat(ex).hasMessageContaining("Contract verification failed: Failed requirement: Purchase has a price,")
}
}
}

0 comments on commit 6485a02

Please sign in to comment.