Skip to content

Commit

Permalink
Merge pull request #24 from blinklabs-io/feat-tests-cip-20
Browse files Browse the repository at this point in the history
feat: add cip20 model tests
  • Loading branch information
verbotenj authored Dec 17, 2023
2 parents 3917fd3 + 91b198e commit 02172c8
Show file tree
Hide file tree
Showing 4 changed files with 326 additions and 4 deletions.
23 changes: 21 additions & 2 deletions cip20.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,29 @@

package models

import "github.com/go-playground/validator/v10"

type Cip20Metadata struct {
Num674 Num674 `cbor:"674,keyasint" json:"674"`
Num674 Num674 `cbor:"674,keyasint" json:"674" validate:"required"`
}

type Num674 struct {
Msg []string `cbor:"msg" json:"msg"`
Msg []string `cbor:"msg" json:"msg" validate:"required,gt=0,dive,max=64"`
}

func NewCip20Metadata(messages []string) (*Cip20Metadata, error) {
validate := validator.New()

metadata := &Cip20Metadata{Num674: Num674{Msg: messages}}

if err := validate.Struct(metadata); err != nil {
return nil, err
}

return metadata, nil
}

func (c *Cip20Metadata) Validate() error {
validate := validator.New()
return validate.Struct(c)
}
257 changes: 257 additions & 0 deletions cip20_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package models

import (
"encoding/hex"
"encoding/json"
"reflect"
"strings"
"testing"

"github.com/fxamacker/cbor/v2"
)

func TestValidCip20Metadata(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
cborHex string
expectedObj Cip20Metadata
jsonData string
expectCBORUnmarshalError bool
expectJSONUnmarshalError bool
expectValidationError bool
expectCBORDeepEqualError bool
expectJSONDeepEqualError bool
}{
{
name: "Valid - Single Message",
expectedObj: Cip20Metadata{
Num674: Num674{
Msg: []string{"This is a comment for the transaction xyz, thank you very much!"},
},
},
cborHex: "A163363734A1636D736781783F54686973206973206120636F6D6D656E7420666F7220746865207472616E73616374696F6E2078797A2C207468616E6B20796F752076657279206D75636821",
jsonData: `{
"674":
{
"msg":
[
"This is a comment for the transaction xyz, thank you very much!"
]
}
}`,
expectCBORUnmarshalError: false,
expectJSONUnmarshalError: false,
expectValidationError: false,
expectCBORDeepEqualError: false,
expectJSONDeepEqualError: false,
},
{
name: "Valid - Multiple Messages",
expectedObj: Cip20Metadata{
Num674: Num674{
Msg: []string{
"Invoice-No: 1234567890",
"Customer-No: 555-1234",
"P.S.: i will shop again at your store :-)",
},
},
},
jsonData: `{
"674":
{
"msg":
[
"Invoice-No: 1234567890",
"Customer-No: 555-1234",
"P.S.: i will shop again at your store :-)"
]
}
}`,
cborHex: "A163363734A1636D73678376496E766F6963652D4E6F3A203132333435363738393075437573746F6D65722D4E6F3A203535352D313233347829502E532E3A20692077696C6C2073686F7020616761696E20617420796F75722073746F7265203A2D29",
expectCBORUnmarshalError: false,
expectJSONUnmarshalError: false,
expectValidationError: false,
expectCBORDeepEqualError: false,
expectJSONDeepEqualError: false,
},
{
name: "Invalid - Message Exceeds 64 Bytes",
expectedObj: Cip20Metadata{
Num674: Num674{
Msg: []string{strings.Repeat("a", 65)}, // 65 'a's to exceed the limit
},
},
jsonData: `{
"674": {
"msg": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
}
}`,
cborHex: "A163363734A1636D73678178416161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161",
expectCBORUnmarshalError: false,
expectJSONUnmarshalError: false,
expectValidationError: true,
expectCBORDeepEqualError: false,
expectJSONDeepEqualError: false,
},
{
name: "Invalid - Message Deep error",
expectedObj: Cip20Metadata{
Num674: Num674{
Msg: []string{strings.Repeat("b", 65)},
},
},
jsonData: `{
"674": {
"msg": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
}
}`,
cborHex: "A163363734A1636D73678178416161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161",
expectCBORUnmarshalError: false,
expectJSONUnmarshalError: false,
expectValidationError: true,
expectCBORDeepEqualError: true,
expectJSONDeepEqualError: true,
},
{
name: "Invalid - Message Deep error 675",
expectedObj: Cip20Metadata{
Num674: Num674{
Msg: []string{strings.Repeat("b", 65)},
},
},
jsonData: `{
"675": {
"msg": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
}
}`,
cborHex: "A163363734A1636D73678178416161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161",
expectCBORUnmarshalError: false,
expectJSONUnmarshalError: false,
expectValidationError: true,
expectCBORDeepEqualError: true,
expectJSONDeepEqualError: true,
},
}

for _, tc := range testCases {
tc := tc // capture range variable for goroutines
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

// Decode the CBOR hex string
cborData, err := hex.DecodeString(tc.cborHex)
if err != nil {
t.Errorf("failed to decode CBOR hex string for %s, error: %v", tc.name, err)
}

// CBOR Unmarshal
var decodedMetadata Cip20Metadata
err = cbor.Unmarshal(cborData, &decodedMetadata)
if tc.expectCBORUnmarshalError {
if err == nil {
t.Errorf("expected CBOR unmarshal error but got none for test: %s", tc.name)
}
} else {
if err != nil {
t.Errorf("did not expect CBOR unmarshal error but got one for test: %s, error: %v", tc.name, err)
}
}

// CBOR Validate
err = decodedMetadata.Validate()
if tc.expectValidationError {
if err == nil {
t.Errorf("expected validation error but got none for test: %s", tc.name)
}
} else {
if err != nil {
t.Errorf("did not expect validation error but got one for test: %s, error: %v", tc.name, err)
}
}

// Deep Equality Check for CBOR
deepEqual := reflect.DeepEqual(decodedMetadata, tc.expectedObj)
if tc.expectCBORDeepEqualError && deepEqual {
t.Errorf("Cbor expected deep equal error but objects are identical for test: %s", tc.name)
}
if !tc.expectCBORDeepEqualError && !deepEqual {
t.Errorf("Cbor expected objects to be identical but they are not for test: %s, expected: %v, got: %v", tc.name, tc.expectedObj, decodedMetadata)
}

// Reset the object for JSON testing
decodedMetadata = Cip20Metadata{}
// Decode the JSON string
err = json.Unmarshal([]byte(tc.jsonData), &decodedMetadata)
if err != nil {
t.Errorf("unexpected result unmarshaling JSON to Cip20Metadata for test %s, error: %v", tc.name, err)
}
if tc.expectJSONUnmarshalError {
if err == nil {
t.Errorf("expected JSON unmarshal error but got none for test: %s", tc.name)
}
} else {
if err != nil {
t.Errorf("did not expect JSON unmarshal error but got one for test: %s, error: %v", tc.name, err)
}
}

// JSON Validate
err = decodedMetadata.Validate()
if tc.expectValidationError {
if err == nil {
t.Errorf("expected validation error but got none for test: %s", tc.name)
}
} else {
if err != nil {
t.Errorf("did not expect validation error but got one for test: %s, error: %v", tc.name, err)
}
}

// Deep Equality Check for JSON
deepEqual = reflect.DeepEqual(decodedMetadata, tc.expectedObj)
if tc.expectJSONDeepEqualError && deepEqual {
t.Errorf("Json expected deep equal error but objects are identical for test: %s", tc.name)
}
if !tc.expectJSONDeepEqualError && !deepEqual {
t.Errorf("Json expected objects to be identical but they are not for test: %s, expected: %v, got: %v", tc.name, tc.expectedObj, decodedMetadata)
}
})
}
}

func TestNewCip20Metadata(t *testing.T) {
t.Parallel()
// Test case: Non-empty messages
messages := []string{"message1", "message2"}
expectedJSON := `{"674":{"msg":["message1","message2"]}}`

metadata, err := NewCip20Metadata(messages)
if err != nil {
t.Errorf("Expected no error, but got: %v", err)
}

actualJSON, err := json.Marshal(metadata)
if err != nil {
t.Errorf("Failed to marshal metadata to JSON: %v", err)
}

if string(actualJSON) != expectedJSON {
t.Errorf("Expected JSON: %s, but got: %s", expectedJSON, string(actualJSON))
}

// Test case: Invalid metadata
messages = []string{}
expectedErr := "Key: 'Cip20Metadata.Num674.Msg' Error:Field validation for 'Msg' failed on the 'gt' tag"

_, err = NewCip20Metadata(messages)
if err == nil {
t.Errorf("Expected validation error, but got no error")
} else {
actualErr := err.Error()
if actualErr != expectedErr {
t.Errorf("Expected error: %s, but got: %s", expectedErr, actualErr)
}
}
}
15 changes: 13 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@ module github.com/blinklabs-io/cardano-models

go 1.20

require github.com/blinklabs-io/gouroboros v0.67.1
require (
github.com/blinklabs-io/gouroboros v0.67.1
github.com/fxamacker/cbor/v2 v2.5.0
github.com/go-playground/validator/v10 v10.16.0
)

require (
github.com/fxamacker/cbor/v2 v2.5.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
)
35 changes: 35 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
github.com/blinklabs-io/gouroboros v0.67.1 h1:SyzxCIEa2rph3KQr1D+PgqsPNFORN/OdwNtrL0h42g8=
github.com/blinklabs-io/gouroboros v0.67.1/go.mod h1:Q154NJPs7gB93Tggt8ts9RGIlW2kkknr6M90Q3jh0s4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 comments on commit 02172c8

Please sign in to comment.