Code

Centralised conversion of user-input data to hyperdb values (bug #802405,
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Tue, 11 Nov 2003 00:35:14 +0000 (00:35 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Tue, 11 Nov 2003 00:35:14 +0000 (00:35 +0000)
bug #817217, rfe #816994)

git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1974 57a73879-2fb5-44c3-a270-3262357dd7e2

CHANGES.txt
roundup/admin.py
roundup/cgi/client.py
roundup/hyperdb.py
roundup/mailgw.py
roundup/password.py
test/test_hyperdbvals.py [new file with mode: 0644]
test/test_mailgw.py

index ee5818e194139e9850bfa32b804c0b8f15ceb8a5..fed3b7593c5c94d1fcb6a7cdf93763611ca10ee3 100644 (file)
@@ -28,6 +28,8 @@ Fixed:
 - Date arithmetic was utterly broken, and has been for a long time.
   Date +/- Interval now works, and Date - Date also works (produces
   an Interval.
+- Centralised conversion of user-input data to hyperdb values (bug #802405,
+  bug #817217, rfe #816994)
 
 Cleanup:
 - Replace curuserid attribute on Database with the extended getuid() method.
index 413509a61db7e280d044e4216366c036158d61c6..107e639b0c0774e099bccf3b0d5d99dd0d01eee4 100644 (file)
@@ -16,7 +16,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: admin.py,v 1.59 2003-10-24 19:48:05 jlgijsbers Exp $
+# $Id: admin.py,v 1.60 2003-11-11 00:35:13 richard Exp $
 
 '''Administration commands for maintaining Roundup trackers.
 '''
@@ -569,42 +569,11 @@ Command help:
 
             properties = cl.getprops()
             for key, value in props.items():
-                proptype =  properties[key]
-                if isinstance(proptype, hyperdb.Multilink):
-                    if value is None:
-                        props[key] = []
-                    else:
-                        props[key] = value.split(',')
-                elif value is None:
-                    continue
-                elif isinstance(proptype, hyperdb.String):
-                    continue
-                elif isinstance(proptype, hyperdb.Password):
-                    m = pwre.match(value)
-                    if m:
-                        # password is being given to us encrypted
-                        p = password.Password()
-                        p.scheme = m.group(1)
-                        p.password = m.group(2)
-                        props[key] = p
-                    else:
-                        props[key] = password.Password(value)
-                elif isinstance(proptype, hyperdb.Date):
-                    try:
-                        props[key] = date.Date(value)
-                    except ValueError, message:
-                        raise UsageError, '"%s": %s'%(value, message)
-                elif isinstance(proptype, hyperdb.Interval):
-                    try:
-                        props[key] = date.Interval(value)
-                    except ValueError, message:
-                        raise UsageError, '"%s": %s'%(value, message)
-                elif isinstance(proptype, hyperdb.Link):
-                    props[key] = value
-                elif isinstance(proptype, hyperdb.Boolean):
-                    props[key] = value.lower() in ('yes', 'true', 'on', '1')
-                elif isinstance(proptype, hyperdb.Number):
-                    props[key] = float(value)
+                try:
+                    props[key] = hyperdb.rawToHyperdb(self.db, cl, itemid,
+                        key, value)
+                except hyperdb.HyperdbValueError, message:
+                    raise UsageError, message
 
             # try the set
             try:
@@ -777,39 +746,11 @@ Command help:
 
         # convert types
         for propname, value in props.items():
-            # get the property
             try:
-                proptype = properties[propname]
-            except KeyError:
-                raise UsageError, _('%(classname)s has no property '
-                    '"%(propname)s"')%locals()
-
-            if isinstance(proptype, hyperdb.Date):
-                try:
-                    props[propname] = date.Date(value)
-                except ValueError, message:
-                    raise UsageError, _('"%(value)s": %(message)s')%locals()
-            elif isinstance(proptype, hyperdb.Interval):
-                try:
-                    props[propname] = date.Interval(value)
-                except ValueError, message:
-                    raise UsageError, _('"%(value)s": %(message)s')%locals()
-            elif isinstance(proptype, hyperdb.Password):
-                m = pwre.match(value)
-                if m:
-                    # password is being given to us encrypted
-                    p = password.Password()
-                    p.scheme = m.group(1)
-                    p.password = m.group(2)
-                    props[propname] = p
-                else:
-                    props[propname] = password.Password(value)
-            elif isinstance(proptype, hyperdb.Multilink):
-                props[propname] = value.split(',')
-            elif isinstance(proptype, hyperdb.Boolean):
-                props[propname] = value.lower() in ('yes', 'true', 'on', '1')
-            elif isinstance(proptype, hyperdb.Number):
-                props[propname] = float(value)
+                props[key] = hyperdb.rawToHyperdb(self.db, cl, None,
+                    propname, value)
+            except hyperdb.HyperdbValueError, message:
+                raise UsageError, message
 
         # check for the key property
         propname = cl.getkey()
index 72edc6fa02c092c2d04e218b4cf1a3be2c3ac714..5e581b08757c40e28ea987c74384e9522f49df62 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: client.py,v 1.143 2003-10-24 09:32:19 jlgijsbers Exp $
+# $Id: client.py,v 1.144 2003-11-11 00:35:14 richard Exp $
 
 __doc__ = """
 WWW request handler (also used in the stand-alone server).
@@ -1675,51 +1675,18 @@ You should then receive another email with the new password.
                 if value != confirm.value:
                     raise FormError, 'Password and confirmation text do '\
                         'not match'
-                value = password.Password(value)
-
-            elif isinstance(proptype, hyperdb.Link):
-                # see if it's the "no selection" choice
-                if value == '-1' or not value:
-                    # if we're creating, just don't include this property
-                    if not nodeid or nodeid.startswith('-'):
-                        continue
-                    value = None
-                else:
-                    # handle key values
-                    link = proptype.classname
-                    if not num_re.match(value):
-                        try:
-                            value = db.classes[link].lookup(value)
-                        except KeyError:
-                            raise FormError, _('property "%(propname)s": '
-                                '%(value)s not a %(classname)s')%{
-                                'propname': propname, 'value': value,
-                                'classname': link}
-                        except TypeError, message:
-                            raise FormError, _('you may only enter ID values '
-                                'for property "%(propname)s": %(message)s')%{
-                                'propname': propname, 'message': message}
+                try:
+                    value = password.Password(value)
+                except hyperdb.HyperdbValueError, msg:
+                    raise FormError, msg
+
             elif isinstance(proptype, hyperdb.Multilink):
-                # perform link class key value lookup if necessary
-                link = proptype.classname
-                link_cl = db.classes[link]
-                l = []
-                for entry in value:
-                    if not entry: continue
-                    if not num_re.match(entry):
-                        try:
-                            entry = link_cl.lookup(entry)
-                        except KeyError:
-                            raise FormError, _('property "%(propname)s": '
-                                '"%(value)s" not an entry of %(classname)s')%{
-                                'propname': propname, 'value': entry,
-                                'classname': link}
-                        except TypeError, message:
-                            raise FormError, _('you may only enter ID values '
-                                'for property "%(propname)s": %(message)s')%{
-                                'propname': propname, 'message': message}
-                    l.append(entry)
-                l.sort()
+                # convert input to list of ids
+                try:
+                    l = hyperdb.rawToHyperdb(self.db, cl, nodeid,
+                        propname, value)
+                except hyperdb.HyperdbValueError, msg:
+                    raise FormError, msg
 
                 # now use that list of ids to modify the multilink
                 if mlaction == 'set':
@@ -1753,13 +1720,10 @@ You should then receive another email with the new password.
                     value.sort()
 
             elif value == '':
-                # if we're creating, just don't include this property
-                if not nodeid or nodeid.startswith('-'):
-                    continue
                 # other types should be None'd if there's no value
                 value = None
             else:
-                # handle ValueErrors for all these in a similar fashion
+                # handle all other types
                 try:
                     if isinstance(proptype, hyperdb.String):
                         if (hasattr(value, 'filename') and
@@ -1777,23 +1741,17 @@ You should then receive another email with the new password.
                                 props['type'] = mimetypes.guess_type(fn)[0]
                                 if not props['type']:
                                     props['type'] = "application/octet-stream"
-                            # finally, read the content
+                            # finally, read the content RAW
                             value = value.value
                         else:
-                            # normal String fix the CRLF/CR -> LF stuff
-                            value = fixNewlines(value)
+                            value = hyperdb.rawToHyperdb(self.db, cl,
+                                nodeid, propname, value)
 
-                    elif isinstance(proptype, hyperdb.Date):
-                        value = date.Date(value, offset=timezone)
-                    elif isinstance(proptype, hyperdb.Interval):
-                        value = date.Interval(value)
-                    elif isinstance(proptype, hyperdb.Boolean):
-                        value = value.lower() in ('yes', 'true', 'on', '1')
-                    elif isinstance(proptype, hyperdb.Number):
-                        value = float(value)
-                except ValueError, msg:
-                    raise FormError, _('Error with %s property: %s')%(
-                        propname, msg)
+                    else:
+                        value = hyperdb.rawToHyperdb(self.db, cl, nodeid,
+                            propname, value)
+                except hyperdb.HyperdbValueError, msg:
+                    raise FormError, msg
 
             # register that we got this property
             if value:
@@ -1881,16 +1839,6 @@ You should then receive another email with the new password.
                       raise FormError, _('File is empty')
         return all_props, all_links
 
-def fixNewlines(text):
-    ''' Homogenise line endings.
-
-        Different web clients send different line ending values, but
-        other systems (eg. email) don't necessarily handle those line
-        endings. Our solution is to convert all line endings to LF.
-    '''
-    text = text.replace('\r\n', '\n')
-    return text.replace('\r', '\n')
-
 def extractFormList(value):
     ''' Extract a list of values from the form value.
 
index 6a1c83108d97b2a0534a22e8c5893dffa86304cd..245b917d4105ee06f69a5b484540b268e3511445 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: hyperdb.py,v 1.90 2003-10-24 22:52:48 richard Exp $
+# $Id: hyperdb.py,v 1.91 2003-11-11 00:35:13 richard Exp $
 
 """
 Hyperdatabase implementation, especially field types.
@@ -572,6 +572,171 @@ class Class:
         '''
         raise NotImplementedError
 
+class HyperdbValueError(ValueError):
+    ''' Error converting a raw value into a Hyperdb value '''
+    pass
+
+def convertLinkValue(db, propname, prop, value, idre=re.compile('\d+')):
+    ''' Convert the link value (may be id or key value) to an id value. '''
+    linkcl = db.classes[prop.classname]
+    if not idre.match(value):
+        if linkcl.getkey():
+            try:
+                value = linkcl.lookup(value)
+            except KeyError, message:
+                raise HyperdbValueError, 'property %s: %r is not a %s.'%(
+                    propname, value, prop.classname)
+        else:
+            raise HyperdbValueError, 'you may only enter ID values '\
+                'for property %s'%propname
+    return value
+
+def fixNewlines(text):
+    ''' Homogenise line endings.
+
+        Different web clients send different line ending values, but
+        other systems (eg. email) don't necessarily handle those line
+        endings. Our solution is to convert all line endings to LF.
+    '''
+    text = text.replace('\r\n', '\n')
+    return text.replace('\r', '\n')
+
+def rawToHyperdb(db, klass, itemid, propname, value,
+        pwre=re.compile(r'{(\w+)}(.+)')):
+    ''' Convert the raw (user-input) value to a hyperdb-storable value. The
+        value is for the "propname" property on itemid (may be None for a
+        new item) of "klass" in "db".
+
+        The value is usually a string, but in the case of multilink inputs
+        it may be either a list of strings or a string with comma-separated
+        values.
+    '''
+    properties = klass.getprops()
+
+    # ensure it's a valid property name
+    propname = propname.strip()
+    try:
+        proptype =  properties[propname]
+    except KeyError:
+        raise HyperdbValueError, '%r is not a property of %s'%(propname,
+            klass.classname)
+
+    # if we got a string, strip it now
+    if isinstance(value, type('')):
+        value = value.strip()
+
+    # convert the input value to a real property value
+    if isinstance(proptype, String):
+        # fix the CRLF/CR -> LF stuff
+        value = fixNewlines(value)
+    if isinstance(proptype, Password):
+        m = pwre.match(value)
+        if m:
+            # password is being given to us encrypted
+            p = password.Password()
+            p.scheme = m.group(1)
+            if p.scheme not in 'SHA crypt plaintext'.split():
+                raise HyperdbValueError, 'property %s: unknown encryption '\
+                    'scheme %r'%(propname, p.scheme)
+            p.password = m.group(2)
+            value = p
+        else:
+            try:
+                value = password.Password(value)
+            except password.PasswordValueError, message:
+                raise HyperdbValueError, 'property %s: %s'%(propname, message)
+    elif isinstance(proptype, Date):
+        try:
+            tz = db.getUserTimezone()
+            value = date.Date(value).local(tz)
+        except ValueError, message:
+            raise HyperdbValueError, 'property %s: %r is an invalid '\
+                'date (%s)'%(propname, value, message)
+    elif isinstance(proptype, Interval):
+        try:
+            value = date.Interval(value)
+        except ValueError, message:
+            raise HyperdbValueError, 'property %s: %r is an invalid '\
+                'date interval (%s)'%(propname, value, message)
+    elif isinstance(proptype, Link):
+        if value == '-1' or not value:
+            value = None
+        else:
+            value = convertLinkValue(db, propname, proptype, value)
+
+    elif isinstance(proptype, Multilink):
+        # get the current item value if it's not a new item
+        if itemid and not itemid.startswith('-'):
+            curvalue = klass.get(itemid, propname)
+        else:
+            curvalue = []
+
+        # if the value is a comma-separated string then split it now
+        if isinstance(value, type('')):
+            value = value.split(',')
+
+        # handle each add/remove in turn
+        # keep an extra list for all items that are
+        # definitely in the new list (in case of e.g.
+        # <propname>=A,+B, which should replace the old
+        # list with A,B)
+        set = 1
+        newvalue = []
+        for item in value:
+            item = item.strip()
+
+            # skip blanks
+            if not item: continue
+
+            # handle +/-
+            remove = 0
+            if item.startswith('-'):
+                remove = 1
+                item = item[1:]
+                set = 0
+            elif item.startswith('+'):
+                item = item[1:]
+                set = 0
+
+            # look up the value
+            itemid = convertLinkValue(db, propname, proptype, item)
+
+            # perform the add/remove
+            if remove:
+                try:
+                    curvalue.remove(itemid)
+                except ValueError:
+                    raise HyperdbValueError, 'property %s: %r is not ' \
+                        'currently an element'%(propname, item)
+            else:
+                newvalue.append(itemid)
+                if itemid not in curvalue:
+                    curvalue.append(itemid)
+
+        # that's it, set the new Multilink property value,
+        # or overwrite it completely
+        if set:
+            value = newvalue
+        else:
+            value = curvalue
+
+        # TODO: one day, we'll switch to numeric ids and this will be
+        # unnecessary :(
+        value = [int(x) for x in value]
+        value.sort()
+        value = [str(x) for x in value]
+    elif isinstance(proptype, Boolean):
+        value = value.strip()
+        value = value.lower() in ('yes', 'true', 'on', '1')
+    elif isinstance(proptype, Number):
+        value = value.strip()
+        try:
+            value = float(value)
+        except ValueError:
+            raise HyperdbValueError, 'property %s: %r is not a number'%(
+                propname, value)
+    return value
+
 class FileClass:
     ''' A class that requires the "content" property and stores it on
         disk.
index ab03bda4e1fbb2db97200ae80d4e1d61b066398b..a45f9e96ee438bc99933cdf517f214559ed8277f 100644 (file)
@@ -73,7 +73,7 @@ are calling the create() method to create a new node). If an auditor raises
 an exception, the original message is bounced back to the sender with the
 explanatory message given in the exception. 
 
-$Id: mailgw.py,v 1.136 2003-11-03 18:34:03 jlgijsbers Exp $
+$Id: mailgw.py,v 1.137 2003-11-11 00:35:13 richard Exp $
 """
 
 import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri
@@ -851,11 +851,10 @@ There was a problem with the message you sent:
         return nodeid
 
  
-def setPropArrayFromString(self, cl, propString, nodeid = None):
+def setPropArrayFromString(self, cl, propString, nodeid=None):
     ''' takes string of form prop=value,value;prop2=value
         and returns (error, prop[..])
     '''
-    properties = cl.getprops()
     props = {}
     errors = []
     for prop in string.split(propString, ';'):
@@ -866,100 +865,13 @@ def setPropArrayFromString(self, cl, propString, nodeid = None):
             errors.append('not of form [arg=value,value,...;'
                 'arg=value,value,...]')
             return (errors, props)
-
-        # ensure it's a valid property name
+        # convert the value to a hyperdb-usable value
         propname = propname.strip()
         try:
-            proptype =  properties[propname]
-        except KeyError:
-            errors.append('refers to an invalid property: "%s"'%propname)
-            continue
-
-        # convert the string value to a real property value
-        if isinstance(proptype, hyperdb.String):
-            props[propname] = value.strip()
-        if isinstance(proptype, hyperdb.Password):
-            props[propname] = password.Password(value.strip())
-        elif isinstance(proptype, hyperdb.Date):
-            try:
-                props[propname] = date.Date(value.strip()).local(self.db.getUserTimezone())
-            except ValueError, message:
-                errors.append('contains an invalid date for %s.'%propname)
-        elif isinstance(proptype, hyperdb.Interval):
-            try:
-                props[propname] = date.Interval(value)
-            except ValueError, message:
-                errors.append('contains an invalid date interval for %s.'%
-                    propname)
-        elif isinstance(proptype, hyperdb.Link):
-            linkcl = self.db.classes[proptype.classname]
-            propkey = linkcl.labelprop(default_to_id=1)
-            try:
-                props[propname] = linkcl.lookup(value)
-            except KeyError, message:
-                errors.append('"%s" is not a value for %s.'%(value, propname))
-        elif isinstance(proptype, hyperdb.Multilink):
-            # get the linked class
-            linkcl = self.db.classes[proptype.classname]
-            propkey = linkcl.labelprop(default_to_id=1)
-            if nodeid:
-                curvalue = cl.get(nodeid, propname)
-            else:
-                curvalue = []
-
-            # handle each add/remove in turn
-            # keep an extra list for all items that are
-            # definitely in the new list (in case of e.g.
-            # <propname>=A,+B, which should replace the old
-            # list with A,B)
-            set = 0
-            newvalue = []
-            for item in value.split(','):
-                item = item.strip()
-
-                # handle +/-
-                remove = 0
-                if item.startswith('-'):
-                    remove = 1
-                    item = item[1:]
-                elif item.startswith('+'):
-                    item = item[1:]
-                else:
-                    set = 1
-
-                # look up the value
-                try:
-                    item = linkcl.lookup(item)
-                except KeyError, message:
-                    errors.append('"%s" is not a value for %s.'%(item,
-                        propname))
-                    continue
-
-                # perform the add/remove
-                if remove:
-                    try:
-                        curvalue.remove(item)
-                    except ValueError:
-                        errors.append('"%s" is not currently in for %s.'%(item,
-                            propname))
-                        continue
-                else:
-                    newvalue.append(item)
-                    if item not in curvalue:
-                        curvalue.append(item)
-
-            # that's it, set the new Multilink property value,
-            # or overwrite it completely
-            if set:
-                props[propname] = newvalue
-            else:
-                props[propname] = curvalue
-        elif isinstance(proptype, hyperdb.Boolean):
-            value = value.strip()
-            props[propname] = value.lower() in ('yes', 'true', 'on', '1')
-        elif isinstance(proptype, hyperdb.Number):
-            value = value.strip()
-            props[propname] = float(value)
+            props[propname] = hyperdb.rawToHyperdb(self.db, cl, nodeid,
+                propname, value)
+        except hyperdb.HyperdbValueError, message:
+            errors.append(message)
     return errors, props
 
 
index a2f01f52c4c71e569f5b877f3f419c46a3272a03..97de75d08be5cd4a4d2ca7dfbd9eaedb726c4f73 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: password.py,v 1.9 2003-04-10 05:12:41 richard Exp $
+# $Id: password.py,v 1.10 2003-11-11 00:35:13 richard Exp $
 
 __doc__ = """
 Password handling (encoding, decoding).
@@ -28,6 +28,10 @@ except:
     crypt = None
     pass
 
+class PasswordValueError(ValueError):
+    ''' The password value is not valid '''
+    pass
+
 def encodePassword(plaintext, scheme, other=None):
     '''Encrypt the plaintext password.
     '''
@@ -45,7 +49,7 @@ def encodePassword(plaintext, scheme, other=None):
     elif scheme == 'plaintext':
         s = plaintext
     else:
-        raise ValueError, 'Unknown encryption scheme "%s"'%scheme
+        raise PasswordValueError, 'unknown encryption scheme %r'%scheme
     return s
 
 def generatePassword(length=8):
diff --git a/test/test_hyperdbvals.py b/test/test_hyperdbvals.py
new file mode 100644 (file)
index 0000000..b99f982
--- /dev/null
@@ -0,0 +1,129 @@
+#
+# Copyright (c) 2003 Richard Jones, richard@commonground.com.au
+# This module is free software, and you may redistribute it and/or modify
+# under the same terms as Python, so long as this copyright message and
+# disclaimer are retained in their original form.
+#
+# This module is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# $Id: test_hyperdbvals.py,v 1.1 2003-11-11 00:35:14 richard Exp $
+
+import unittest, os, shutil, errno, sys, difflib, cgi, re, sha
+
+from roundup import init, instance, password, hyperdb, date
+
+class TestClass:
+    def getprops(self):
+        return {
+            'string': hyperdb.String(),
+            'number': hyperdb.Number(),
+            'boolean': hyperdb.Boolean(),
+            'password': hyperdb.Password(),
+            'date': hyperdb.Date(),
+            'interval': hyperdb.Interval(),
+            'link': hyperdb.Link('test'),
+            'link2': hyperdb.Link('test2'),
+            'multilink': hyperdb.Multilink('test'),
+            'multilink2': hyperdb.Multilink('test2'),
+        }
+    def getkey(self):
+        return 'string'
+    def lookup(self, value):
+        if value == 'valid':
+            return '1'
+        raise KeyError
+    def get(self, nodeid, propname):
+        assert propname.startswith('multilink')
+        assert nodeid is not None
+        return ['2', '3']
+
+class TestClass2:
+    def properties(self):
+        return {
+            'string': hyperdb.String(),
+        }
+    def getkey(self):
+        return None
+    def labelprop(self, default_to_id=1):
+        return 'id'
+
+class TestDatabase:
+    classes = {'test': TestClass(), 'test2': TestClass2()}
+    def getUserTimezone(self):
+        return 0
+
+class RawToHyperdbTest(unittest.TestCase):
+    def _test(self, propname, value, itemid=None):
+        return hyperdb.rawToHyperdb(TestDatabase(), TestClass(), itemid,
+            propname, value)
+    def testString(self):
+        self.assertEqual(self._test('string', '  a string '), 'a string')
+    def testNumber(self):
+        self.assertEqual(self._test('number', '  10 '), 10)
+        self.assertEqual(self._test('number', '  1.5 '), 1.5)
+    def testBoolean(self):
+        for true in 'yes true on 1'.split():
+            self.assertEqual(self._test('boolean', '  %s '%true), 1)
+        for false in 'no false off 0'.split():
+            self.assertEqual(self._test('boolean', '  %s '%false), 0)
+    def testPassword(self):
+        self.assertEqual(self._test('password', '  a string '), 'a string')
+        val = self._test('password', '  a string ')
+        self.assert_(isinstance(val, password.Password))
+        val = self._test('password', '{plaintext}a string')
+        self.assert_(isinstance(val, password.Password))
+        val = self._test('password', '{crypt}a string')
+        self.assert_(isinstance(val, password.Password))
+        s = sha.sha('a string').hexdigest()
+        val = self._test('password', '{SHA}'+s)
+        self.assert_(isinstance(val, password.Password))
+        self.assertEqual(val, 'a string')
+        self.assertRaises(hyperdb.HyperdbValueError, self._test,
+            'password', '{fubar}a string')
+    def testDate(self):
+        val = self._test('date', ' 2003-01-01  ')
+        self.assert_(isinstance(val, date.Date))
+        val = self._test('date', ' 2003/01/01  ')
+        self.assert_(isinstance(val, date.Date))
+        val = self._test('date', ' 2003/1/1  ')
+        self.assert_(isinstance(val, date.Date))
+        val = self._test('date', ' 2003-1-1  ')
+        self.assert_(isinstance(val, date.Date))
+        self.assertRaises(hyperdb.HyperdbValueError, self._test, 'date',
+            'fubar')
+    def testInterval(self):
+        val = self._test('interval', ' +1d  ')
+        self.assert_(isinstance(val, date.Interval))
+        self.assertRaises(hyperdb.HyperdbValueError, self._test, 'interval',
+            'fubar')
+    def testLink(self):
+        self.assertEqual(self._test('link', '1'), '1')
+        self.assertEqual(self._test('link', 'valid'), '1')
+        self.assertRaises(hyperdb.HyperdbValueError, self._test, 'link',
+            'invalid')
+    def testMultilink(self):
+        self.assertEqual(self._test('multilink', '', '1'), [])
+        self.assertEqual(self._test('multilink', '1', '1'), ['1'])
+        self.assertEqual(self._test('multilink', 'valid', '1'), ['1'])
+        self.assertRaises(hyperdb.HyperdbValueError, self._test, 'multilink',
+            'invalid', '1')
+        self.assertEqual(self._test('multilink', '+1', '1'), ['1', '2', '3'])
+        self.assertEqual(self._test('multilink', '+valid', '1'), ['1', '2',
+            '3'])
+        self.assertEqual(self._test('multilink', '+1,-2', '1'), ['1', '3'])
+        self.assertEqual(self._test('multilink', '+valid,-3', '1'), ['1', '2'])
+        self.assertEqual(self._test('multilink', '+1', None), ['1'])
+        self.assertEqual(self._test('multilink', '+valid', None), ['1'])
+        self.assertEqual(self._test('multilink', '', None), [])
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(RawToHyperdbTest))
+    return suite
+
+if __name__ == '__main__':
+    runner = unittest.TextTestRunner()
+    unittest.main(testRunner=runner)
+# vim: set filetype=python ts=4 sw=4 et si
index b8019c3116ccd1a569a0e67eeabb9a5ac7a2a899..211b4c458a827c714488a3f4c4a11f8a33c09cc7 100644 (file)
@@ -8,7 +8,7 @@
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 #
-# $Id: test_mailgw.py,v 1.60 2003-11-03 22:23:02 jlgijsbers Exp $
+# $Id: test_mailgw.py,v 1.61 2003-11-11 00:35:14 richard Exp $
 
 import unittest, tempfile, os, shutil, errno, imp, sys, difflib, rfc822
 
@@ -93,7 +93,7 @@ class MailgwTestCase(unittest.TestCase, DiffHelper):
         init.install(self.dirname, 'templates/classic')
         init.write_select_db(self.dirname, 'anydbm')
         init.initialise(self.dirname, 'sekrit')
-        
+
         # check we can load the package
         self.instance = instance.open(self.dirname)