github.com/s7techlab/cckit@v0.10.5/state/mapping/mapping_test.go (about)

     1  package mapping_test
     2  
     3  import (
     4  	"strconv"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/gomega"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	"github.com/golang/protobuf/ptypes"
    14  
    15  	identitytestdata "github.com/s7techlab/cckit/identity/testdata"
    16  	"github.com/s7techlab/cckit/state"
    17  	"github.com/s7techlab/cckit/state/mapping"
    18  	"github.com/s7techlab/cckit/state/mapping/testdata"
    19  	"github.com/s7techlab/cckit/state/mapping/testdata/schema"
    20  	state_schema "github.com/s7techlab/cckit/state/schema"
    21  	testcc "github.com/s7techlab/cckit/testing"
    22  	expectcc "github.com/s7techlab/cckit/testing/expect"
    23  )
    24  
    25  func TestState(t *testing.T) {
    26  	RegisterFailHandler(Fail)
    27  	RunSpecs(t, "State suite")
    28  }
    29  
    30  var (
    31  	compositeIDCC, complexIDCC, sliceIDCC, indexesCC, configCC *testcc.MockStub
    32  
    33  	Owner = identitytestdata.Certificates[0].MustIdentity(`SOME_MSP`)
    34  )
    35  var _ = Describe(`State mapping in chaincode`, func() {
    36  
    37  	BeforeSuite(func() {
    38  
    39  		compositeIDCC = testcc.NewMockStub(`proto`, testdata.NewCompositeIdCC())
    40  		compositeIDCC.From(Owner).Init()
    41  
    42  		complexIDCC = testcc.NewMockStub(`complex_id`, testdata.NewComplexIdCC())
    43  		complexIDCC.From(Owner).Init()
    44  
    45  		sliceIDCC = testcc.NewMockStub(`slice_id`, testdata.NewSliceIdCC())
    46  		sliceIDCC.From(Owner).Init()
    47  
    48  		indexesCC = testcc.NewMockStub(`indexes`, testdata.NewIndexesCC())
    49  		indexesCC.From(Owner).Init()
    50  
    51  		configCC = testcc.NewMockStub(`config`, testdata.NewCCWithConfig())
    52  		configCC.From(Owner).Init()
    53  	})
    54  
    55  	Describe(`Entity with composite id`, func() {
    56  		create1 := testdata.CreateEntityWithCompositeId[0]
    57  		create2 := testdata.CreateEntityWithCompositeId[1]
    58  		create3 := testdata.CreateEntityWithCompositeId[2]
    59  
    60  		It("Allow to get mapping data by namespace", func() {
    61  			mapping, err := testdata.EntityWithCompositeIdStateMapping.GetByNamespace(testdata.EntityCompositeIdNamespace)
    62  			Expect(err).NotTo(HaveOccurred())
    63  			Expect(mapping.Schema()).To(BeEquivalentTo(&schema.EntityWithCompositeId{}))
    64  
    65  			key, err := mapping.PrimaryKey(&schema.EntityWithCompositeId{
    66  				IdFirstPart:  create1.IdFirstPart,
    67  				IdSecondPart: create1.IdSecondPart,
    68  				IdThirdPart:  create1.IdThirdPart,
    69  			})
    70  
    71  			Expect(err).NotTo(HaveOccurred())
    72  			Expect(key).To(Equal(
    73  				testdata.EntityCompositeIdNamespace.Append(
    74  					state.Key{create1.IdFirstPart, strconv.Itoa(int(create1.IdSecondPart)), testdata.Dates[0]})))
    75  		})
    76  
    77  		It("Allow to add data to chaincode state", func(done Done) {
    78  			events, closer := compositeIDCC.EventSubscription()
    79  			expectcc.ResponseOk(compositeIDCC.Invoke(`create`, create1))
    80  
    81  			expectcc.EventStringerEqual(<-events,
    82  				`CreateEntityWithCompositeId`, create1)
    83  
    84  			expectcc.ResponseOk(compositeIDCC.Invoke(`create`, create2))
    85  			expectcc.ResponseOk(compositeIDCC.Invoke(`create`, create3))
    86  
    87  			_ = closer()
    88  			close(done)
    89  		})
    90  
    91  		It("Disallow to insert entries with same primary key", func() {
    92  			expectcc.ResponseError(compositeIDCC.Invoke(`create`, create1), state.ErrKeyAlreadyExists)
    93  		})
    94  
    95  		It("Allow to get entry list", func() {
    96  			entities := expectcc.PayloadIs(compositeIDCC.Query(`list`),
    97  				&schema.EntityWithCompositeIdList{}).(*schema.EntityWithCompositeIdList)
    98  			Expect(len(entities.Items)).To(Equal(3))
    99  			Expect(entities.Items[0].Name).To(Equal(create1.Name))
   100  			Expect(entities.Items[0].Value).To(BeNumerically("==", create1.Value))
   101  		})
   102  
   103  		It("Allow to get entry raw protobuf", func() {
   104  			dataFromCC := compositeIDCC.Query(`get`,
   105  				&schema.EntityCompositeId{
   106  					IdFirstPart:  create1.IdFirstPart,
   107  					IdSecondPart: create1.IdSecondPart,
   108  					IdThirdPart:  create1.IdThirdPart,
   109  				},
   110  			).Payload
   111  
   112  			e := &schema.EntityWithCompositeId{
   113  				IdFirstPart:  create1.IdFirstPart,
   114  				IdSecondPart: create1.IdSecondPart,
   115  				IdThirdPart:  create1.IdThirdPart,
   116  
   117  				Name:  create1.Name,
   118  				Value: create1.Value,
   119  			}
   120  			Expect(dataFromCC).To(Equal(testcc.MustProtoMarshal(e)))
   121  		})
   122  
   123  		It("Allow update data in chaincode state", func() {
   124  			expectcc.ResponseOk(compositeIDCC.Invoke(`update`, &schema.UpdateEntityWithCompositeId{
   125  				IdFirstPart:  create1.IdFirstPart,
   126  				IdSecondPart: create1.IdSecondPart,
   127  				IdThirdPart:  create1.IdThirdPart,
   128  				Name:         `New name`,
   129  				Value:        1000,
   130  			}))
   131  
   132  			entityFromCC := expectcc.PayloadIs(
   133  				compositeIDCC.Query(`get`, &schema.EntityCompositeId{
   134  					IdFirstPart:  create1.IdFirstPart,
   135  					IdSecondPart: create1.IdSecondPart,
   136  					IdThirdPart:  create1.IdThirdPart,
   137  				}),
   138  				&schema.EntityWithCompositeId{}).(*schema.EntityWithCompositeId)
   139  
   140  			// state is updated
   141  			Expect(entityFromCC.Name).To(Equal(`New name`))
   142  			Expect(entityFromCC.Value).To(BeNumerically("==", 1000))
   143  		})
   144  
   145  		It("Allow to delete entry", func() {
   146  			toDelete := &schema.EntityCompositeId{
   147  				IdFirstPart:  create1.IdFirstPart,
   148  				IdSecondPart: create1.IdSecondPart,
   149  				IdThirdPart:  create1.IdThirdPart,
   150  			}
   151  
   152  			expectcc.ResponseOk(compositeIDCC.Invoke(`delete`, toDelete))
   153  			ee := expectcc.PayloadIs(
   154  				compositeIDCC.Invoke(`list`),
   155  				&schema.EntityWithCompositeIdList{}).(*schema.EntityWithCompositeIdList)
   156  
   157  			Expect(len(ee.Items)).To(Equal(2))
   158  			expectcc.ResponseError(compositeIDCC.Invoke(`get`, toDelete), state.ErrKeyNotFound)
   159  		})
   160  
   161  		It("Allow to insert entry once more time", func() {
   162  			expectcc.ResponseOk(compositeIDCC.Invoke(`create`, create1))
   163  		})
   164  	})
   165  
   166  	Describe(`Entity with complex id`, func() {
   167  
   168  		ent1 := testdata.CreateEntityWithComplextId[0]
   169  
   170  		It("Allow to add data to chaincode state", func() {
   171  			expectcc.ResponseOk(complexIDCC.From(Owner).Invoke(`entityInsert`, ent1))
   172  			keys := expectcc.PayloadIs(complexIDCC.From(Owner).Invoke(
   173  				`debugStateKeys`, `EntityWithComplexId`), &[]string{}).([]string)
   174  			Expect(len(keys)).To(Equal(1))
   175  
   176  			timeStr := time.Unix(ent1.Id.IdPart3.GetSeconds(), int64(ent1.Id.IdPart3.GetNanos())).Format(`2006-01-02`)
   177  			// from hyperledger/fabric/core/chaincode/shim/chaincode.go
   178  			Expect(keys[0]).To(Equal(
   179  				string(rune(0)) +
   180  					`EntityWithComplexId` + string(rune(0)) +
   181  					ent1.Id.IdPart1[0] + string(rune(0)) +
   182  					ent1.Id.IdPart1[1] + string(rune(0)) +
   183  					ent1.Id.IdPart2 + string(rune(0)) +
   184  					timeStr + string(rune(0))))
   185  		})
   186  
   187  		It("Allow to get entity", func() {
   188  			// use Id as key
   189  			ent1FromCC := expectcc.ResponseOk(complexIDCC.Query(`entityGet`, ent1.Id)).Payload
   190  			Expect(ent1FromCC).To(Equal(testcc.MustProtoMarshal(ent1)))
   191  		})
   192  
   193  		It("Allow to list entity", func() {
   194  			// use Id as key
   195  			listFromCC := expectcc.PayloadIs(complexIDCC.Query(`entityList`), &state_schema.List{}).(*state_schema.List)
   196  			Expect(listFromCC.Items).To(HaveLen(1))
   197  
   198  			Expect(listFromCC.Items[0].Value).To(Equal(testcc.MustProtoMarshal(ent1)))
   199  		})
   200  	})
   201  
   202  	Describe(`Entity with slice id`, func() {
   203  
   204  		ent2 := &schema.EntityWithSliceId{Id: []string{`aa`, `bb`}, SomeDate: ptypes.TimestampNow()}
   205  
   206  		It("Allow to add data to chaincode state", func() {
   207  			expectcc.ResponseOk(sliceIDCC.Invoke(`entityInsert`, ent2))
   208  			keys := expectcc.PayloadIs(sliceIDCC.From(Owner).Invoke(
   209  				`debugStateKeys`, `EntityWithSliceId`), &[]string{}).([]string)
   210  
   211  			Expect(len(keys)).To(Equal(1))
   212  
   213  			// from hyperledger/fabric/core/chaincode/shim/chaincode.go
   214  			Expect(keys[0]).To(Equal("\x00" + `EntityWithSliceId` + string(rune(0)) + ent2.Id[0] + string(rune(0)) + ent2.Id[1] + string(rune(0))))
   215  		})
   216  
   217  		It("Allow to get entity", func() {
   218  			// use Id as key
   219  			ent1FromCC := expectcc.ResponseOk(sliceIDCC.Query(`entityGet`, state.StringsIdToStr(ent2.Id))).Payload
   220  			Expect(ent1FromCC).To(Equal(testcc.MustProtoMarshal(ent2)))
   221  		})
   222  
   223  		It("Allow to list entity", func() {
   224  			// use Id as key
   225  			listFromCC := expectcc.PayloadIs(sliceIDCC.Query(`entityList`), &state_schema.List{}).(*state_schema.List)
   226  			Expect(listFromCC.Items).To(HaveLen(1))
   227  
   228  			Expect(listFromCC.Items[0].Value).To(Equal(testcc.MustProtoMarshal(ent2)))
   229  		})
   230  	})
   231  
   232  	Describe(`Entity with indexes`, func() {
   233  
   234  		create1 := testdata.CreateEntityWithIndexes[0]
   235  		create2 := testdata.CreateEntityWithIndexes[1]
   236  
   237  		It("Allow to add data with single external id", func() {
   238  			expectcc.ResponseOk(indexesCC.Invoke(`create`, create1))
   239  		})
   240  
   241  		It("Disallow to add data to chaincode state with same uniq key fields", func() {
   242  			createWithNewId := proto.Clone(create1).(*schema.CreateEntityWithIndexes)
   243  			createWithNewId.Id = `abcdef` // id is really new
   244  
   245  			// errored on checking uniq key
   246  			expectcc.ResponseError(
   247  				indexesCC.Invoke(`create`, create1),
   248  				mapping.ErrMappingUniqKeyExists)
   249  		})
   250  
   251  		It("Allow finding data by uniq key", func() {
   252  			fromCCByExtId := expectcc.PayloadIs(
   253  				indexesCC.Query(`getByExternalId`, create1.ExternalId),
   254  				&schema.EntityWithIndexes{}).(*schema.EntityWithIndexes)
   255  
   256  			fromCCById := expectcc.PayloadIs(
   257  				indexesCC.Query(`get`, create1.Id),
   258  				&schema.EntityWithIndexes{}).(*schema.EntityWithIndexes)
   259  
   260  			Expect(fromCCByExtId).To(BeEquivalentTo(fromCCById))
   261  		})
   262  
   263  		It("Allow to get idx state key by uniq key", func() {
   264  			idxKey, err := testdata.EntityWithIndexesStateMapping.IdxKey(
   265  				&schema.EntityWithIndexes{}, `ExternalId`, []string{create1.ExternalId})
   266  			Expect(err).NotTo(HaveOccurred())
   267  
   268  			Expect(idxKey).To(BeEquivalentTo([]string{
   269  				mapping.KeyRefNamespace,
   270  				strings.Join(mapping.SchemaNamespace(&schema.EntityWithIndexes{}), `-`),
   271  				`ExternalId`,
   272  				create1.ExternalId,
   273  			}))
   274  		})
   275  
   276  		It("Disallow finding data by non existent uniq key", func() {
   277  			expectcc.ResponseError(
   278  				indexesCC.Query(`getByExternalId`, `some-non-existent-id`),
   279  				mapping.ErrIndexReferenceNotFound)
   280  		})
   281  
   282  		It("Allow to add data with multiple external id", func() {
   283  			expectcc.ResponseOk(indexesCC.Invoke(`create`, create2))
   284  		})
   285  
   286  		It("Allow to find data by multi key", func() {
   287  			fromCCByExtId1 := expectcc.PayloadIs(
   288  				indexesCC.Query(`getByOptMultiExternalId`, create2.OptionalExternalIds[0]),
   289  				&schema.EntityWithIndexes{}).(*schema.EntityWithIndexes)
   290  
   291  			fromCCByExtId2 := expectcc.PayloadIs(
   292  				indexesCC.Query(`getByOptMultiExternalId`, create2.OptionalExternalIds[1]),
   293  				&schema.EntityWithIndexes{}).(*schema.EntityWithIndexes)
   294  
   295  			fromCCById := expectcc.PayloadIs(
   296  				indexesCC.Query(`get`, create2.Id),
   297  				&schema.EntityWithIndexes{}).(*schema.EntityWithIndexes)
   298  
   299  			Expect(fromCCByExtId1).To(BeEquivalentTo(fromCCById))
   300  			Expect(fromCCByExtId2).To(BeEquivalentTo(fromCCById))
   301  		})
   302  
   303  		It("Allow update indexes value", func() {
   304  			update2 := &schema.UpdateEntityWithIndexes{
   305  				Id:                  create2.Id,
   306  				ExternalId:          `some_new_external_id`,
   307  				OptionalExternalIds: []string{create2.OptionalExternalIds[0], `AND SOME NEW`},
   308  			}
   309  			expectcc.ResponseOk(indexesCC.Invoke(`update`, update2))
   310  		})
   311  
   312  		It("Allow to find data by updated multi key", func() {
   313  			fromCCByExtId1 := expectcc.PayloadIs(
   314  				indexesCC.Query(`getByOptMultiExternalId`, create2.OptionalExternalIds[0]),
   315  				&schema.EntityWithIndexes{}).(*schema.EntityWithIndexes)
   316  
   317  			fromCCByExtId2 := expectcc.PayloadIs(
   318  				indexesCC.Query(`getByOptMultiExternalId`, `AND SOME NEW`),
   319  				&schema.EntityWithIndexes{}).(*schema.EntityWithIndexes)
   320  
   321  			Expect(fromCCByExtId1.Id).To(Equal(create2.Id))
   322  			Expect(fromCCByExtId2.Id).To(Equal(create2.Id))
   323  
   324  			Expect(fromCCByExtId2.OptionalExternalIds).To(
   325  				BeEquivalentTo([]string{create2.OptionalExternalIds[0], `AND SOME NEW`}))
   326  		})
   327  
   328  		It("Disallow to find data by previous multi key", func() {
   329  			expectcc.ResponseError(
   330  				indexesCC.Query(`getByOptMultiExternalId`, create2.OptionalExternalIds[1]),
   331  				mapping.ErrIndexReferenceNotFound)
   332  		})
   333  
   334  		It("Allow to find data by updated uniq key", func() {
   335  			fromCCByExtId := expectcc.PayloadIs(
   336  				indexesCC.Query(`getByExternalId`, `some_new_external_id`),
   337  				&schema.EntityWithIndexes{}).(*schema.EntityWithIndexes)
   338  
   339  			Expect(fromCCByExtId.Id).To(Equal(create2.Id))
   340  			Expect(fromCCByExtId.ExternalId).To(Equal(`some_new_external_id`))
   341  		})
   342  
   343  		It("Disallow to find data by previous uniq key", func() {
   344  			expectcc.ResponseError(
   345  				indexesCC.Query(`getByExternalId`, create2.ExternalId),
   346  				mapping.ErrIndexReferenceNotFound)
   347  		})
   348  
   349  		It("Allow to delete entry", func() {
   350  			expectcc.ResponseOk(indexesCC.Invoke(`delete`, create2.Id))
   351  
   352  			ee := expectcc.PayloadIs(
   353  				indexesCC.Invoke(`list`),
   354  				&schema.EntityWithIndexesList{}).(*schema.EntityWithIndexesList)
   355  
   356  			Expect(len(ee.Items)).To(Equal(1))
   357  			expectcc.ResponseError(indexesCC.Invoke(`get`, create2.Id), state.ErrKeyNotFound)
   358  		})
   359  
   360  		It("Allow to insert entry once more time", func() {
   361  			expectcc.ResponseOk(indexesCC.Invoke(`create`, create2))
   362  		})
   363  
   364  	})
   365  
   366  	Describe(`Entity with static key`, func() {
   367  		configSample := &schema.Config{
   368  			Field1: `aaa`,
   369  			Field2: `bbb`,
   370  		}
   371  
   372  		It("Disallow to get config before set", func() {
   373  			expectcc.ResponseError(configCC.Invoke(`configGet`), `state entry not found: Config`)
   374  		})
   375  
   376  		It("Allow to set config", func() {
   377  			expectcc.ResponseOk(configCC.Invoke(`configSet`, configSample))
   378  		})
   379  
   380  		It("Allow to get config", func() {
   381  			confFromCC := expectcc.PayloadIs(configCC.Invoke(`configGet`), &schema.Config{}).(*schema.Config)
   382  			Expect(confFromCC.Field1).To(Equal(configSample.Field1))
   383  			Expect(confFromCC.Field2).To(Equal(configSample.Field2))
   384  		})
   385  
   386  	})
   387  })