Skip to content

Commit

Permalink
A lot more automated tests and minor improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
bandreghetti committed Jan 11, 2021
1 parent 56fd429 commit f15fbf2
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 100 deletions.
2 changes: 1 addition & 1 deletion assets/asset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

func TestAssetUnmarshal(t *testing.T) {
assetJSON := []byte("{\"@assetType\": \"sampleBook\",\"title\": \"Meu Nome é Maria\",\"author\": \"Maria Viana\",\"currentTenant\": {\"name\": \"Maria\"},\"genres\": [\"biography\", \"non-fiction\"],\"published\": \"2019-05-06T22:12:41Z\"}")
assetJSON := []byte("{\"@assetType\": \"samplePerson\",\"name\": \"Maria\",\"cpf\": \"318.207.920-48\",\"readerScore\": 70}")
var a Asset
err := json.Unmarshal(assetJSON, &a)
if err != nil {
Expand Down
38 changes: 25 additions & 13 deletions assets/assets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,23 @@ func TestMain(m *testing.M) {

InitAssetList(assetList)

err = StartupCheck()
os.Exit(m.Run())
}

func TestStartUp(t *testing.T) {
err := StartupCheck()
if err != nil {
log.Println(err)
os.Exit(1)
t.FailNow()
}
}

os.Exit(m.Run())
func TestAssetList(t *testing.T) {
l := AssetTypeList()
if len(l) != 3 {
fmt.Println("expected only 3 asset types in asset type list")
t.FailNow()
}
}

var assetList = []AssetType{
Expand Down Expand Up @@ -57,11 +67,17 @@ var assetList = []AssetType{
},
},
{
Tag: "readerScore",
Label: "Reader Score",
Required: true,
DataType: "number",
Writers: []string{`org1MSP`},
Tag: "readerScore",
Label: "Reader Score",
DefaultValue: 0.0,
DataType: "number",
Writers: []string{`org1MSP`},
},
{
Tag: "active",
Label: "Active",
DefaultValue: false,
DataType: "boolean",
},
},
},
Expand Down Expand Up @@ -127,7 +143,7 @@ var assetList = []AssetType{

var customDataTypes = map[string]DataType{
"cpf": {
Parse: func(data interface{}) (string, interface{}, error) {
Parse: func(data interface{}) (string, interface{}, errors.ICCError) {
cpf, ok := data.(string)
if !ok {
return "", nil, errors.NewCCError("property must be a string", 400)
Expand All @@ -140,10 +156,6 @@ var customDataTypes = map[string]DataType{
return "", nil, errors.NewCCError("CPF must have 11 digits", 400)
}

for _, _ = range cpf {
// perform CPF validation
}

return cpf, cpf, nil
},
},
Expand Down
46 changes: 26 additions & 20 deletions assets/dataType.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type DataType struct {

DropDownValues map[string]interface{}

Parse func(interface{}) (string, interface{}, error) `json:"-"`
Parse func(interface{}) (string, interface{}, errors.ICCError) `json:"-"`

legacyMode bool
KeyString func(interface{}) (string, error) `json:"-"` // DEPRECATED. Use Parse instead.
Expand Down Expand Up @@ -57,7 +57,7 @@ func DataTypeMap() map[string]DataType {
var dataTypeMap = map[string]DataType{
"string": {
AcceptedFormats: []string{"string"},
Parse: func(data interface{}) (string, interface{}, error) {
Parse: func(data interface{}) (string, interface{}, errors.ICCError) {
parsedData, ok := data.(string)
if !ok {
return parsedData, nil, errors.NewCCError("property must be a string", 400)
Expand All @@ -67,18 +67,21 @@ var dataTypeMap = map[string]DataType{
},
"number": {
AcceptedFormats: []string{"number"},
Parse: func(data interface{}) (string, interface{}, error) {
dataVal, ok := data.(float64)
if !ok {
propValStr, okStr := data.(string)
if !okStr {
return "", nil, errors.NewCCError("asset property must be a number", 400)
}
Parse: func(data interface{}) (string, interface{}, errors.ICCError) {
var dataVal float64
switch v := data.(type) {
case float64:
dataVal = v
case int:
dataVal = (float64)(v)
case string:
var err error
dataVal, err = strconv.ParseFloat(propValStr, 64)
dataVal, err = strconv.ParseFloat(v, 64)
if err != nil {
return "", nil, errors.WrapErrorWithStatus(err, fmt.Sprintf("asset property must be a number"), 400)
}
default:
return "", nil, errors.NewCCError("asset property must be a number", 400)
}

// Float IEEE 754 hexadecimal representation
Expand All @@ -87,18 +90,21 @@ var dataTypeMap = map[string]DataType{
},
"integer": {
AcceptedFormats: []string{"number"},
Parse: func(data interface{}) (string, interface{}, error) {
dataVal, ok := data.(float64)
if !ok {
propValStr, okStr := data.(string)
if !okStr {
return "", nil, errors.NewCCError("asset property must be an integer", 400)
}
Parse: func(data interface{}) (string, interface{}, errors.ICCError) {
var dataVal float64
switch v := data.(type) {
case float64:
dataVal = v
case int:
dataVal = (float64)(v)
case string:
var err error
dataVal, err = strconv.ParseFloat(propValStr, 64)
dataVal, err = strconv.ParseFloat(v, 64)
if err != nil {
return "", nil, errors.WrapErrorWithStatus(err, fmt.Sprintf("asset property must be an integer"), 400)
}
default:
return "", nil, errors.NewCCError("asset property must be an integer", 400)
}

retVal := math.Trunc(dataVal)
Expand All @@ -113,7 +119,7 @@ var dataTypeMap = map[string]DataType{
},
"boolean": {
AcceptedFormats: []string{"boolean"},
Parse: func(data interface{}) (string, interface{}, error) {
Parse: func(data interface{}) (string, interface{}, errors.ICCError) {
dataVal, ok := data.(bool)
if !ok {
dataValStr, okStr := data.(string)
Expand All @@ -136,7 +142,7 @@ var dataTypeMap = map[string]DataType{
},
"datetime": {
AcceptedFormats: []string{"string"},
Parse: func(data interface{}) (string, interface{}, error) {
Parse: func(data interface{}) (string, interface{}, errors.ICCError) {
dataTime, ok := data.(time.Time)
if !ok {
dataVal, ok := data.(string)
Expand Down
92 changes: 92 additions & 0 deletions assets/dataType_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package assets

import (
"fmt"
"reflect"
"testing"
)

func testParseValid(t *testing.T, dtype DataType, inputVal interface{}, expectedKey string, expectedVal interface{}) {
var key string
var val interface{}
var err error
key, val, err = dtype.Parse(inputVal)
if err != nil {
fmt.Println(err)
t.FailNow()
}
if key != expectedKey {
fmt.Printf("parsing %v expected key: %q but got %q\n", inputVal, expectedKey, key)
t.FailNow()
}
if val != expectedVal {
fmt.Printf("parsing %v expected parsed val: \"%v\" of type %s but got \"%v\" of type %s\n", inputVal, expectedVal, reflect.TypeOf(expectedVal), val, reflect.TypeOf(val))
t.FailNow()
}
}

func testParseInvalid(t *testing.T, dtype DataType, inputVal interface{}, expectedErr int32) {
_, _, err := dtype.Parse(inputVal)
if err == nil {
fmt.Println("expected error but DataType.Parse was successful")
t.FailNow()
}
if err.Status() != expectedErr {
fmt.Printf("expected error code %d but got %d\n", expectedErr, err.Status())
}
}

func TestDataTypeString(t *testing.T) {
dtypeName := "string"
dtype, exists := DataTypeMap()[dtypeName]
if !exists {
fmt.Printf("%s datatype not declared in DataTypeMap\n", dtypeName)
t.FailNow()
}
testParseValid(t, dtype, "string válida", "string válida", "string válida")
testParseInvalid(t, dtype, 32.0, 400)
}

func TestDataTypeNumber(t *testing.T) {
dtypeName := "number"
dtype, exists := DataTypeMap()[dtypeName]
if !exists {
fmt.Printf("%s datatype not declared in DataTypeMap\n", dtypeName)
t.FailNow()
}
testParseValid(t, dtype, 472.7, "407d8b3333333333", 472.7)
testParseValid(t, dtype, 472, "407d800000000000", 472.0)
testParseValid(t, dtype, "472", "407d800000000000", 472.0)
testParseInvalid(t, dtype, "32d.0", 400)
testParseInvalid(t, dtype, false, 400)
}

func TestDataTypeInteger(t *testing.T) {
dtypeName := "integer"
dtype, exists := DataTypeMap()[dtypeName]
if !exists {
fmt.Printf("%s datatype not declared in DataTypeMap\n", dtypeName)
t.FailNow()
}
testParseValid(t, dtype, 470, "470", int64(470))
testParseValid(t, dtype, 412.0, "412", int64(412))
testParseValid(t, dtype, "472", "472", int64(472))
testParseInvalid(t, dtype, 472.1, 400)
testParseInvalid(t, dtype, "32d.0", 400)
testParseInvalid(t, dtype, false, 400)
}

func TestDataTypeBoolean(t *testing.T) {
dtypeName := "boolean"
dtype, exists := DataTypeMap()[dtypeName]
if !exists {
fmt.Printf("%s datatype not declared in DataTypeMap\n", dtypeName)
t.FailNow()
}
testParseValid(t, dtype, true, "t", true)
testParseValid(t, dtype, false, "f", false)
testParseValid(t, dtype, "true", "t", true)
testParseValid(t, dtype, "false", "f", false)
testParseInvalid(t, dtype, "True", 400)
testParseInvalid(t, dtype, 37.3, 400)
}
6 changes: 0 additions & 6 deletions assets/getProp.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,3 @@ func (a Asset) GetProp(propTag string) interface{} {
val, _ := a[propTag]
return val
}

// GetProp returns the prop value. It returns nil if it doesn't exist.
func (k Key) GetProp(propTag string) interface{} {
val, _ := k[propTag]
return val
}
56 changes: 0 additions & 56 deletions assets/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package assets

import (
"encoding/json"
"fmt"
"strings"

"github.com/goledgerdev/cc-tools/errors"
"github.com/hyperledger/fabric/core/chaincode/shim"
Expand Down Expand Up @@ -69,60 +67,6 @@ func NewKey(m map[string]interface{}) (k Key, err errors.ICCError) {
return
}

// ValidateProps checks if all props are compliant to format
func (k Key) ValidateProps() error {
// Perform validation of the @assetType field
assetType, exists := k["@assetType"]
if !exists {
return errors.NewCCError("property @assetType is required", 400)
}
assetTypeString, ok := assetType.(string)
if !ok {
return errors.NewCCError("property @assetType must be a string", 400)
}

// Fetch asset definition
assetTypeDef := FetchAssetType(assetTypeString)
if assetTypeDef == nil {
return errors.NewCCError(fmt.Sprintf("assetType named '%s' does not exist", assetTypeString), 400)
}

// Validate asset properties
for _, prop := range assetTypeDef.Keys() {
// Check if required property is included
propInterface, propIncluded := k[prop.Tag]
if !propIncluded {
if prop.Required {
return errors.NewCCError(fmt.Sprintf("property %s (%s) is required", prop.Tag, prop.Label), 400)
}
if prop.IsKey {
return errors.NewCCError(fmt.Sprintf("key property %s (%s) is required", prop.Tag, prop.Label), 400)
}
continue
}

// Validate data types
propInterface, err := validateProp(propInterface, prop)
if err != nil {
msg := fmt.Sprintf("error validating asset '%s' property", prop.Tag)
return errors.WrapError(err, msg)
}

k[prop.Tag] = propInterface
}

for propTag := range k {
if strings.HasPrefix(propTag, "@") {
continue
}
if !assetTypeDef.HasProp(propTag) {
return errors.NewCCError(fmt.Sprintf("property %s is not defined in type %s", propTag, assetTypeString), 400)
}
}

return nil
}

// GetBytes reads asset bytes from ledger
func (k *Key) GetBytes(stub shim.ChaincodeStubInterface) ([]byte, errors.ICCError) {
var assetBytes []byte
Expand Down
39 changes: 39 additions & 0 deletions assets/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,43 @@ func TestKeyUnmarshal(t *testing.T) {
t.FailNow()
}
}
if k.String() == "" {
fmt.Println("failed to print asset as string")
}
}

func TestKeyIsPrivate(t *testing.T) {
keyJSON := []byte("{\"@assetType\": \"sampleBook\",\"title\": \"Meu Nome é Maria\",\"author\": \"Maria Viana\"}")
var k Key
err := json.Unmarshal(keyJSON, &k)
if err != nil {
fmt.Println(err)
t.FailNow()
}

if k.IsPrivate() {
fmt.Println("false positive in Key.IsPrivate")
t.FailNow()
}

pvtKeyJSON := []byte("{\"@assetType\": \"sampleSecret\",\"secretName\": \"mySecret\"}")
var pvtKey Key
err = json.Unmarshal(pvtKeyJSON, &pvtKey)
if err != nil {
fmt.Println(err)
t.FailNow()
}

if !pvtKey.IsPrivate() {
fmt.Println("false negative in Key.IsPrivate")
t.FailNow()
}
}

func TestNewKeyWithNilMap(t *testing.T) {
_, err := NewKey(nil)
if err == nil {
fmt.Println("NewKey should fail if nil map is passed as argument")
t.FailNow()
}
}
Loading

0 comments on commit f15fbf2

Please sign in to comment.