Skip to content

Commit 599dba6

Browse files
committed
added contract tests
1 parent f719bd1 commit 599dba6

File tree

6 files changed

+179
-101
lines changed

6 files changed

+179
-101
lines changed

Features/encumbrance-avatar/contracts/src/main/java/com/template/contracts/AvatarContract.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentExceptio
3434
Avatar avatar = tx.outputsOfType(Avatar.class).get(0);
3535
require.using("Avatar Owner must always sign the newly created Avatar.", signers.contains(avatar.getOwner().getOwningKey()));
3636

37+
Integer avatarEncumbrance = tx.getOutputs().stream().filter(o -> o.getData() instanceof Avatar).findFirst().get().getEncumbrance();
38+
require.using("Avatar needs to be encumbered", avatarEncumbrance != null);
39+
3740
return null;
3841
});
3942
} else if (value instanceof Commands.Transfer) {
Lines changed: 169 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,180 @@
11
package com.template.contracts;
22

3+
import com.google.common.collect.ImmutableList;
4+
import com.template.states.Avatar;
5+
import com.template.states.Expiry;
6+
import net.corda.core.identity.CordaX500Name;
7+
import net.corda.testing.core.TestIdentity;
38
import net.corda.testing.node.MockServices;
49
import org.junit.Test;
510

11+
import java.time.Duration;
12+
import java.time.Instant;
13+
import java.time.temporal.ChronoUnit;
14+
15+
import static net.corda.testing.node.NodeTestUtils.ledger;
16+
617
public class ContractTests {
7-
private final MockServices ledgerServices = new MockServices();
818

19+
static private final MockServices ledgerServices = new MockServices();
20+
static private final TestIdentity seller = new TestIdentity(new CordaX500Name("MegaCorp", "London", "GB"));
21+
static private final TestIdentity buyer = new TestIdentity(new CordaX500Name("MiniCorp", "London", "GB"));
22+
23+
24+
//Both the encumbrance and the encumbered state must be added to the transaction
25+
@Test
26+
public void thereMustBeTwoOutputs() {
27+
ledger(ledgerServices, (ledger -> {
28+
ledger.transaction(tx -> {
29+
tx.output(AvatarContract.AVATAR_CONTRACT_ID, new Avatar(seller.getParty(), "1"));
30+
tx.command(ImmutableList.of(seller.getPublicKey()), new AvatarContract.Commands.Create());
31+
tx.fails();
32+
return null;
33+
});
34+
return null;
35+
}));
36+
}
37+
38+
39+
@Test
40+
public void encumbranceIndexMustBeSpecified() {
41+
//not specifying the encumbrance index fails the contract
42+
ledger(ledgerServices, (ledger -> {
43+
ledger.transaction(tx -> {
44+
tx.output(AvatarContract.AVATAR_CONTRACT_ID, new Avatar(seller.getParty(), "1"));
45+
tx.output(ExpiryContract.EXPIRY_CONTRACT_ID,
46+
new Expiry(Instant.now().plus(2, ChronoUnit.MINUTES), "1", seller.getParty()));
47+
tx.command(ImmutableList.of(seller.getPublicKey()), new AvatarContract.Commands.Create());
48+
tx.timeWindow(Instant.now(), Duration.ofMinutes(1));
49+
tx.failsWith("Avatar needs to be encumbered");
50+
51+
return null;
52+
});
53+
return null;
54+
}));
55+
56+
//specifying the encumbrance index while adding output states passes the contract
57+
ledger(ledgerServices, (ledger -> {
58+
ledger.transaction(tx -> {
59+
tx.output(AvatarContract.AVATAR_CONTRACT_ID, 1, new Avatar(seller.getParty(), "1"));
60+
tx.output(ExpiryContract.EXPIRY_CONTRACT_ID, 0,
61+
new Expiry(Instant.now().plus(2, ChronoUnit.MINUTES), "1", seller.getParty()));
62+
tx.command(ImmutableList.of(seller.getPublicKey()), new AvatarContract.Commands.Create());
63+
tx.timeWindow(Instant.now(), Duration.ofMinutes(1));
64+
tx.verifies();
65+
return null;
66+
});
67+
return null;
68+
}));
69+
}
70+
71+
//Specifying time window is mandatory. This is checked in the encumbrance Expiry state.
972
@Test
10-
public void dummyTest() {
73+
public void specifyTimeWindow() {
74+
ledger(ledgerServices, (ledger -> {
75+
ledger.transaction(tx -> {
76+
//this fails as time window is not specified
77+
tx.output(AvatarContract.AVATAR_CONTRACT_ID, new Avatar(seller.getParty(), "1"));
78+
tx.output(ExpiryContract.EXPIRY_CONTRACT_ID,
79+
new Expiry(Instant.now().plus(2, ChronoUnit.MINUTES), "1", seller.getParty()));
80+
tx.command(ImmutableList.of(seller.getPublicKey()), new AvatarContract.Commands.Create());
81+
tx.fails();
1182

83+
//this will pass once we specify time window
84+
tx.timeWindow(Instant.now(), Duration.ofMinutes(1));
85+
return tx.verifies();
86+
});
87+
return null;
88+
}));
89+
}
90+
91+
//For selling, the Expiry of avatar must be greater than the time window
92+
@Test
93+
public void avatarIsRejectedIfItIsExpired() {
94+
ledger(ledgerServices, (ledger -> {
95+
ledger.transaction(tx -> {
96+
tx.output(AvatarContract.AVATAR_CONTRACT_ID, new Avatar(seller.getParty(), "1"));
97+
tx.output(ExpiryContract.EXPIRY_CONTRACT_ID,
98+
new Expiry(Instant.now().plus(2, ChronoUnit.MINUTES), "1", seller.getParty()));
99+
tx.command(ImmutableList.of(seller.getPublicKey()), new AvatarContract.Commands.Create());
100+
tx.timeWindow(Instant.now(), Duration.ofMinutes(3));
101+
tx.fails();
102+
return null;
103+
});
104+
return null;
105+
}));
106+
}
107+
108+
//For selling, the Expiry of avatar must be greater than the time window
109+
@Test
110+
public void expirationDateShouldBeAfterTheTimeWindow() {
111+
ledger(ledgerServices, (ledger -> {
112+
ledger.transaction(tx -> {
113+
tx.output(AvatarContract.AVATAR_CONTRACT_ID, new Avatar(seller.getParty(), "1"));
114+
tx.output(ExpiryContract.EXPIRY_CONTRACT_ID,
115+
new Expiry(Instant.now().plus(3, ChronoUnit.MINUTES), "1", seller.getParty()));
116+
tx.command(ImmutableList.of(seller.getPublicKey()), new AvatarContract.Commands.Create());
117+
tx.timeWindow(Instant.now(), Duration.ofMinutes(2));
118+
tx.verifies();
119+
return null;
120+
});
121+
return null;
122+
}));
123+
}
124+
125+
//test transaction which has encumbered states as inputs
126+
@Test
127+
public void transferAvatar() {
128+
ledger(ledgerServices, ledger -> {
129+
ledger.unverifiedTransaction(tx -> {
130+
tx.output(AvatarContract.AVATAR_CONTRACT_ID, "avatarLabel", 1,
131+
new Avatar(seller.getParty(), "1"));
132+
tx.output(ExpiryContract.EXPIRY_CONTRACT_ID, "expiryLabel", 0,
133+
new Expiry(Instant.now().plus(3, ChronoUnit.MINUTES), "1", seller.getParty()));
134+
return null;
135+
});
136+
ledger.transaction(tx -> {
137+
tx.input("avatarLabel");
138+
tx.input("expiryLabel");
139+
tx.output(AvatarContract.AVATAR_CONTRACT_ID, "avatarLabel2", 1,
140+
new Avatar(buyer.getParty(), "1"));
141+
tx.output(ExpiryContract.EXPIRY_CONTRACT_ID, "expiryLabel2", 0,
142+
new Expiry(Instant.now().plus(3, ChronoUnit.MINUTES), "1", buyer.getParty()));
143+
tx.command(ImmutableList.of(seller.getPublicKey(), buyer.getPublicKey()), new AvatarContract.Commands.Transfer());
144+
tx.command(ImmutableList.of(seller.getPublicKey(), buyer.getPublicKey()), new ExpiryContract.Commands.Pass());
145+
tx.timeWindow(Instant.now(), Duration.ofMinutes(2));
146+
tx.verifies();
147+
return null;
148+
});
149+
return null;
150+
});
151+
}
152+
153+
154+
@Test
155+
public void avatarCannotBeSpentWithoutExpiry() {
156+
ledger(ledgerServices, ledger -> {
157+
ledger.unverifiedTransaction(tx -> {
158+
tx.output(AvatarContract.AVATAR_CONTRACT_ID, "avatarLabel", 1,
159+
new Avatar(seller.getParty(), "1"));
160+
tx.output(ExpiryContract.EXPIRY_CONTRACT_ID, "expiryLabel", 0,
161+
new Expiry(Instant.now().plus(3, ChronoUnit.MINUTES), "1", seller.getParty()));
162+
return null;
163+
});
164+
ledger.transaction(tx -> {
165+
tx.input("avatarLabel");
166+
// tx.input(expiryLabel);
167+
tx.output(AvatarContract.AVATAR_CONTRACT_ID, "avatarLabel2", 1,
168+
new Avatar(buyer.getParty(), "1"));
169+
tx.output(ExpiryContract.EXPIRY_CONTRACT_ID, "expiryLabel2", 0,
170+
new Expiry(Instant.now().plus(3, ChronoUnit.MINUTES), "1", buyer.getParty()));
171+
tx.command(ImmutableList.of(seller.getPublicKey(), buyer.getPublicKey()), new AvatarContract.Commands.Transfer());
172+
tx.command(ImmutableList.of(seller.getPublicKey(), buyer.getPublicKey()), new ExpiryContract.Commands.Pass());
173+
tx.timeWindow(Instant.now(), Duration.ofMinutes(2));
174+
tx.fails();
175+
return null;
176+
});
177+
return null;
178+
});
12179
}
13180
}

Features/encumbrance-avatar/workflows/src/main/java/com/template/flows/CreateAvatarFlow.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public SignedTransaction call() throws FlowException {
3838
Expiry expiry = new Expiry(Instant.now().plus(expiryAfterMinutes, ChronoUnit.MINUTES), avatarId, avatar.getOwner());
3939

4040
//add expiry and avatar as outputs by specifying encumbrance as index. add time window
41+
//encumbrance can be identified by the output index. expiry is at output index 1 so we add 1 as the encumbrance
42+
//value while adding avatar as an output state and vice versa.
4143
TransactionBuilder txBuilder = new TransactionBuilder(notary)
4244
.addOutputState(avatar, AvatarContract.AVATAR_CONTRACT_ID, notary, 1) //specify the encumbrance as the 3rd parameter
4345
.addOutputState(expiry, ExpiryContract.EXPIRY_CONTRACT_ID, notary, 0) //specify the encumbrance as the 3rd parameter

Features/encumbrance-avatar/workflows/src/main/java/com/template/flows/SellAvatarFlow.java renamed to Features/encumbrance-avatar/workflows/src/main/java/com/template/flows/TransferAvatarFlow.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020

2121
@InitiatingFlow
2222
@StartableByRPC
23-
public class SellAvatarFlow extends FlowLogic<SignedTransaction> {
23+
public class TransferAvatarFlow extends FlowLogic<SignedTransaction> {
2424

2525
private final String avatarId;
2626
private final String buyer;
2727

28-
public SellAvatarFlow(String avatarId, String buyer) {
28+
public TransferAvatarFlow(String avatarId, String buyer) {
2929
this.avatarId = avatarId;
3030
this.buyer = buyer;
3131
}

Features/encumbrance-avatar/workflows/src/main/java/com/template/flows/SellAvatarResponderFlow.java renamed to Features/encumbrance-avatar/workflows/src/main/java/com/template/flows/TransferAvatarResponderFlow.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
import net.corda.core.flows.*;
66
import net.corda.core.transactions.SignedTransaction;
77

8-
@InitiatedBy(SellAvatarFlow.class)
9-
public class SellAvatarResponderFlow extends FlowLogic<SignedTransaction> {
8+
@InitiatedBy(TransferAvatarFlow.class)
9+
public class TransferAvatarResponderFlow extends FlowLogic<SignedTransaction> {
1010
private final FlowSession counterpartySession;
1111

12-
public SellAvatarResponderFlow(FlowSession counterpartySession) {
12+
public TransferAvatarResponderFlow(FlowSession counterpartySession) {
1313
this.counterpartySession = counterpartySession;
1414
}
1515

Features/encumbrance-avatar/workflows/src/test/java/com/template/ContractTests.java

Lines changed: 0 additions & 94 deletions
This file was deleted.

0 commit comments

Comments
 (0)