diff --git a/.gitignore b/.gitignore index 2f836aa..287ab43 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *~ *.pyc +.DS* diff --git a/test/test_site/app.yaml b/test/test_site/app.yaml new file mode 100644 index 0000000..75dc8e5 --- /dev/null +++ b/test/test_site/app.yaml @@ -0,0 +1,8 @@ +application: appengine +version: 1 +runtime: python +api_version: 1 + +handlers: +- url: .* + script: index.py diff --git a/test/test_site/index.py b/test/test_site/index.py new file mode 100644 index 0000000..b7cbbf3 --- /dev/null +++ b/test/test_site/index.py @@ -0,0 +1,1058 @@ +from google.appengine.api import users +from google.appengine.ext import db +from google.appengine.api import datastore + +import datetime +import time +import types + +print 'Content-Type: text/html' +print '' +print '' + +print 'Datastore API
' +print 'Test a simple db example...
' +class Person(db.Model): + name = db.StringProperty(required=True) + +for result in Person.all().fetch(1000): + result.delete() + +test = Person(name="Mike") +key = test.put() +result = Person.get(key) +assert result.name == "Mike" + +print 'Test that ids get incremented properly between sessions...
' +class Blah(db.Model): + something = db.StringProperty() + +# NOT erasing old data here + +prev_count = Blah.all().count() +Blah().put() +Blah(something="hello").put() +assert Blah.all().count() == prev_count + 2 + +print 'Slightly less simple db test...
' +class Pet(db.Model): + name = db.StringProperty(required=True) + type = db.StringProperty(required=True, choices=set(["cat", "dog", "bird"])) + birthdate = db.DateProperty() + weight_in_pounds = db.IntegerProperty() + spayed_or_neutered = db.BooleanProperty() + +for result in Pet.all().fetch(1000): + result.delete() + +pet = Pet(name="Fluffy", type="cat") +pet.weight_in_pounds = 24 +key = db.put(pet) +result = db.get(key) +assert result.name == "Fluffy" +assert result.type == "cat" +assert result.weight_in_pounds == 24 +assert result.birthdate == None +assert result.spayed_or_neutered == None + +print 'Test db exceptions...
' +pet = Pet(name="Fluffy", type="cat") +try: + pet.type = "mike" + assert False +except db.BadValueError: + pass +pet.type = "dog" +try: + pet.name = None + assert False +except db.BadValueError: + pass +pet.name = "mike" +try: + pet.weight_in_pounds = "hello" + assert False +except db.BadValueError: + pass +pet.weight_in_pounds = 30 +try: + pet.spayed_or_neutered = 24 + assert False +except db.BadValueError: + pass +pet.spayed_or_neutered = False + +print 'Test a delete...
' +key = pet.put() +result = Pet.get(key) +assert result.name == "mike" +pet.delete() +assert pet.name == "mike" +result = db.get(key) +assert result == None + +print 'Test a delete on an unsaved object...
' +pet = Pet(name="Fluffy", type="cat") +try: + pet.delete() + assert False +except db.NotSavedError: + pass +assert pet.name == "Fluffy" + +print 'Test a date property...
' +class Story(db.Model): + title = db.StringProperty(required=True) + created = db.DateTimeProperty(auto_now_add=True) + +for result in Story.all().fetch(1000): + result.delete() + +story = Story(title="My Story") +key = story.put() +result = Story.get(key) +assert result.title == "My Story" +assert result.created.year in [2008, 2009, 2010] +result.delete() + +print 'Test a simple query...
' + +s1 = Story(title="The Three Little Pigs") +db.put(s1) + +time.sleep(0.25) +s2 = Story(title="Little Red Riding Hood") +s2.put() + +time.sleep(0.25) +try: + s = Story() + assert False +except db.BadValueError: + pass + +s3 = Story(title="Winnie the Pooh") +s3.put() + +assert s1.created < s2.created or s2.created < s3.created + +query = Story.all() +query.order('-created') +s = query.get() +assert s.title == "Winnie the Pooh" +query = db.Query(Story) +query.order('created') +s = query.get() +assert s.title == "The Three Little Pigs" + +print 'Test some different properties...
' +class Article(db.Model): + title = db.StringProperty(required=True, default="no title") + content = db.TextProperty(required=True) + tags = db.ListProperty(db.Category) + author_mail = db.EmailProperty() + link = db.LinkProperty(required=True) + rating = db.RatingProperty() + +for result in Article.all().fetch(1000): + result.delete() + +print '    Validation
' +try: + art = Article() + assert False +except db.BadValueError: + pass +try: + art = Article(content="some content") + assert False +except db.BadValueError: + pass +art = Article(content="some content", link="http://www.example.com") +assert art.title == 'no title' +try: + art = Article(content="some content", link="not a link") + assert False +except db.BadValueError: + pass +art = Article(content="some content", link="http://www.example.com", author_mail="not an email") +try: + art = Article(content="some content", link="http://www.example.com", author_mail="not an email", rating=101) + assert False +except db.BadValueError: + pass +art = Article(title="my title", content="some content", tags=[db.Category("awesome"), db.Category("super")], + author_mail="test@example.com", link="http://www.example.com", + rating=65) +assert art.title == "my title" +assert art.tags[1] == "super" +print '    Put and Fetch
' + +art.put() +out = Article.all().fetch(10)[0] +assert art.title == out.title +assert art.content == out.content +assert art.tags == out.tags +assert art.author_mail == out.author_mail +assert art.link == out.link +assert art.rating == out.rating +print '    Some more query orderings
' +art = Article(title="a title", content="writing", tags=[db.Category("super")], + author_mail="anothertest@example.com", link="http://www.10gen.com", + rating=95) +art.put() +art = Article(title="some title", content="writing i wrote", tags=[db.Category("writing"), db.Category("other")], + link="http://www.example.org", + rating=70) +art.put() +assert Article.all().order('title').fetch(1, 1)[0].title == "my title" +try: + print Article.all().order('title').fetch(1, 1)[1].title + assert False +except IndexError: + pass +assert Article.all().order('-title').fetch(5)[2].title == "a title" +assert Article.all().order('link').get().title == 'a title' +assert Article.all().order('-link').get().title == 'some title' +assert Article.all().order('rating').get().title == 'my title' +assert Article.all().order('-rating').get().title == 'a title' +assert Article.all().order('tags').get().title == 'my title' +assert Article.all().order('-tags').get().title == 'some title' + +print 'Test a foreign key...
' +class FirstModel(db.Model): + prop = db.IntegerProperty() + +for result in FirstModel.all().fetch(1000): + result.delete() + +try: + class Bad(db.Model): + ref_one = db.ReferenceProperty(FirstModel) + ref_two = db.ReferenceProperty(FirstModel) + assert False +except db.DuplicatePropertyError: + pass + +class SecondModel(db.Model): + reference = db.ReferenceProperty(FirstModel) + ref2 = db.ReferenceProperty(FirstModel, collection_name="testcollection") + selfref = db.SelfReferenceProperty() + +for result in SecondModel.all().fetch(1000): + result.delete() + +obj1 = FirstModel() +obj1.prop = 42 +obj1.put() + +obj2 = SecondModel() +obj2.reference = obj1.key() +obj2.ref2 = obj1 +key2 = obj2.put() + +obj3 = SecondModel() +obj3.reference = obj1 +obj3.selfref = obj2 +key3 = obj3.put() + +obj4 = FirstModel() +obj4.prop = 6 +obj4.put() + +obj5 = SecondModel() +obj5.reference = obj4 +obj5.selfref = obj3 +key5 = obj5.put() + +obj2.reference.prop = 999 +obj2.reference.put() + +assert FirstModel.all().count() == 2 + +assert db.get(key3).reference.prop == 999 + +assert db.get(key5).selfref.reference.prop == 999 + +assert db.get(obj1.key()).prop == 999 + +assert obj1.testcollection.count() == 1 + +assert obj1.secondmodel_set.count() == 2 + +assert isinstance(obj1.secondmodel_set[0], SecondModel) + +print 'Test query counts...
' +class CountTest(db.Model): + prop = db.StringProperty() + +for result in CountTest.all().fetch(1000): + result.delete() + +assert CountTest.all().count() == 0 + +CountTest(prop="hello").put() +CountTest(prop="hello").put() +CountTest(prop="hello").put() +CountTest(prop="goodbye").put() +CountTest(prop="hello").put() +CountTest(prop="hello").put() +CountTest(prop="goodbye").put() +CountTest(prop="hello").put() +CountTest(prop="goodbye").put() +CountTest(prop="hello").put() + +assert CountTest.all().count() == 10 +assert CountTest.all().count(5) == 5 +assert CountTest.all().filter('prop =', 'hello').count() == 7 +assert CountTest.all().filter('prop =', 'hello').count(5) == 5 +assert CountTest.all().filter('prop =', 'goodbye').count() == 3 +assert CountTest.all().filter('prop =', 'hello').filter('prop =', 'goodbye').count() == 0 + +print 'Test filtering on a non-existent key...
' +assert CountTest.all().filter('test <', 5).count() == 0 + +print 'Test a query using the datastore API directly...
' +query = datastore.Query('CountTest') +query['prop ='] = 'hello' +assert query.Count() == 7 + +print 'Test creating and querying on an Entity directly...
' +for entity in datastore.Query('mike').Get(1000): + datastore.Delete(entity.key()) + +entity = datastore.Entity("mike") +entity["demo"] = 5 +datastore.Put(entity) + +entity = datastore.Entity("mike") +entity["demo"] = 10 +datastore.Put(entity) + +entity = datastore.Entity("mike") +entity["demo"] = -4 +datastore.Put(entity) + +query = datastore.Query('mike') +query['demo >'] = 3 +assert query.Count() == 2 + +query = datastore.Query('mike') +query['demo ='] = -4 +entities = query.Get(1) +assert len(entities) == 1 +assert entities[0]['demo'] == -4 + +print 'Test gets and deletes using key_name...
' +class KeyName(db.Model): + x = db.IntegerProperty() +for result in KeyName.all().fetch(1000): + result.delete() + +k = KeyName(x=2, key_name="test") +db.put(k) +assert k.key().id() == None +assert k.key().name() == "test" +assert KeyName.all().count() == 1 +assert db.get(k.key()).x == 2 +db.delete(k.key()) +assert KeyName.all().count() == 0 + + +print 'Test __key__ queries...
' +class KeySortTest(db.Model): + x = db.IntegerProperty() + +for result in KeySortTest.all().fetch(1000): + result.delete() + +zero = KeySortTest(x=0).put() +one = KeySortTest(x=1, key_name="test").put() +two = KeySortTest(x=2).put() +three = KeySortTest(x=3, key_name="again").put() +four = KeySortTest(x=4).put() +five = KeySortTest(x=5, key_name="something").put() + +assert KeySortTest.gql('WHERE __key__ > :1', one).count() == 0 +assert KeySortTest.gql('WHERE __key__ <= :1', one).count() == 6 +assert KeySortTest.gql('WHERE __key__ > :1', three).count() == 2 +assert KeySortTest.gql('WHERE __key__ <= :1', three).count() == 4 +assert KeySortTest.gql('WHERE __key__ < :1', zero).count() < 3 +assert KeySortTest.gql('WHERE __key__ < :1', zero).count() != KeySortTest.gql('WHERE __key__ < :1', two).count() +assert KeySortTest.gql('WHERE __key__ < :1', zero).count() + KeySortTest.gql('WHERE __key__ >= :1', zero).count() == 6 + +res = KeySortTest.gql('ORDER BY __key__') +assert res[3].x == 3 +assert res[4].x == 5 +assert res[5].x == 1 + +first_three = [a.x for a in res[:3]] +db.delete(two) +no_2 = [a.x for a in KeySortTest.gql('ORDER BY __key__')] +assert first_three.index(no_2[0]) < first_three.index(no_2[1]) + + +print "Test paths in __key__ queries...
" +class KeyPath(db.Model): + x = db.IntegerProperty() +for result in KeyPath.all().fetch(1000): + result.delete() + +a = KeyPath(x=0, key_name="test").put() +b = KeyPath(x=1, key_name="mike").put() +c = KeyPath(x=2, key_name="test", parent=a).put() +d = KeyPath(x=3, key_name="mike", parent=a).put() +e = KeyPath(x=4, key_name="test", parent=b).put() +f = KeyPath(x=5, key_name="mike", parent=c).put() +g = KeyPath(x=6, key_name="test", parent=f).put() + +assert [a.x for a in KeyPath.gql('ORDER BY __key__ ASC')] == [1, 4, 0, 3, 2, 5, 6] +assert [a.x for a in KeyPath.gql('ORDER BY __key__ DESC')] == [6, 5, 2, 3, 0, 4, 1] + + +print "Test key uniqueness constraints...
" +class KeyUnique(db.Model): + x = db.IntegerProperty() +for result in KeyUnique.all().fetch(1000): + result.delete() + +assert KeyUnique.all().count() == 0 + +KeyUnique(x=1).put() +assert KeyUnique.all().count() == 1 + +KeyUnique(x=2, key_name="test").put() +assert KeyUnique.all().count() == 2 + +KeyUnique(x=3, key_name="test").put() +assert KeyUnique.all().count() == 2 + +a = KeySortTest(x=4, key_name="test").put() +assert KeyUnique.all().count() == 2 + +b = KeyUnique(x=5, key_name="test", parent=a).put() +assert KeyUnique.all().count() == 3 + +c = KeyUnique(x=6, key_name="test", parent=b).put() +assert KeyUnique.all().count() == 4 + +KeyUnique(x=7, key_name="test", parent=a).put() +assert KeyUnique.all().count() == 4 + +KeyUnique(x=8, key_name="test", parent=c).put() +assert KeyUnique.all().count() == 5 + + +print "Test that an entity's key is the same after a round trip to the db...
" +class KeyUnchangedTest(db.Model): + x = db.IntegerProperty() +for result in KeyUnchangedTest.all().fetch(1000): + result.delete() + +a = KeyUnchangedTest(x=1).put() +b = KeyUnchangedTest(x=2, parent=a).put() +c = KeyUnchangedTest(x=3, parent=a, key_name="test").put() + +assert b.parent() == a +assert c.parent() == a +assert b.name() == None +assert c.name() == "test" +assert b.id() != None +assert c.id() == None +assert isinstance(b.id(), (int, long)) + +bprime = KeyUnchangedTest.all().filter("x =", 2).get().key() +cprime = KeyUnchangedTest.all().filter("x =", 3).get().key() + +assert bprime.parent() == a +assert cprime.parent() == a +assert bprime.name() == None +assert cprime.name() == "test" +assert bprime.id() != None +assert cprime.id() == None +assert isinstance(bprime.id(), (int, long)) + + +print "Test basic ancestor queries...
" +class Ancestor(db.Model): + x = db.IntegerProperty() +for result in Ancestor.all().fetch(1000): + result.delete() + +a = Ancestor(x=0).put() +b = Ancestor(x=1, parent=a) +b.put() +c = Ancestor(x=2, parent=b).put() +d = Ancestor(x=3, parent=a).put() + +assert Ancestor.all().ancestor(a).count() == 4 +assert Ancestor.all().ancestor(b).count() == 2 +assert Ancestor.all().ancestor(d).count() == 1 + + +print "Test ancestor queries across kinds...
" +class Ancestor2(db.Model): + x = db.IntegerProperty() +for result in Ancestor.all().fetch(1000): + result.delete() + +e = Ancestor2(x=4, parent=d).put() +f = Ancestor(x=5, parent=e).put() + +assert Ancestor.all().ancestor(d).count() == 1 +assert Ancestor.all().ancestor(e).count() == 1 +assert Ancestor.all().ancestor(f).count() == 1 +assert Ancestor2.all().ancestor(d).count() == 1 +assert Ancestor2.all().ancestor(e).count() == 1 +assert Ancestor2.all().ancestor(f).count() == 0 + + +print "Test trickier ancestor queries...
" +a = Ancestor(x=10).put() +b = Ancestor(x=11, key_name="test", parent=a).put() +c = Ancestor(x=12, key_name="test").put() +d = Ancestor(x=13, parent=b).put() + +assert db.GqlQuery("SELECT * FROM Ancestor WHERE ANCESTOR IS :1", a).count() == 3 +assert db.GqlQuery("SELECT * FROM Ancestor WHERE ANCESTOR IS :1", b).count() == 2 +assert db.GqlQuery("SELECT * FROM Ancestor WHERE ANCESTOR IS :1", c).count() == 1 +assert db.GqlQuery("SELECT * FROM Ancestor WHERE ANCESTOR IS :1", d).count() == 1 + + +print "Test some queries that should have a count of 0...
" +class NeverBeenSaved(db.Model): + something = db.StringProperty() + +assert NeverBeenSaved.all().count() == 0 +assert NeverBeenSaved.all().order("something").get() == None + +class UnorderableProperty(db.Model): + text = db.TextProperty() + +for result in UnorderableProperty.all().fetch(1000): + result.delete() + +UnorderableProperty(text="hello").put() +UnorderableProperty(text="goodbye").put() + +assert UnorderableProperty.all().count() == 2 +assert UnorderableProperty.all().order("text").count() == 0 + + +print 'Test get_by_id...
' +CountTest(prop="abeginning").put() +akey = CountTest(prop="zend", key_name="a").put() +CountTest(prop="middle", key_name="b").put() +CountTest(prop="huh?").put() +key1 = CountTest(prop="mike").put() +key = CountTest(prop="10gen").put() + +assert CountTest.get_by_id(key.id()).prop == "10gen" +assert [a.prop for a in CountTest.get_by_id([key1.id(), key.id()])] == ["mike", "10gen"] +assert CountTest.get_by_id(key5.id()) == None + + +print 'Test get_by_key_name...
' +assert CountTest.get_by_key_name("a").prop == "zend" +assert [a.prop for a in CountTest.get_by_key_name(["b", "a"])] == ["middle", "zend"] + +assert CountTest.all().filter('prop =', 'zend').count() == 1 +CountTest(prop="zend1", key_name="a").put() +assert CountTest.get_by_key_name("a").prop == "zend1" +assert CountTest.get(akey).prop == "zend1" +assert CountTest.all().filter('prop =', 'zend').count() == 0 + + +print 'Test get_or_insert...
' +class WikiTopic(db.Model): + creation_date = db.DateTimeProperty(auto_now_add=True) + body = db.TextProperty(required=True) + +# The first time through we'll create the new topic. +wiki_word = 'CommonIdioms' +topic = WikiTopic.get_or_insert(wiki_word, + body='This topic is totally new!') +assert topic.key().name() == 'CommonIdioms' +assert topic.body == 'This topic is totally new!' + +# The second time through will just retrieve the entity. +overwrite_topic = WikiTopic.get_or_insert(wiki_word, + body='A totally different message!') +assert topic.key().name() == 'CommonIdioms' +assert topic.body == 'This topic is totally new!' + + +print 'Test filters...
' +assert CountTest.all().filter('prop <', 'hello').count() == 5 +assert CountTest.all().filter('prop <=', 'hello').count() == 12 +assert CountTest.all().filter('prop >', 'hello').count() == 4 +assert CountTest.all().filter('prop >=', 'hello').count() == 11 + +class FilterTest(db.Model): + num = db.IntegerProperty() + +for result in FilterTest.all().fetch(1000): + result.delete() + +FilterTest(num=1).put() +FilterTest(num=19).put() +FilterTest(num=10).put() +FilterTest(num=2).put() +FilterTest(num=8).put() +FilterTest(num=11).put() +FilterTest(num=8).put() +FilterTest(num=1).put() + +assert FilterTest.all().filter('num <', 2).count() == 2 +assert FilterTest.all().filter('num <=', 2).count() == 3 +assert FilterTest.all().filter('num =', 6).count() == 0 +assert FilterTest.all().filter('num =', 8).count() == 2 +assert FilterTest.all().filter('num >=', 10).count() == 3 +assert FilterTest.all().filter('num >', 10).count() == 2 + + +print 'Test deleting an entity directly using its key...
' +db.delete(key) +assert CountTest.all().count() == 15 + + +print 'Test a list filter...
' +class ListFilterTest(db.Model): + tags = db.StringListProperty() + list = db.ListProperty(int) + +for result in ListFilterTest.all().fetch(1000): + result.delete() + +ListFilterTest(tags=["hello", "world"], list=[5,5,1986]).put() +ListFilterTest(tags=["world", "of", "warcraft"], list=[100, 19]).put() +ListFilterTest(tags=["huh", "what"], list=[]).put() +ListFilterTest(tags=[], list=[19, 5]).put() + +assert ListFilterTest.all().filter('tags =', 'hello').count() == 1 +assert ListFilterTest.all().filter('tags =', 'goodbye').count() == 0 +assert ListFilterTest.all().filter('tags =', 'world').count() == 2 +assert ListFilterTest.all().filter('tags =', 'huh').count() == 1 +assert ListFilterTest.all().filter('list =', 1986).count() == 1 +assert ListFilterTest.all().filter('list =', 2008).count() == 0 +assert ListFilterTest.all().filter('list =', 5).count() == 2 +assert ListFilterTest.all().filter('list =', 100).count() == 1 +assert ListFilterTest.all().filter('tags >', "what").count() == 2 +assert ListFilterTest.all().filter('tags >=', "what").count() == 3 +assert ListFilterTest.all().filter('tags <', "huh").count() == 1 +assert ListFilterTest.all().filter('tags <=', "huh").count() == 2 + +print 'Test Expandos...
' +class Song(db.Expando): + title = db.StringProperty() + +for result in Song.all().fetch(1000): + result.delete() + +crazy = Song(title='Crazy like a diamond', + author='Lucy Sky', + publish_date='yesterday', + rating=5.0) +crazy_key = crazy.put() +hoboken = Song(title='The man from Hoboken', + author=['Anthony', 'Lou'], + publish_date=datetime.datetime(1977, 5, 3)) +hobo_key = hoboken.put() + +crazy.last_minute_note=db.Text('Get a train to the station.') +crazy.put() + +a = db.get(crazy_key) +assert a.author == "Lucy Sky" +assert a.rating == 5.0 +assert a.last_minute_note == "Get a train to the station." +del a.publish_date +a.put() +try: + db.get(crazy_key).publish_date + assert False +except AttributeError: + pass +b = db.get(hobo_key) + +assert 'Anthony' in b.author + +assert b.publish_date.year == 1977 + +print 'Test that query results are iterable...
' +count = 0 +for a in FilterTest.all(): + count += 1 +assert count == 8 + +count = 0 +for a in FilterTest.all().filter('num >=', 10): + count += 1 +assert count == 3 + +count = 0 +for a in FilterTest.all().filter('num >=', 10).fetch(1000): + count += 1 +assert count == 3 + +count = 0 +query = db.Query(FilterTest) +for a in query: + count += 1 +assert count == 8 + +print 'Test some simple GQL...
' +numbers = db.GqlQuery("SELECT * FROM FilterTest ORDER BY num DESC LIMIT 4") +numbers = [a.num for a in numbers] +assert numbers == [19, 11, 10, 8] +numbers = db.GqlQuery("SELECT * FROM FilterTest WHERE num = 11") +assert numbers.count() == 1 +numbers = db.GqlQuery("SELECT * FROM FilterTest WHERE num = :1", 8) +assert numbers.count() == 2 +numbers = db.GqlQuery("SELECT * FROM FilterTest WHERE num = :number", hello="mike", number=19) +assert numbers.count() == 1 + +print 'Test the different arguments to property constructors...
' + +def validateHaha(x): + if x != "haha": + raise Exception("Not haha!") + +class Test(db.Model): + a = db.StringProperty(verbose_name="hello", default="mike", required=True) + b = db.StringProperty(name="hello") + c = db.StringProperty(default="testing", choices=None) + d = db.StringProperty(required=True) + e = db.StringProperty(validator=validateHaha) + f = db.StringProperty(choices=["mike", "10gen"]) + +for result in Test.all().fetch(1000): + result.delete() + +t1 = Test(d="hello", e="haha") +assert t1.a == "mike" +assert t1.c == "testing" +assert not t1.b +assert not t1.f +assert t1.d == "hello" +assert t1.e == "haha" +t1.put() + +t2 = Test(a="hello", hello="world", c="random", d="something", e="haha", f="10gen") +assert t2.a == "hello" +assert t2.b == "world" +assert t2.c == "random" +assert t2.d == "something" +assert t2.e == "haha" +assert t2.f == "10gen" +t2.put() + +assert Test.all().filter('hello =', 'world').count() == 1 + +try: + t1 = Test(a=None, d="hello", e="haha") + assert False +except db.BadValueError: + pass + +try: + t1 = Test(e="haha") + assert False +except db.BadValueError: + pass + +try: + t1 = Test(d="hello", e="hello") + assert False +except Exception: + pass + +try: + t1 = Test(d="hello", e="haha", f="random") + assert False +except db.BadValueError: + pass + +print 'Test arguments for DateTimeProperty...
' +class DateTimeTest(db.Model): + normal = db.DateTimeProperty() + change = db.DateTimeProperty(auto_now=True) + create = db.DateTimeProperty(auto_now_add=True) + +dt = datetime.datetime.now() +dt = datetime.datetime(dt.year, + dt.month, + dt.day, + dt.hour, + dt.minute, + dt.second, + int(dt.microsecond / 1000) * 1000) +time.sleep(0.5) +d = DateTimeTest(normal=dt, change=dt) +time.sleep(0.5) +key = d.put() +d = db.get(key) +create = d.create +change = d.change +assert d.normal == dt +assert d.change > d.create +assert d.change > d.normal +assert d.create > dt +key = d.put() +d = db.get(key) +assert d.normal == dt +assert d.change > d.create +assert d.change > d.normal +assert d.create > dt +assert d.create == create +assert d.change > change + +print 'Test arguments for ListProperty...
' +try: + class ListTest2(db.Model): + list = db.ListProperty() + assert False +except TypeError: + pass + +class ListTest(db.Model): + list = db.ListProperty(item_type=int) + list2 = db.ListProperty(int, default=None) + +l = ListTest(list=[]) +l = ListTest(list=[1,2,3]) +assert l.list2 == [] +try: + l = ListTest(list=None) + assert False +except db.BadValueError: + pass + +print 'Test multiline StringProperty...
' +class StringTest(db.Model): + yes = db.StringProperty(multiline=True) + no = db.StringProperty(multiline=False) +s = StringTest(yes="hello\nworld", no="hello world") +try: + s = StringTest(yes="hello\nworld", no="hello\nworld") + assert False +except db.BadValueError: + pass + +print 'Test sorting on mixed string and unicode objects...
' +class StringSort(db.Model): + prop = db.StringProperty() +for result in StringSort.all().fetch(1000): + result.delete() +StringSort(prop="hello").put() +StringSort(prop="goodbye").put() +StringSort(prop="test").put() +StringSort(prop=u"mike").put() +StringSort(prop=u"example").put() +StringSort(prop=u"random").put() +res = StringSort.all().order("prop").fetch(6) +res = [e.prop for e in res] +assert res == ["example", "goodbye", "hello", "mike", "random", "test"] +assert StringSort.all().filter("prop >", "oops").count() == 2 +assert StringSort.all().filter("prop >", u"oops").count() == 2 + + +print 'Test saving and restoring strings and unicode...
' +class TestStrUni(db.Model): + string = db.StringProperty() + uni = db.StringProperty() + +for result in TestStrUni.all().fetch(1000): + result.delete() + +test = TestStrUni(string = "hello", uni = u"hello") +assert isinstance(test.string, types.StringType) +assert isinstance(test.uni, types.UnicodeType) +out = db.get(test.put()) +assert isinstance(out.string, types.UnicodeType) +assert isinstance(out.uni, types.UnicodeType) + + +print 'Test saving and restoring every kind of property...
' +class Everything(db.Model): + str = db.StringProperty() + bool = db.BooleanProperty() + int = db.IntegerProperty() + float = db.FloatProperty() + datetime = db.DateTimeProperty() + date = db.DateProperty() + time = db.TimeProperty() + list = db.ListProperty(types.IntType) + strlist = db.StringListProperty() + user = db.UserProperty() + blob = db.BlobProperty() + text = db.TextProperty() + category = db.CategoryProperty() + link = db.LinkProperty() + email = db.EmailProperty() + geopt = db.GeoPtProperty() + im = db.IMProperty() + phonenumber = db.PhoneNumberProperty() + postaladdress = db.PostalAddressProperty() + rating = db.RatingProperty() + +for result in Everything.all().fetch(1000): + result.delete() + +d2 = datetime.datetime.now() +d2 = datetime.datetime(d2.year, + d2.month, + d2.day, + d2.hour, + d2.minute, + d2.second, + int(d2.microsecond / 1000) * 1000) +time.sleep(0.5) +d = datetime.datetime.now() +d = datetime.datetime(d.year, + d.month, + d.day, + d.hour, + d.minute, + d.second, + int(d.microsecond / 1000) * 1000) + +e1 = Everything(str=u"hello", + bool=True, + int=10, + float=5.05, + datetime=d, + date=d.date(), + time=d.time(), + list=[1,2,3], + strlist=["hello", u'world'], + user=users.User("mike@example.com"), + blob=db.Blob("somerandomdata"), + text=db.Text("some random text"), + category=db.Category("awesome"), + link=db.Link("http://www.10gen.com"), + email=db.Email("test@example.com"), + geopt=db.GeoPt(40.74067, -73.99367), + im=db.IM("http://aim.com/", "example"), + phonenumber=db.PhoneNumber("1 (999) 123-4567"), + postaladdress=db.PostalAddress("40 W 20th St., New York, NY"), + rating=db.Rating(99), + ) +out = db.get(e1.put()) +def failIfNot(reference, value, type): + assert value == reference + assert isinstance(value, type) + +failIfNot(e1.str, out.str, types.UnicodeType) + +failIfNot(e1.bool, out.bool, types.BooleanType) + +# TODO on AE this would always be types.LongType +# This gets difficult with our database, as longs are stored as doubles. +# For now our datastore API just stores and fetches (int, long) as +# their respective type. +failIfNot(e1.int, out.int, (types.IntType, types.LongType)) + +failIfNot(e1.float, out.float, types.FloatType) +failIfNot(e1.datetime, out.datetime, datetime.datetime) +failIfNot(e1.date, out.date, datetime.date) +failIfNot(e1.time, out.time, datetime.time) +failIfNot(e1.list, out.list, list) +failIfNot(e1.strlist, out.strlist, list) +failIfNot(e1.user, out.user, users.User) +failIfNot(e1.blob, out.blob, db.Blob) +failIfNot(e1.text, out.text, db.Text) +failIfNot(e1.category, out.category, db.Category) +failIfNot(e1.link, out.link, db.Link) +failIfNot(e1.email, out.email, db.Email) +failIfNot(e1.geopt, out.geopt, db.GeoPt) +failIfNot(e1.im, out.im, db.IM) +failIfNot(e1.phonenumber, out.phonenumber, db.PhoneNumber) +failIfNot(e1.postaladdress, out.postaladdress, db.PostalAddress) +failIfNot(e1.rating, out.rating, db.Rating) + +e2 = Everything(str="goodbye", + bool=False, + int=5, + float=1.01, + datetime=d2, + date=d2.date(), + time=d2.time(), + list=[10,0,3], + strlist=["zinc", u'alpha'], + user=users.User("dave@example.com"), + blob=db.Blob("aoeunthauneot"), + text=db.Text(";qtjkhrchauenth"), + category=db.Category("aaawesome"), + link=db.Link("http://10gen.com"), + email=db.Email("mike@example.com"), + geopt=db.GeoPt(38.5, -70.99367), + im=db.IM("http://aim.com/", "dave"), + phonenumber=db.PhoneNumber("1 (888) 123-4567"), + postaladdress=db.PostalAddress("39 W 20th St., New York, NY"), + rating=db.Rating(90), + ) +e2.put() + +def checkOrder(attr, order, str_value): + assert Everything.all().order(order + attr).get().str == str_value + +checkOrder('str', '', 'goodbye') +checkOrder('bool', '', 'goodbye') +checkOrder('float', '', 'goodbye') +checkOrder('datetime', '', 'goodbye') +checkOrder('time', '', 'goodbye') +checkOrder('list', '', 'goodbye') +checkOrder('strlist', '', 'goodbye') +checkOrder('user', '', 'goodbye') +try: + checkOrder('blob', '', 'goodbye') + assert False +except AttributeError: + pass +try: + checkOrder('text', '', 'goodbye') + assert False +except AttributeError: + pass +checkOrder('category', '', 'goodbye') +checkOrder('link', '', 'goodbye') +checkOrder('email', '', 'goodbye') +checkOrder('geopt', '', 'goodbye') +checkOrder('im', '', 'goodbye') +checkOrder('phonenumber', '', 'goodbye') +checkOrder('postaladdress', '', 'goodbye') +checkOrder('rating', '', 'goodbye') + +checkOrder('str', '-', 'hello') +checkOrder('bool', '-', 'hello') +checkOrder('float', '-', 'hello') +checkOrder('datetime', '-', 'hello') +checkOrder('time', '-', 'hello') +checkOrder('list', '-', 'goodbye') +checkOrder('strlist', '-', 'goodbye') +checkOrder('user', '-', 'hello') +try: + checkOrder('blob', '-', 'hello') + assert False +except AttributeError: + pass +try: + checkOrder('text', '-', 'hello') + assert False +except AttributeError: + pass +checkOrder('category', '-', 'hello') +checkOrder('link', '-', 'hello') +checkOrder('email', '-', 'hello') +checkOrder('geopt', '-', 'hello') +checkOrder('im', '-', 'hello') +checkOrder('phonenumber', '-', 'hello') +checkOrder('postaladdress', '-', 'hello') +checkOrder('rating', '-', 'hello') + +assert Everything.all().order('list').order('blob').get() == None +assert Everything.all().order('blob').order('list').get() == None +assert Everything.all().order('list').order('-bool').get().str == 'goodbye' + +print '' diff --git a/test/test_site/index.yaml b/test/test_site/index.yaml new file mode 100644 index 0000000..d3bacf6 --- /dev/null +++ b/test/test_site/index.yaml @@ -0,0 +1,188 @@ +indexes: + +# AUTOGENERATED + +# This index.yaml is automatically updated whenever the dev_appserver +# detects that a new type of query is run. If you want to manage the +# index.yaml file manually, remove the above marker line (the line +# saying "# AUTOGENERATED"). If you want to manage some indexes +# manually, move them above the marker line. The index.yaml file is +# automatically uploaded to the admin console when you next deploy +# your application using appcfg.py. + +# Used 20 times in query history. +- kind: Ancestor + ancestor: yes + +# Used 6 times in query history. +- kind: Ancestor2 + ancestor: yes + +# Unused in query history -- copied from input. +- kind: Article + properties: + - name: link + direction: desc + +# Unused in query history -- copied from input. +- kind: Article + properties: + - name: rating + direction: desc + +# Unused in query history -- copied from input. +- kind: Article + properties: + - name: tags + direction: desc + +# Unused in query history -- copied from input. +- kind: Article + properties: + - name: title + direction: desc + +# Used 2 times in query history. +- kind: Everything + properties: + - name: blob + - name: list + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: blob + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: bool + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: category + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: datetime + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: email + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: float + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: geopt + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: im + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: link + direction: desc + +# Used 2 times in query history. +- kind: Everything + properties: + - name: list + - name: blob + +# Used 2 times in query history. +- kind: Everything + properties: + - name: list + - name: bool + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: list + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: phonenumber + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: postaladdress + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: rating + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: str + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: strlist + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: text + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: time + direction: desc + +# Unused in query history -- copied from input. +- kind: Everything + properties: + - name: user + direction: desc + +# Unused in query history -- copied from input. +- kind: FilterTest + properties: + - name: num + direction: desc + +# Used 2 times in query history. +- kind: KeyPath + properties: + - name: __key__ + direction: desc + +# Unused in query history -- copied from input. +- kind: Story + properties: + - name: created + direction: desc