view test/session_common.py @ 6808:375d40a9e730

Add tests to BasicDatabase and merge implementations test/session_common.py: add new tests: * test set with bad __timestamps * test get on missing item with no default * test clear() method * test new lifetime() method everything below here was needed to get the tests to work across all db's. roundup/backends/sessions_dbm.py: make set() validate __timestamp as float. If invalid and item is new, set it to time.time(). If invalid and item exists keep original timestamp. add lifetime(key_lifetime) method. Given key_lifetime in seconds, generate a __timestamp that will be deleted after that many seconds. roundup/backends/sessions_rdbms.py: make set() behave the same as session_dbm. add lifetime method as above. roundup/backends/sessions_sqlite.py: import session_rdbms::BasicDatabase and override __init__. rather than cloning all the code. Kept a few logging and sql methods. roundup/test/memorydb.py: make get() on a missing key return KeyError make set() conform to dbm/rdbms implementations.
author John Rouillard <rouilj@ieee.org>
date Mon, 25 Jul 2022 21:21:26 -0400
parents bdd28b244839
children 3f60a71b0812
line wrap: on
line source

import os, shutil, time, unittest

from .db_test_base import config

"""
here are three different impementations for these. I am trying to fix
them so they all act the same.

set with invalid timestamp:

   session_dbm/memorydb - sets to invalid timestamp if new or existing item.
   session_rdbms - sets to time.time if new item, keeps original
                   if item exists. (note that the timestamp is
                   a separate column, the timestamp embedded in the
                   value object in the db has the bad __timestamp.
   reconciled: set to time.time for new item, keeps original time
               of existing item.

Also updateTimestamp does not update the marshalled values idea of
   __timestamp. So get(item, '__timestamp') will not work as expected
   for rdbms backends, need a sql query to get the timestamp column.

FIXME need to add getTimestamp method to sessions_rdbms.py and
sessions_dbm.py.

"""
class SessionTest(object):
    def setUp(self):
        # remove previous test, ignore errors
        if os.path.exists(config.DATABASE):
            shutil.rmtree(config.DATABASE)
        os.makedirs(config.DATABASE + '/files')
        self.db = self.module.Database(config, 'admin')
        self.sessions = self.db.getSessionManager()
        self.otks = self.db.getOTKManager()

    def tearDown(self):
        if hasattr(self, 'db'):
            self.db.close()
        if os.path.exists(config.DATABASE):
            shutil.rmtree(config.DATABASE)

    def testList(self):
        '''Under dbm/memory sessions store, keys are returned as
           byte strings. self.s2b converts string to byte under those
           backends but is a no-op for rdbms based backends.

           Unknown why keys can be strings not bytes for get/set
           and work correctly.
        '''
        self.sessions.list()
        self.sessions.set('random_key', text='hello, world!')
        self.sessions.set('random_key2', text='hello, world!')
        self.assertEqual(self.sessions.list().sort(),
                [self.s2b('random_key'), self.s2b('random_key2')].sort())

    def testGetMissingKey(self):
        self.sessions.set('random_key', text='hello, world!', otherval='bar')
        with self.assertRaises(KeyError) as e:
            self.sessions.get('badc_key', 'text')

    def testGetAll(self):
        self.sessions.set('random_key', text='hello, world!', otherval='bar')
        self.assertEqual(self.sessions.getall('random_key'),
            {'text': 'hello, world!', 'otherval': 'bar'})

    def testDestroy(self):
        self.sessions.set('random_key', text='hello, world!')
        self.assertEqual(self.sessions.getall('random_key'),
            {'text': 'hello, world!'})
        self.sessions.destroy('random_key')
        self.assertRaises(KeyError, self.sessions.getall, 'random_key')

    def testClear(self):
        self.sessions.set('random_key', text='hello, world!')
        self.sessions.set('random_key2', text='hello, world!')
        self.sessions.set('random_key3', text='hello, world!')
        self.assertEqual(self.sessions.getall('random_key3'),
            {'text': 'hello, world!'})
        self.assertEqual(len(self.sessions.list()), 3)
        self.sessions.clear()
        self.assertEqual(len(self.sessions.list()), 0)

    def testSetSession(self):
        self.sessions.set('random_key', text='hello, world!', otherval='bar')
        self.assertEqual(self.sessions.get('random_key', 'text'),
            'hello, world!')
        self.assertEqual(self.sessions.get('random_key', 'otherval'),
            'bar')

    def testUpdateSession(self):
        self.sessions.set('random_key', text='hello, world!')
        self.assertEqual(self.sessions.get('random_key', 'text'),
            'hello, world!')
        self.sessions.set('random_key', text='nope')
        self.assertEqual(self.sessions.get('random_key', 'text'), 'nope')

    def testBadTimestamp(self):
        self.sessions.set('random_key',
                          text='hello, world!',
                          __timestamp='not a timestamp')
        ts = self.sessions.get('random_key', '__timestamp')
        self.assertNotEqual(ts, 'not a timestamp')
        # use {1,7} because db's don't pad the fraction to 7 digits.
        ts_re=r'^[0-9]{10,16}\.[0-9]{1,7}$'
        try:
            self.assertRegex(str(ts), ts_re)
        except AttributeError:   # 2.7 version
            import warnings
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore",category=DeprecationWarning)
                self.assertRegexpMatches(str(ts), ts_re)

        # now update with a bad timestamp, original timestamp should
        # be kept.
        self.sessions.set('random_key',
                          text='hello, world2!',
                          __timestamp='not a timestamp')
        item = self.sessions.get('random_key', "text")
        item_ts = self.sessions.get('random_key', "__timestamp")
        self.assertEqual(item, 'hello, world2!')
        self.assertAlmostEqual(ts, item_ts, 2)

    # overridden in dbm and memory backends
    def testUpdateTimestamp(self):
        def get_ts_via_sql(self):
            sql = '''select %(name)s_time from %(name)ss
                 where %(name)s_key = '%(session)s';'''% \
                     {'name': self.sessions.name,
                      'session': 'random_session'}

            self.sessions.cursor.execute(sql)
            db_tstamp = self.sessions.cursor.fetchone()
            return db_tstamp

        # make sure timestamp is older than one minute so update will apply
        timestamp = time.time() - 62
        self.sessions.set('random_session', text='hello, world!',
                          __timestamp=timestamp)

        self.sessions.updateTimestamp('random_session')
        # this doesn't work as the rdbms backends have a
        # session_time, otk_time column and the timestamp in the
        # session marshalled payload isn't updated. The dbm
        # backend does update the __timestamp value so it works
        # for dbm.
        #self.assertNotEqual (self.sessions.get('random_session',
        #                                       '__timestamp'),
        #                     timestamp)

        # use 61 to allow a fudge factor
        self.assertGreater(get_ts_via_sql(self)[0] - timestamp, 61)

    def testLifetime(self):
        ts = self.sessions.lifetime(300)
        week_ago =  time.time() - 60*60*24*7
        self.assertGreater(week_ago + 302, ts)
        self.assertLess(week_ago + 298, ts)

Roundup Issue Tracker: http://roundup-tracker.org/