Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5b4e678
Checkpoint passing without rekey scenarios
michaeldiamant Jul 26, 2022
fb22aa5
Checkpoint v1 tests passing
michaeldiamant Jul 26, 2022
9fa864b
Attempt to address flaky transaction out of range error
michaeldiamant Jul 26, 2022
2f8f629
Add devmode network override
michaeldiamant Jul 26, 2022
56621c9
Set env var
michaeldiamant Jul 26, 2022
53c880b
Set env var
michaeldiamant Jul 26, 2022
010b492
Revert formatting change
michaeldiamant Jul 26, 2022
c309942
Rename stale helper method
michaeldiamant Jul 26, 2022
923caf7
Try to address flaky transaction out of range error
michaeldiamant Jul 26, 2022
8cb1367
Try to address another flaky transaction out of range error
michaeldiamant Jul 26, 2022
5392293
Remove flaky assertion
michaeldiamant Jul 26, 2022
0f7567c
Update comment
michaeldiamant Jul 26, 2022
1db8a16
Point to devmodenet algorand-sdk-testing brnach
michaeldiamant Jul 26, 2022
d0aa733
Address PR feedback
michaeldiamant Jul 26, 2022
3390a6d
Support larger random number generation
michaeldiamant Jul 26, 2022
e67d6a9
Refactor to remove duplicative state management
michaeldiamant Jul 26, 2022
fbcbf63
Minimize use of transactions to advance rounds
michaeldiamant Jul 28, 2022
c0cd66f
Update renamed step
michaeldiamant Jul 28, 2022
93dfa21
Limit round waiting in dev mode
michaeldiamant Jul 28, 2022
462691c
Rename rekey annotation
michaeldiamant Aug 1, 2022
2ac83d4
Remove stale override
michaeldiamant Aug 1, 2022
bb7c7fb
Restore default algorand-sdk-testing branch
michaeldiamant Aug 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ unit:
mvn test -Dcucumber.filter.tags="@unit.offline or @unit.algod or @unit.indexer or @unit.rekey or @unit.indexer.rekey or @unit.transactions or @unit.transactions.keyreg or @unit.responses or @unit.applications or @unit.dryrun or @unit.tealsign or @unit.responses.messagepack or @unit.responses.231 or @unit.responses.messagepack.231 or @unit.feetest or @unit.indexer.logs or @unit.abijson or @unit.abijson.byname or @unit.atomic_transaction_composer or @unit.transactions.payment or @unit.responses.unlimited_assets or @unit.algod.ledger_refactoring or @unit.indexer.ledger_refactoring or @unit.dryrun.trace.application"

integration:
mvn test -Dcucumber.filter.tags="@algod or @assets or @auction or @kmd or @send or @send.keyregtxn or @indexer or @rekey or @applications.verified or @applications or @compile or @dryrun or @indexer.applications or @indexer.231 or @abi or @c2c"
mvn test \
-Dtest=com.algorand.algosdk.integration.RunCucumberIntegrationTest \
-Dcucumber.filter.tags="@algod or @assets or @auction or @kmd or @send or @send.keyregtxn or @indexer or @rekey_v1 or @applications.verified or @applications or @compile or @dryrun or @indexer.applications or @indexer.231 or @abi or @c2c"

docker-test:
./run_integration_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public void sendTransactionWithTransientAccountAndCheckForError(String error) th

@Given("I wait for the transaction to be confirmed.")
public void waitForTransactionToBeConfirmed() throws Exception {
Utils.waitForConfirmation(clients.v2Client, txId, 5);
Utils.waitForConfirmation(clients.v2Client, txId, 1);
}

// TODO: Use V2 Pending Transaction endpoint when it is available.
Expand Down Expand Up @@ -165,7 +165,7 @@ public void fundAppAccount(Integer amount) throws Exception {
SignedTransaction stx = base.signWithAddress(tx, sender);

Response<PostTransactionsResponse> rPost = clients.v2Client.RawTransaction().rawtxn(Encoder.encodeToMsgPack(stx)).execute();
Utils.waitForConfirmation(clients.v2Client, rPost.body().txId, 5);
Utils.waitForConfirmation(clients.v2Client, rPost.body().txId, 1);
}

@Then("I get the account address for the current application and see that it matches the app id's hash")
Expand Down
144 changes: 129 additions & 15 deletions src/test/java/com/algorand/algosdk/integration/Stepdefs.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;

import static com.algorand.algosdk.util.ResourceUtils.loadResource;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -118,6 +119,31 @@ public class Stepdefs {
Response<CompileResponse> compileResponse;
Response<DryrunResponse> dryrunResponse;

private static class DevModeState {
static final long ACCOUNT_FUNDING_MICROALGOS = 100_000_000;
private Account advanceRounds;

/**
* randomAmount minimizes the chance `advanceRounds` issues duplicate transactions by randomizing the payment amount.
Comment thread
michaeldiamant marked this conversation as resolved.
*/
private long randomAmount() {
return ThreadLocalRandom.current().nextLong(1, (long) (ACCOUNT_FUNDING_MICROALGOS * .01));
}

public SignedTransaction selfPay(TransactionParams tp) throws Exception {
Transaction tx =
Transaction.PaymentTransactionBuilder()
.sender(advanceRounds.getAddress())
.suggestedParams(tp)
.amount(randomAmount())
.receiver(advanceRounds.getAddress())
.build();
return advanceRounds.signTransaction(tx);
}
}

private final DevModeState dms = new DevModeState();

protected Address getAddress(int i) {
if (addresses == null) {
throw new RuntimeException("Addresses not initialized, must use given 'wallet information'");
Expand Down Expand Up @@ -158,6 +184,48 @@ public SignedTransaction signWithAddress(Transaction tx, Address addr) throws co
return acct.signTransaction(tx);
}

/**
* advanceRound is a convenience method intended for testing with DevMode networks.
* <p>
* Since DevMode block generation requires a transaction rather than time passing, test assertions may require advancing rounds. advanceRound issues advanceCount payments to advance rounds.
*/
private void advanceRoundsV1(int advanceCount) {
Comment thread
michaeldiamant marked this conversation as resolved.
initializeDevModeAccount();
for (int i = 0; i < advanceCount; i++) {
try {
acl.rawTransaction(Encoder.encodeToMsgPack(dms.selfPay(acl.transactionParams())));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

/**
* initializeDevModeAccount performs a one-time account initialization per inclusion in a scenario outline. No attempt is made to delete the account.
*/
public void initializeDevModeAccount() {
if (dms.advanceRounds != null) {
return;
}

try {
getParams();
dms.advanceRounds = new Account();
Address sender = getAddress(0);
Transaction tx =
Transaction.PaymentTransactionBuilder()
.sender(sender)
.suggestedParams(acl.transactionParams())
.amount(DevModeState.ACCOUNT_FUNDING_MICROALGOS)
.receiver(dms.advanceRounds.getAddress())
.build();
SignedTransaction st = signWithAddress(tx, sender);
acl.rawTransaction(Encoder.encodeToMsgPack(st));
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* Convenience method to export a key and initialize an account to use for signing.
*/
Expand Down Expand Up @@ -447,8 +515,8 @@ public void status() throws ApiException{
}

@When("I get status after this block")
public void statusBlock() throws ApiException, InterruptedException {
Thread.sleep(4000);
public void statusBlock() throws Exception {
advanceRoundsV1(1);
statusAfter = acl.waitForBlock(status.getLastRound());
}

Expand Down Expand Up @@ -526,21 +594,48 @@ public void msigNotInWallet()throws com.algorand.algosdk.kmd.client.ApiException
}

@When("I generate a key using kmd")
public void genKeyKmd() throws com.algorand.algosdk.kmd.client.ApiException, NoSuchAlgorithmException{
public void genKeyKmd() throws com.algorand.algosdk.kmd.client.ApiException, NoSuchAlgorithmException {
GenerateKeyRequest req = new GenerateKeyRequest();
req.setDisplayMnemonic(false);
req.setWalletHandleToken(handle);
pk = new Address(kcl.generateKey(req).getAddress());
}

@When("I generate a key using kmd for rekeying and fund it")
public void genKeyKmdRekey() throws com.algorand.algosdk.kmd.client.ApiException, NoSuchAlgorithmException {
GenerateKeyRequest req = new GenerateKeyRequest();
req.setDisplayMnemonic(false);
req.setWalletHandleToken(handle);
rekey = new Address(kcl.generateKey(req).getAddress());

// Fund rekey address
Comment thread
winder marked this conversation as resolved.
try {
getParams();
Address sender = getAddress(0);
Transaction tx =
Transaction.PaymentTransactionBuilder()
.sender(sender)
.suggestedParams(acl.transactionParams())
.amount(100_000_000)
Comment thread
algochoi marked this conversation as resolved.
.receiver(rekey)
.build();
SignedTransaction st = signWithAddress(tx, sender);
acl.rawTransaction(Encoder.encodeToMsgPack(st));
} catch (Exception e) {
throw new RuntimeException(e);
}
}

Address rekey;

@Then("the key should be in the wallet")
public void keyInWallet() throws com.algorand.algosdk.kmd.client.ApiException {
ListKeysRequest req = new ListKeysRequest();
req.setWalletHandleToken(handle);
List<String> keys = kcl.listKeysInWallet(req).getAddresses();
boolean exists = false;
for (String k : keys){
if (k.equals(pk.toString())){
for (String k : keys) {
if (k.equals(pk.toString())) {
exists = true;
}
}
Expand Down Expand Up @@ -632,6 +727,7 @@ public void aClient() throws FileNotFoundException, IOException{
algodClient.setBasePath("http://localhost:" + algodPort);
acl = new AlgodApi(algodClient);
}

@Given("an algod v2 client")
public void aClientv2() throws FileNotFoundException, IOException{
aclv2 = new com.algorand.algosdk.v2.client.common.AlgodClient(
Expand Down Expand Up @@ -660,21 +756,30 @@ public void walletInfo() throws com.algorand.algosdk.kmd.client.ApiException, No
}

@Given("default transaction with parameters {int} {string}")
public void defaultTxn(int amt, String note) throws ApiException, NoSuchAlgorithmException{
public void defaultTxn(int amt, String note) throws ApiException, NoSuchAlgorithmException {
defaultTxnWithAddress(amt, note, getAddress(0));
}

@Given("default transaction with parameters {int} {string} and rekeying key")
public void defaultTxnForRekeying(int amt, String note) {
defaultTxnWithAddress(amt, note, rekey);
}

private void defaultTxnWithAddress(int amt, String note, Address sender) {
getParams();
if (note.equals("none")){
if (note.equals("none")) {
this.note = null;
} else{
} else {
this.note = Encoder.decodeFromBase64(note);
}
txnBuilder = Transaction.PaymentTransactionBuilder()
.sender(getAddress(0))
.sender(sender)
.suggestedParams(params)
.note(this.note)
.amount(amt)
.receiver(getAddress(1));
txn = txnBuilder.build();
pk = getAddress(0);
pk = sender;
}

@Given("default multisig transaction with parameters {int} {string}")
Expand Down Expand Up @@ -715,23 +820,32 @@ public void sendMsigTxn() throws JsonProcessingException, ApiException{
}

@Then("the transaction should go through")
public void checkTxn() throws ApiException, InterruptedException{
public void checkTxn() throws Exception {
String ans = acl.pendingTransactionInformation(txid).getFrom();
assertThat(this.txn.sender.toString()).isEqualTo(ans);
acl.waitForBlock(lastRound.add(BigInteger.valueOf(2)));
waitForAlgodTransactionProcessingToComplete();
Comment thread
michaeldiamant marked this conversation as resolved.
String senderFromResponse = acl.transactionInformation(txn.sender.toString(), txid).getFrom();
assertThat(senderFromResponse).isEqualTo(txn.sender.toString());
assertThat(acl.transaction(txid).getFrom()).isEqualTo(senderFromResponse);
}

/**
* waitForAlgodTransactionProcessingToComplete is a Dev mode helper method that's a rough analog to `acl.waitForBlock(lastRound.add(BigInteger.valueOf(2)));`.
* <p>
* Since Dev mode produces blocks on a per transaction basis, it's possible algod generates a block _before_ the corresponding SDK call to wait for a block. Without _any_ wait, it's possible the SDK looks for the transaction before algod completes processing. So, the method performs a local sleep to simulate waiting for a block.
*/
private static void waitForAlgodTransactionProcessingToComplete() throws Exception {
Comment thread
michaeldiamant marked this conversation as resolved.
Thread.sleep(500);
}

@Then("I can get the transaction by ID")
public void txnbyID() throws ApiException, InterruptedException{
acl.waitForBlock(lastRound.add(BigInteger.valueOf(2)));
public void txnByID() throws Exception {
waitForAlgodTransactionProcessingToComplete();
assertThat(acl.transaction(txid).getFrom()).isEqualTo(pk.toString());
}

@Then("the transaction should not go through")
public void txnFail(){
public void txnFail() {
assertThat(err).isTrue();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void createAndFundTransientAccount(Long amount) throws Exception {
SignedTransaction stx = base.signWithAddress(tx, sender);

Response<PostTransactionsResponse> rPost = clients.v2Client.RawTransaction().rawtxn(Encoder.encodeToMsgPack(stx)).execute();
Utils.waitForConfirmation(clients.v2Client, rPost.body().txId, 5);
Utils.waitForConfirmation(clients.v2Client, rPost.body().txId, 1);
}

}