func TestLoadBatchFromDatastore(t *testing.T) {
	gaeCtx, _ := aetest.NewContext(nil)
	defer gaeCtx.Close()

	Convey("Given I have a load batch from datastore transformer", t, func() {
		riversCtx := rivers.NewContext()
		loadBatchProcessor := appx.NewStep(riversCtx).LoadBatchFromDatastore(gaeCtx)

		Convey("And I have a few entities in datastore", func() {
			user1 := NewUser(User{
				Name:  "Borges",
				Email: "*****@*****.**",
				SSN:   "123123123",
			})

			user2 := NewUser(User{
				Name:  "Borges",
				Email: "*****@*****.**",
				SSN:   "123123123",
			})

			err := appx.NewKeyResolver(gaeCtx).Resolve(user1)
			So(err, ShouldBeNil)
			err = appx.NewKeyResolver(gaeCtx).Resolve(user2)
			So(err, ShouldBeNil)

			_, err = datastore.Put(gaeCtx, user1.Key(), user1)
			So(err, ShouldBeNil)
			_, err = datastore.Put(gaeCtx, user2.Key(), user2)
			So(err, ShouldBeNil)

			Convey("When I transform the incoming batch", func() {
				userFromDatastore1 := NewUser(User{Name: user1.Name})
				userFromDatastore2 := NewUser(User{Name: user2.Name})
				appx.NewKeyResolver(gaeCtx).Resolve(userFromDatastore1)
				appx.NewKeyResolver(gaeCtx).Resolve(userFromDatastore2)

				batch := &appx.DatastoreBatch{
					Size: 2,
					Keys: []*datastore.Key{
						userFromDatastore1.Key(),
						userFromDatastore2.Key(),
					},
					Items: []appx.Entity{
						userFromDatastore1,
						userFromDatastore2,
					},
				}

				loadBatchProcessor(batch)

				Convey("And entities are loaded from datastore", func() {
					So(userFromDatastore1, ShouldResemble, user1)
					So(userFromDatastore2, ShouldResemble, user2)
				})
			})
		})
	})
}
Exemple #2
0
func createAll(c appengine.Context, tags ...*Tag) {
	keys := make([]*datastore.Key, len(tags))
	for i, tag := range tags {
		appx.NewKeyResolver(c).Resolve(tag)
		keys[i] = tag.Key()
	}
	datastore.PutMulti(c, keys, tags)
	time.Sleep(2 * time.Second)
}
Exemple #3
0
func TestDatastoreDelete(t *testing.T) {
	context, _ := aetest.NewContext(nil)
	defer context.Close()

	Convey("Given I have an entity in datastore", t, func() {
		userInDatastore := NewUser(User{
			Name:  "Borges",
			Email: "*****@*****.**",
			SSN:   "123123123",
		})

		appx.NewKeyResolver(context).Resolve(userInDatastore)
		datastore.Put(context, userInDatastore.Key(), userInDatastore)

		Convey("And I have a cached entity though not yet saved to datastore", func() {
			cachedUser := NewUser(User{
				Name:  "Diego",
				Email: "*****@*****.**",
				SSN:   "321321321",
			})

			cached := appx.CachedEntity{
				Entity:    cachedUser,
				Key:       cachedUser.Key(),
				ParentKey: cachedUser.ParentKey(),
			}

			memcache.JSON.Set(context, &memcache.Item{
				Key:    cachedUser.CacheID(),
				Object: cached,
			})

			Convey("And I have a non existent user", func() {
				nonExistentUser := NewUser(User{Name: "not existent"})

				Convey("When I delete the all", func() {
					err := appx.NewDatastore(context).Delete(userInDatastore, cachedUser, nonExistentUser)

					Convey("Then the entities are deleted from cache and datastore", func() {
						So(err, ShouldBeNil)

						err := datastore.Get(context, userInDatastore.Key(), userInDatastore)
						So(err, ShouldEqual, datastore.ErrNoSuchEntity)

						_, err = memcache.Get(context, cachedUser.CacheID())
						So(err, ShouldEqual, memcache.ErrCacheMiss)
					})
				})
			})
		})
	})
}
Exemple #4
0
func TestDatastoreSave(t *testing.T) {
	context, _ := aetest.NewContext(nil)
	defer context.Close()

	Convey("Given I have an entity in datastore", t, func() {
		user1 := NewUser(User{
			Name:  "Borges",
			Email: "*****@*****.**",
			SSN:   "123123123",
		})

		appx.NewKeyResolver(context).Resolve(user1)
		datastore.Put(context, user1.Key(), user1)

		Convey("And I have an entity not yet saved to datastore", func() {
			user2 := NewUser(User{
				Name:  "Diego",
				Email: "*****@*****.**",
				SSN:   "321321321",
			})

			Convey("When I update the entities in datastore", func() {
				err := appx.NewDatastore(context).Save(user1, user2)

				Convey("Then the entities are updated in the datastore", func() {
					So(err, ShouldBeNil)
					user1FromDatastore := &User{}
					user2FromDatastore := &User{}
					user1FromDatastore.SetKey(user1.Key())
					user2FromDatastore.SetKey(user2.Key())

					datastore.Get(context, user1.Key(), user1FromDatastore)
					datastore.Get(context, user2.Key(), user2FromDatastore)

					So(user1FromDatastore.SSN, ShouldEqual, user1.SSN)
					So(user1FromDatastore.Name, ShouldEqual, user1.Name)
					So(user1FromDatastore.Email, ShouldEqual, user1.Email)
					So(user1FromDatastore.Key(), ShouldResemble, user1.Key())

					So(user2FromDatastore.SSN, ShouldEqual, user2.SSN)
					So(user2FromDatastore.Name, ShouldEqual, user2.Name)
					So(user2FromDatastore.Email, ShouldEqual, user2.Email)
					So(user2FromDatastore.Key(), ShouldResemble, user2.Key())
				})
			})
		})
	})

	Convey("Given I have an entity with incomplete key in datastore", t, func() {
		user := &User{
			Name: "Borges",
			keySpec: &appx.KeySpec{
				Kind:       "Users",
				Incomplete: true,
			},
		}

		err := appx.NewDatastore(context).Save(user)
		So(err, ShouldBeNil)

		userKey := user.Key()

		Convey("When I update the entity", func() {
			user.Email = "*****@*****.**"

			err := appx.NewDatastore(context).Save(user)
			So(err, ShouldBeNil)

			Convey("Then the user key is not overriden", func() {
				So(userKey, ShouldResemble, user.Key())
			})
		})
	})
}
Exemple #5
0
func TestDatastoreLoad(t *testing.T) {
	context, _ := aetest.NewContext(nil)
	defer context.Close()

	Convey("Given I have a cached entity", t, func() {
		user := NewUser(User{
			Name:  "Borges",
			Email: "*****@*****.**",
			SSN:   "123123123",
		})

		appx.NewKeyResolver(context).Resolve(user)

		cached := appx.CachedEntity{
			Entity:    user,
			Key:       user.Key(),
			ParentKey: user.ParentKey(),
		}

		memcache.JSON.Set(context, &memcache.Item{
			Key:    user.CacheID(),
			Object: cached,
		})

		Convey("When I load it with appx datastore", func() {
			userFromCache := NewUser(User{
				SSN: user.SSN,
			})

			err := appx.NewDatastore(context).Load(userFromCache)

			Convey("Then the entity data is properly loaded", func() {
				So(err, ShouldBeNil)
				So(userFromCache.SSN, ShouldEqual, user.SSN)
				So(userFromCache.Name, ShouldEqual, user.Name)
				So(userFromCache.Email, ShouldEqual, user.Email)
				So(userFromCache.Key(), ShouldResemble, user.Key())
			})
		})
	})

	Convey("Given I have a queriable entity", t, func() {
		user := NewUser(User{
			Name:  "Borges",
			Email: "*****@*****.**",
			SSN:   "321321",
		})

		appx.NewKeyResolver(context).Resolve(user)

		datastore.Put(context, user.Key(), user)

		// Give datastore some time to index the data before querying
		time.Sleep(200 * time.Millisecond)

		Convey("When I load it with appx datastore", func() {
			userFromDatastore := NewUser(User{
				Email: user.Email,
				SSN:   "321321",
			})

			err := appx.NewDatastore(context).Load(userFromDatastore)

			Convey("Then the entity data is properly loaded", func() {
				So(err, ShouldBeNil)
				So(userFromDatastore.SSN, ShouldEqual, user.SSN)
				So(userFromDatastore.Name, ShouldEqual, user.Name)
				So(userFromDatastore.Email, ShouldEqual, user.Email)
				So(userFromDatastore.Key(), ShouldResemble, user.Key())

				Convey("And the entity is cached in case it is cacheable", func() {
					userFromCache := NewUser(User{Name: "Borges"})
					cached := appx.CachedEntity{
						Entity: userFromCache,
					}
					item, err := memcache.Get(context, user.CacheID())
					json.Unmarshal(item.Value, &cached)
					appx.NewKeyResolver(context).Resolve(userFromCache)
					So(err, ShouldBeNil)
					So(userFromCache, ShouldResemble, user)
				})
			})
		})
	})

	Convey("Given I have a lookupable entity", t, func() {
		user := NewUser(User{
			Name:  "Borges",
			Email: "*****@*****.**",
			SSN:   "987987",
		})

		appx.NewKeyResolver(context).Resolve(user)
		datastore.Put(context, user.Key(), user)

		Convey("When I load it with appx datastore", func() {
			userFromDatastore := NewUser(User{
				Name: "Borges",
			})

			err := appx.NewDatastore(context).Load(userFromDatastore)

			Convey("Then the entity data is properly loaded", func() {
				So(err, ShouldBeNil)
				So(userFromDatastore.SSN, ShouldEqual, user.SSN)
				So(userFromDatastore.Name, ShouldEqual, user.Name)
				So(userFromDatastore.Email, ShouldEqual, user.Email)
				So(userFromDatastore.Key(), ShouldResemble, user.Key())

				Convey("And the entity is not cached if its cache id is empty", func() {
					_, err := memcache.Get(context, user.CacheID())
					So(err, ShouldEqual, memcache.ErrCacheMiss)
				})
			})
		})
	})
}
func TestLoadBatchFromCache(t *testing.T) {
	gaeCtx, _ := aetest.NewContext(nil)
	defer gaeCtx.Close()

	Convey("Given I have a load batch from cache transformer", t, func() {
		riversCtx := rivers.NewContext()
		loadBatchProcessor := appx.NewStep(riversCtx).LoadBatchFromCache(gaeCtx)

		Convey("And I have a few entities in the cache", func() {
			user1 := NewUser(User{
				Name:  "Borges",
				Email: "*****@*****.**",
				SSN:   "123123123",
			})

			user2 := NewUser(User{
				Name:  "Diego",
				Email: "*****@*****.**",
				SSN:   "321321",
			})

			appx.NewKeyResolver(gaeCtx).Resolve(user1)
			appx.NewKeyResolver(gaeCtx).Resolve(user2)

			memcache.JSON.Set(gaeCtx, &memcache.Item{
				Key: user1.CacheID(),
				Object: appx.CachedEntity{
					Entity: user1,
					Key:    user1.Key(),
				},
			})

			memcache.JSON.Set(gaeCtx, &memcache.Item{
				Key: user2.CacheID(),
				Object: appx.CachedEntity{
					Entity: user2,
					Key:    user2.Key(),
				},
			})

			Convey("When I transform the incoming batch", func() {
				notCachedUser := NewUser(User{
					Name: "not cached",
					SSN:  "notcached",
				})

				userFromCache1 := NewUser(User{Name: user1.Name})
				userFromCache2 := NewUser(User{Name: user2.Name})

				batchItems := make(map[string]*appx.CachedEntity)
				batchItems[user1.CacheID()] = &appx.CachedEntity{
					Entity: userFromCache1,
				}
				batchItems[user2.CacheID()] = &appx.CachedEntity{
					Entity: userFromCache2,
				}
				batchItems[notCachedUser.CacheID()] = &appx.CachedEntity{
					Entity: notCachedUser,
				}

				batch := &appx.MemcacheLoadBatch{
					Keys:  []string{user1.CacheID(), user2.CacheID()},
					Items: batchItems,
				}

				in, out := stream.New(1)
				loadBatchProcessor(batch, stream.NewEmitter(rivers.NewContext(), out))
				close(out)

				Convey("Then cache misses are sent downstream", func() {
					So(in.ReadAll(), ShouldResemble, []stream.T{notCachedUser})

					Convey("And entities are loaded from cache", func() {
						So(userFromCache1, ShouldResemble, user1)
						So(userFromCache2, ShouldResemble, user2)
					})
				})
			})
		})
	})
}
Exemple #7
0
func TestKeyResolver(t *testing.T) {
	context, _ := aetest.NewContext(nil)
	defer context.Close()

	Convey("Given I have a key manager", t, func() {
		manager := appx.NewKeyResolver(context)

		Convey("When I resolve a key of an entity with the key already set", func() {
			entity := &User{
				keySpec: &appx.KeySpec{
					Kind:  "Entity",
					IntID: 123,
				},
			}

			key := datastore.NewKey(context, entity.keySpec.Kind, "", entity.keySpec.IntID, nil)
			entity.SetKey(key)

			err := manager.Resolve(entity)

			Convey("Then it succeeds", func() {
				So(err, ShouldBeNil)

				Convey("And the entity key is not overriden", func() {
					So(entity.Key(), ShouldEqual, key)
				})
			})
		})

		Convey("When I resolve a key of an entity with no parent", func() {
			entity := &User{
				keySpec: &appx.KeySpec{
					Kind:  "Entity",
					IntID: 123,
				},
			}

			err := manager.Resolve(entity)

			Convey("Then it succeeds", func() {
				So(err, ShouldBeNil)

				Convey("And the resolved key is set back to the entity", func() {
					So(entity.Key().StringID(), ShouldEqual, "")
					So(entity.Key().IntID(), ShouldEqual, entity.keySpec.IntID)
					So(entity.Key().Kind(), ShouldEqual, entity.keySpec.Kind)
					So(entity.Key().Parent(), ShouldBeNil)
				})
			})
		})

		Convey("When I resolve a key of an entity with parent", func() {
			entity := &User{
				keySpec: &appx.KeySpec{
					Kind:      "Entity",
					IntID:     123,
					HasParent: true,
				},
			}
			parentKey := datastore.NewKey(context, "Parent", "key", 0, nil)
			entity.SetParentKey(parentKey)

			err := manager.Resolve(entity)

			Convey("Then it succeeds", func() {
				So(err, ShouldBeNil)

				Convey("And the resolved key is set back to the entity", func() {
					So(entity.Key().Kind(), ShouldEqual, entity.keySpec.Kind)
					So(entity.Key().IntID(), ShouldEqual, entity.keySpec.IntID)
					So(entity.Key().StringID(), ShouldEqual, "")

					So(entity.Key().Parent().Kind(), ShouldEqual, parentKey.Kind())
					So(entity.Key().Parent().IntID(), ShouldEqual, 0)
					So(entity.Key().Parent().StringID(), ShouldEqual, parentKey.StringID())
				})
			})
		})

		Convey("When I resolve a key of an entity whose key spec is incomplete", func() {
			entity := &User{
				keySpec: &appx.KeySpec{
					Kind:       "People",
					Incomplete: true,
				},
			}

			err := manager.Resolve(entity)

			Convey("Then key is resolved as incomplete", func() {
				So(err, ShouldBeNil)
				So(entity.Key().Incomplete(), ShouldBeTrue)
			})
		})

		Convey("When I resolve a key of an entity whose key spec is missing kind information", func() {
			err := manager.Resolve(&User{
				keySpec: &appx.KeySpec{},
			})

			Convey("Then it fails key resolution", func() {
				So(err, ShouldEqual, appx.ErrMissingEntityKind)
			})
		})

		Convey("When I resolve a key of an entity whose key spec is of an incomplete key", func() {
			err := manager.Resolve(&User{
				keySpec: &appx.KeySpec{Kind: "Entity"},
			})

			Convey("Then it fails key resolution", func() {
				So(err, ShouldEqual, appx.ErrIncompleteKey)
			})
		})

		Convey("When I resolve a key of an entity whose key spec is incomplete an StringID is set", func() {
			err := manager.Resolve(&User{
				keySpec: &appx.KeySpec{
					Kind:       "Entity",
					Incomplete: true,
					StringID:   "string id",
				},
			})

			Convey("Then it fails key resolution", func() {
				So(err, ShouldEqual, appx.ErrIncompleteKey)
			})
		})

		Convey("When I resolve a key of an entity whose key spec is incomplete an IntID is set", func() {
			err := manager.Resolve(&User{
				keySpec: &appx.KeySpec{
					Kind:       "Entity",
					Incomplete: true,
					IntID:      123,
				},
			})

			Convey("Then it fails key resolution", func() {
				So(err, ShouldEqual, appx.ErrIncompleteKey)
			})
		})

		Convey("When I resolve a key of an entity whose key spec requires a parent key and it's missing", func() {
			err := manager.Resolve(&User{
				keySpec: &appx.KeySpec{
					Kind:      "Entity",
					IntID:     123,
					HasParent: true,
				},
			})

			Convey("Then it fails key resolution", func() {
				So(err, ShouldEqual, appx.ErrMissingParentKey)
			})
		})

		Convey("When I resolve a key of an entity whose key spec requires a parent key and parent key is incomplete", func() {
			entity := &User{
				keySpec: &appx.KeySpec{
					Kind:      "Entity",
					IntID:     123,
					HasParent: true,
				},
			}

			entity.SetParentKey(datastore.NewIncompleteKey(context, "parent", nil))
			err := manager.Resolve(entity)

			Convey("Then it fails key resolution", func() {
				So(err, ShouldEqual, appx.ErrIncompleteParentKey)
			})
		})
	})
}
func TestQueryEntityFromDatastore(t *testing.T) {
	gaeCtx, _ := aetest.NewContext(nil)
	defer gaeCtx.Close()

	user := &User{
		Name:  "Borges",
		Email: "*****@*****.**",
		SSN:   "123123123",
		keySpec: &appx.KeySpec{
			Kind:     "Users",
			StringID: "borges",
		},
	}

	parentKey := datastore.NewKey(gaeCtx, "Parent", "parent id", 0, nil)
	user.SetParentKey(parentKey)

	Convey("Given I have a query entity from datastore transformer", t, func() {
		riversCtx := rivers.NewContext()
		queryProcessor := appx.NewStep(riversCtx).QueryEntityFromDatastore(gaeCtx)

		Convey("When I transform the inbound stream with non existent entity", func() {
			nonExistentUser := &User{
				Email: "*****@*****.**",
				keySpec: &appx.KeySpec{
					Kind: "Users",
				},
			}

			runQuery := func() {
				queryProcessor(nonExistentUser)
			}

			Convey("Then query processor panics", func() {
				So(runQuery, ShouldPanic)
			})
		})

		Convey("And I have an entity in datastore", func() {
			err := appx.NewKeyResolver(gaeCtx).Resolve(user)
			So(err, ShouldBeNil)

			_, err = datastore.Put(gaeCtx, user.Key(), user)
			So(err, ShouldBeNil)

			// Give datastore some time so that the created entity is available to be queried
			time.Sleep(200 * time.Millisecond)

			Convey("When I transform the inbound stream", func() {
				userFromDatastore := &User{
					Email: "*****@*****.**",
					keySpec: &appx.KeySpec{
						Kind: "Users",
					},
				}

				queryProcessor(userFromDatastore)

				Convey("And queryable entities are loaded from datastore", func() {
					So(userFromDatastore.Name, ShouldEqual, user.Name)
					So(userFromDatastore.Email, ShouldEqual, user.Email)
					So(userFromDatastore.Key(), ShouldResemble, user.Key())
					So(userFromDatastore.ParentKey(), ShouldResemble, user.ParentKey())
				})
			})
		})
	})
}