cosmos-sdk/tx/textual/valuerenderer/valuerenderer_test.go
Jim Larson ef4ad67c9e
feat: value renderer for timestamp protos (#12860)
## Description

Closes: #12709



Part of Sign Mode Textual (ADR 050) implementation.

Renders Timestamp messages as RFC 3339 (simplified ISO 8601).

---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [x] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting))
- [x] provided a link to the relevant issue or specification
- [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules)
- [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [x] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [x] updated the relevant documentation or specification
- [x] reviewed "Files changed" and left comments if necessary
- [x] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
2022-08-23 19:58:29 +00:00

163 lines
4.5 KiB
Go

package valuerenderer_test
import (
"context"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/reflect/protoreflect"
tspb "google.golang.org/protobuf/types/known/timestamppb"
"cosmossdk.io/math"
"cosmossdk.io/tx/textual/internal/testpb"
"cosmossdk.io/tx/textual/valuerenderer"
)
func TestFormatInteger(t *testing.T) {
type integerTest []string
var testcases []integerTest
raw, err := os.ReadFile("../internal/testdata/integers.json")
require.NoError(t, err)
err = json.Unmarshal(raw, &testcases)
require.NoError(t, err)
for _, tc := range testcases {
// Parse test case strings as protobuf uint64
i, err := strconv.ParseUint(tc[0], 10, 64)
if err == nil {
r, err := valueRendererOf(i)
require.NoError(t, err)
b := new(strings.Builder)
err = r.Format(context.Background(), protoreflect.ValueOf(i), b)
require.NoError(t, err)
require.Equal(t, tc[1], b.String())
}
// Parse test case strings as protobuf uint32
i, err = strconv.ParseUint(tc[0], 10, 32)
if err == nil {
r, err := valueRendererOf(i)
require.NoError(t, err)
b := new(strings.Builder)
err = r.Format(context.Background(), protoreflect.ValueOf(i), b)
require.NoError(t, err)
require.Equal(t, tc[1], b.String())
}
// Parse test case strings as sdk.Ints
sdkInt, ok := math.NewIntFromString(tc[0])
if ok {
r, err := valueRendererOf(sdkInt)
require.NoError(t, err)
b := new(strings.Builder)
err = r.Format(context.Background(), protoreflect.ValueOf(tc[0]), b)
require.NoError(t, err)
require.Equal(t, tc[1], b.String())
}
}
}
func TestFormatDecimal(t *testing.T) {
type decimalTest []string
var testcases []decimalTest
raw, err := os.ReadFile("../internal/testdata/decimals.json")
require.NoError(t, err)
err = json.Unmarshal(raw, &testcases)
require.NoError(t, err)
for _, tc := range testcases {
tc := tc
t.Run(tc[0], func(t *testing.T) {
d, err := math.LegacyNewDecFromStr(tc[0])
require.NoError(t, err)
r, err := valueRendererOf(d)
require.NoError(t, err)
b := new(strings.Builder)
err = r.Format(context.Background(), protoreflect.ValueOf(tc[0]), b)
require.NoError(t, err)
require.Equal(t, tc[1], b.String())
})
}
}
func TestGetADR050ValueRenderer(t *testing.T) {
testcases := []struct {
name string
v interface{}
expErr bool
}{
{"uint32", uint32(1), false},
{"uint64", uint64(1), false},
{"sdk.Int", math.NewInt(1), false},
{"sdk.Dec", math.LegacyNewDec(1), false},
{"[]byte", []byte{1}, false},
{"float32", float32(1), true},
{"float64", float64(1), true},
}
for _, tc := range testcases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
_, err := valueRendererOf(tc.v)
if tc.expErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
func TestTimestampDispatch(t *testing.T) {
a := (&testpb.A{}).ProtoReflect().Descriptor().Fields()
textual := valuerenderer.NewTextual()
rend, err := textual.GetValueRenderer(a.ByName(protoreflect.Name("TIMESTAMP")))
require.NoError(t, err)
require.IsType(t, valuerenderer.NewTimestampValueRenderer(), rend)
}
// valueRendererOf is like GetADR050ValueRenderer, but taking a Go type
// as input instead of a protoreflect.FieldDescriptor.
func valueRendererOf(v interface{}) (valuerenderer.ValueRenderer, error) {
a, b := (&testpb.A{}).ProtoReflect().Descriptor().Fields(), (&testpb.B{}).ProtoReflect().Descriptor().Fields()
textual := valuerenderer.NewTextual()
switch v := v.(type) {
// Valid types for SIGN_MODE_TEXTUAL
case uint32:
return textual.GetValueRenderer(a.ByName(protoreflect.Name("UINT32")))
case uint64:
return textual.GetValueRenderer(a.ByName(protoreflect.Name("UINT64")))
case int32:
return textual.GetValueRenderer(a.ByName(protoreflect.Name("INT32")))
case int64:
return textual.GetValueRenderer(a.ByName(protoreflect.Name("INT64")))
case []byte:
return textual.GetValueRenderer(a.ByName(protoreflect.Name("BYTES")))
case math.Int:
return textual.GetValueRenderer(a.ByName(protoreflect.Name("SDKINT")))
case math.LegacyDec:
return textual.GetValueRenderer(a.ByName(protoreflect.Name("SDKDEC")))
case tspb.Timestamp:
return textual.GetValueRenderer(a.ByName(protoreflect.Name("TIMESTAMP")))
// Invalid types for SIGN_MODE_TEXTUAL
case float32:
return textual.GetValueRenderer(b.ByName(protoreflect.Name("FLOAT")))
case float64:
return textual.GetValueRenderer(b.ByName(protoreflect.Name("FLOAT")))
default:
return nil, fmt.Errorf("value %s of type %T not recognized", v, v)
}
}