app/taggable/taggable_tests.py
author Madhusudan C.S <madhusudancs@gmail.com>
Sat, 25 Jul 2009 01:09:46 +0530
changeset 2679 0ede2f3adbc1
permissions -rw-r--r--
Adds to Melange a tags framework based on taggable-mixin. The taggable-mixin allowed only tag per Datastore model. This is extended framework allows any arbitrary number of tags per Datastore model. Also, now one can define different models for different Tag types which are all inherited from the base Tag model provided by taggable-mixin. The GHOPTask model makes use of 2 tags per model, one for difficulty and the other for task_type, both using the tags framework. Reviewed by: Paweł Sołyga

#!/usr/bin/env python

#Copyright 2008 Adam A. Crossland
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
#http://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.

import sys
import os.path

APPENGINE_PATH = '../../thirdparty/google_appengine'

# Add app-engine related libraries to your path
paths = [
    APPENGINE_PATH,
    os.path.join(APPENGINE_PATH, 'lib', 'django'),
    os.path.join(APPENGINE_PATH, 'lib', 'webob'),
    os.path.join(APPENGINE_PATH, 'lib', 'yaml', 'lib')
]
for path in paths:
  if not os.path.exists(path): 
    raise 'Path does not exist: %s' % path
sys.path = paths + sys.path

import unittest
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import datastore_file_stub
from google.appengine.api import mail_stub
from google.appengine.api import user_service_stub
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.api.memcache import memcache_stub
from taggable import *

APP_ID = u'taggable'
AUTH_DOMAIN = 'gmail.com'
LOGGED_IN_USER = 'me@example.com'  # set to '' for no logged in user

BLOG_NAME='test_blog'

class BlogIndex(db.Model):
    "A global counter used to provide the index of the next blog post."
    index = db.IntegerProperty(required=True, default=0)
    "The next available index for a Post."

class Post(Taggable, db.Model):
    index = db.IntegerProperty(required=True, default=0)
    body = db.TextProperty(required = True)
    title = db.StringProperty()
    added = db.DateTimeProperty(auto_now_add=True)
    added_month = db.IntegerProperty()
    added_year = db.IntegerProperty()
    edited = db.DateTimeProperty()
        
    def __init__(self, parent=None, key_name=None, app=None, **entity_values):
        db.Model.__init__(self, parent, key_name, app, **entity_values)
        Taggable.__init__(self)
        
    def get_all_posts():
        return db.GqlQuery("SELECT * from Post ORDER BY added DESC")
    Get_All_Posts = staticmethod(get_all_posts)
        
    @classmethod        
    def get_posts(cls, start_index=0, count=10):
        start_index = int(start_index) # Just make sure that we have an int
        posts = Post.gql('WHERE index <= :1 ORDER BY index DESC', start_index).fetch(count + 1)
        if len(posts) > count:
            posts = posts[:count]
            
        return posts

    @classmethod
    def new_post(cls, new_title=None, new_body=None, new_tags=[]):
        new_post = None
        if new_title is not None and new_body is not None:
            def txn():
                blog_index = BlogIndex.get_by_key_name(BLOG_NAME)
                if blog_index is None:
                    blog_index = BlogIndex(key_name=BLOG_NAME)
                new_index = blog_index.index
                blog_index.index += 1
                blog_index.put()
            
                new_post_key_name = BLOG_NAME + str(new_index)
                new_post = Post(key_name=new_post_key_name, parent=blog_index,
                               index = new_index, title = new_title,
                               body = new_body)
                new_post.put()
                
                return new_post
            new_post = db.run_in_transaction(txn)
        
            new_post.tags = new_tags
            new_post.put()
        else:
            raise Exception("Must supply both new_title and new_body when creating a new Post.")
        
        return new_post
    
    def delete(self):
        # Perform any actions that are required to maintain data integrity
        # when this Post is delete.
        # Disassociate this Post from any Tag
        self.set_tags([])
        
        # Finally, call the real delete
        db.Model.delete(self)

class MyTest(unittest.TestCase):

    def setUp(self):
      # Start with a fresh api proxy.
      apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()

      # Use a fresh stub datastore.
      stub = datastore_file_stub.DatastoreFileStub(APP_ID, '/dev/null', '/dev/null')
      apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', stub)

      # Use a fresh memcache stub.
      apiproxy_stub_map.apiproxy.RegisterStub('memcache', memcache_stub.MemcacheServiceStub())
        
      # Use a fresh stub UserService.
      apiproxy_stub_map.apiproxy.RegisterStub(
          'user', user_service_stub.UserServiceStub())
      os.environ['AUTH_DOMAIN'] = AUTH_DOMAIN
      os.environ['USER_EMAIL'] = LOGGED_IN_USER
      os.environ['APPLICATION_ID'] = APP_ID

    def testSimpleTagAdding(self):
      new_post = Post.new_post(new_title='test post 1', new_body='This is a test post.  Please ignore.')
      assert new_post is not None
        
      new_post.tags = "test, testing, tests"
      self.assertEqual(len(new_post.tags), 3)

    def testComplexTagAdding(self):
      new_post = Post.new_post(new_title='test post 1', new_body='This is a test post.  Please ignore.')
      assert new_post is not None
        
      new_post.tags = "  test, testing, tests,,,tag with spaces"
      self.assertEqual(len(new_post.tags), 4)

      tag = new_post.tags[3]
      assert tag is not None
      self.assertEqual(tag.tag, 'tag with spaces')
      self.assertEqual(tag.tagged_count, 1)

      tag2 = Tag.get_by_name('tag with spaces')
      assert tag2 is not None
      self.assertEqual(tag.tag, 'tag with spaces')
      self.assertEqual(tag.tagged_count, 1)

    def testTagDeletion(self):
      new_post = Post.new_post(new_title='test post 2', new_body='This is a test post.  Please continue to ignore.')
      assert new_post is not None
        
      new_post.tags = "test, testing, tests"
      self.assertEqual(len(new_post.tags), 3)

      new_post.tags = "test"
      self.assertEqual(len(new_post.tags), 1)

    def testTagCounts(self):
      new_post3 = Post.new_post(new_title='test post 3', new_body='This is a test post.  Please continue to ignore.')
      assert new_post3 is not None
      new_post3.tags = "foo, bar, baz"
      new_post4 = Post.new_post(new_title='test post 4', new_body='This is a test post.  Please continue to ignore.')
      assert new_post4 is not None
      new_post4.tags = "bar, baz, bletch"
      new_post5 = Post.new_post(new_title='test post 5', new_body='This is a test post.  Please continue to ignore.')
      assert new_post5 is not None
      new_post5.tags = "baz, bletch, quux"
      
      foo_tag = Tag.get_by_name('foo')
      assert foo_tag is not None
      self.assertEqual(foo_tag.tagged_count, 1)
      
      bar_tag = Tag.get_by_name('bar')
      assert bar_tag is not None
      self.assertEqual(bar_tag.tagged_count, 2)
      
      baz_tag = Tag.get_by_name('baz')
      assert baz_tag is not None
      self.assertEqual(baz_tag.tagged_count, 3)
      
      bletch_tag = Tag.get_by_name('bletch')
      assert bletch_tag is not None
      self.assertEqual(bletch_tag.tagged_count, 2)
      
      quux_tag = Tag.get_by_name('quux')
      assert quux_tag is not None
      self.assertEqual(quux_tag.tagged_count, 1)
      
      new_post3.tags = 'bar, baz'
      foo_tag = Tag.get_by_name('foo')
      assert foo_tag is not None
      self.assertEqual(len(new_post3.tags), 2)
      self.assertEqual(foo_tag.tagged_count, 0)
      
    def testTagGetTagsForKey(self):
      new_post = Post.new_post(new_title='test post 6', new_body='This is a test post.  Please continue to ignore.', new_tags='foo,bar,bletch,quux')
      assert new_post is not None

      tags = Tag.get_tags_for_key(new_post.key())
      assert tags is not None
      self.assertEqual(type(tags), type([]))
      self.assertEqual(len(tags), 4)
    
    def testTagGetByName(self):
      new_post = Post.new_post(new_title='test post 6', new_body='This is a test post.  Please continue to ignore.', new_tags='foo,bar,bletch,quux')
      assert new_post is not None

      quux_tag = Tag.get_by_name('quux')
      assert quux_tag is not None
      
      zizzle_tag = Tag.get_by_name('zizzle')
      assert zizzle_tag is None
      
    def testTagsString(self):
      new_post = Post.new_post(new_title='test post 6', new_body='This is a test post.  Please continue to ignore.', new_tags='  pal,poll ,,pip,pony')
      assert new_post is not None
      self.assertEqual(new_post.tags_string(), "pal,poll,pip,pony")
      new_post.tag_separator = "|"
      self.assertEqual(new_post.tags_string(), "pal|poll|pip|pony")
      new_post.tag_separator = " , "
      self.assertEqual(new_post.tags_string(), "pal , poll , pip , pony")
      
      new_post.tag_separator = ", "
      new_post.tags = "pal, pill, pip"
      self.assertEqual(len(new_post.tags), 3)
      self.assertEqual(new_post.tags_string(), "pal, pill, pip")
      
if __name__ == '__main__':
    unittest.main()