Skip to content

Commit 0bc0ce7

Browse files
Enhancement: Use Sandbox for Testing (#360)
Co-authored-by: Michael Diamant <michaeldiamant@users.noreply.github.com>
1 parent 716da0f commit 0bc0ce7

16 files changed

+170
-1152
lines changed

.test-env

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Configs for testing repo download:
2+
SDK_TESTING_URL="https://github.com/algorand/algorand-sdk-testing"
3+
SDK_TESTING_BRANCH="master"
4+
SDK_TESTING_HARNESS="test-harness"
5+
6+
VERBOSE_HARNESS=0
7+
8+
# WARNING: If set to 1, new features will be LOST when downloading the test harness.
9+
# REGARDLESS: modified features are ALWAYS overwritten.
10+
REMOVE_LOCAL_FEATURES=0
11+
12+
# WARNING: Be careful when turning on the next variable.
13+
# In that case you'll need to provide all variables expected by `algorand-sdk-testing`'s `.env`
14+
OVERWRITE_TESTING_ENVIRONMENT=0

Makefile

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
SRCPATH := $(shell pwd)
22
TEST_SOURCES := $(shell cd $(SRCPATH) && go list ./...)
33
TEST_SOURCES_NO_CUCUMBER := $(shell cd $(SRCPATH) && go list ./... | grep -v test)
4+
UNIT_TAGS := "$(shell awk '{print $2}' test/unit.tags | paste -s -d, -)"
5+
INTEGRATIONS_TAGS := "$(shell awk '{print $2}' test/integration.tags | paste -s -d, -)"
6+
GO_IMAGE := golang:$(subst go,,$(shell go version | cut -d' ' -f 3 | cut -d'.' -f 1,2))-stretch
47

58
lint:
69
golint `go list ./... | grep -v /vendor/`
@@ -19,13 +22,27 @@ test:
1922

2023
unit:
2124
go test $(TEST_SOURCES_NO_CUCUMBER)
22-
cd test && go test -timeout 0s --godog.strict=true --godog.format=pretty --godog.tags="@unit.sourcemap,@unit.offline,@unit.algod,@unit.indexer,@unit.transactions.keyreg,@unit.rekey,@unit.tealsign,@unit.dryrun,@unit.responses,@unit.applications,@unit.transactions,@unit.indexer.rekey,@unit.responses.messagepack,@unit.responses.231,@unit.responses.messagepack.231,@unit.responses.genesis,@unit.feetest,@unit.indexer.logs,@unit.abijson,@unit.abijson.byname,@unit.transactions.payment,@unit.atomic_transaction_composer,@unit.responses.unlimited_assets,@unit.indexer.ledger_refactoring,@unit.algod.ledger_refactoring,@unit.dryrun.trace.application" --test.v .
25+
cd test && go test -timeout 0s --godog.strict=true --godog.format=pretty --godog.tags=$(UNIT_TAGS) --test.v .
2326

2427
integration:
2528
go test $(TEST_SOURCES_NO_CUCUMBER)
26-
cd test && go test -timeout 0s --godog.strict=true --godog.format=pretty --godog.tags="@algod,@assets,@auction,@kmd,@send,@indexer,@rekey_v1,@send.keyregtxn,@dryrun,@compile,@applications.verified,@indexer.applications,@indexer.231,@abi,@c2c,@compile.sourcemap" --test.v .
29+
cd test && go test -timeout 0s --godog.strict=true --godog.format=pretty --godog.tags=$(INTEGRATIONS_TAGS) --test.v .
30+
31+
display-all-go-steps:
32+
find test -name "*.go" | xargs grep "github.com/cucumber/godog" 2>/dev/null | cut -d: -f1 | sort | uniq | xargs grep -Eo "Step[(].[^\`]+" | awk '{sub(/:Step\(./,":")} 1' | sed -E 's/", [a-zA-Z0-9]+\)//g'
33+
34+
harness:
35+
./test-harness.sh
36+
37+
docker-gosdk-build:
38+
echo "Building docker image from base $(GO_IMAGE)"
39+
docker build -t go-sdk-testing --build-arg GO_IMAGE="$(GO_IMAGE)" -f test/docker/Dockerfile $(shell pwd)
40+
41+
docker-gosdk-run:
42+
docker ps -a
43+
docker run -it --network host go-sdk-testing:latest
44+
45+
docker-test: harness docker-gosdk-build docker-gosdk-run
2746

28-
docker-test:
29-
./test/docker/run_docker.sh
3047

3148
.PHONY: test fmt

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ In `client/v2` the `indexer` package contains a client for the Algorand Indexer
3131

3232
# SDK Development
3333

34-
Run tests with `make docker-test`
34+
Run tests with `make docker-test`. To set up the sandbox-based test harness without standing up the go-algorand docker image use `make harness`.
3535

3636
# Quick Start
3737

test-harness.sh

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
START=$(date "+%s")
6+
7+
THIS=$(basename "$0")
8+
ENV_FILE=".test-env"
9+
TEST_DIR="test"
10+
11+
set -a
12+
source "$ENV_FILE"
13+
set +a
14+
15+
rootdir=$(dirname "$0")
16+
pushd "$rootdir"
17+
18+
## Reset test harness
19+
if [ -d "$SDK_TESTING_HARNESS" ]; then
20+
pushd "$SDK_TESTING_HARNESS"
21+
./scripts/down.sh
22+
popd
23+
rm -rf "$SDK_TESTING_HARNESS"
24+
else
25+
echo "$THIS: directory $SDK_TESTING_HARNESS does not exist - NOOP"
26+
fi
27+
28+
git clone --depth 1 --single-branch --branch "$SDK_TESTING_BRANCH" "$SDK_TESTING_URL" "$SDK_TESTING_HARNESS"
29+
30+
31+
if [[ $OVERWRITE_TESTING_ENVIRONMENT == 1 ]]; then
32+
echo "$THIS: OVERWRITE replaced $SDK_TESTING_HARNESS/.env with $ENV_FILE:"
33+
cp "$ENV_FILE" "$SDK_TESTING_HARNESS"/.env
34+
fi
35+
36+
## Copy feature files into the project resources
37+
if [[ $REMOVE_LOCAL_FEATURES == 1 ]]; then
38+
echo "$THIS: OVERWRITE wipes clean $TEST_DIR/features"
39+
if [[ $VERBOSE_HARNESS == 1 ]]; then
40+
( tree $TEST_DIR/features && echo "$THIS: see the previous for files deleted" ) || true
41+
fi
42+
rm -rf $TEST_DIR/features
43+
fi
44+
mkdir -p $TEST_DIR/features
45+
cp -r "$SDK_TESTING_HARNESS"/features/* $TEST_DIR/features
46+
if [[ $VERBOSE_HARNESS == 1 ]]; then
47+
( tree $TEST_DIR/features && echo "$THIS: see the previous for files copied over" ) || true
48+
fi
49+
echo "$THIS: seconds it took to get to end of cloning and copying: $(($(date "+%s") - START))s"
50+
51+
52+
## Start test harness environment
53+
pushd "$SDK_TESTING_HARNESS"
54+
./scripts/up.sh
55+
popd
56+
echo "$THIS: seconds it took to finish testing sdk's up.sh: $(($(date "+%s") - START))s"
57+
echo ""
58+
echo "--------------------------------------------------------------------------------"
59+
echo "|"
60+
echo "| To run sandbox commands, cd into $SDK_TESTING_HARNESS/.sandbox "
61+
echo "|"
62+
echo "--------------------------------------------------------------------------------"

test/algodclientv2_test.go

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88

99
"github.com/algorand/go-algorand-sdk/client/v2/algod"
10-
"github.com/algorand/go-algorand-sdk/client/v2/common/models"
1110
modelsV2 "github.com/algorand/go-algorand-sdk/client/v2/common/models"
1211
"github.com/algorand/go-algorand-sdk/types"
1312

@@ -20,7 +19,6 @@ func AlgodClientV2Context(s *godog.Suite) {
2019
s.Step(`^we make any Pending Transaction Information call$`, weMakeAnyPendingTransactionInformationCall)
2120
s.Step(`^the parsed Pending Transaction Information response should have sender "([^"]*)"$`, theParsedResponseShouldEqualTheMockResponse)
2221
s.Step(`^we make any Pending Transactions Information call$`, weMakeAnyPendingTransactionsInformationCall)
23-
s.Step(`^the parsed Pending Transactions Information response should have sender "([^"]*)"$`, theParsedResponseShouldEqualTheMockResponse)
2422
s.Step(`^we make any Send Raw Transaction call$`, weMakeAnySendRawTransactionCall)
2523
s.Step(`^the parsed Send Raw Transaction response should have txid "([^"]*)"$`, theParsedResponseShouldEqualTheMockResponse)
2624
s.Step(`^we make any Pending Transactions By Address call$`, weMakeAnyPendingTransactionsByAddressCall)
@@ -38,11 +36,8 @@ func AlgodClientV2Context(s *godog.Suite) {
3836
s.Step(`^we make any Suggested Transaction Parameters call$`, weMakeAnySuggestedTransactionParametersCall)
3937
s.Step(`^the parsed Suggested Transaction Parameters response should have first round valid of (\d+)$`, theParsedResponseShouldEqualTheMockResponse)
4038
s.Step(`^expect the path used to be "([^"]*)"$`, expectThePathUsedToBe)
41-
s.Step(`^we make a Pending Transaction Information against txid "([^"]*)" with max (\d+)$`, weMakeAPendingTransactionInformationAgainstTxidWithMax)
42-
s.Step(`^we make a Pending Transactions By Address call against account "([^"]*)" and max (\d+)$`, weMakeAPendingTransactionsByAddressCallAgainstAccountAndMax)
4339
s.Step(`^we make a Status after Block call with round (\d+)$`, weMakeAStatusAfterBlockCallWithRound)
4440
s.Step(`^we make an Account Information call against account "([^"]*)"$`, weMakeAnAccountInformationCallAgainstAccount)
45-
s.Step(`^we make a Get Block call against block number (\d+)$`, weMakeAGetBlockCallAgainstBlockNumber)
4641
s.Step(`^the parsed Pending Transactions Information response should contain an array of len (\d+) and element number (\d+) should have sender "([^"]*)"$`, theParsedResponseShouldEqualTheMockResponse)
4742
s.Step(`^we make a Pending Transaction Information against txid "([^"]*)" with format "([^"]*)"$`, weMakeAPendingTransactionInformationAgainstTxidWithFormat)
4843
s.Step(`^we make a Pending Transaction Information with max (\d+) and format "([^"]*)"$`, weMakeAPendingTransactionInformationWithMaxAndFormat)
@@ -120,24 +115,6 @@ func weMakeAnySuggestedTransactionParametersCall() error {
120115
return weMakeAnyCallTo("algod", "TransactionParams")
121116
}
122117

123-
func weMakeAPendingTransactionInformationAgainstTxidWithMax(txid string, max int) error {
124-
algodClient, err := algod.MakeClient(mockServer.URL, "")
125-
if err != nil {
126-
return err
127-
}
128-
_, _, globalErrForExamination = algodClient.PendingTransactionInformation(txid).Do(context.Background())
129-
return nil
130-
}
131-
132-
func weMakeAPendingTransactionsByAddressCallAgainstAccountAndMax(account string, max int) error {
133-
algodClient, err := algod.MakeClient(mockServer.URL, "")
134-
if err != nil {
135-
return err
136-
}
137-
_, _, globalErrForExamination = algodClient.PendingTransactionsByAddress(account).Max(uint64(max)).Do(context.Background())
138-
return nil
139-
}
140-
141118
func weMakeAStatusAfterBlockCallWithRound(round int) error {
142119
algodClient, err := algod.MakeClient(mockServer.URL, "")
143120
if err != nil {
@@ -156,22 +133,18 @@ func weMakeAnAccountInformationCallAgainstAccount(account string) error {
156133
return nil
157134
}
158135

159-
func weMakeAGetBlockCallAgainstBlockNumber(blocknum int) error {
136+
func weMakeAPendingTransactionInformationAgainstTxidWithFormat(txid, format string) error {
137+
if format != "msgpack" {
138+
return fmt.Errorf("this sdk does not support format %s", format)
139+
}
160140
algodClient, err := algod.MakeClient(mockServer.URL, "")
161141
if err != nil {
162142
return err
163143
}
164-
_, globalErrForExamination = algodClient.Block(uint64(blocknum)).Do(context.Background())
144+
_, _, globalErrForExamination = algodClient.PendingTransactionInformation(txid).Do(context.Background())
165145
return nil
166146
}
167147

168-
func weMakeAPendingTransactionInformationAgainstTxidWithFormat(txid, format string) error {
169-
if format != "msgpack" {
170-
return fmt.Errorf("this sdk does not support format %s", format)
171-
}
172-
return weMakeAPendingTransactionInformationAgainstTxidWithMax(txid, 0)
173-
}
174-
175148
func weMakeAPendingTransactionInformationWithMaxAndFormat(max int, format string) error {
176149
if format != "msgpack" {
177150
return fmt.Errorf("this sdk does not support format %s", format)
@@ -188,17 +161,27 @@ func weMakeAPendingTransactionsByAddressCallAgainstAccountAndMaxAndFormat(accoun
188161
if format != "msgpack" {
189162
return fmt.Errorf("this sdk does not support format %s", format)
190163
}
191-
return weMakeAPendingTransactionsByAddressCallAgainstAccountAndMax(account, max)
164+
algodClient, err := algod.MakeClient(mockServer.URL, "")
165+
if err != nil {
166+
return err
167+
}
168+
_, _, globalErrForExamination = algodClient.PendingTransactionsByAddress(account).Max(uint64(max)).Do(context.Background())
169+
return nil
192170
}
193171

194172
func weMakeAGetBlockCallAgainstBlockNumberWithFormat(blocknum int, format string) error {
195173
if format != "msgpack" {
196174
return fmt.Errorf("this sdk does not support format %s", format)
197175
}
198-
return weMakeAGetBlockCallAgainstBlockNumber(blocknum)
176+
algodClient, err := algod.MakeClient(mockServer.URL, "")
177+
if err != nil {
178+
return err
179+
}
180+
_, globalErrForExamination = algodClient.Block(uint64(blocknum)).Do(context.Background())
181+
return nil
199182
}
200183

201-
var dryrunResponse models.DryrunResponse
184+
var dryrunResponse modelsV2.DryrunResponse
202185

203186
func weMakeAnyDryrunCall() (err error) {
204187
algodClient, err := algod.MakeClient(mockServer.URL, "")

test/applications_integration_test.go

Lines changed: 2 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"regexp"
1313
"strconv"
1414
"strings"
15-
"time"
1615

1716
"github.com/cucumber/godog"
1817

@@ -211,7 +210,7 @@ func iBuildAnApplicationTransaction(
211210
}
212211

213212
case "call":
214-
tx, err = future.MakeApplicationCallTx(applicationId, args, accs,
213+
tx, _ = future.MakeApplicationCallTx(applicationId, args, accs,
215214
fApp, fAssets, types.NoOpOC, approvalP, clearP, gSchema, lSchema,
216215
suggestedParams, transientAccount.Address, nil, types.Digest{}, [32]byte{}, types.Address{})
217216
case "optin":
@@ -448,124 +447,11 @@ func theTransientAccountShouldHave(appCreated string, byteSlices, uints int,
448447
}
449448
}
450449
if !found {
451-
fmt.Errorf("Could not find key '%s'", key)
450+
return fmt.Errorf("Could not find key '%s'", key)
452451
}
453452
return nil
454453
}
455454

456-
func theUnconfirmedPendingTransactionByIDShouldHaveNoApplyDataFields() error {
457-
status, _, err := algodV2client.PendingTransactionInformation(txid).Do(context.Background())
458-
if err != nil {
459-
return err
460-
}
461-
if status.ConfirmedRound == 0 {
462-
if len(status.GlobalStateDelta) != 0 {
463-
return fmt.Errorf("unexpected global state delta, there should be none: %v", status.GlobalStateDelta)
464-
}
465-
if len(status.LocalStateDelta) != 0 {
466-
return fmt.Errorf("unexpected local state delta, there should be none: %v", status.LocalStateDelta)
467-
}
468-
}
469-
return nil
470-
}
471-
472-
func getAccountDelta(addr string, data []models.AccountStateDelta) []models.EvalDeltaKeyValue {
473-
for _, v := range data {
474-
if v.Address == addr {
475-
return v.Delta
476-
}
477-
}
478-
return nil
479-
}
480-
func theConfirmedPendingTransactionByIDShouldHaveAStateChangeForToIndexerShouldAlsoConfirmThis(stateLocation, key, newValue string, indexer int) error {
481-
status, _, err := algodV2client.PendingTransactionInformation(txid).Do(context.Background())
482-
if err != nil {
483-
return err
484-
}
485-
486-
c1 := make(chan models.Transaction, 1)
487-
488-
go func() {
489-
for true {
490-
indexerResponse, _ := indexerClients[indexer].SearchForTransactions().TXID(txid).Do(context.Background())
491-
if len(indexerResponse.Transactions) == 1 {
492-
c1 <- indexerResponse.Transactions[0]
493-
}
494-
time.Sleep(time.Second)
495-
}
496-
}()
497-
498-
var indexerTx models.Transaction
499-
select {
500-
case res := <-c1:
501-
indexerTx = res
502-
case <-time.After(5 * time.Second):
503-
return fmt.Errorf("timeout waiting for indexer trasaction")
504-
}
505-
506-
var algodKeyValues []models.EvalDeltaKeyValue
507-
var indexerKeyValues []models.EvalDeltaKeyValue
508-
509-
switch stateLocation {
510-
case "local":
511-
addr := indexerTx.Sender
512-
algodKeyValues = getAccountDelta(addr, status.LocalStateDelta)
513-
indexerKeyValues = getAccountDelta(addr, indexerTx.LocalStateDelta)
514-
case "global":
515-
algodKeyValues = status.GlobalStateDelta
516-
indexerKeyValues = indexerTx.GlobalStateDelta
517-
default:
518-
return fmt.Errorf("unknown location: " + stateLocation)
519-
}
520-
521-
// algod
522-
if len(algodKeyValues) != 1 {
523-
return fmt.Errorf("expected 1 key value, found: %d", len(algodKeyValues))
524-
}
525-
if algodKeyValues[0].Key != key {
526-
return fmt.Errorf("wrong key in algod: %s != %s", algodKeyValues[0].Key, key)
527-
}
528-
529-
// indexer
530-
if len(indexerKeyValues) != 1 {
531-
return fmt.Errorf("expected 1 key value, found: %d", len(indexerKeyValues))
532-
}
533-
if indexerKeyValues[0].Key != key {
534-
return fmt.Errorf("wrong key in indexer: %s != %s", indexerKeyValues[0].Key, key)
535-
}
536-
537-
if indexerKeyValues[0].Value.Action != algodKeyValues[0].Value.Action {
538-
return fmt.Errorf("action mismatch between algod and indexer")
539-
}
540-
541-
switch algodKeyValues[0].Value.Action {
542-
case uint64(1):
543-
// bytes
544-
if algodKeyValues[0].Value.Bytes != newValue {
545-
return fmt.Errorf("algod value mismatch: %s != %s", algodKeyValues[0].Value.Bytes, newValue)
546-
}
547-
if indexerKeyValues[0].Value.Bytes != newValue {
548-
return fmt.Errorf("indexer value mismatch: %s != %s", indexerKeyValues[0].Value.Bytes, newValue)
549-
}
550-
case uint64(2):
551-
// int
552-
newValueInt, err := strconv.ParseUint(newValue, 10, 64)
553-
if err != nil {
554-
return fmt.Errorf("problem parsing new int value: %s", newValue)
555-
}
556-
557-
if algodKeyValues[0].Value.Uint != newValueInt {
558-
return fmt.Errorf("algod value mismatch: %d != %s", algodKeyValues[0].Value.Uint, newValue)
559-
}
560-
if indexerKeyValues[0].Value.Uint != newValueInt {
561-
return fmt.Errorf("indexer value mismatch: %d != %s", indexerKeyValues[0].Value.Uint, newValue)
562-
}
563-
default:
564-
return fmt.Errorf("unexpected action: %d", algodKeyValues[0].Value.Action)
565-
}
566-
567-
return nil
568-
}
569455

570456
func suggestedParamsAlgodV2() error {
571457
var err error
@@ -883,8 +769,6 @@ func ApplicationsContext(s *godog.Suite) {
883769
s.Step(`^I get the account address for the current application and see that it matches the app id\'s hash$`, iGetTheAccountAddressForTheCurrentApp)
884770
s.Step(`^The transient account should have the created app "([^"]*)" and total schema byte-slices (\d+) and uints (\d+), the application "([^"]*)" state contains key "([^"]*)" with value "([^"]*)"$`,
885771
theTransientAccountShouldHave)
886-
s.Step(`^the unconfirmed pending transaction by ID should have no apply data fields\.$`, theUnconfirmedPendingTransactionByIDShouldHaveNoApplyDataFields)
887-
s.Step(`^the confirmed pending transaction by ID should have a "([^"]*)" state change for "([^"]*)" to "([^"]*)", indexer (\d+) should also confirm this\.$`, theConfirmedPendingTransactionByIDShouldHaveAStateChangeForToIndexerShouldAlsoConfirmThis)
888772
s.Step(`^suggested transaction parameters from the algod v2 client$`, suggestedParamsAlgodV2)
889773
s.Step(`^I add the current transaction with signer to the composer\.$`, iAddTheCurrentTransactionWithSignerToTheComposer)
890774
s.Step(`^I clone the composer\.$`, iCloneTheComposer)

0 commit comments

Comments
 (0)