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 })