Skip to content

Commit 316e82b

Browse files
Re-implemented membership and revocation lock tests.
1 parent 1042412 commit 316e82b

22 files changed

+1204
-6
lines changed

onixlabs-corda-bnms-contract/src/main/kotlin/io/onixlabs/corda/bnms/contract/Extensions.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ internal val KClass<*>.contractClassName: ContractClassName
3232
} else this.java.enclosingClass.canonicalName
3333
}
3434

35-
internal fun StateAndRef<Membership>.getNextOutput() = state.data.copy(previousStateRef = ref)
35+
fun StateAndRef<Membership>.getNextOutput() = state.data.copy(previousStateRef = ref)
3636

37-
internal fun StateAndRef<Relationship>.getNextOutput() = state.data.copy(previousStateRef = ref)
37+
fun StateAndRef<Relationship>.getNextOutput() = state.data.copy(previousStateRef = ref)
3838

3939
private object IdentityComparator : Comparator<AbstractParty> {
4040
override fun compare(p0: AbstractParty?, p1: AbstractParty?): Int {

onixlabs-corda-bnms-contract/src/main/kotlin/io/onixlabs/corda/bnms/contract/membership/MembershipSchema.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ object MembershipSchema {
3232
@Column(name = "network_operator", nullable = true)
3333
val networkOperator: AbstractParty? = null,
3434

35-
@Column(name = "network_identity", nullable = false)
36-
val networkIdentity: AbstractParty = NULL_PARTY,
37-
3835
@Column(name = "network_hash", nullable = false)
3936
val networkHash: String = "",
4037

onixlabs-corda-bnms-workflow/src/main/kotlin/io/onixlabs/corda/bnms/workflow/membership/RevokeMembershipAttestationFlow.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class RevokeMembershipAttestationFlow(
3131

3232
val transaction = transaction(attestation.state.notary) {
3333
addInputState(attestation)
34-
addCommand(MembershipAttestationContract.Issue, attestation.state.data.attestor.owningKey)
34+
addCommand(MembershipAttestationContract.Revoke, attestation.state.data.attestor.owningKey)
3535
}
3636

3737
val signedTransaction = verifyAndSign(transaction)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.onixlabs.corda.bnms.workflow
2+
3+
import net.corda.core.contracts.LinearState
4+
import net.corda.core.contracts.UniqueIdentifier
5+
import net.corda.core.identity.AbstractParty
6+
7+
data class DummyLinearState(
8+
override val linearId: UniqueIdentifier = UniqueIdentifier(),
9+
override val participants: List<AbstractParty> = emptyList()
10+
) : LinearState
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package io.onixlabs.corda.bnms.workflow
2+
3+
import io.onixlabs.corda.bnms.contract.Network
4+
import io.onixlabs.corda.bnms.contract.membership.Membership
5+
import net.corda.core.concurrent.CordaFuture
6+
import net.corda.core.contracts.ContractState
7+
import net.corda.core.contracts.StateAndRef
8+
import net.corda.core.flows.FlowLogic
9+
import net.corda.core.identity.CordaX500Name
10+
import net.corda.core.identity.Party
11+
import net.corda.core.transactions.SignedTransaction
12+
import net.corda.core.utilities.getOrThrow
13+
import net.corda.testing.common.internal.testNetworkParameters
14+
import net.corda.testing.core.TestIdentity
15+
import net.corda.testing.core.singleIdentity
16+
import net.corda.testing.node.MockNetwork
17+
import net.corda.testing.node.MockNetworkParameters
18+
import net.corda.testing.node.StartedMockNode
19+
import net.corda.testing.node.TestCordapp
20+
import org.junit.jupiter.api.AfterAll
21+
import org.junit.jupiter.api.BeforeAll
22+
import org.junit.jupiter.api.TestInstance
23+
import java.time.Duration
24+
25+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
26+
abstract class FlowTest : AutoCloseable {
27+
28+
protected companion object {
29+
30+
val cordapps = listOf(
31+
"io.onixlabs.corda.claims.contract",
32+
"io.onixlabs.corda.claims.workflow",
33+
"io.onixlabs.corda.bnms.contract.membership",
34+
"io.onixlabs.corda.bnms.contract.relationship",
35+
"io.onixlabs.corda.bnms.contract.revocation",
36+
"io.onixlabs.corda.bnms.workflow.membership",
37+
"io.onixlabs.corda.bnms.workflow.relationship",
38+
"io.onixlabs.corda.bnms.workflow.revocation"
39+
)
40+
41+
val MEMBER_A = TestIdentity(CordaX500Name("Member A", "London", "GB"))
42+
val MEMBER_B = TestIdentity(CordaX500Name("Member B", "New York", "US"))
43+
val MEMBER_C = TestIdentity(CordaX500Name("Member C", "Paris", "FR"))
44+
val OPERATOR = TestIdentity(CordaX500Name("Operator", "London", "GB"))
45+
46+
fun partiesOf(vararg identities: TestIdentity) = identities.map { it.party }
47+
fun keysOf(vararg identities: TestIdentity) = identities.map { it.publicKey }
48+
}
49+
50+
protected val network: MockNetwork get() = _network
51+
52+
protected val notaryNode: StartedMockNode get() = _notaryNode
53+
protected val memberNodeA: StartedMockNode get() = _memberNodeA
54+
protected val memberNodeB: StartedMockNode get() = _memberNodeB
55+
protected val memberNodeC: StartedMockNode get() = _memberNodeC
56+
protected val operatorNode: StartedMockNode get() = _operatorNode
57+
58+
protected val notaryParty: Party get() = _notaryParty
59+
protected val memberPartyA: Party get() = _memberPartyA
60+
protected val memberPartyB: Party get() = _memberPartyB
61+
protected val memberPartyC: Party get() = _memberPartyC
62+
protected val operatorParty: Party get() = _operatorParty
63+
64+
private lateinit var _network: MockNetwork
65+
66+
private lateinit var _notaryNode: StartedMockNode
67+
private lateinit var _memberNodeA: StartedMockNode
68+
private lateinit var _memberNodeB: StartedMockNode
69+
private lateinit var _memberNodeC: StartedMockNode
70+
private lateinit var _operatorNode: StartedMockNode
71+
72+
private lateinit var _notaryParty: Party
73+
private lateinit var _memberPartyA: Party
74+
private lateinit var _memberPartyB: Party
75+
private lateinit var _memberPartyC: Party
76+
private lateinit var _operatorParty: Party
77+
78+
val centralizedNetwork by lazy { Network("Centralized Network", operatorParty) }
79+
val decentralizedNetwork by lazy { Network("Decentralized Network") }
80+
81+
@BeforeAll
82+
private fun setup() {
83+
_network = MockNetwork(
84+
MockNetworkParameters(
85+
cordappsForAllNodes = cordapps.map { TestCordapp.findCordapp(it) },
86+
networkParameters = testNetworkParameters(
87+
minimumPlatformVersion = 5
88+
)
89+
)
90+
)
91+
92+
_notaryNode = network.defaultNotaryNode
93+
_memberNodeA = network.createPartyNode(MEMBER_A.name)
94+
_memberNodeB = network.createPartyNode(MEMBER_B.name)
95+
_memberNodeC = network.createPartyNode(MEMBER_C.name)
96+
_operatorNode = network.createPartyNode(OPERATOR.name)
97+
98+
_notaryParty = notaryNode.info.singleIdentity()
99+
_memberPartyA = memberNodeA.info.singleIdentity()
100+
_memberPartyB = memberNodeB.info.singleIdentity()
101+
_memberPartyC = memberNodeC.info.singleIdentity()
102+
_operatorParty = operatorNode.info.singleIdentity()
103+
104+
initialize()
105+
}
106+
107+
@AfterAll
108+
private fun tearDown() {
109+
network.stopNodes()
110+
finalize()
111+
}
112+
113+
override fun close() = finalize()
114+
protected open fun initialize() = Unit
115+
protected open fun finalize() = Unit
116+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.onixlabs.corda.bnms.workflow
2+
3+
import net.corda.core.flows.FlowLogic
4+
import net.corda.core.utilities.getOrThrow
5+
import net.corda.testing.node.MockNetwork
6+
import net.corda.testing.node.StartedMockNode
7+
import java.time.Duration
8+
9+
class Pipeline<T>(val result: T, val network: MockNetwork, val timeout: Duration) {
10+
11+
companion object {
12+
fun create(network: MockNetwork, duration: Duration = Duration.ofSeconds(30)): Pipeline<Any?> {
13+
return Pipeline(null, network, duration)
14+
}
15+
}
16+
17+
fun <U> run(node: StartedMockNode, action: (T) -> FlowLogic<U>): Pipeline<U> {
18+
val future = node.startFlow(action(result))
19+
network.runNetwork()
20+
return Pipeline(future.getOrThrow(timeout), network, timeout)
21+
}
22+
23+
fun finally(action: (T) -> Unit) {
24+
action(result)
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.onixlabs.corda.bnms.workflow
2+
3+
import net.corda.core.contracts.ContractState
4+
import net.corda.core.contracts.StateAndRef
5+
import net.corda.core.transactions.SignedTransaction
6+
7+
inline fun <reified T : ContractState> SignedTransaction.getOutput(): StateAndRef<T> {
8+
return tx.outRefsOfType<T>().single()
9+
}
10+
11+
inline fun <reified T : ContractState> SignedTransaction.getOutputs(): List<StateAndRef<T>> {
12+
return tx.outRefsOfType<T>()
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package io.onixlabs.corda.bnms.workflow.membership.centralized
2+
3+
import io.onixlabs.corda.bnms.contract.AttestationStatus
4+
import io.onixlabs.corda.bnms.contract.membership.Membership
5+
import io.onixlabs.corda.bnms.contract.membership.MembershipAttestation
6+
import io.onixlabs.corda.bnms.workflow.FlowTest
7+
import io.onixlabs.corda.bnms.workflow.Pipeline
8+
import io.onixlabs.corda.bnms.workflow.getOutput
9+
import io.onixlabs.corda.bnms.workflow.membership.AmendMembershipAttestationFlow
10+
import io.onixlabs.corda.bnms.workflow.membership.IssueMembershipAttestationFlow
11+
import io.onixlabs.corda.bnms.workflow.membership.IssueMembershipFlow
12+
import net.corda.core.transactions.SignedTransaction
13+
import org.junit.jupiter.api.Test
14+
import org.junit.jupiter.api.fail
15+
import kotlin.test.assertEquals
16+
17+
class AmendMembershipAttestationFlowTests : FlowTest() {
18+
19+
private lateinit var transaction: SignedTransaction
20+
private lateinit var membershipAttestation: MembershipAttestation
21+
22+
override fun initialize() {
23+
Pipeline
24+
.create(network)
25+
.run(memberNodeA) {
26+
val membership = Membership(centralizedNetwork, memberPartyA)
27+
IssueMembershipFlow.Initiator(membership)
28+
}
29+
.run(operatorNode) {
30+
val membership = it.getOutput<Membership>()
31+
val attestation = MembershipAttestation(operatorParty, membership, AttestationStatus.ACCEPTED)
32+
IssueMembershipAttestationFlow.Initiator(attestation)
33+
}
34+
.run(operatorNode) {
35+
val oldAttestation = it.getOutput<MembershipAttestation>()
36+
val newAttestation = oldAttestation.state.data.accept()
37+
AmendMembershipAttestationFlow.Initiator(
38+
oldAttestation,
39+
newAttestation
40+
)
41+
}
42+
.finally {
43+
transaction = it
44+
membershipAttestation = it.getOutput<MembershipAttestation>().state.data
45+
}
46+
}
47+
48+
@Test
49+
fun `AmendMembershipAttestationFlow transaction should be signed by the initiator`() {
50+
transaction.verifyRequiredSignatures()
51+
}
52+
53+
@Test
54+
fun `AmendMembershipAttestationFlow should record a transaction for all participants and observers`() {
55+
listOf(memberNodeA, operatorNode).forEach {
56+
it.transaction {
57+
val recordedTransaction = it.services.validatedTransactions.getTransaction(transaction.id)
58+
?: fail("Could not find a recorded transaction with id: ${transaction.id}.")
59+
60+
assertEquals(transaction, recordedTransaction)
61+
assertEquals(1, recordedTransaction.tx.inputs.size)
62+
assertEquals(1, recordedTransaction.tx.outputs.size)
63+
}
64+
}
65+
}
66+
67+
@Test
68+
fun `AmendMembershipAttestationFlow should record a membership state for all participants and observers`() {
69+
listOf(memberNodeA, operatorNode).forEach {
70+
it.transaction {
71+
val recordedTransaction = it.services.validatedTransactions.getTransaction(transaction.id)
72+
?: fail("Could not find a recorded transaction with id: ${transaction.id}.")
73+
74+
val recordedMembershipAttestation = recordedTransaction
75+
.tx.outputsOfType<MembershipAttestation>().single()
76+
77+
assertEquals(membershipAttestation, recordedMembershipAttestation)
78+
}
79+
}
80+
}
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package io.onixlabs.corda.bnms.workflow.membership.centralized
2+
3+
import io.onixlabs.corda.bnms.contract.Role
4+
import io.onixlabs.corda.bnms.contract.getNextOutput
5+
import io.onixlabs.corda.bnms.contract.membership.Membership
6+
import io.onixlabs.corda.bnms.workflow.FlowTest
7+
import io.onixlabs.corda.bnms.workflow.Pipeline
8+
import io.onixlabs.corda.bnms.workflow.getOutput
9+
import io.onixlabs.corda.bnms.workflow.membership.AmendMembershipFlow
10+
import io.onixlabs.corda.bnms.workflow.membership.IssueMembershipFlow
11+
import net.corda.core.transactions.SignedTransaction
12+
import org.junit.jupiter.api.Test
13+
import org.junit.jupiter.api.fail
14+
import kotlin.test.assertEquals
15+
16+
class AmendMembershipFlowTests : FlowTest() {
17+
18+
private lateinit var transaction: SignedTransaction
19+
private lateinit var membership: Membership
20+
21+
override fun initialize() {
22+
Pipeline
23+
.create(network)
24+
.run(memberNodeA) {
25+
val membership = Membership(centralizedNetwork, memberPartyA)
26+
IssueMembershipFlow.Initiator(membership)
27+
}
28+
.run(memberNodeA) {
29+
val oldMembership = it.getOutput<Membership>()
30+
val newMembership = oldMembership.getNextOutput().addRoles(Role.USER)
31+
AmendMembershipFlow.Initiator(oldMembership, newMembership)
32+
}
33+
.run(operatorNode) {
34+
val oldMembership = it.getOutput<Membership>()
35+
val newMembership = oldMembership.getNextOutput().addRoles(Role.ADMINISTRATOR)
36+
AmendMembershipFlow.Initiator(oldMembership, newMembership)
37+
}
38+
.finally {
39+
transaction = it
40+
membership = it.getOutput<Membership>().state.data
41+
}
42+
}
43+
44+
@Test
45+
fun `AmendMembershipFlow transaction should be signed by the initiator`() {
46+
transaction.verifyRequiredSignatures()
47+
}
48+
49+
@Test
50+
fun `AmendMembershipFlow should record a transaction for all participants and observers`() {
51+
listOf(memberNodeA, operatorNode).forEach {
52+
it.transaction {
53+
val recordedTransaction = it.services.validatedTransactions.getTransaction(transaction.id)
54+
?: fail("Could not find a recorded transaction with id: ${transaction.id}.")
55+
56+
assertEquals(transaction, recordedTransaction)
57+
assertEquals(1, recordedTransaction.tx.inputs.size)
58+
assertEquals(1, recordedTransaction.tx.outputs.size)
59+
}
60+
}
61+
}
62+
63+
@Test
64+
fun `AmendMembershipFlow should record a membership state for all participants and observers`() {
65+
listOf(memberNodeA, operatorNode).forEach {
66+
it.transaction {
67+
val recordedTransaction = it.services.validatedTransactions.getTransaction(transaction.id)
68+
?: fail("Could not find a recorded transaction with id: ${transaction.id}.")
69+
70+
val recordedMembership = recordedTransaction
71+
.tx.outputsOfType<Membership>().single()
72+
73+
assertEquals(membership, recordedMembership)
74+
assert(recordedMembership.hasRole(Role.USER))
75+
assert(recordedMembership.hasRole(Role.ADMINISTRATOR))
76+
}
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)