diff --git a/examples/script_integration_test.go b/examples/script_integration_test.go index efcc1ac..735df8c 100644 --- a/examples/script_integration_test.go +++ b/examples/script_integration_test.go @@ -77,6 +77,23 @@ func TestScript(t *testing.T) { assert.Contains(t, err.Error(), "json: cannot unmarshal array into Go value of type main.TestReturn") }) + t.Run("Named arguments", func(t *testing.T) { + value := g.ScriptFromFile("test"). + NamedArguments(map[string]string{ + "account": "first", + }).RunReturnsInterface() + + assert.Equal(t, "0x01cf0e2f2f715450", value) + }) + + t.Run("Named arguments blank", func(t *testing.T) { + value := g.ScriptFromFile("block"). + NamedArguments(map[string]string{}). + RunReturnsInterface() + + assert.Equal(t, "4", value) + }) + } type TestReturn struct { diff --git a/examples/transaction_integration_test.go b/examples/transaction_integration_test.go index b07c0a7..b8b8ee2 100644 --- a/examples/transaction_integration_test.go +++ b/examples/transaction_integration_test.go @@ -134,4 +134,32 @@ transaction(user:Address) { assert.NotContains(t, str.String(), "0x1cf0e2f2f715450") }) + + t.Run("Named arguments", func(t *testing.T) { + g.TransactionFromFile("mint_tokens"). + SignProposeAndPayAsService(). + NamedArguments(map[string]string{ + "recipient": "first", + "amount": "100.0", + }). + Test(t).AssertSuccess() + }) + t.Run("Named arguments error if not all arguments", func(t *testing.T) { + g.TransactionFromFile("mint_tokens"). + SignProposeAndPayAsService(). + NamedArguments(map[string]string{ + "recipient": "first", + }). + Test(t).AssertFailure("the following arguments where not present [amount]") + }) + + t.Run("Named arguments error if file not correct", func(t *testing.T) { + g.TransactionFromFile("mint_tokens2"). + SignProposeAndPayAsService(). + NamedArguments(map[string]string{ + "recipient": "first", + }). + Test(t).AssertFailure("Could not read transaction file from path=./transactions/mint_tokens2.cdc") + }) + } diff --git a/overflow/account.go b/overflow/account.go index 2272a94..02b4386 100644 --- a/overflow/account.go +++ b/overflow/account.go @@ -60,6 +60,10 @@ func (f *Overflow) InitializeContracts() *Overflow { // GetAccount takes the account name and returns the state of that account on the given network. func (f *Overflow) GetAccount(key string) (*flow.Account, error) { - rawAddress := f.Account(key).Address() + account, err := f.AccountE(key) + if err != nil { + return nil, err + } + rawAddress := account.Address() return f.Services.Accounts.Get(rawAddress) } diff --git a/overflow/account_test.go b/overflow/account_test.go index a929bc6..2044728 100644 --- a/overflow/account_test.go +++ b/overflow/account_test.go @@ -25,14 +25,18 @@ func TestGetAccount(t *testing.T) { assert.Equal(t, "f8d6e0586b0a20c7", account.Address.String()) }) - // TODO: test case for non-existent account - // For some reason, when fetching an account that doesnt exist, - // flowkit's Services.Accounts.Get() method throws an error & exits, rather than just returning an error. + t.Run("Should return an error if account doesn't exist", func(t *testing.T) { + g, _ := NewTestingEmulator().StartE() + _, err := g.GetAccount("doesnotexist") + assert.ErrorContains(t, err, "could not find account with name emulator-doesnotexist in the configuration") + + }) - // t.Run("Should return an error if account doesn't exist", func(t *testing.T) { - // g, _ := NewTestingEmulator().StartE() - // account, _ := g.GetAccount("doesnotexist") + t.Run("Should return an error if sa does not exist", func(t *testing.T) { + _, err := NewTestingEmulator().SetServiceSuffix("dummy").StartE() + + assert.ErrorContains(t, err, "could not find account with name emulator-dummy in the configuration") + + }) - // assert.Nil(t, account) - // }) } diff --git a/overflow/argument.go b/overflow/argument.go index c27c1fb..25febe8 100644 --- a/overflow/argument.go +++ b/overflow/argument.go @@ -21,8 +21,7 @@ type FlowArgumentsBuilder struct { Arguments []cadence.Value } -func (f *Overflow) ParseArgumentsWithoutType(fileName string, code []byte, inputArgs map[string]string) (scriptArgs []cadence.Value, err error) { - +func (f *Overflow) ParseArgumentsWithoutType(fileName string, code []byte, inputArgs map[string]string) ([]cadence.Value, error) { var resultArgs []cadence.Value = make([]cadence.Value, 0) codes := map[common.LocationID]string{} @@ -50,9 +49,21 @@ func (f *Overflow) ParseArgumentsWithoutType(fileName string, code []byte, input return resultArgs, nil } + argumentNotPresent := []string{} args := []string{} for _, parameter := range parameterList { - args = append(args, inputArgs[parameter.Identifier.Identifier]) + parameterName := parameter.Identifier.Identifier + value, ok := inputArgs[parameterName] + if !ok { + argumentNotPresent = append(argumentNotPresent, parameterName) + } else { + args = append(args, value) + } + } + + if len(argumentNotPresent) > 0 { + err := fmt.Errorf("the following arguments where not present %v", argumentNotPresent) + return nil, err } if len(parameterList) != len(args) { diff --git a/overflow/setup.go b/overflow/setup.go index 5e24c41..044d539 100644 --- a/overflow/setup.go +++ b/overflow/setup.go @@ -39,20 +39,29 @@ func (o *Overflow) ServiceAccountName() string { } //Account fetch an account from flow.json, prefixing the name with network- as default (can be turned off) -func (f *Overflow) Account(key string) *flowkit.Account { +func (f *Overflow) AccountE(key string) (*flowkit.Account, error) { if f.PrependNetworkToAccountNames { key = fmt.Sprintf("%s-%s", f.Network, key) } account, err := f.State.Accounts().ByName(key) if err != nil { - log.Fatal(err) + return nil, err } - return account + return account, nil } +//Account fetch an account from flow.json, prefixing the name with network- as default (can be turned off) +func (f *Overflow) Account(key string) *flowkit.Account { + a, err := f.AccountE(key) + if err != nil { + log.Fatal(err) + } + return a +} + type OverflowBuilder struct { Network string InMemory bool @@ -85,15 +94,11 @@ func NewOverflow() *OverflowBuilder { } func NewOverflowBuilder(network string, newEmulator bool, logLevel int) *OverflowBuilder { - if network == "" { - network = "embedded" - } - inMemory := false deployContracts := newEmulator initializeAccounts := newEmulator - if network == "embedded" { + if network == "embedded" || network == "" { inMemory = true network = "emulator" } @@ -129,6 +134,11 @@ func (o *OverflowBuilder) DoNotPrependNetworkToAccountNames() *OverflowBuilder { return o } +func (o *OverflowBuilder) SetServiceSuffix(suffix string) *OverflowBuilder { + o.ServiceSuffix = suffix + return o +} + func (o *OverflowBuilder) NoneLog() *OverflowBuilder { o.LogLevel = output.NoneLog return o diff --git a/overflow/setup_test.go b/overflow/setup_test.go new file mode 100644 index 0000000..3175b9e --- /dev/null +++ b/overflow/setup_test.go @@ -0,0 +1,69 @@ +package overflow + +import ( + "testing" + + "github.com/onflow/flow-cli/pkg/flowkit/output" + "github.com/stretchr/testify/assert" +) + +func TestSetup(t *testing.T) { + t.Parallel() + + t.Run("default builder", func(t *testing.T) { + o := NewOverflow() + assert.Equal(t, output.InfoLog, o.LogLevel) + }) + + t.Run("default builder with loglevel fro env", func(t *testing.T) { + t.Setenv("OVERFLOW_LOGGING", "2") + o := NewOverflow() + assert.Equal(t, output.DebugLog, o.LogLevel) + }) + + t.Run("new overflow builder for network", func(t *testing.T) { + o := NewOverflowForNetwork("testnet") + assert.Equal(t, "testnet", o.Network) + }) + + t.Run("new overflow testnet", func(t *testing.T) { + o := NewOverflowTestnet() + assert.Equal(t, "testnet", o.Network) + }) + t.Run("new overflow mainnet", func(t *testing.T) { + o := NewOverflowMainnet() + assert.Equal(t, "mainnet", o.Network) + }) + + t.Run("new overflow emulator", func(t *testing.T) { + o := NewOverflowEmulator() + assert.Equal(t, "emulator", o.Network) + }) + + t.Run("new overflow builder without network", func(t *testing.T) { + o := NewOverflowBuilder("", false, 1) + assert.Equal(t, "emulator", o.Network) + }) + + t.Run("existing emulator", func(t *testing.T) { + o := NewOverflow().ExistingEmulator() + assert.Equal(t, false, o.DeployContracts) + assert.Equal(t, false, o.InitializeAccounts) + }) + + t.Run("do not prepend network names", func(t *testing.T) { + o := NewOverflow().DoNotPrependNetworkToAccountNames() + assert.Equal(t, false, o.PrependNetworkName) + }) + + t.Run("default gas", func(t *testing.T) { + o := NewOverflow().DefaultGas(100) + assert.Equal(t, 100, o.GasLimit) + }) + + t.Run("base path", func(t *testing.T) { + o := NewOverflow().BasePath("../") + assert.Equal(t, "../", o.Path) + }) + +} diff --git a/overflow/transaction.go b/overflow/transaction.go index ec774f1..73f9dc2 100644 --- a/overflow/transaction.go +++ b/overflow/transaction.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io" - "log" "os" "time" @@ -50,11 +49,12 @@ func (t FlowTransactionBuilder) NamedArguments(args map[string]string) FlowTrans codeFileName := fmt.Sprintf("%s/%s.cdc", t.BasePath, t.FileName) code, err := t.getContractCode(codeFileName) if err != nil { - panic(err) + fmt.Println(err.Error()) + t.Error = err } parseArgs, err := t.Overflow.ParseArgumentsWithoutType(t.FileName, code, args) if err != nil { - panic(err) + t.Error = err } t.Arguments = parseArgs return t @@ -93,7 +93,11 @@ func (t FlowTransactionBuilder) Gas(limit uint64) FlowTransactionBuilder { // SignProposeAndPayAs set the payer, proposer and envelope signer func (t FlowTransactionBuilder) SignProposeAndPayAs(signer string) FlowTransactionBuilder { - t.MainSigner = t.Overflow.Account(signer) + account, err := t.Overflow.AccountE(signer) + if err != nil { + t.Error = err + } + t.MainSigner = account return t } @@ -102,7 +106,7 @@ func (t FlowTransactionBuilder) SignProposeAndPayAsService() FlowTransactionBuil key := t.Overflow.ServiceAccountName() account, err := t.Overflow.State.Accounts().ByName(key) if err != nil { - log.Fatal(err) + t.Error = err } t.MainSigner = account return t @@ -142,27 +146,37 @@ func (t FlowTransactionBuilder) RunGetIdFromEventPrintAll(eventName string, fiel } PrintEvents(result, map[string][]string{}) - return getUInt64FieldFromEvent(result, eventName, fieldName) + number, err := getUInt64FieldFromEvent(result, eventName, fieldName) + if err != nil { + panic(err) + } + return number } -func getUInt64FieldFromEvent(result []flow.Event, eventName string, fieldName string) uint64 { +func getUInt64FieldFromEvent(result []flow.Event, eventName string, fieldName string) (uint64, error) { for _, event := range result { ev := ParseEvent(event, uint64(0), time.Unix(0, 0), []string{}) if ev.Name == eventName { - return ev.GetFieldAsUInt64(fieldName) + return ev.GetFieldAsUInt64(fieldName), nil } } - panic("did not find field") + return 0, fmt.Errorf("did not find field %s", fieldName) } func (t FlowTransactionBuilder) RunGetIdFromEvent(eventName string, fieldName string) uint64 { result, err := t.RunE() if err != nil { - panic(err) + t.Error = err + return 0 } - return getUInt64FieldFromEvent(result, eventName, fieldName) + value, err := getUInt64FieldFromEvent(result, eventName, fieldName) + if err != nil { + t.Error = err + return 0 + } + return value } func (t FlowTransactionBuilder) RunGetIds(eventName string, fieldName string) ([]uint64, error) { @@ -215,15 +229,17 @@ func (t FlowTransactionBuilder) RunGetEventsWithName(eventName string) []Formate // RunE runs returns events and error func (t FlowTransactionBuilder) RunE() ([]flow.Event, error) { - result := t.Send() return result.RawEvents, result.Err } // The new main way of running an overflow transaction func (t FlowTransactionBuilder) Send() *OverflowResult { - result := &OverflowResult{} + if t.Error != nil { + result.Err = t.Error + return result + } if t.MainSigner == nil { fmt.Println("err") @@ -359,6 +375,7 @@ type FlowTransactionBuilder struct { PayloadSigners []*flowkit.Account GasLimit uint64 BasePath string + Error error } type OverflowResult struct { @@ -372,7 +389,11 @@ type OverflowResult struct { } func (o OverflowResult) GetIdFromEvent(eventName string, fieldName string) uint64 { - return getUInt64FieldFromEvent(o.RawEvents, eventName, fieldName) + number, err := getUInt64FieldFromEvent(o.RawEvents, eventName, fieldName) + if err != nil { + panic(err) + } + return number } func (o OverflowResult) GetIdsFromEvent(eventName string, fieldName string) []uint64 { diff --git a/overflow/transaction_test.go b/overflow/transaction_test.go index 2d479a8..33c3795 100644 --- a/overflow/transaction_test.go +++ b/overflow/transaction_test.go @@ -19,6 +19,11 @@ func TestTransactionArguments(t *testing.T) { assert.Equal(t, uint64(100), builder.GasLimit) }) + t.Run("Signer", func(t *testing.T) { + builder := g.Transaction("").SignProposeAndPayAs("asd") + assert.ErrorContains(t, builder.Error, "asd") + }) + t.Run("Argument test builder", func(t *testing.T) { ufix, _ := cadence.NewUFix64("1.0") builder := g.Transaction("").Args(g.Arguments().UFix64(1.0))