コード例 #1
0
ファイル: datastore_test.go プロジェクト: drborges/appx
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)
					})
				})
			})
		})
	})
}
コード例 #2
0
ファイル: functional_test.go プロジェクト: drborges/appx
func TestLoadMultiEntityKind(t *testing.T) {
	context, _ := aetest.NewContext(nil)
	defer context.Close()

	Convey("Given I have users and devices in datastore", t, func() {
		user1 := NewUser(User{
			Name:  "borges",
			Email: "*****@*****.**",
		})

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

		device1 := &Device{
			ID:    1,
			Owner: "borges",
		}

		device2 := &Device{
			ID:    2,
			Owner: "diego",
		}

		db := appx.NewDatastore(context)
		err := db.Save(user1, device1, user2, device2)
		So(err, ShouldBeNil)

		Convey("When I load them all with appx", func() {
			user1FromDatastore := NewUser(User{Name: "borges"})
			user2FromDatastore := NewUser(User{Name: "diego"})
			device1FromDatastore := &Device{ID: 1}
			device2FromDatastore := &Device{ID: 2}

			err := db.Load(
				user1FromDatastore,
				device1FromDatastore,
				user2FromDatastore,
				device2FromDatastore)

			So(err, ShouldBeNil)
			So(user1FromDatastore, ShouldResemble, user1)
			So(user2FromDatastore, ShouldResemble, user2)
			So(device1FromDatastore, ShouldResemble, device1)
			So(device2FromDatastore, ShouldResemble, device2)
		})
	})
}
コード例 #3
0
func Register(router *httprouter.Router) {
	r := render.New()

	router.POST("/deployments", func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		deployment := DeploymentFrom(req)
		macaroon := CreateDeploymentMacaroon(deployment)
		RequestApproval(appengine.NewContext(req), deployment, macaroon)

		db := appx.NewDatastore(appengine.NewContext(req))
		if err := db.Save(deployment); err != nil {
			log.Panic(err)
		}

		b, _ := macaroon.MarshalBinary()
		token := base64.URLEncoding.EncodeToString(b)

		r.JSON(w, 200, JSON{
			"token": token,
		})
	})

	router.POST("/validate", func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		form := &MacaroonForm{}
		json.NewDecoder(req.Body).Decode(form)
		bytes, err := base64.URLEncoding.DecodeString(form.Token)
		if err != nil {
			r.JSON(w, 400, JSON{
				"message": "Error deserializing macaroon.",
				"error":   err.Error(),
			})
			return
		}

		err = VerifyMacaroon(bytes)
		if err != nil {
			r.JSON(w, 400, JSON{
				"message": "Macaroon invalid.",
				"error":   err.Error(),
			})
			return
		}

		r.JSON(w, 200, JSON{
			"message": "Macaroon valid.",
		})
	})
}
コード例 #4
0
ファイル: items_iterator_test.go プロジェクト: drborges/appx
func TestItemsIterator(t *testing.T) {
	c, _ := aetest.NewContext(nil)
	defer c.Close()

	tags := []*Tag{
		&Tag{Name: "golang", Owner: "Borges"},
		&Tag{Name: "ruby", Owner: "Borges"},
		&Tag{Name: "scala", Owner: "Borges"},
		&Tag{Name: "swift", Owner: "Diego"},
	}

	createAll(c, tags...)

	// TODO fix non deterministic issue
	Convey("ItemsIterator", t, func() {
		Convey("Given I have an items iterator with 3 pages each with 1 item", func() {
			q := datastore.NewQuery(new(Tag).KeySpec().Kind).Filter("Owner=", "Borges").Limit(1)
			iter := appx.NewDatastore(c).Query(q).ItemsIterator()
			tagsFromIterator := []*Tag{&Tag{}, &Tag{}, &Tag{}}

			Convey("Then I can load the first item", func() {
				So(iter.HasNext(), ShouldBeTrue)
				So(iter.Cursor(), ShouldBeEmpty)
				So(iter.LoadNext(tagsFromIterator[0]), ShouldBeNil)
				So(iter.HasNext(), ShouldBeTrue)
				So(iter.Cursor(), ShouldNotBeEmpty)
				So(tagsFromIterator[0], ShouldResemble, tags[0])

				Convey("Then I can load the second item", func() {
					So(iter.LoadNext(tagsFromIterator[1]), ShouldBeNil)
					So(iter.HasNext(), ShouldBeTrue)
					So(iter.Cursor(), ShouldNotBeEmpty)
					So(tagsFromIterator[1], ShouldResemble, tags[2])

					Convey("Then I can load the third item", func() {
						So(iter.LoadNext(tagsFromIterator[2]), ShouldBeNil)
						So(iter.HasNext(), ShouldBeTrue)
						So(iter.Cursor(), ShouldNotBeEmpty)
						So(tagsFromIterator[2], ShouldResemble, tags[1])

						Convey("Then I cannot load more items", func() {
							So(iter.LoadNext(&Tag{}), ShouldEqual, datastore.Done)
							So(iter.HasNext(), ShouldBeFalse)
							So(iter.Cursor(), ShouldBeEmpty)
						})
					})
				})

				Convey("I can create a new iterator using the cursor from the previous one", func() {
					iterWithCursor := appx.NewDatastore(c).Query(q).StartFrom(iter.Cursor()).ItemsIterator()

					Convey("I can load the second item", func() {
						So(iterWithCursor.LoadNext(tagsFromIterator[1]), ShouldBeNil)
						So(iterWithCursor.HasNext(), ShouldBeTrue)
						So(iter.Cursor(), ShouldNotBeEmpty)
						So(tagsFromIterator[1], ShouldResemble, tags[1])
					})
				})
			})

			Convey("Then I can load items until iterator has no more items", func() {
				items := []*Tag{}
				for iter.HasNext() {
					item := &Tag{}
					if err := iter.LoadNext(item); err == nil {
						items = append(items, item)
					}
				}

				So(len(items), ShouldEqual, 3)
				So(items[0], ShouldResemble, tags[0])
				So(items[1], ShouldResemble, tags[1])
				So(items[2], ShouldResemble, tags[2])
			})
		})

		Convey("Given I have an items iterator with zero items", func() {
			q := datastore.NewQuery(new(Tag).KeySpec().Kind).Filter("Owner=", "non existent").Limit(1)
			iter := appx.NewDatastore(c).Query(q).ItemsIterator()

			Convey("When I load the next item", func() {
				firstItem := Tag{}
				So(iter.Cursor(), ShouldBeEmpty)
				So(iter.LoadNext(&firstItem), ShouldEqual, datastore.Done)
				So(iter.Cursor(), ShouldBeEmpty)

				Convey("Then the item is not populated", func() {
					So(firstItem, ShouldResemble, Tag{})

					Convey("Then it has no more results", func() {
						So(iter.HasNext(), ShouldBeFalse)
					})
				})
			})
		})
	})
}
コード例 #5
0
ファイル: datastore_test.go プロジェクト: drborges/appx
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())
			})
		})
	})
}
コード例 #6
0
ファイル: datastore_test.go プロジェクト: drborges/appx
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)
				})
			})
		})
	})
}
コード例 #7
0
ファイル: runner_test.go プロジェクト: drborges/appx
func TestQuery(t *testing.T) {
	c, _ := aetest.NewContext(nil)
	defer c.Close()

	golang := &Tag{Name: "golang", Owner: "Borges"}
	swift := &Tag{Name: "swift", Owner: "Borges"}
	ruby := &Tag{Name: "ruby", Owner: "Diego"}

	createAll(c, golang, swift, ruby)

	Convey("Given I have a QueryRunner", t, func() {
		byOwner := datastore.NewQuery(new(Tag).KeySpec().Kind).Filter("Owner=", "Borges")
		runner := appx.NewDatastore(c).Query(byOwner)

		Convey("When I run Results", func() {
			result := []*Tag{}
			err := runner.Results(&result)

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

				Convey("Then it loads the matched entities into the given slice", func() {
					So(result, ShouldResemble, []*Tag{golang, swift})

					Convey("Then it sets keys back to the entities", func() {
						So(result[0].Key(), ShouldNotBeNil)
						So(result[1].Key(), ShouldNotBeNil)
					})
				})
			})
		})

		Convey("When I run Result", func() {
			tag := &Tag{}
			err := runner.Result(tag)

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

				Convey("Then it loads data into the given entity", func() {
					So(tag, ShouldResemble, golang)

					Convey("Then it sets the key back to the entity", func() {
						So(tag.Key(), ShouldNotBeNil)
					})
				})
			})
		})

		Convey("When I run Count", func() {
			count, err := runner.Count()

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

				Convey("Then count is 2", func() {
					So(count, ShouldEqual, 2)
				})
			})
		})

		Convey("When I stream the data", func() {
			s := runner.StreamOf(Tag{}).Stream

			Convey("Then the entities are streammed", func() {
				So(s.ReadAll(), ShouldResemble, []stream.T{golang, swift})
			})
		})
	})
}
コード例 #8
0
func AppxProvider(c martini.Context, context appengine.Context) {
	c.Map(appx.NewDatastore(context))
}
コード例 #9
0
ファイル: functional_test.go プロジェクト: drborges/appx
func TestLoadOver1000Entities(t *testing.T) {
	context, _ := aetest.NewContext(nil)
	defer context.Close()

	Convey("Given I have tons of entities in datastore", t, func() {
		datastoreUsers := []appx.Entity{}
		for i := 0; i < 1200; i++ {
			datastoreUsers = append(datastoreUsers, NewUser(User{
				Name:  fmt.Sprintf("borges %v", i),
				Email: fmt.Sprintf("*****@*****.**", i),
				SSN:   fmt.Sprintf("SSN %v", i),
			}))
		}
		db := appx.NewDatastore(context)
		err := db.Save(datastoreUsers...)
		So(err, ShouldBeNil)

		Convey("And some entities only in the cache", func() {
			for i := 0; i < 100; i++ {
				err := datastore.Delete(context, datastoreUsers[i].Key())
				So(err, ShouldBeNil)
			}

			// Give datastore some time to index data
			time.Sleep(5 * time.Second)

			Convey("When I load all of them with appx", func() {
				loadedUsers := []appx.Entity{}
				// from cache
				for i := 0; i < 100; i++ {
					loadedUsers = append(loadedUsers, NewUser(User{
						SSN: fmt.Sprintf("SSN %v", i),
					}))
				}

				// by querying in datastore
				for i := 100; i < 200; i++ {
					loadedUsers = append(loadedUsers, NewUser(User{
						Email: fmt.Sprintf("*****@*****.**", i),
					}))
				}

				// by datastore key lookup
				for i := 200; i < 1200; i++ {
					loadedUsers = append(loadedUsers, NewUser(User{
						Name: fmt.Sprintf("borges %v", i),
					}))
				}

				err := db.Load(loadedUsers...)

				Convey("Then all entities are loaded accordingly", func() {
					So(err, ShouldBeNil)

					for i := 0; i < 1200; i++ {
						So(loadedUsers[i].(*User).Name, ShouldEqual, datastoreUsers[i].(*User).Name)
						So(loadedUsers[i].(*User).Email, ShouldEqual, datastoreUsers[i].(*User).Email)
						So(loadedUsers[i].(*User).SSN, ShouldEqual, datastoreUsers[i].(*User).SSN)
						So(loadedUsers[i].Key(), ShouldResemble, datastoreUsers[i].Key())
					}
				})
			})
		})
	})
}
コード例 #10
0
func FromAccount(context appengine.Context) *AccountAppxRepository {
	return &AccountAppxRepository{
		db: appx.NewDatastore(context),
	}
}
コード例 #11
0
ファイル: pages_iterator_test.go プロジェクト: drborges/appx
func TestPagesIterator(t *testing.T) {
	c, _ := aetest.NewContext(nil)
	defer c.Close()

	tags := []*Tag{
		&Tag{Name: "golang", Owner: "Borges"},
		&Tag{Name: "ruby", Owner: "Borges"},
		&Tag{Name: "scala", Owner: "Borges"},
		&Tag{Name: "swift", Owner: "Diego"},
	}

	createAll(c, tags...)

	Convey("PagesIterator", t, func() {
		Convey("Given I have a pages iterator with 2 pages each with 2 items", func() {
			q := datastore.NewQuery(new(Tag).KeySpec().Kind).Limit(2)
			iter := appx.NewDatastore(c).Query(q).PagesIterator()

			Convey("Then I can load the first page", func() {
				firstPage := []*Tag{}
				So(iter.Cursor(), ShouldBeEmpty)
				So(iter.LoadNext(&firstPage), ShouldBeNil)
				So(iter.HasNext(), ShouldBeTrue)
				So(firstPage, ShouldResemble, tags[0:2])

				Convey("Then I can load the second page", func() {
					secondPage := []*Tag{}
					So(iter.Cursor(), ShouldNotBeEmpty)
					So(iter.LoadNext(&secondPage), ShouldBeNil)
					So(iter.HasNext(), ShouldBeTrue)
					So(secondPage, ShouldResemble, tags[2:])

					Convey("Then I cannot load more pages", func() {
						page := []*Tag{}
						So(iter.LoadNext(&page), ShouldEqual, datastore.Done)
						So(iter.HasNext(), ShouldBeFalse)
						So(iter.Cursor(), ShouldBeEmpty)
						So(page, ShouldBeEmpty)
					})
				})
			})
		})

		Convey("Given I have a pages iterator with 4 pages each with 1 item", func() {
			q := datastore.NewQuery(new(Tag).KeySpec().Kind).Limit(1)
			iter := appx.NewDatastore(c).Query(q).PagesIterator()

			Convey("Then I can load pages until iterator has no more pages", func() {
				pages := [][]*Tag{}
				for iter.HasNext() {
					page := []*Tag{}
					if err := iter.LoadNext(&page); err == nil {
						pages = append(pages, page)
					}
				}

				So(len(pages), ShouldEqual, 4)
				So(pages[0], ShouldResemble, []*Tag{tags[0]})
				So(pages[1], ShouldResemble, []*Tag{tags[1]})
				So(pages[2], ShouldResemble, []*Tag{tags[2]})
				So(pages[3], ShouldResemble, []*Tag{tags[3]})
			})
		})

		Convey("Given I have a pages iterator with cursor starting from the second and last page", func() {
			q := datastore.NewQuery(new(Tag).KeySpec().Kind).Limit(2)
			firstPage := &[]*Tag{}
			prevIter := appx.NewDatastore(c).Query(q).PagesIterator()
			prevIter.LoadNext(firstPage)

			iterStartingFromSecondPage := appx.NewDatastore(c).Query(q).StartFrom(prevIter.Cursor()).PagesIterator()

			Convey("Then I can load the page", func() {
				secondPage := []*Tag{}
				So(iterStartingFromSecondPage.LoadNext(&secondPage), ShouldBeNil)
				So(iterStartingFromSecondPage.HasNext(), ShouldBeTrue)
				So(iterStartingFromSecondPage.Cursor(), ShouldNotBeEmpty)
				So(secondPage, ShouldResemble, tags[2:])

				Convey("Then I cannot load more pages", func() {
					page := []*Tag{}
					So(iterStartingFromSecondPage.LoadNext(&page), ShouldEqual, datastore.Done)
					So(iterStartingFromSecondPage.HasNext(), ShouldBeFalse)
					So(iterStartingFromSecondPage.Cursor(), ShouldBeEmpty)
					So(page, ShouldBeEmpty)
				})
			})
		})

		Convey("Given I have a pages iterator with zero items", func() {
			q := datastore.NewQuery(new(Tag).KeySpec().Kind).Filter("Owner=", "non existent").Limit(1)
			iter := appx.NewDatastore(c).Query(q).PagesIterator()

			Convey("When I load the next page", func() {
				firstPage := []*Tag{}
				So(iter.LoadNext(&firstPage), ShouldEqual, datastore.Done)

				Convey("Then the page is empty", func() {
					So(firstPage, ShouldBeEmpty)

					Convey("Then the cursor is empty", func() {
						So(iter.Cursor(), ShouldBeEmpty)

						Convey("Then it has no more results", func() {
							So(iter.HasNext(), ShouldBeFalse)
						})
					})
				})
			})
		})
	})
}