testIssueTracker.py :  » Issue-Tracker » IssueTracker » IssueTrackerProduct » tests » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Issue Tracker » IssueTracker 
IssueTracker » IssueTrackerProduct » tests » testIssueTracker.py
# -*- coding: iso-8859-1 -*
##
## <peter@fry-it.com>
##

import unittest
import re
from pprint import pprint
from time import time
from random import randint

import sys, os
if __name__ == '__main__':
    execfile(os.path.join(sys.path[0], 'framework.py'))

from Globals import SOFTWARE_HOME
from Testing import ZopeTestCase
from AccessControl import getSecurityManager
from AccessControl.SecurityManagement import newSecurityManager,noSecurityManager
from DateTime import DateTime
from zExceptions import Unauthorized
import Acquisition

from base import TestBase
#ZopeTestCase.installProduct('MailHost')
#ZopeTestCase.installProduct('ZCatalog')
#ZopeTestCase.installProduct('ZCTextIndex')
#ZopeTestCase.installProduct('SiteErrorLog')
#ZopeTestCase.installProduct('PythonScripts')
#ZopeTestCase.installProduct('IssueTrackerProduct')

from Products.IssueTrackerProduct.Permissions import IssueTrackerManagerRole,IssueTrackerUserRole
from Products.IssueTrackerProduct.Constants import ISSUEUSERFOLDER_METATYPE,\
 DEBUG, ISSUE_DRAFT_METATYPE, TEMPFOLDER_REQUEST_KEY, \
 FILTERVALUEFOLDER_THRESHOLD_CLEANING, FILTEROPTION_METATYPE
from Products.IssueTrackerProduct.Errors import DataSubmitError,UserSubmitError

#------------------------------------------------------------------------------
#
# Some constants
#

#------------------------------------------------------------------------------

pre_submitissue_script_src = """
## Script (Python) "pre_SubmitIssue"
##parameters=
##title=
##
request = context.REQUEST
title = request.get('title',u'')
if title.lower().startswith(u'a'):
    return {'title':u'Subject line must NOT start with an A'}
"""


post_submitissue_script_src = """
## Script (Python) "post_SubmitIssue"
##parameters=issue
##title=
##

if 'Security' in issue.getSections():
   # increase the urgency by one notch

   urgency = issue.getUrgency()

   options = issue.getUrgencyOptions() # acquired 

   try:
       urgency = options[options.index(urgency)+1]
   except IndexError:
       # was already at the topmost
       return
   issue.editIssueDetails(urgency=urgency)
   
"""
    

#------------------------------------------------------------------------------

class NewFileUpload:
    def __init__(self, file_path):
        self.file = open(file_path)
        self.filename = os.path.basename(file_path)
        self.file_path = file_path
        
    def read(self, bytes=None):
        if bytes:
            return self.file.read(bytes)
        else:
            return self.file.read()
    
    def seek(self, bytes, mode=0):
        self.file.seek(bytes, mode)
        
    def tell(self):
        return self.file.tell()
        
class DodgyNewFileUpload:
    """ a file upload that returns a blank string no when you read it """
    
    def __init__(self, file_path):
        self.file = open(file_path)
        self.filename = os.path.basename(file_path)
        self.file_path = file_path    
    
    def read(self, bytes=None, mode=0):
        return ""


from Products.IssueTrackerProduct.IssueTracker import FilterValuer
        
#------------------------------------------------------------------------------    

class TestBasex(ZopeTestCase.ZopeTestCase):

    def dummy_redirect(self, *a, **kw):
        self.has_redirected = a[0]
        if kw:
            print "*** Redirecting to %r + (%s)" % (a[0], kw)
        else:
            print "*** Redirecting to %r" % a[0]
    
    def afterSetUp(self):
        # install an issue tracker
        dispatcher = self.folder.manage_addProduct['IssueTrackerProduct']
        dispatcher.manage_addIssueTracker('tracker', 'Issue Tracker')
        
        # install an error_log
        #dispatcher = self.folder.manage_addProduct['SiteErrorLog']
        #dispatcher.manage_addErrorLog()

        # install a MailHost
        if not DEBUG:
            dispatcher = self.folder.manage_addProduct['MailHost']
            dispatcher.manage_addMailHost('MailHost')
        
        # if you set this override you won't be able to do a transaction.get().commit()
        # in the unit tests.
        #self.mexpenses.http_redirect = self.dummy_redirect 
        
        request = self.app.REQUEST
        sdm = self.app.session_data_manager
        request.set('SESSION', sdm.getSessionData())
        
        #self.has_redirected = False
        
    def set_cookie(self, key, value, expires=365, path='/',
                   across_domain_cookie_=False,
                   **kw):
        
        self.app.REQUEST.cookies[key] = value
        
    #def afterClear(self):
    #    global __trapped_emails__
    #    __trapped_emails__ = []
        
        
try:
    from zope import traversing,component,interface
except ImportError:
    traversing = None
    TestFunctionalBase = None
        
if traversing:
    
    from zope.traversing.adapters import DefaultTraversable
    from zope.traversing.interfaces import ITraversable
    from zope.component import provideAdapter
    from zope import interface
    from zope.interface import implements
    
    class TestFunctionalBase(ZopeTestCase.FunctionalTestCase, TestBase):
    
        def afterSetUp(self):
            # install an issue tracker
            dispatcher = self.folder.manage_addProduct['IssueTrackerProduct']
            dispatcher.manage_addIssueTracker('tracker', 'Issue Tracker')
    
            # install a MailHost
            if not DEBUG:
                dispatcher = self.folder.manage_addProduct['MailHost']
                dispatcher.manage_addMailHost('MailHost')
    
            request = self.app.REQUEST
            sdm = self.app.session_data_manager
            request.set('SESSION', sdm.getSessionData())
            
        def beforeSetUp(self):
            super(ZopeTestCase.FunctionalTestCase, self).beforeSetUp()
            component.provideAdapter( \
                        traversing.adapters.DefaultTraversable, (interface.Interface,),ITraversable)
            
            
    
class IssueTrackerTestCase(TestBase):
    
    try:
        # For zope 2.10+ to make it shut up about 
        from zope.interface import implements
        from zope.traversing.interfaces import ITraversable
        from zope.traversing.adapters import DefaultTraversable
        from zope import interface
        from zope.component import provideAdapter
        implements(ITraversable)
        provideAdapter(DefaultTraversable,
                       (interface.Interface,),ITraversable)
    except ImportError:
        pass
    
    def test_addingIssue(self):
        """ test something """
        #self.tracker = self.folder['mexpenses']
        tracker = self.folder.tracker
        
        request = self.app.REQUEST
        request.set('title', u'TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        
        tracker.SubmitIssue(request)
        
        self.assertEqual(len(tracker.getIssueObjects()), 1)
        
        
    def test_modifyingIssue(self):
        """ test something """
        #self.tracker = self.folder['mexpenses']
        tracker = self.folder.tracker
        
        request = self.app.REQUEST
        request.set('title', u'TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        tracker.SubmitIssue(request)
        
        issue = tracker.getIssueObjects()[0]
        
        request.set('comment', u'COMMENT')
        issue.ModifyIssue(request)
        
        self.assertEqual(len(issue.getThreadObjects()), 1)
        
        
    def test_debatingIssue(self):
        """ test posting a followup under a different email address than the original """
        tracker = self.folder.tracker
        tracker.sitemaster_email = 'something@valid.com'

        request = self.app.REQUEST
        request.set('title', u'TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        tracker.SubmitIssue(request)
        
        issue = tracker.getIssueObjects()[0]
        
        request.set('comment', u'COMMENT')
        request.set('fromname', u'Someone Else')
        request.set('email', u'else@address.com')
        request.set('notify', 1)
        issue.ModifyIssue(request)
        
        # there should now be a notifiation object
        self.assertEqual(len(issue.getCreatedNotifications()), 1)
        
        # have a look at that notification object
        notification = issue.getCreatedNotifications()[0]
        self.assertEqual(notification.getTitle(), u'TITLE')
        self.assertTrue(isinstance(notification.getTitle(), unicode))
        self.assertEqual(notification.getIssueID(), issue.getId())
        self.assertEqual(notification.getEmails(), [u'email@address.com'])
        if tracker.doDispatchOnSubmit():
            self.assertTrue(notification.isDispatched())

        # we should now expect an email to have been sent to email@address.com
        assert self.snatched_emails, "not trapped emails"
        latest_email = self.snatched_emails[-1]
        self.assertTrue(latest_email['to'].find('email@address.com') > -1)
        self.assertTrue(latest_email['fr'].find('something@valid.com') > -1)
        
            
    def test_debatingIssue_withSmartAvoidanceOfNotifications(self):
        """ If A posts an issue, B follows up and shortly there after A 
        follows up too, then if the automatic dispatcher is switched off,
        the notification to A can be ignored since A has already seen the 
        followup.
        """
        tracker = self.folder.tracker
        
        # Important
        tracker.dispatch_on_submit = False
        
        A = u'email@address.com'
        Af = u'From name'
        
        request = self.app.REQUEST
        request.set('title', u'TITLE')
        request.set('fromname', Af)
        request.set('email', A)
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        tracker.SubmitIssue(request)

        
        B = u'else@address.com'
        Bf = u'Someone Else'
        
        issue = tracker.getIssueObjects()[0]
        request.set('comment', u'COMMENT')
        request.set('fromname', Bf)
        request.set('email', B)
        request.set('notify', 1)
        issue.ModifyIssue(request)
        
        # have a look at that notification object
        notification = issue.getCreatedNotifications()[0]
        self.assertFalse(notification.isDispatched())            

        # A returns and posts a followup
        request.set('comment', u'REPLY')
        request.set('fromname', Af)
        request.set('email', A)
        request.set('notify', 1)
        issue.ModifyIssue(request)

        # Since the second followup done by A must mean that A
        # doesn't need to be notified about the first notification
        # since A has already made a newer followup.
        # However, B's notification should still be there.
        
        self.assertEqual(len(issue.getCreatedNotifications()), 1)
        notification = issue.getCreatedNotifications()[0]
        # this should be designated to B
        self.assertEqual(notification.getEmails(), [B])
        
        # Ok, let's do it again.
        # Now, C joins in so that each notification is designated
        # to two people. By adding a followup by A or B, there is 
        # no need to send out the notification to A or B.
        
        C = u'C@email.com'
        Cf = u'Mr. C'
        
        request.set('comment', u'COMMENT BY C')
        request.set('fromname', Cf)
        request.set('email', C)
        request.set('notify', 1)
        issue.ModifyIssue(request)
        
        # there should now be one new notification designated
        # to A AND B.
        
        self.assertEqual(len(issue.getCreatedNotifications()), 2)
        # let's look at the latest notification
        notification = issue.getCreatedNotifications(sort=True)[1]
        self.assertEqual(notification.getEmails(), [A, B])
        
        request.set('comment', u'COMMENT BY B again')
        request.set('fromname', Bf)
        request.set('email', B)
        request.set('notify', 1)
        issue.ModifyIssue(request)        
        
        self.assertEqual(len(issue.getCreatedNotifications()), 2)
        # let's look at the latest notification
        notification = issue.getCreatedNotifications(sort=True)[1]
        self.assertEqual(notification.getEmails(), [A, C])
        
        
        
    def test_debatingIssue_withSmartAvoidanceOfNotifications_part2(self):
        """ same test_debatingIssue_withSmartAvoidanceOfNotifications() but this time
        test what happens if an issue is created with always notify on or
        an issue is assigned to someone. """
        
        
        tracker = self.folder.tracker
        
        # Important
        tracker.dispatch_on_submit = False
        
        # add an issue user folder
        # Since manage_addIssueUserFolder() needs to add the two extra roles
        # we have to do that first because manage_addIssueUserFolder() isn't
        # allowed to it because it's not a POST request.
        tracker._addRole(IssueTrackerUserRole)
        tracker._addRole(IssueTrackerManagerRole)
        tracker.manage_addProduct['IssueTrackerProduct']\
          .manage_addIssueUserFolder(keep_usernames=True)
        tracker.acl_users.userFolderAddUser("user", "secret", [IssueTrackerUserRole], [],
                                            email="user@test.com",
                                            fullname="User Name")

        # make the always notify of issuetracker be 'user' and A
        A = 'a@a.com'
        Af = 'Aaa'
        
        checked = []
        for each in (A, 'user'):
            valid, better_spelling = tracker._checkAlwaysNotify(each)
            if valid:
                checked.append(better_spelling)
        tracker.always_notify = checked

        # If someone else now adds an issue, a notification should
        # be made going out to user@test.com adn a@a.com
        
        B = u'email@address.com'
        Bf = u'From name'
        
        request = self.app.REQUEST
        request.set('title', u'TITLE')
        request.set('fromname', Bf)
        request.set('email', B)
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        tracker.SubmitIssue(request)
        
        issue = tracker.getIssueObjects()[0]
        
        # have a look at that notification object
        self.assertEqual(len(issue.getCreatedNotifications()), 1)
        notification = issue.getCreatedNotifications()[0]
        self.assertFalse(notification.isDispatched())
        self.assertEqual(notification.getEmails(), ['a@a.com','user@test.com'])
        
        # now, if a@a.com follows up on B's new issue, there'll be
        # you can cross off a@a.com from the notification
        # object.
        request.set('comment', u'COMMENT')
        request.set('fromname', Af)
        request.set('email', A)
        request.set('notify', 1)
        issue.ModifyIssue(request)
        
        # there should now be a new notification object where
        # the latest one goes out to the submitter of the issue
        self.assertEqual(len(issue.getCreatedNotifications()), 2)
        latest_notification = issue.getCreatedNotifications(sort=True)[-1]
        self.assertEqual(latest_notification.getEmails(), [B])
        
        
        
    def test_debatingIssue_withSmartAvoidanceOfNotifications_part3(self):
        """ same as test_debatingIssue_withSmartAvoidanceOfNotifications() 
        but create an assignment and then later as that assignee, 
        participate in the issue and that should cancel the notification
        going out to the assignee. 
        """
        
        
        tracker = self.folder.tracker
        
        # Important
        tracker.dispatch_on_submit = False
        
        # add an issue user folder
        # Since manage_addIssueUserFolder() needs to add the two extra roles
        # we have to do that first because manage_addIssueUserFolder() isn't
        # allowed to it because it's not a POST request.
        tracker._addRole(IssueTrackerUserRole)
        tracker._addRole(IssueTrackerManagerRole)
        tracker.manage_addProduct['IssueTrackerProduct']\
          .manage_addIssueUserFolder(keep_usernames=True)
        tracker.acl_users.userFolderAddUser("user", "secret", [IssueTrackerUserRole], [],
                                            email="user@test.com",
                                            fullname="User Name")

        # switch on issue assignment
        tracker.manage_UseIssueAssignmentToggle()
        self.assertEqual(len(tracker.getAllIssueUsers()), 1)
        assignee_option = tracker.getAllIssueUsers()[0]
        self.assertEqual(assignee_option['user'].getUserName(), 'user')
        
        # If someone else now adds an issue, a notification should
        # be made going out to user@test.com adn a@a.com
        
        B = u'email@address.com'
        Bf = u'From name'
        
        request = self.app.REQUEST
        request.set('title', u'TITLE')
        request.set('fromname', Bf)
        request.set('email', B)
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('assignee', tracker.getAllIssueUsers()[0]['identifier'])
        request.form['notify-assignee'] = '1'
        tracker.SubmitIssue(request)
        
        issue = tracker.getIssueObjects()[0]
        
        # have a look at that notification object
        self.assertEqual(len(issue.getCreatedNotifications()), 1)
        notification = issue.getCreatedNotifications()[0]
        self.assertFalse(notification.isDispatched())
        self.assertEqual(notification.getEmails(), ['user@test.com'])
        self.assertTrue(notification.getAssignmentObject() is not None)
        assignment = notification.getAssignmentObject()
        self.assertEqual(assignment.getEmail(), B) # who added it
        self.assertEqual(assignment.getAssigneeEmail(), "user@test.com") # assigned to

        # log in as this assignee
        uf = tracker.acl_users
        assert uf.meta_type == ISSUEUSERFOLDER_METATYPE
        user = uf.getUserById('user')
        user = user.__of__(uf)
        newSecurityManager(None, user)

        assert getSecurityManager().getUser().getUserName() == 'user'

        # now reply a comment as this logged in user which should
        # evetually nullify the notification going to this assignee
        
        request.set('comment', u'COMMENT')
        #request.set('fromname', Af)
        #request.set('email', A)
        request.set('notify', 1)
        issue.ModifyIssue(request)
        
        # there should now be a new notification object where
        # the latest one goes out to the submitter of the issue
        self.assertEqual(len(issue.getCreatedNotifications()), 1)
        latest_notification = issue.getCreatedNotifications()[0]
        self.assertEqual(latest_notification.getEmails(), [B])
        
        
        
    def test_Real0695_bug(self): # in lack of a better name 
        """ test that RSS and RDF feeds have the same security protection
        like viewing the issuetracker, the list of issues or an issue. """
        tracker = self.folder.tracker
        request = self.app.REQUEST

        # Adding an issue
        add_issue_html = tracker.AddIssue(request)
        self.assertTrue(add_issue_html.find('Description:') > -1)
        
        # add an issue so there's something in the ListIssues, and the XML feeds
        A = u'email@address.com'
        Af = u'From name'
        request.set('title', u'TITLE')
        request.set('fromname', Af)
        request.set('email', A)
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        tracker.SubmitIssue(request)
        

        # first of all, viewing these with the current user should be fine.
        #template_list_html = tracker.ListIssues(request)
        template_list_html = tracker.restrictedTraverse('ListIssues')(request)
        # expect it to say "# Issues: 0" since there are no issues added
        self.assertTrue(template_list_html.find('# Issues: 1') > -1)

        # rss.xml
        rss_xml = getattr(tracker, 'rss.xml')()
        self.assertTrue(rss_xml.find('<title><![CDATA[TITLE #001 (Open)]]></title>') > -1)
        
        # rdf.xml
        rdf_xml = getattr(tracker, 'rdf.xml')()
        self.assertTrue(rdf_xml.find('<title>TITLE (Open)</title>') > -1)
        
        # Now, let's disallow anonymous access
        msg = tracker.manage_ViewPermissionToggle()
        self.assertEqual(msg, 'View permission disabled for Anonymous')
        
        # before we log out, let's create a user with the IssueTracker 
        # IssueTrackerManagerRole
        self.folder.acl_users.userFolderAddUser("manager", "secret", [IssueTrackerManagerRole], [])
        self.folder.acl_users.userFolderAddUser("user", "secret", [IssueTrackerUserRole], [])

        # Now, if I log out, none of the viewings above should work
        self.logout()
        assert getSecurityManager().getUser().getUserName() == 'Anonymous User'
        
        self.assertRaises(Unauthorized, tracker.restrictedTraverse, 'ListIssues')
        self.assertRaises(Unauthorized, tracker.restrictedTraverse, 'rss.xml')
        self.assertRaises(Unauthorized, tracker.restrictedTraverse, 'rdf.xml')
        

        
    def test_preSubmitIssue_hook(self):
        """ test adding an issue with a script hook called 'pre_SubmitIssue()' """
        tracker = self.folder.tracker
        tracker.dispatch_on_submit = False # no annoying emails on stdout
        
        adder = tracker.manage_addProduct['PythonScripts'].manage_addPythonScript
        adder('pre_SubmitIssue')
        script = getattr(tracker, 'pre_SubmitIssue')
        script.write(pre_submitissue_script_src)
        
        # With this hook it won't be possible to add a issue where
        # the subject line starts with the letter a. (silly, yes)
        
        request = self.app.REQUEST
        request.set('title', u'A TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        
        tracker.SubmitIssue(request)
        
        self.assertEqual(len(tracker.getIssueObjects()), 0)

        request.set('title', u'Some TITLE')

        tracker.SubmitIssue(request)
        
        self.assertEqual(len(tracker.getIssueObjects()), 1)

        
        
    def test_postSubmitIssue_hook(self):
        """ test adding an issue with a script hook called 'post_SubmitIssue()' """
        tracker = self.folder.tracker
        tracker.dispatch_on_submit = False # no annoying emails on stdout
        tracker.can_add_new_sections = True
        
        # add an issue user folder
        # Since manage_addIssueUserFolder() needs to add the two extra roles
        # we have to do that first because manage_addIssueUserFolder() isn't
        # allowed to it because it's not a POST request.
        tracker._addRole(IssueTrackerUserRole)
        tracker._addRole(IssueTrackerManagerRole)
        tracker.manage_addProduct['IssueTrackerProduct']\
          .manage_addIssueUserFolder(keep_usernames=True)
        tracker.acl_users.userFolderAddUser("user", "secret", [IssueTrackerManagerRole,'Manager'], [],
                                            email="user@test.com",
                                            fullname="User Name")
        
        
        adder = tracker.manage_addProduct['PythonScripts'].manage_addPythonScript
        adder('post_SubmitIssue')
        script = getattr(tracker, 'post_SubmitIssue')
        script.write(post_submitissue_script_src)
        
        # With this hook it won't be possible to add a issue where
        # the subject line starts with the letter a. (silly, yes)
        
        request = self.app.REQUEST
        request.set('title', u'A TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('newsection', 'Security')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())

        uf = tracker.acl_users
        assert uf.meta_type == ISSUEUSERFOLDER_METATYPE
        user = uf.getUserById('user')
        user = user.__of__(uf)
        newSecurityManager(None, user)

        assert getSecurityManager().getUser().getUserName() == 'user'
        


    def test_getModifyTimestamp(self):
        """ test issuetracker.getModifyTimestamp() """
        tracker = self.folder.tracker
        
        # with no issues, the getModifyTimestamp() should be the 
        # same as the issuetrackers' bobobase_modification_time()
        self.assertEqual(int(tracker.bobobase_modification_time()), 
                         tracker.getModifyTimestamp())
                         
        # if we add an issue, the issuetrackers' getModifyTimestamp()
        # should be that of the last added issue.
        request = self.app.REQUEST
        request.set('title', u'TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        
        tracker.SubmitIssue(request)
        
        issue = tracker.getIssueObjects()[0]
        self.assertEqual(issue.getModifyTimestamp(), tracker.getModifyTimestamp())

    def test_okFileAttachment(self):
        """ try to add an issue with a crap file attachment """
        tracker = self.folder.tracker
        request = self.app.REQUEST
        request.set('title', u'TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('fileattachment', NewFileUpload(os.path.abspath(__file__)))
        
        tracker.SubmitIssue(request)
        issue = tracker.getIssueObjects()[0]
        self.assertEqual(issue.countFileattachments(), 1)
        
    def test_crapFileAttachment(self):
        """ try to add an issue with a crap file attachment """
        
        tracker = self.folder.tracker
        request = self.app.REQUEST
        request.set('title', u'TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('fileattachment', DodgyNewFileUpload(os.path.abspath(__file__)))
        
        # this should fail to add an issue
        tracker.SubmitIssue(request)
        self.assertEqual(len(tracker.getIssueObjects()), 0)
        
        # this time, try to upload it with a file that is empty
        empty_file = os.path.join(os.path.dirname(__file__), 'size0_file.jpg')
        request.set('fileattachment', NewFileUpload(os.path.abspath(empty_file)))
        
        tracker.SubmitIssue(request)
        self.assertEqual(len(tracker.getIssueObjects()), 0)
        
        
    def test_saveIssueDraft(self):
        """ try to add an issue with a crap file attachment """
        tracker = self.folder.tracker
        request = self.app.REQUEST
        title = u'TITLE'; request.set('title', title)
        fromname = u'From name'; request.set('fromname', fromname)
        email = u'email@address.com'; request.set('email', email)
        description = u'DESCRIPTION'; request.set('description', description)
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('fileattachment', NewFileUpload(os.path.abspath(__file__)))
        
        tracker.SaveDraftIssue(request)

        # Because getMyIssueDrafts() depends on cookies and cookies don't
        # work in ZopeTestCase :( we have to fake this a bit
        drafts = tracker.getDraftsContainer().objectValues(ISSUE_DRAFT_METATYPE)
        assert len(drafts) == 1

        # Now check that draft
        draft = drafts[0]
        self.assertEqual(draft.getTitle(), title)
        self.assertEqual(draft.getDescription(), description)
        self.assertEqual(draft.getFromname(), fromname)
        self.assertEqual(draft.getEmail(), email)
        self.assertEqual(draft.getType(), tracker.getDefaultType())
        self.assertEqual(draft.getUrgency(), tracker.getDefaultUrgency())
        
        # from this it will be possible to get the files back via the
        # tempfolder
        assert request.get(TEMPFOLDER_REQUEST_KEY), "no tempfolder set in request"
        tempfolder = tracker._getTempFolder()
        files = tempfolder[request.get(TEMPFOLDER_REQUEST_KEY)].objectValues('File')
        assert files, "no temp files"
        temp_file = files[0]
        self.assertEqual(temp_file.getId(), os.path.basename(__file__))

    def test_searchIssues(self):
        """ basic search tests """
        tracker = self.folder.tracker
        request = self.app.REQUEST
        title = u'titles are working'; request.set('title', title)
        fromname = u'From name'; request.set('fromname', fromname)
        email = u'email@address.com'; request.set('email', email)
        description = u'DESCRIPTION is a in the this test'
        request.set('description', description)
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('fileattachment', NewFileUpload(os.path.abspath(__file__)))
        
        tracker.SubmitIssue(request)
        assert tracker.getIssueObjects()
        
        issue = tracker.getIssueObjects()[0]

        # search and find it
        q = 'working'
        self.assertEqual(issue, tracker._searchCatalog(q, search_only_on=None)[0])
        self.assertEqual(issue, tracker._searchCatalog(q, search_only_on='title')[0])
        
        # don't expect to find it
        q = 'notmentioned'
        self.assertEqual(tracker._searchCatalog(q, search_only_on='description'), [])
        self.assertEqual(tracker._searchCatalog(q), [])
        
        # search fuzzy and find it
        q = 'title'
        self.assertEqual(issue, tracker._searchCatalog(q)[0])
        # it should be case insensitive
        q = 'DeScriPtion'
        self.assertEqual(issue, tracker._searchCatalog(q)[0])
        
        # low level test of searching by filename
        self.assertEqual(issue, tracker._searchByFilename(os.path.basename(__file__))[0])
        # and do it fuzzy
        self.assertEqual(issue, tracker._searchByFilename(os.path.basename(__file__).upper())[0])
        # or without the extension
        name = os.path.splitext(os.path.basename(__file__))[0]
        self.assertEqual(issue, tracker._searchByFilename(name)[0])
        
        # Post a followup and expect to be able to search and find it
        request.set('comment', u'COMMENT')
        this_file = os.path.abspath(__file__)
        a_file = os.listdir(os.path.dirname(this_file))[-1]
        a_file = os.path.join(os.path.dirname(this_file), a_file)
        request.set('fileattachment', NewFileUpload(a_file))
        issue.ModifyIssue(request)
        
        thread = issue.getThreadObjects()[0]
        
        # search for the comment
        q = 'COMmenT'
        # and expect to find it's parent object which is the issue
        self.assertEqual(issue, tracker._searchCatalog(q)[0])
        # when finding things by threads it puts this into REQUEST
        self.assertEqual(request.get('FirstThreadResultId'), issue.getId())
        
        # search for a threads file
        q = os.path.basename(a_file)
        # and expect to find it's parent object which is the issue
        self.assertEqual(issue, tracker._searchCatalog(q)[0])
        # and a variation of that
        q = os.path.splitext(q)[0]
        self.assertEqual(issue, tracker._searchCatalog(q)[0])
        
        # If you run the UpdateEverything() we can expect the same 
        # content in the catalog
        tracker.manage_delObjects(['ICatalog'])
        tracker.UpdateEverything()
        
        # search for the comment
        q = 'COMmenT'
        # and expect to find it's parent object which is the issue
        self.assertEqual(issue, tracker._searchCatalog(q)[0])
        # when finding things by threads it puts this into REQUEST
        self.assertEqual(request.get('FirstThreadResultId'), issue.getId())
        # search for a threads file
        q = os.path.basename(a_file)
        # and expect to find it's parent object which is the issue
        self.assertEqual(issue, tracker._searchCatalog(q)[0])
        # and a variation of that
        q = os.path.splitext(q)[0]
        self.assertEqual(issue, tracker._searchCatalog(q)[0])
        
        
    def test_filterIssues(self):
        """ test to call filter issues and note how the filter should be saved and 
        should be reusable later. """
        
        tracker = self.folder.tracker
        request = self.app.REQUEST
        
        # first, the user who performs this test is a normal zope acl 
        # user. Let's add a name and email so that we can make sure 
        # this is information is saved in the saved filter
        self.set_cookie(tracker.getCookiekey('name'), u'Bob')
        self.set_cookie(tracker.getCookiekey('email'), u'bob@test.com')
            
        # initially there shouldn't be a folder called 'saved-filters'
        self.assertFalse(hasattr(tracker, 'saved-filters'))
        # and there shouldn't be a zcatalog called 'saved-filters-catalog'
        self.assertFalse(hasattr(tracker, 'saved-filters-catalog'))
        
        # On the homepage, the links to see only say issues "On hold" is
        # /ListIssues?Filterlogic=show&f-statuses=on%20hold
        # Let's mimick that:
        request.set('Filterlogic','show')
        request.set('f-statuses','on hold')
        tracker.ListIssuesFiltered()
        
        # this should now have create the saved-filters folder 
        # and the saved-filters-catalog
        self.assertTrue(hasattr(tracker, 'saved-filters'))
        self.assertTrue(hasattr(tracker, 'saved-filters-catalog'))
        
        # let's look at what was created in the saved-filters folder
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 1)
        
        # since I'm here logged into Zope as a normal Zope user
        # we can expect to find that the saved filter should be to me
        zopeuser = tracker.getZopeUser()
        path = '/'.join(zopeuser.getPhysicalPath())
        name = zopeuser.getUserName()
        acl_adder = ','.join([path, name])
        saved_filter = saved_filters[0]
        
        self.assertEqual(saved_filter.acl_adder, acl_adder)
        self.assertEqual(saved_filter.getTitle(), u"Only on hold issues")
        # this is who created the filter (quite unimportant)
        self.assertEqual(saved_filter.adder_fromname, u'Bob')
        self.assertEqual(saved_filter.adder_email, 'bob@test.com')
        # and we didn't need to associate with a cookie key
        self.assertEqual(saved_filter.key, '')
        
        # the logic was to show
        self.assertEqual(saved_filter.filterlogic, 'show')
        
        # some attributes are automatically set for the issue metadata
        self.assertEqual(saved_filter.sections, None)
        self.assertEqual(saved_filter.urgencies, None)
        self.assertEqual(saved_filter.types, None)
        self.assertEqual(saved_filter.statuses, [u'on hold'])
        
        # We should have a saved-filters-catalog created
        catalog = tracker.getFilterValuerCatalog()
        self.assertTrue(catalog is not None)
        # and there should only be one brain it right now
        self.assertTrue(len(catalog.searchResults()) == 1)
        
        saved_filter_from_brain = catalog.searchResults()[0].getObject()
        self.assertTrue(saved_filter_from_brain == saved_filter)
        
        # the high level function getMySavedFilters() uses the catalog
        # to extract the saved filters with the most recent one
        # first.
        saved_filter_from_mysavedfilters = tracker.getMySavedFilters()[0]
        self.assertTrue(saved_filter_from_mysavedfilters == saved_filter)
        

        # If you run ListIssuesFiltered() again, it should have to create 
        # one more new saved filter
        tracker.ListIssuesFiltered()
        
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 1)
        
        # But if we change the parameters a little bit it should have 
        # created a new saved filter
        request.set('f-statuses','taken')
        tracker.ListIssuesFiltered()
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 2)
        
        # getMySavedFilters() is smart in that it returns the filters ordered.
        # Test that the more recent one comes first
        saved_filters_from_mysavedfilters = tracker.getMySavedFilters()
        self.assertEqual(saved_filters_from_mysavedfilters[0].statuses, [u'taken'])
        
        # Test the function getCurrentlyUsedSavedFilter(request_only=True)
        assert tracker.getCurrentlyUsedSavedFilter() is None
        
        saved_filter_id = tracker.getCurrentlyUsedSavedFilter(request_only=False)
        # now check that this is the correct one
        self.assertEqual(saved_filter_id, saved_filters_from_mysavedfilters[0].getId())
        # another (more long winded) way of checking this is by that since the 
        # last filter was to filter by "taken". Check that this is what the 
        # filter does that comes from getCurrentlyUsedSavedFilter(request_only=False)
        current_saved_filter = getattr(getattr(tracker, 'saved-filters'), saved_filter_id)
        self.assertEqual(current_saved_filter.statuses, [u'taken'])
        
        
        # Some options that can be passed directly to _ListIssuesFiltered
        # are: 
        #   skip_filter
        #   skip_sort
        #
        # To set these for ListIssuesFiltered() you have to put them in the
        # REQUEST. These are useful if you for example want to ignore possibly
        # filters in session such as for the homepage where it uses 
        # ListIssuesFiltered() but without any filtering.
        # The skip_sort is useful to set when you don't want any sorting since
        # sorting will only cost time.
        # XXX: Only able to test this WITH issues

        
    def test_filterIssues_anonymous_user(self):
        """ test filtering issues when the user is not logged in """
        # when *not* logged in there are two ways to remember a saved filter:
        #   by name and email
        #   by a cookie key
        # Let's first try to filter issues as a complete nobody
        
        noSecurityManager()
        tracker = self.folder.tracker
        request = self.app.REQUEST
        
        tracker.set_cookie = self.set_cookie
        
        # On the homepage, the links to see only say issues "On hold" is
        # /ListIssues?Filterlogic=show&f-statuses=on%20hold
        # Let's mimick that:
        request.set('Filterlogic','show')
        request.set('f-statuses','on hold')
        tracker.ListIssuesFiltered()
        
        # let's look at what was created in the saved-filters folder
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 1)
        saved_filter = saved_filters[0]
        self.assertTrue(saved_filter.getKey() in request.cookies.values())
        
        # run it again and it shouldn't create another saved filter
        tracker.ListIssuesFiltered()
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 1)
        
    def test_filterIssues_anonymous_named_user(self):
        """ test filtering when the user is no logged in but has a name
        and email in the cookie. """
        
        noSecurityManager()
        tracker = self.folder.tracker
        request = self.app.REQUEST
        
        tracker.set_cookie = self.set_cookie
        
        self.set_cookie(tracker.getCookiekey('name'), u'Bob')
        self.set_cookie(tracker.getCookiekey('email'), u'bob@test.com')
        
        # On the homepage, the links to see only say issues "On hold" is
        # /ListIssues?Filterlogic=show&f-statuses=on%20hold
        # Let's mimick that:
        request.set('Filterlogic','show')
        request.set('f-statuses','on hold')
        tracker.ListIssuesFiltered()
        
        # let's look at what was created in the saved-filters folder
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 1)
        saved_filter = saved_filters[0]
        self.assertEqual(saved_filter.adder_email, 'bob@test.com')
        self.assertEqual(saved_filter.adder_fromname, u'Bob')
        
        # run it again and it shouldn't create another saved filter
        tracker.ListIssuesFiltered()
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 1)
        
        
    def test_filterIssues_anonymous_named_user_no_email(self):
        """ test filtering when the user is no logged in but has a name
        and email in the cookie. """
        
        noSecurityManager()
        tracker = self.folder.tracker
        request = self.app.REQUEST
        
        tracker.set_cookie = self.set_cookie
        
        self.set_cookie(tracker.getCookiekey('name'), u'Bob')
        
        # On the homepage, the links to see only say issues "On hold" is
        # /ListIssues?Filterlogic=show&f-statuses=on%20hold
        # Let's mimick that:
        request.set('Filterlogic','show')
        request.set('f-statuses','on hold')
        tracker.ListIssuesFiltered()
        
        # let's look at what was created in the saved-filters folder
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 1)
        saved_filter = saved_filters[0]
        self.assertEqual(saved_filter.adder_fromname, u'Bob')
        
        # run it again and it shouldn't create another saved filter
        tracker.ListIssuesFiltered()
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 1)
        
        
    def test_filterIssues_anonymous_bot(self):
        """ When the http user agent is a bot (Googlebot, msnbot etc.) don't
        save the filter persistently.
        """
        
        noSecurityManager()
        tracker = self.folder.tracker
        request = self.app.REQUEST
        
        
        request.set('HTTP_USER_AGENT',
        "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
        
        # On the homepage, the links to see only say issues "On hold" is
        # /ListIssues?Filterlogic=show&f-statuses=on%20hold
        # Let's mimick that:
        request.set('Filterlogic','show')
        request.set('f-statuses','on hold')
        tracker.ListIssuesFiltered()
        
        # there shouldn't even be a folder called 'saved-filters'
        self.assertEqual(getattr(tracker, 'saved-filters', None), None)
        
    

    def test_filterIssues_recycleable(self):
        """ test to call filter issues and note how the filter should be saved and 
        should be reusable later. 
        When you *go back* to run a filter you've already done before it should be
        able to reuse an existing object instead of having to create a new one."""

        tracker = self.folder.tracker
        request = self.app.REQUEST
        
        # 1
        request.set('Filterlogic','show')
        request.set('f-statuses','on hold')
        tracker.ListIssuesFiltered()
        
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 1)

        # 2
        request.set('f-statuses','taken')
        tracker.ListIssuesFiltered()
        
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 2)
        
        # 3
        request.set('f-statuses','on hold')
        tracker.ListIssuesFiltered()
        
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 2)
        
    def test_filterIssues_from_cookie_after_purge(self):
        """ If all the saved-filters are deleted and someone has a cookie
        of an old savedfilter, if the saved filter is deleted, getting it
        from the cookie shouldn't raise an AttributeError.
        """
        
        tracker = self.folder.tracker
        request = self.app.REQUEST
        
        tracker.set_cookie = self.set_cookie
        
        ckey = tracker.getCookiekey('remember_savedfilter_persistently')
        tracker.set_cookie(ckey, 1)
        
        # 1
        request.set('Filterlogic','show')
        request.set('f-statuses','on hold')
        tracker.ListIssuesFiltered()
        
        self.assertTrue('__issuetracker_savedfilter_id-tracker' in request.cookies)
        
        saved_filters = getattr(tracker, 'saved-filters').objectValues()
        self.assertEqual(len(saved_filters), 1)
        
        # now, mess with it
        tracker.manage_delObjects(['saved-filters','saved-filters-catalog'])

        # if the cookie causes an AttributeError, then ListIssuesFiltered()
        # here wouldn't work. Just running this is the final test.
        tracker.ListIssuesFiltered()

        
    def test_clean_saved_filters(self):
        """ CleanOldSavedFilters() is called by manage_UpdateEverything() but
        also if the number of saved filters exceeds FILTERVALUEFOLDER_THRESHOLD_CLEANING.
        We'll first try the manual way of cleaning.
        """
        tracker = self.folder.tracker
        
        container = tracker._getFilterValueContainer()
        for i in range(100):
            oid = 'random-%s' % i
            instance = FilterValuer(oid, 'filter name')
            container._setObject(oid, instance)
            valuer = container._getOb(oid)
            
            if randint(1, 5) == 1:
                valuer.set('acl_adder', 'fake acl adder')
            elif randint(1, 4) == 1:
                valuer.set('key', str(randint(1, 10)))
            else:
                valuer.set('adder_email', 'email')
                
            valuer.mod_date = DateTime(time() - randint(1000, 1000000)*10)
            valuer.index_object()
            
        
        max_ = FILTERVALUEFOLDER_THRESHOLD_CLEANING
        count_filtervaluers = len(container.objectIds())
        assert count_filtervaluers < max_
        
        # there should also be equally many indexed objects as there
        # are reported by len(objectids)
        catalog = tracker.getFilterValuerCatalog()
        search = {'meta_type': FILTEROPTION_METATYPE}
        brains = catalog.searchResults(**search)
        assert len(brains) == count_filtervaluers
        
        # there are now 100 old saved filters whose
        # bobobase_modification_time ranges between 4 to 0 months old
        msg = tracker.CleanOldSavedFilters()
        msg_regex = re.compile('Deleted (\d+) old saved filters')
        no_deleted = int(msg_regex.findall(msg)[0])
        
        self.assertTrue(no_deleted < count_filtervaluers)
        left = count_filtervaluers - no_deleted
        
        brains = catalog.searchResults(**search)
        assert len(brains) == left, \
        "no. brains=%s, left=%s" %(len(brains), left)
        

        
    def test_clean_saved_filters_then_implode(self):
        """ Similar to test_clean_saved_filters() except this time we're
        making sure ALL saved filters are so old that if we send
        implode_if_possible=True to CleanOldSavedFilters() and expect the 
        container to disappear.
        """
        tracker = self.folder.tracker
        container = tracker._getFilterValueContainer()
        for i in range(100):
            oid = 'random-%s' % i
            instance = FilterValuer(oid, 'filter name')
            container._setObject(oid, instance)
            valuer = container._getOb(oid)
            
            if randint(1, 5) == 1:
                valuer.set('acl_adder', 'fake acl adder')
            elif randint(1, 4) == 1:
                valuer.set('key', str(randint(1, 10)))
            else:
                valuer.set('adder_email', 'email')
                
            valuer.mod_date = DateTime(time() - randint(1000, 10000)*3600)
            valuer.index_object()
            
        self.assertTrue('saved-filters' in tracker.objectIds())
        msg = tracker.CleanOldSavedFilters(implode_if_possible=True)
        self.assertTrue('saved-filters' not in tracker.objectIds())
        
        # and the catalog should be empty
        catalog = tracker.getFilterValuerCatalog()
        search = {'meta_type': FILTEROPTION_METATYPE}
        brains = catalog.searchResults(**search)
        self.assertTrue(len(brains) == 0)
        
    def test_unicode_in_statuses(self):
        """ test that it's possible to set a verb:action pair to the 
        statuses that is unicode. """
        
        tracker = self.folder.tracker
        request = self.app.REQUEST
        
        for status, verb in tracker.getStatusesMerged(aslist=True):
            self.assertTrue(isinstance(status, unicode))
            self.assertTrue(isinstance(verb, unicode))
            
        # All the default ones are easy, lets spice it up a bit
        statuses_and_verbs = [u'open, open', 
                              u'taken, take', 
                              u'on hold, put on hold', 
                              u'a pr\xe9c\xe9dente, faire pr\xe9c\xe9dente', 
                              u'rejected, reject', 
                              u'completed, complete']
        request.set('statuses-and-verbs', statuses_and_verbs)
        
        tracker.manage_editIssueTrackerProperties(carefulbooleans=True, 
                                                  REQUEST=request)
        
        for status, verb in tracker.getStatusesMerged(aslist=True):
            self.assertTrue(isinstance(status, unicode))
            self.assertTrue(isinstance(verb, unicode))

            
    def test_changeIssueDetails(self):
        """ when you change issuedetails the changes are record as
        an attribute in the issue. """
        
        tracker = self.folder.tracker
        tracker.dispatch_on_submit = False # no annoying emails on stdout
        tracker.can_add_new_sections = True

        request = self.app.REQUEST
        request.set('title', u'A TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        
        tracker.SubmitIssue(request)
        
        issue = tracker.getIssueObjects()[0]
        changes = issue.getDetailChanges()
        self.assertEqual(len(changes), 0)
        
        # pretend to change sections but actually not
        issue.editIssueDetails(sections=issue.getSections(),
                               type=issue.getType(),
                               urgency=issue.getUrgency())
        changes = issue.getDetailChanges()
        self.assertEqual(len(changes), 0)
        
        # now actually change something (sections)
        sections_before = issue.getSections()
        issue.editIssueDetails(sections=['Foo'])
        changes = issue.getDetailChanges()
        self.assertEqual(len(changes), 1)
        change = changes[-1]
        self.assertEqual(change['sections']['old'], sections_before)
        self.assertEqual(change['sections']['new'], ['Foo'])
        self.assertTrue('change_date' in change)
        self.assertTrue(hasattr(change['change_date'], 'strftime'))

        # now actually change something (type)
        type_before = issue.getType()
        issue.editIssueDetails(type='feature request')
        changes = issue.getDetailChanges()
        self.assertEqual(len(changes), 1) # merged with the previous one
        change = changes[-1]
        self.assertEqual(change['type']['old'], type_before)
        self.assertEqual(change['type']['new'], 'feature request')
        
        
        # change several things
        urgency_before = issue.getUrgency()
        confidential_before = issue.isConfidential()
        url2issue_before = issue.getURL2Issue()
        tracker.use_estimated_time = True
        tracker.use_actual_time = True
        estimated_time_hours_before = issue.getEstimatedTimeHours()
        actual_time_hours_before = issue.getActualTimeHours()
        
        issue.editIssueDetails(urgency='low', 
                               confidential=not confidential_before,
                               url2issue='http://www.issuetrackerproduct.com',
                               estimated_time_hours=1,
                               actual_time_hours=2
                               )
        changes = issue.getDetailChanges()
        self.assertEqual(len(changes), 1) # merged with the previous two
        
        change = changes[-1]
        self.assertEqual(change['acl_adder'], '/test_folder_1_/acl_users,test_user_1_')\
        
        
    def test_changeIssueDetails_twice_merged(self):
        """ if you change details of an issue twice within the same minute, 
        merge the changes into one change to prevent the interface from looking
        like this:
            Today 12:16 by Peter
               Size: 5 6
            Today 12:16 by Peter
               Age: 28 29
        """
        tracker = self.folder.tracker
        tracker.dispatch_on_submit = False # no annoying emails on stdout
        tracker.can_add_new_sections = True

        request = self.app.REQUEST
        request.set('title', u'A TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        
        tracker.SubmitIssue(request)
        
        issue = tracker.getIssueObjects()[0]
       
        # now actually change something (sections)
        sections_before = issue.getSections()
        issue.editIssueDetails(sections=['Foo'])
        changes = issue.getDetailChanges()
        self.assertEqual(len(changes), 1)
        change = changes[-1]
        self.assertEqual(change['sections']['old'], sections_before)
        self.assertEqual(change['sections']['new'], ['Foo'])
        self.assertTrue('change_date' in change)
        self.assertTrue(hasattr(change['change_date'], 'strftime'))
        
        # change the urgency too as a separate request
        issue.editIssueDetails(urgency='high')
        changes = issue.getDetailChanges()
        self.assertEqual(len(changes), 1)
        
    def test_changeIssueDetails_twice_void(self):
        """ if you change details of an issue twice within the same minute, 
        merge the changes into one change to prevent the interface from looking
        like this:
            Today 12:16 by Peter
               Size: 5 6
            Today 12:16 by Peter
               Age: 6 5
               
        But if the change, within one minute, goes back to the same value 
        as before, drop the change altogether. 
        """
        tracker = self.folder.tracker
        tracker.dispatch_on_submit = False # no annoying emails on stdout
        tracker.can_add_new_sections = True

        request = self.app.REQUEST
        request.set('title', u'A TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        
        tracker.SubmitIssue(request)
        
        issue = tracker.getIssueObjects()[0]
       
        # now actually change something (sections)
        sections_before = issue.getSections()
        issue.editIssueDetails(sections=['Foo'])
        changes = issue.getDetailChanges()
        self.assertEqual(len(changes), 1)
        change = changes[-1]
        self.assertEqual(change['sections']['old'], sections_before)
        self.assertEqual(change['sections']['new'], ['Foo'])
        self.assertTrue('change_date' in change)
        self.assertTrue(hasattr(change['change_date'], 'strftime'))
        
        # change the urgency too as a separate request
        issue.editIssueDetails(sections=sections_before)
        changes = issue.getDetailChanges()
        self.assertEqual(len(changes), 0)
        
        
    def test_cataloging_issues(self):
        """ adding an issue and threads to that should be indexed in the 
        ICatalog correctly.
        If you do an UpdateEverything they should still be there.
        """
        tracker = self.folder.tracker
        tracker.dispatch_on_submit = False # no annoying emails on stdout
        tracker.can_add_new_sections = True

        request = self.app.REQUEST
        request.set('title', u'A TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        
        tracker.SubmitIssue(request)
        
        issue = tracker.getIssueObjects()[0]
        
        request.set('comment', u'COMMENT')
        issue.ModifyIssue(request)
        
        self.assertEqual(len(tracker.getIssueObjects()), 1)
        self.assertEqual(len(issue.getThreadObjects()), 1)
        
        # lets check what's in the catalog
        catalog = tracker.getCatalog()
        # seaching inside it for path '/' should find two brains
        brains = catalog.searchResults(path='/')
        self.assertEqual(len(brains), 2)
        meta_types = [x.meta_type for x in brains]
        self.assertTrue('Issue Tracker Issue Thread' in meta_types)
        self.assertTrue('Issue Tracker Issue' in meta_types)
        
        # If we run UpdateEverything, the same content is expected in the 
        # catalog
        tracker.UpdateEverything()
        brains = catalog.searchResults(path='/')
        self.assertEqual(len(brains), 2)
        meta_types = [x.meta_type for x in brains]
        self.assertTrue('Issue Tracker Issue Thread' in meta_types)
        self.assertTrue('Issue Tracker Issue' in meta_types)
        
    def test_assign_on_submit_email(self):
        """ Make sure the issue title is in the subjectline when you assign an issue to
        someone """
        tracker = self.folder.tracker
        # set a valid sitemaster email address
        tracker.sitemaster_email = 'peter@test.com'
        # switch on assignment
        tracker.use_issue_assignment = True
        # add a user to assign stuff to
        tracker._addRole(IssueTrackerUserRole)
        tracker._addRole(IssueTrackerManagerRole)
        tracker.manage_addProduct['IssueTrackerProduct']\
          .manage_addIssueUserFolder(keep_usernames=True)
        tracker.acl_users.userFolderAddUser("user", "secret", [IssueTrackerUserRole], [],
                                            email="user@test.com",
                                            fullname="User Name")
        user = tracker.getAllIssueUsers()[0]
        
        request = self.app.REQUEST
        request.set('title', u'A TITLE')
        request.set('fromname', u'From name')
        request.set('email', u'email@address.com')
        request.set('description', u'DESCRIPTION')
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('assignee', user['identifier'])
        request.form['notify-assignee'] = '1'
        
        no_trapped_emails = len(self.snatched_emails)
        tracker.SubmitIssue(request)
        
        # There should now be one issue...
        issues = tracker.getIssueObjects()
        self.assertEqual(len(issues), 1)
        issue = issues[0]
        # and it should have an assignment
        assignments = issue.getAssignments()
        self.assertEqual(len(assignments), 1)
        assignment = assignments[0]
        
        self.assertEqual(assignment.getFromname(), request.get('fromname'))
        self.assertEqual(assignment.getEmail(), request.get('email'))
        
        self.assertEqual(assignment.getAssigneeFullname(), u"User Name")
        self.assertEqual(assignment.getAssigneeEmail(), "user@test.com")
        
        # Look at the notifications inside the issue
        notifications = issue.getCreatedNotifications()
        self.assertEqual(len(notifications), 1)
        notification = notifications[0]
        
        assert len(self.snatched_emails) == no_trapped_emails + 1
        
        # check that the
        assert notification.dispatched
        
        latest_email = self.snatched_emails[-1]
        
        self.assertTrue(latest_email['subject'].count(request.get('title')))
        
        # then, later change the assignment and that should trigger another 
        # email notifications
        tracker.acl_users.userFolderAddUser("user2", "secret", [IssueTrackerUserRole], [],
                                            email="user2@test.com",
                                            fullname="Second Name")
        users = tracker.getAllIssueUsers()
        user2 = [x for x in users if x['identifier'].endswith('user2')][0]

        uf = tracker.acl_users
        user = uf.getUserById('user')
        user = user.__of__(uf)
        newSecurityManager(None, user)

        assert getSecurityManager().getUser().getUserName() == 'user'
        
        issue.changeAssignment(user2['identifier'], send_email=True)
        
        assert len(self.snatched_emails) == no_trapped_emails + 2
        
        assignments = issue.getAssignments()
        self.assertEqual(len(assignments), 2)
        
        # Look at the notifications inside the issue
        notifications = issue.getCreatedNotifications()
        self.assertEqual(len(notifications), 2)
        notification = notifications[-1]
        assert notification.dispatched
        
        latest_email = self.snatched_emails[-1]
        
        self.assertTrue(latest_email['subject'].count(request.get('title')))
        self.assertTrue(latest_email['to'], 'user2@test.com')
        
        
    def test_showStrftimeFriendly(self):
        """test the base script showStrftimeFriendly()"""
        tracker = self.folder.tracker
        func = tracker.showStrftimeFriendly
        
        tests = [('%d/%m %Y', 'DD/MM YYYY'),
                 ('%d/%m %Y %H:%M', 'DD/MM YYYY hh:mm'),
                 ('%d %B %Y', 'DD month YYYY'),
                 ('%d %B %Y %H:%M', 'DD month YYYY hh:mm'),
                 ]
        
        for input_, output in tests:
            self.assertEqual(func(input_), output)
            
        tests = [('%d/%m %Y', 'DD/MM YYYY'),
                 ('%d/%m %Y %H:%M', 'DD/MM YYYY'),
                 ('%d %B %Y', 'DD month YYYY'),
                 ('%d %B %Y %H:%M', 'DD month YYYY'),
                 ]
        
        for input_, output in tests:
            self.assertEqual(func(input_, strip_hour_part=True),
                             output)            
            
    def test_getDueDateCSSSelector(self):
        """return some suitable CSS selectors based on the due date"""
        tracker = self.folder.tracker
        func = tracker.getDueDateCSSSelector
        
        # feed it a date in the past and it will return 'dd-past'
        past = DateTime('2009/01/01')
        self.assertEqual(func(past), 'dd-past')
        # as a string
        self.assertEqual(func('2009/01/01'), 'dd-past')
        
        today = DateTime(DateTime().strftime('%Y/%m/%d'))
        self.assertEqual(func(today), 'dd-today')
        # as a string
        self.assertEqual(func(DateTime().strftime('%Y/%m/%d')), 'dd-today')

        tomorrow = DateTime(DateTime().strftime('%Y/%m/%d')) + 1
        self.assertEqual(func(tomorrow), 'dd-tomorrow')
        # as a string
        self.assertEqual(func((DateTime()+1).strftime('%Y/%m/%d')), 'dd-tomorrow')
        
        future = DateTime(DateTime().strftime('%Y/%m/%d')) + 100
        self.assertEqual(func(future), 'dd-future')
        # as a string
        self.assertEqual(func((DateTime()+100).strftime('%Y/%m/%d')), 'dd-future')
        
        # junk
        self.assertEqual(func('xajsd'), '')
        self.assertEqual(func(1230), '')
        
        
    def test_submitting_with_due_date(self):
        """submit an issue with a due date"""
        
        title = u"Title"
        description = u"Description"
        
        tracker = self.folder.tracker
        request = self.app.REQUEST
        request.set('title', title)
        request.set('fromname', u'B\xc3\xa9b')
        request.set('email', u'email@address.com')
        request.set('description', description)
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('due_date', '2010/01/01')
        
        tracker.SubmitIssue(request)
        
        # it should be possible to display it
        issue = tracker.getIssueObjects()[0]
        # because due dates aren't enabled yet
        self.assertEqual(issue.getDueDate(), None)
        
        tracker.enable_due_date = True

        title = u"Somethjing"
        description = u"Different"

        request.set('title', title)
        request.set('fromname', u'B\xc3\xa9b')
        request.set('email', u'email@address.com')
        request.set('description', description)
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('due_date', '2010/01/01')
        
        tracker.SubmitIssue(request)
        #issue = [x for x in tracker.getIssueObjects() if x.title==title][0]
        issue = tracker.getIssueObjects()[-1]
        self.assertEqual(issue.getDueDate(), DateTime('2010/01/01'))
        
        # But just because you've enabled it doesn't necessarily mean
        # you have to set it every time
        title = u"Entirely"
        description = u"Different"

        request.set('title', title)
        request.set('fromname', u'B\xc3\xa9b')
        request.set('email', u'email@address.com')
        request.set('description', description)
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('due_date', '')
        
        tracker.SubmitIssue(request)
        issue = tracker.getIssueObjects()[-1]
        self.assertEqual(issue.getDueDate(), None)
        
    def test_editing_due_date(self):
        """test changing date"""
        tracker = self.folder.tracker
        tracker.enable_due_date = True
        
        title = u"Title"
        description = u"Description"
        
        request = self.app.REQUEST
        request.set('title', title)
        request.set('fromname', u'B\xc3\xa9b')
        request.set('email', u'email@address.com')
        request.set('description', description)
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('due_date', '2010/01/01')
        
        tracker.SubmitIssue(request)
        
        # it should be possible to display it
        issue = tracker.getIssueObjects()[0]
        #pprint(list(issue.detail_changes))
        assert not issue.detail_changes
        self.assertEqual(issue.getDueDate(), DateTime('2010/01/01'))
        
        issue.editIssueDetails(due_date='2010/02/02')
        # there should now be one item in the issue.detail_changes
        assert len(issue.detail_changes) == 1
        change = issue.detail_changes[0]
        # it should have a key called 'due_date'
        assert change['due_date']
        self.assertEqual(change['due_date']['old'], DateTime('2010/01/01'))
        self.assertEqual(change['due_date']['new'], DateTime('2010/02/02'))
        
        self.assertEqual(issue.getDueDate(), DateTime('2010/02/02'))
        
        issue.editIssueDetails(due_date='')
        self.assertEqual(issue.getDueDate(), None)
        
        issue.editIssueDetails(due_date='2010/03/03')
        self.assertEqual(issue.getDueDate(), DateTime('2010/03/03'))
        
        
    def test_submitting_bad_due_dates(self):
        """test setting faux due dates"""
        tracker = self.folder.tracker
        
        title = u"Title"
        description = u"Description"
        
        tracker.enable_due_date = True
        
        request = self.app.REQUEST
        request.set('title', title)
        request.set('fromname', u'B\xc3\xa9b')
        request.set('email', u'email@address.com')
        request.set('description', description)
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('due_date', 'xxx')
        
        html = tracker.SubmitIssue(request)
        self.assertTrue(html.count('<span class="submiterror">Invalid date</span>'))
        
        # only 28 days in feb
        request.set('due_date', '2009/02/29')
        html = tracker.SubmitIssue(request)
        self.assertTrue(html.count('<span class="submiterror">Invalid date</span>'))        
        
        assert not list(tracker.getIssueObjects())
        
    def test_parseDueDate(self):
        """parseDueDate() takes a date string and returns a date object or None"""
        
        tracker = self.folder.tracker
        func = tracker.parseDueDate
        
        self.assertEqual(func('2009/01/01'), DateTime('2009/01/01'))
        self.assertEqual(func('13 april 2009'), DateTime('13 april 2009'))
        self.assertEqual(func('today'), 
                         DateTime(DateTime().strftime('%Y/%m/%d')))
        self.assertEqual(func('tomorrow'),
                         DateTime((DateTime()+1).strftime('%Y/%m/%d')))
        
        self.assertEqual(func('junk'), None)
        self.assertEqual(func('2009/02/29'), None)
        # but 2008 was a leap year
        self.assertEqual(func('2008/02/29'), DateTime('2008/02/29'))
        
        
    def test_filter_by_due_date(self):
        """test to list issues with a filter"""
        tracker = self.folder.tracker
        
        title = u"Title"
        description = u"Description"
        
        tracker.enable_due_date = True
        def hourless(d):
            return DateTime(d.strftime('%Y/%m/%d'))
        
        today = hourless(DateTime())
        yesterday = hourless(DateTime()-1)
        tomorrow = hourless(DateTime()+1)
        future = hourless(DateTime()+7)
        
        title_no_due_date = 'NO DUE DATE'
        title_today = 'TODAY'
        title_yesterday = 'YESTERDAY'
        title_tomorrow = 'TOMORROW'
        title_future = 'FUTURE'
        
        request = self.app.REQUEST
        
        request.set('fromname', u'B\xc3\xa9b')
        request.set('email', u'email@address.com')
        request.set('description', description)
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        
        request.set('title', title_no_due_date)
        request.set('due_date', '')
        html = tracker.SubmitIssue(request)

        request.set('title', title_today)
        request.set('due_date', today)
        html = tracker.SubmitIssue(request)

        request.set('title', title_yesterday)
        request.set('due_date', yesterday)
        html = tracker.SubmitIssue(request)

        request.set('title', title_tomorrow)
        request.set('due_date', tomorrow)
        html = tracker.SubmitIssue(request)

        request.set('title', title_future)
        request.set('due_date', future)
        html = tracker.SubmitIssue(request)
        
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 5)
        
        
        request.set('Filterlogic', 'block')
        request.set('filteroptions', 1)
        
        request.set('f-due', '')
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 5)
        
        request.set('f-due', 'FutUrE') # case insensitive
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 4)
        self.assertTrue(title_future not in [x.title for x in seq])
        
        request.set('f-due', 'Overdue')
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 4)
        self.assertTrue(title_yesterday not in [x.title for x in seq])
        
        request.set('f-due', 'Today')
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 4)
        self.assertTrue(title_today not in [x.title for x in seq])
        
        request.set('f-due', 'TomorroW')
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 4)
        self.assertTrue(title_tomorrow not in [x.title for x in seq])
        
        # combos
        request.set('f-due', ['FutUrE','Today']) 
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 3)
        self.assertTrue(title_future not in [x.title for x in seq])
        self.assertTrue(title_today not in [x.title for x in seq])
        
        request.set('f-due', ['Overdue','Today']) 
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 3)
        self.assertTrue(title_yesterday not in [x.title for x in seq])
        self.assertTrue(title_today not in [x.title for x in seq])        
        
        request.set('f-due', ['FutUrE','OverDUE']) 
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 3)
        self.assertTrue(title_future not in [x.title for x in seq])
        self.assertTrue(title_yesterday not in [x.title for x in seq])
        
        
        # Change the filter logic
        request.set('Filterlogic', 'show')
        
        request.set('f-due', '')
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 5)
        
        request.set('f-due', 'TODAy')
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 1)
        self.assertTrue(title_today in [x.title for x in seq])
        
        request.set('f-due', 'OVERdue')
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 1)
        self.assertTrue(title_yesterday in [x.title for x in seq])        
        
        request.set('f-due', 'tomorrow')
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 1)
        self.assertTrue(title_tomorrow in [x.title for x in seq])
        
        request.set('f-due', 'futurE')
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 1)
        self.assertTrue(title_future in [x.title for x in seq])
        
        # combos
        request.set('f-due', ['tomorrow','future'])
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 2)
        self.assertTrue(title_tomorrow in [x.title for x in seq])
        self.assertTrue(title_future in [x.title for x in seq])
        
        request.set('f-due', ['overdue','future'])
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 2)
        self.assertTrue(title_yesterday in [x.title for x in seq])
        self.assertTrue(title_future in [x.title for x in seq])
        
        request.set('f-due', ['overdue','today'])
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 2)
        self.assertTrue(title_yesterday in [x.title for x in seq])
        self.assertTrue(title_today in [x.title for x in seq])
        
        
    def test_sort_by_due_date(self):
        """test to sort issues by due date"""
        tracker = self.folder.tracker
        
        title = u"Title"
        description = u"Description"
        
        tracker.enable_due_date = True
        def hourless(d):
            return DateTime(d.strftime('%Y/%m/%d'))
        
        today = hourless(DateTime())
        yesterday = hourless(DateTime()-1)
        tomorrow = hourless(DateTime()+1)
        future = hourless(DateTime()+7)
        
        title_no_due_date = 'NO DUE DATE'
        title_today = 'TODAY'
        title_yesterday = 'YESTERDAY'
        title_tomorrow = 'TOMORROW'
        title_future = 'FUTURE'
        
        request = self.app.REQUEST
        
        request.set('fromname', u'B\xc3\xa9b')
        request.set('email', u'email@address.com')
        request.set('description', description)
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        
        request.set('title', title_no_due_date)
        request.set('due_date', '')
        html = tracker.SubmitIssue(request)

        request.set('title', title_today)
        request.set('due_date', today)
        html = tracker.SubmitIssue(request)

        request.set('title', title_yesterday)
        request.set('due_date', yesterday)
        html = tracker.SubmitIssue(request)

        request.set('title', title_tomorrow)
        request.set('due_date', tomorrow)
        html = tracker.SubmitIssue(request)

        request.set('title', title_future)
        request.set('due_date', future)
        html = tracker.SubmitIssue(request)
        
        request.set('sortorder', 'due_date')
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 5)
        
        # If you sort by due date, the most pressing one should come first.
        # The most pressing one is the one that was due longest ago.
        # Last should be any issues that don't have a due date.
        
        self.assertEqual([x.getDueDate() for x in seq],
                         [yesterday, today, tomorrow, future, None])

        request.set('reverse', 'true')
        
        seq = tracker.ListIssuesFiltered()
        self.assertEqual(len(seq), 5)
        
        self.assertEqual([x.getDueDate() for x in seq],
                         [future, tomorrow, today, yesterday, None])
        
        
        
    def test_change_password(self):
        """test to change your password when you're an acl user in a 
        Issue User Folder"""
        tracker = self.folder.tracker
        # create an Issue User Folder inside
        tracker._addRole(IssueTrackerUserRole)
        tracker._addRole(IssueTrackerManagerRole)
        tracker.manage_addProduct['IssueTrackerProduct']\
        .manage_addIssueUserFolder(keep_usernames=True)
        
        uf = tracker.acl_users
        
        uf.userFolderAddUser("user", "secret", [IssueTrackerUserRole], [],
                            email="user@test.com",
                            fullname="User Name")
        
        # first of all, you can't change your password if you're
        # not logged in
        self.assertRaises(UserSubmitError,
                          tracker.IssueUserChangePassword,
                          "secret",
                          "newpassword",
                          "newpassword")

        user = uf.getUserById('user')
        user = user.__of__(uf)
        newSecurityManager(None, user)
        assert getSecurityManager().getUser().getUserName() == 'user'
        
        # I should now be able to change my password
        self.assertRaises(DataSubmitError,
                          tracker.IssueUserChangePassword,
                          "not right",
                          "newpassword",
                          "newpassword")
        
        self.assertRaises(DataSubmitError,
                          tracker.IssueUserChangePassword,
                          "secret",
                          "new is",
                          "different")
        
        tracker.IssueUserChangePassword("secret", "newpass", "newpass")
        
        
    def test_catalog_search_by_issuedate(self):
        """it should be possible to search the issuetracker catalog by the issuedate
        """
        # create an issue
        request = self.app.REQUEST
        
        tracker = self.folder.tracker
        catalog = tracker.getCatalog()
        self.assertTrue('issuedate' in catalog._catalog.indexes.keys())
        
        request.set('fromname', u'B\xc3\xa9b')
        request.set('email', u'email@address.com')
        request.set('description', u"\xef Description")
        request.set('type', tracker.getDefaultType())
        request.set('urgency', tracker.getDefaultUrgency())
        request.set('title', u"Sample issue")
        html = tracker.SubmitIssue(request)
        
        # find it again
        # just check basic catalog search first
        brains = catalog()
        self.assertTrue(brains)
        self.assertEqual(brains[0].getObject().getTitle(), u"Sample issue")
        
        
        brains = catalog(issuedate={'query':DateTime()-1, 'range':'min'})
        self.assertTrue(brains)
        self.assertEqual(brains[0].getObject().getTitle(), u"Sample issue")
        
################################################################################        
            
        
def test_suite():
    from unittest import TestSuite,makeSuite
    suite = TestSuite()
    suite.addTest(makeSuite(IssueTrackerTestCase))
    if traversing:
        suite.addTest(makeSuite(TestFunctionalBase))
    return suite

    
if __name__ == '__main__':
    framework()
    
    
        

www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.