Skip to content

Commit

Permalink
test(x/ecocredit): basket put acceptance tests (#1065)
Browse files Browse the repository at this point in the history
* test(x/ecocredit): basket acceptance tests

* update put acceptance tests

* add put check balance steps

* add put balance and supply steps

* consolidate steps with background

* simplify steps and use scenario outlines

* Update x/ecocredit/server/basket/keeper_test.go

Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com>

* add credit type util test

* clean up id and key names

* check ok on new int

* update credit and token amount scenarios

* clean up and add exceeds max decimal

* clean up language

* fix merge

* fix miscommit

Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com>
  • Loading branch information
ryanchristo and technicallyty authored May 9, 2022
1 parent 3841ec5 commit fae569f
Show file tree
Hide file tree
Showing 16 changed files with 1,112 additions and 681 deletions.
148 changes: 148 additions & 0 deletions x/ecocredit/basket/features/msg_put.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
Feature: MsgPut

Scenario: a valid message
Given the message
"""
{
"owner": "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27",
"basket_denom": "NCT",
"credits": [
{
"batch_denom": "C01-001-20200101-20210101-001",
"amount": "100"
}
]
}
"""
When the message is validated
Then expect no error

Scenario: an error is returned if owner is empty
Given the message
"""
{}
"""
When the message is validated
Then expect the error "empty address string is not allowed: invalid request"

Scenario: an error is returned if owner is not a bech32 address
Given the message
"""
{
"owner": "foo"
}
"""
When the message is validated
Then expect the error "decoding bech32 failed: invalid bech32 string length 3: invalid request"

Scenario: an error is returned if basket denom is empty
Given the message
"""
{
"owner": "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27"
}
"""
When the message is validated
Then expect the error "basket denom cannot be empty: invalid request"

Scenario: an error is returned if basket denom is not formatted
Given the message
"""
{
"owner": "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27",
"basket_denom": "1"
}
"""
When the message is validated
Then expect the error "1 is not a valid basket denom: invalid request"

Scenario: an error is returned if credit list is empty
Given the message
"""
{
"owner": "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27",
"basket_denom": "NCT"
}
"""
When the message is validated
Then expect the error "credits cannot be empty: invalid request"

Scenario: an error is returned if a credit batch denom is empty
Given the message
"""
{
"owner": "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27",
"basket_denom": "NCT",
"credits": [
{}
]
}
"""
When the message is validated
Then expect the error "credit batch denom cannot be empty: invalid request"

Scenario: an error is returned if a credit batch denom is not formatted
Given the message
"""
{
"owner": "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27",
"basket_denom": "NCT",
"credits": [
{
"batch_denom": "foo"
}
]
}
"""
When the message is validated
Then expect the error "invalid denom: expected format A00-000-00000000-00000000-000: parse error: invalid request"

Scenario: an error is returned if a credit amount is empty
Given the message
"""
{
"owner": "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27",
"basket_denom": "NCT",
"credits": [
{
"batch_denom": "C01-001-20200101-20210101-001"
}
]
}
"""
When the message is validated
Then expect the error "credit amount cannot be empty: invalid request"

Scenario: an error is returned if a credit amount is not an integer
Given the message
"""
{
"owner": "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27",
"basket_denom": "NCT",
"credits": [
{
"batch_denom": "C01-001-20200101-20210101-001",
"amount": "foo"
}
]
}
"""
When the message is validated
Then expect the error "parse mantissa: foo: invalid decimal string: invalid decimal string: invalid request"

Scenario: an error is returned if a credit amount is less than zero
Given the message
"""
{
"owner": "cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27",
"basket_denom": "NCT",
"credits": [
{
"batch_denom": "C01-001-20200101-20210101-001",
"amount": "-100"
}
]
}
"""
When the message is validated
Then expect the error "expected a positive decimal, got -100: invalid decimal string: invalid request"
17 changes: 16 additions & 1 deletion x/ecocredit/basket/msg_put.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,35 @@ func (m MsgPut) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(m.Owner); err != nil {
return sdkerrors.ErrInvalidRequest.Wrapf(err.Error())
}

if len(m.BasketDenom) == 0 {
return sdkerrors.ErrInvalidRequest.Wrap("basket denom cannot be empty")
}

if err := sdk.ValidateDenom(m.BasketDenom); err != nil {
return sdkerrors.ErrInvalidRequest.Wrapf("%s is not a valid basket denom", m.BasketDenom)
}

if len(m.Credits) > 0 {
for _, credit := range m.Credits {
if len(credit.BatchDenom) == 0 {
return sdkerrors.ErrInvalidRequest.Wrap("credit batch denom cannot be empty")
}

if err := core.ValidateBatchDenom(credit.BatchDenom); err != nil {
return sdkerrors.ErrInvalidRequest.Wrap(err.Error())
}

if len(credit.Amount) == 0 {
return sdkerrors.ErrInvalidRequest.Wrap("credit amount cannot be empty")
}

if _, err := math.NewPositiveDecFromString(credit.Amount); err != nil {
return sdkerrors.ErrInvalidRequest.Wrap(err.Error())
}
}
} else {
return sdkerrors.ErrInvalidRequest.Wrap("no credits were specified to put into the basket")
return sdkerrors.ErrInvalidRequest.Wrap("credits cannot be empty")
}

return nil
Expand Down
132 changes: 28 additions & 104 deletions x/ecocredit/basket/msg_put_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,116 +2,40 @@ package basket

import (
"testing"
"time"

"github.com/gogo/protobuf/jsonpb"
"github.com/regen-network/gocuke"
"github.com/stretchr/testify/require"
)

"github.com/cosmos/cosmos-sdk/testutil/testdata"
type msgPutSuite struct {
t gocuke.TestingT
msg *MsgPut
err error
}

"github.com/regen-network/regen-ledger/x/ecocredit/core"
)
func TestMsgPut(t *testing.T) {
gocuke.NewRunner(t, &msgPutSuite{}).Path("./features/msg_put.feature").Run()
}

func TestMsgPut_ValidateBasic(t *testing.T) {
t.Parallel()
func (s *msgPutSuite) Before(t gocuke.TestingT) {
s.t = t
}

_, _, addr := testdata.KeyTestPubAddr()
t1, t2 := time.Now(), time.Now()
denom, err := core.FormatBatchDenom("C02-001", 1, &t1, &t2)
require.NoError(t, err)
func (s *msgPutSuite) TheMessage(a gocuke.DocString) {
s.msg = &MsgPut{}
err := jsonpb.UnmarshalString(a.Content, s.msg)
require.NoError(s.t, err)
}

func (s *msgPutSuite) TheMessageIsValidated() {
s.err = s.msg.ValidateBasic()
}

type fields struct {
Owner string
BasketDenom string
Credits []*BasketCredit
}
tests := []struct {
name string
fields fields
wantErr bool
}{
{
name: "valid",
fields: fields{
Owner: addr.String(),
BasketDenom: "COOL",
Credits: []*BasketCredit{{BatchDenom: denom, Amount: "100.5302"}},
},
},
{
name: "bad addr",
fields: fields{
Owner: "oops!",
BasketDenom: "COOL",
Credits: []*BasketCredit{{BatchDenom: denom, Amount: "100.5302"}},
},
wantErr: true,
},
{
name: "no credits",
fields: fields{
Owner: addr.String(),
BasketDenom: "COOL",
},
wantErr: true,
},
{
name: "bad batch denom",
fields: fields{
Owner: addr.String(),
BasketDenom: "COOL",
Credits: []*BasketCredit{{BatchDenom: "bad bad not good!", Amount: "100.5302"}},
},
wantErr: true,
},
{
name: "bad amount",
fields: fields{
Owner: addr.String(),
BasketDenom: "COOL",
Credits: []*BasketCredit{{BatchDenom: denom, Amount: "100.52.302.35.2"}},
},
wantErr: true,
},
{
name: "zero amount",
fields: fields{
Owner: addr.String(),
BasketDenom: "COOL",
Credits: []*BasketCredit{{BatchDenom: denom, Amount: "0"}},
},
wantErr: true,
},
{
name: "negative amount",
fields: fields{
Owner: addr.String(),
BasketDenom: "COOL",
Credits: []*BasketCredit{{BatchDenom: denom, Amount: "-50.329"}},
},
wantErr: true,
},
{
name: "bad basket denom",
fields: fields{
Owner: addr.String(),
BasketDenom: "CO:OL",
Credits: []*BasketCredit{{BatchDenom: denom, Amount: "100"}},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
func (s *msgPutSuite) ExpectTheError(a string) {
require.EqualError(s.t, s.err, a)
}

m := MsgPut{
Owner: tt.fields.Owner,
BasketDenom: tt.fields.BasketDenom,
Credits: tt.fields.Credits,
}
if err := m.ValidateBasic(); (err != nil) != tt.wantErr {
t.Errorf("ValidateBasic() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
func (s *msgPutSuite) ExpectNoError() {
require.NoError(s.t, s.err)
}
16 changes: 15 additions & 1 deletion x/ecocredit/core/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"sort"
"strings"
"time"
"unicode"

sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

Expand Down Expand Up @@ -129,7 +130,7 @@ func ValidateProjectId(projectId string) error {
func ValidateBatchDenom(denom string) error {
matches := regexBatchDenom.FindStringSubmatch(denom)
if matches == nil {
return ecocredit.ErrParseFailure.Wrapf("invalid denom: %s", denom)
return ecocredit.ErrParseFailure.Wrap("invalid denom: expected format A00-000-00000000-00000000-000")
}
return nil
}
Expand Down Expand Up @@ -161,6 +162,19 @@ func GetClassIdFromBatchDenom(denom string) string {
return s.String()
}

// GetCreditTypeAbbrevFromClassId returns the credit type abbreviation in a credit class id
func GetCreditTypeAbbrevFromClassId(classId string) string {
var s strings.Builder
for _, r := range classId {
if !unicode.IsNumber(r) {
s.WriteRune(r)
continue
}
break
}
return s.String()
}

// exponent prefix map https://en.wikipedia.org/wiki/Metric_prefix
var exponentPrefixMap = map[uint32]string{
0: "",
Expand Down
10 changes: 10 additions & 0 deletions x/ecocredit/core/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func TestUtils(t *testing.T) {
t.Run("TestFormatBatchDenom", rapid.MakeCheck(testFormatBatchDenom))
t.Run("TestInvalidBatchDenom", rapid.MakeCheck(testInvalidBatchDenom))
t.Run("TestGetClassIdFromBatchDenom", rapid.MakeCheck(testGetClassIdFromBatchDenom))
t.Run("GetCreditTypeAbbrevFromClassId", rapid.MakeCheck(testGetCreditTypeAbbrevFromClassId))
}

func testFormatClassId(t *rapid.T) {
Expand Down Expand Up @@ -95,6 +96,15 @@ func testGetClassIdFromBatchDenom(t *rapid.T) {
require.Equal(t, classId, result)
}

func testGetCreditTypeAbbrevFromClassId(t *rapid.T) {
creditType := genCreditType.Draw(t, "creditType").(*CreditType)
classSeq := rapid.Uint64().Draw(t, "classSeq").(uint64)

classId := FormatClassId(creditType.Abbreviation, classSeq)
result := GetCreditTypeAbbrevFromClassId(classId)
require.Equal(t, creditType.Abbreviation, result)
}

// genCreditType generates an empty credit type with a random valid abbreviation
var genCreditType = rapid.Custom(func(t *rapid.T) *CreditType {
abbr := rapid.StringMatching(`[A-Z]{1,3}`).Draw(t, "abbr").(string)
Expand Down
Loading

0 comments on commit fae569f

Please sign in to comment.