class StringsTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() def test_insert_unicode(self): self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-unicode', u'ünicöde')) self.assertEqual([(u'ünicöde',)], self.env.db_query( "SELECT value FROM system WHERE name='test-unicode'")) def test_insert_empty(self): from trac.util.text import empty self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-empty', empty)) self.assertEqual([(u'',)], self.env.db_query( "SELECT value FROM system WHERE name='test-empty'")) def test_insert_markup(self): from genshi.core import Markup self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-markup', Markup(u'<em>märkup</em>'))) self.assertEqual([(u'<em>märkup</em>',)], self.env.db_query( "SELECT value FROM system WHERE name='test-markup'"))
class VersionTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True) def tearDown(self): self.env.reset_db() def test_exists_negative(self): def get_fake_version(): return Version(self.env, "-1") self.assertRaises(TracError, get_fake_version) def test_exists(self): """ http://trac.edgewall.org/ticket/4247 """ for v in Version.select(self.env): self.assertEqual(v.exists, True) def test_create_and_update(self): version = Version(self.env) version.name = 'Test' version.insert() self.assertEqual([('Test', 0, None)], self.env.db_query( "SELECT name, time, description FROM version WHERE name='Test'")) # Use the same model object to update the version version.description = 'Some text' version.update() self.assertEqual([('Test', 0, 'Some text')], self.env.db_query( "SELECT name, time, description FROM version WHERE name='Test'"))
class ComponentTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True) def tearDown(self): self.env.reset_db() def test_exists_negative(self): def get_fake_component(): return Component(self.env, "Shrubbery") self.assertRaises(TracError, get_fake_component) def test_exists(self): """ http://trac.edgewall.org/ticket/4247 """ for c in Component.select(self.env): self.assertEqual(c.exists, True) def test_create_and_update(self): component = Component(self.env) component.name = 'Test' component.insert() self.assertEqual([('Test', None, None)], self.env.db_query(""" SELECT name, owner, description FROM component WHERE name='Test'""")) # Use the same model object to update the component component.owner = 'joe' component.update() self.assertEqual([('Test', 'joe', None)], self.env.db_query( "SELECT name, owner, description FROM component WHERE name='Test'"))
class StringsTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() def test_insert_unicode(self): self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-unicode', u'ünicöde')) self.assertEqual([(u'ünicöde',)], self.env.db_query( "SELECT value FROM system WHERE name='test-unicode'")) def test_insert_empty(self): from trac.util.text import empty self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-empty', empty)) self.assertEqual([(u'',)], self.env.db_query( "SELECT value FROM system WHERE name='test-empty'")) def test_insert_markup(self): from genshi.core import Markup self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-markup', Markup(u'<em>märkup</em>'))) self.assertEqual([(u'<em>märkup</em>',)], self.env.db_query( "SELECT value FROM system WHERE name='test-markup'")) def test_quote(self): db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute('SELECT 1 AS %s' % \ db.quote(r'alpha\`\"\'\\beta``gamma""delta')) self.assertEqual(r'alpha\`\"\'\\beta``gamma""delta', get_column_names(cursor)[0])
class DatabaseManagerTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True) self.dbm = DatabaseManager(self.env) def tearDown(self): self.env.reset_db() def test_destroy_db(self): """Database doesn't exist after calling destroy_db.""" self.env.db_query("SELECT name FROM system") self.assertIsNotNone(self.dbm._cnx_pool) self.dbm.destroy_db() self.assertIsNone(self.dbm._cnx_pool) # No connection pool self.assertFalse(self.dbm.db_exists()) def test_get_column_names(self): """Get column names for the default database.""" for table in default_schema: column_names = [col.name for col in table.columns] self.assertEqual(column_names, self.dbm.get_column_names(table.name)) def test_get_default_database_version(self): """Get database version for the default entry named `database_version`. """ self.assertEqual(default_db_version, self.dbm.get_database_version()) def test_get_table_names(self): """Get table names for the default database.""" self.assertEqual(sorted(table.name for table in default_schema), sorted(self.dbm.get_table_names())) def test_set_default_database_version(self): """Set database version for the default entry named `database_version`. """ new_db_version = default_db_version + 1 self.dbm.set_database_version(new_db_version) self.assertEqual(new_db_version, self.dbm.get_database_version()) # Restore the previous version to avoid destroying the database # on teardown self.dbm.set_database_version(default_db_version) self.assertEqual(default_db_version, self.dbm.get_database_version()) def test_set_get_plugin_database_version(self): """Get and set database version for an entry with an arbitrary name. """ name = 'a_trac_plugin_version' db_ver = 1 self.assertFalse(self.dbm.get_database_version(name)) self.dbm.set_database_version(db_ver, name) self.assertEqual(db_ver, self.dbm.get_database_version(name))
class SystemInfoTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() def tearDown(self): self.env.reset_db() def test_database_backend_version(self): """Database backend is returned in system_info.""" # closes the pooled connections to use `DatabaseManager.shutdown()` # instead of `env.shutdown()` to avoid `log.shutdown()` DatabaseManager(self.env).shutdown() info_before = self.env.system_info self.env.db_query("SELECT 42") # just connects database info_after = self.env.system_info def get_info(system_info, name): for info in system_info: if info[0] == name: return info[1] self.fail('Missing %r' % name) if self.env.dburi.startswith('mysql'): self.assertRegexpMatches( get_info(info_before, 'MySQL'), r'^server: \(not-connected\), ' r'client: "\d+(\.\d+)+([-.].+)?", ' r'thread-safe: True$') self.assertRegexpMatches( get_info(info_after, 'MySQL'), r'^server: "\d+(\.\d+)+([-.].+)?", ' r'client: "\d+(\.\d+)+([-.].+)?", ' r'thread-safe: True$') self.assertRegexpMatches(get_info(info_before, 'pymysql'), r'^\d+(\.\d+)+$') self.assertRegexpMatches(get_info(info_after, 'pymysql'), r'^\d+(\.\d+)+$') elif self.env.dburi.startswith('postgres'): self.assertRegexpMatches( get_info(info_before, 'PostgreSQL'), r'^server: \(not-connected\), ' r'client: (\d+(\.\d+)+|\(unknown\))$') self.assertRegexpMatches( get_info(info_after, 'PostgreSQL'), r'^server: \d+(\.\d+)+, ' r'client: (\d+(\.\d+)+|\(unknown\))$') self.assertRegexpMatches(get_info(info_before, 'psycopg2'), r'^\d+(\.\d+)+$') self.assertRegexpMatches(get_info(info_after, 'psycopg2'), r'^\d+(\.\d+)+$') elif self.env.dburi.startswith('sqlite'): self.assertEqual(info_before, info_after) self.assertRegexpMatches(get_info(info_before, 'SQLite'), r'^\d+(\.\d+)+$') self.assertRegexpMatches(get_info(info_before, 'pysqlite'), r'^\d+(\.\d+)+$') else: self.fail("Unknown value for dburi %s" % self.env.dburi)
class ConnectionTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() def tearDown(self): self.env.reset_db() def test_get_last_id(self): id1 = id2 = None q = "INSERT INTO report (author) VALUES ('anonymous')" with self.env.db_transaction as db: cursor = db.cursor() cursor.execute(q) # Row ID correct before... id1 = db.get_last_id(cursor, 'report') self.assertNotEqual(0, id1) db.commit() cursor.execute(q) # ... and after commit() db.commit() id2 = db.get_last_id(cursor, 'report') self.assertEqual(id1 + 1, id2) def test_update_sequence(self): self.env.db_transaction( "INSERT INTO report (id, author) VALUES (42, 'anonymous')") with self.env.db_transaction as db: cursor = db.cursor() db.update_sequence(cursor, 'report', 'id') self.env.db_transaction( "INSERT INTO report (author) VALUES ('next-id')") self.assertEqual( 43, self.env.db_query("SELECT id FROM report WHERE author='next-id'") [0][0])
class ConnectionTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() def tearDown(self): self.env.reset_db() def test_get_last_id(self): id1 = id2 = None q = "INSERT INTO report (author) VALUES ('anonymous')" with self.env.db_transaction as db: cursor = db.cursor() cursor.execute(q) # Row ID correct before... id1 = db.get_last_id(cursor, 'report') self.assertNotEqual(0, id1) db.commit() cursor.execute(q) # ... and after commit() db.commit() id2 = db.get_last_id(cursor, 'report') self.assertEqual(id1 + 1, id2) def test_update_sequence(self): self.env.db_transaction( "INSERT INTO report (id, author) VALUES (42, 'anonymous')") with self.env.db_transaction as db: cursor = db.cursor() db.update_sequence(cursor, 'report', 'id') self.env.db_transaction( "INSERT INTO report (author) VALUES ('next-id')") self.assertEqual(43, self.env.db_query( "SELECT id FROM report WHERE author='next-id'")[0][0])
class StringsTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() def test_insert_unicode(self): self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-unicode', u'ünicöde')) self.assertEqual( [(u'ünicöde', )], self.env.db_query( "SELECT value FROM system WHERE name='test-unicode'")) def test_insert_empty(self): from trac.util.text import empty self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-empty', empty)) self.assertEqual( [(u'', )], self.env.db_query( "SELECT value FROM system WHERE name='test-empty'")) def test_insert_markup(self): from genshi.core import Markup self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-markup', Markup(u'<em>märkup</em>'))) self.assertEqual( [(u'<em>märkup</em>', )], self.env.db_query( "SELECT value FROM system WHERE name='test-markup'")) def test_quote(self): db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute('SELECT 1 AS %s' % \ db.quote(r'alpha\`\"\'\\beta``gamma""delta')) self.assertEqual(r'alpha\`\"\'\\beta``gamma""delta', get_column_names(cursor)[0])
class EnvironmentUpgradeTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() def tearDown(self): self.env.reset_db() def test_multiple_upgrade_participants(self): class Participant1(Component): implements(IEnvironmentSetupParticipant) def environment_created(self): pass def environment_needs_upgrade(self): return True def upgrade_environment(self): insert_value('value1', 1) class Participant2(Component): implements(IEnvironmentSetupParticipant) def environment_created(self): pass def environment_needs_upgrade(self): return True def upgrade_environment(self): insert_value('value2', 2) def insert_value(name, value): self.env.db_transaction( """ INSERT INTO system (name, value) VALUES (%s, %s) """, (name, value)) def select_value(name): for value, in self.env.db_query( """ SELECT value FROM system WHERE name=%s """, (name, )): return value self.env.enable_component(Participant1) self.env.enable_component(Participant2) self.assertTrue(self.env.needs_upgrade()) self.assertTrue(self.env.upgrade()) self.assertEqual('1', select_value('value1')) self.assertEqual('2', select_value('value2'))
class LoginModuleTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.module = LoginModule(self.env) def tearDown(self): self.env.reset_db() def test_anonymous_access(self): req = MockRequest(self.env, remote_user=None) self.assertIsNone(self.module.authenticate(req)) def test_unknown_cookie_access(self): incookie = Cookie() incookie['trac_auth'] = '123' req = MockRequest(self.env, remote_user=None) self.assertIsNone(self.module.authenticate(req)) def test_known_cookie_access(self): self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") req = MockRequest(self.env, remote_user=None) req.incookie['trac_auth'] = '123' self.assertEqual('john', self.module.authenticate(req)) self.assertNotIn('auth_cookie', req.outcookie) def test_known_cookie_ip_check_enabled(self): self.env.config.set('trac', 'check_auth_ip', 'yes') self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") req = MockRequest(self.env, remote_addr='192.168.0.100', remote_user=None) req.incookie['trac_auth'] = '123' self.assertIsNone(self.module.authenticate(req)) self.assertIn('trac_auth', req.outcookie) def test_known_cookie_ip_check_disabled(self): self.env.config.set('trac', 'check_auth_ip', 'no') self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") req = MockRequest(self.env, remote_addr='192.168.0.100', remote_user=None) req.incookie['trac_auth'] = '123' self.assertEqual('john', self.module.authenticate(req)) self.assertNotIn('auth_cookie', req.outcookie) def test_login(self): # remote_user must be upper case to test that by default, case is # preserved. req = MockRequest(self.env, authname='john') self.module._do_login(req) self.assertIn('trac_auth', req.outcookie, '"trac_auth" Cookie not set') auth_cookie = req.outcookie['trac_auth'].value self.assertEqual([('john', '127.0.0.1')], self.env.db_query( "SELECT name, ipnr FROM auth_cookie WHERE cookie=%s", (auth_cookie,))) def test_login_ignore_case(self): """ Test that login is succesful when the usernames differ in case, but case is ignored. """ self.env.config.set('trac', 'ignore_auth_case', 'yes') req = MockRequest(self.env, remote_user='******') self.module._do_login(req) self.assertIn('trac_auth', req.outcookie, '"trac_auth" Cookie not set') auth_cookie = req.outcookie['trac_auth'].value self.assertEqual([('john', '127.0.0.1')], self.env.db_query( "SELECT name, ipnr FROM auth_cookie WHERE cookie=%s", (auth_cookie,))) def test_login_no_username(self): req = MockRequest(self.env, remote_user=None) self.assertRaises(TracError, self.module._do_login, req) def test_already_logged_in_same_user(self): self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") req = MockRequest(self.env, authname='john') self.module._do_login(req) # this shouldn't raise an error def test_already_logged_in_different_user(self): self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") req = MockRequest(self.env, authname='john', remote_user='******') self.assertRaises(TracError, self.module._do_login, req) def test_logout(self): self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") req = MockRequest(self.env, authname='john', method='POST') self.module._do_logout(req) self.assertIn('trac_auth', req.outcookie) self.assertFalse(self.env.db_query( "SELECT name, ipnr FROM auth_cookie WHERE name='john'")) def test_logout_not_logged_in(self): req = MockRequest(self.env, method='POST') self.module._do_logout(req) # this shouldn't raise an error def test_logout_protect(self): self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") req = MockRequest(self.env, authname='john') self.module._do_logout(req) self.assertNotIn('trac_auth', req.outcookie) self.assertEqual( [('john', '127.0.0.1')], self.env.db_query("SELECT name, ipnr FROM auth_cookie " "WHERE cookie='123'"))
class SessionTestCase(unittest.TestCase): """Unit tests for the persistent session support.""" def setUp(self): self.env = EnvironmentStub() def tearDown(self): self.env.reset_db() def test_new_session(self): """ Verify that a session cookie gets sent back to the client for a new session. """ cookie = Cookie() req = Mock(incookie=Cookie(), outcookie=cookie, authname='anonymous', base_path='/') session = Session(self.env, req) self.assertEqual(session.sid, cookie['trac_session'].value) self.assertEqual(0, self.env.db_query( "SELECT COUNT(*) FROM session")[0][0]) def test_anonymous_session(self): """ Verify that session variables are stored in the database. """ incookie = Cookie() incookie['trac_session'] = '123456' outcookie = Cookie() req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=outcookie) session = Session(self.env, req) self.assertEquals('123456', session.sid) self.failIf(outcookie.has_key('trac_session')) def test_authenticated_session(self): """ Verifies that a session cookie does not get used if the user is logged in, and that Trac expires the cookie. """ incookie = Cookie() incookie['trac_session'] = '123456' outcookie = Cookie() req = Mock(authname='john', base_path='/', incookie=incookie, outcookie=outcookie) session = Session(self.env, req) self.assertEqual('john', session.sid) session['foo'] = 'bar' session.save() self.assertEquals(0, outcookie['trac_session']['expires']) def test_session_promotion(self): """ Verifies that an existing anonymous session gets promoted to an authenticated session when the user logs in. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") incookie = Cookie() incookie['trac_session'] = '123456' outcookie = Cookie() req = Mock(authname='john', base_path='/', incookie=incookie, outcookie=outcookie) session = Session(self.env, req) self.assertEqual('john', session.sid) session.save() self.assertEqual([('john', 1)], self.env.db_query( "SELECT sid, authenticated FROM session")) def test_new_session_promotion(self): """ Verifies that even without a preexisting anonymous session, an authenticated session will be created when the user logs in. (same test as above without the initial INSERT) """ with self.env.db_transaction as db: incookie = Cookie() incookie['trac_session'] = '123456' outcookie = Cookie() req = Mock(authname='john', base_path='/', incookie=incookie, outcookie=outcookie) session = Session(self.env, req) self.assertEqual('john', session.sid) session.save() self.assertEqual([('john', 1)], self.env.db_query( "SELECT sid, authenticated FROM session")) def test_add_anonymous_session_var(self): """ Verify that new variables are inserted into the 'session' table in the database for an anonymous session. """ incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) session['foo'] = 'bar' session.save() self.assertEqual('bar', self.env.db_query( "SELECT value FROM session_attribute WHERE sid='123456'")[0][0]) def test_modify_anonymous_session_var(self): """ Verify that modifying an existing variable updates the 'session' table accordingly for an anonymous session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) self.assertEqual('bar', session['foo']) session['foo'] = 'baz' session.save() self.assertEqual('baz', self.env.db_query( "SELECT value FROM session_attribute WHERE sid='123456'")[0][0]) def test_delete_anonymous_session_var(self): """ Verify that modifying a variable updates the 'session' table accordingly for an anonymous session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) self.assertEqual('bar', session['foo']) del session['foo'] session.save() self.assertEqual(0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='123456' AND name='foo' """)[0][0]) def test_purge_anonymous_session(self): """ Verify that old sessions get purged. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, %s)", (0,)) db("INSERT INTO session VALUES ('987654', 0, %s)", (int(time.time() - PURGE_AGE - 3600),)) db(""" INSERT INTO session_attribute VALUES ('987654', 0, 'foo', 'bar') """) # We need to modify a different session to trigger the purging incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) session['foo'] = 'bar' session.save() self.assertEqual(0, self.env.db_query(""" SELECT COUNT(*) FROM session WHERE sid='987654' AND authenticated=0 """)[0][0]) def test_delete_empty_session(self): """ Verify that a session gets deleted when it doesn't have any data except for the 'last_visit' timestamp. """ now = time.time() # Make sure the session has data so that it doesn't get dropped with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, %s)", (int(now - UPDATE_INTERVAL - 3600),)) db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) del session['foo'] session.save() self.assertEqual(0, self.env.db_query(""" SELECT COUNT(*) FROM session WHERE sid='123456' AND authenticated=0 """)[0][0]) def test_change_anonymous_session(self): """ Verify that changing from one anonymous session to an inexisting anonymous session creates the new session and doesn't carry over variables from the previous session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) self.assertEqual({'foo': 'bar'}, session) session.get_session('7890') session['baz'] = 'moo' session.save() self.assertEqual({'baz': 'moo'}, session) with self.env.db_query as db: self.assertEqual(1, db(""" SELECT COUNT(*) FROM session WHERE sid='7890' AND authenticated=0 """)[0][0]) self.assertEqual([('baz', 'moo')], db(""" SELECT name, value FROM session_attribute WHERE sid='7890' AND authenticated=0 """)) def test_add_authenticated_session_var(self): """ Verify that new variables are inserted into the 'session' table in the database for an authenticated session. """ req = Mock(authname='john', base_path='/', incookie=Cookie()) session = Session(self.env, req) session['foo'] = 'bar' session.save() self.assertEqual('bar', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_modify_authenticated_session_var(self): """ Verify that modifying an existing variable updates the 'session' table accordingly for an authenticated session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john',1,'foo','bar')") req = Mock(authname='john', base_path='/', incookie=Cookie()) session = Session(self.env, req) self.assertEqual('bar', session['foo']) session['foo'] = 'baz' session.save() self.assertEqual('baz', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_authenticated_session_independence_var(self): """ Verify that an anonymous session with the same name as an authenticated session doesn't interfere with the latter. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john',1,'foo','bar')") self.assertEqual('bar', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND authenticated=1 AND name='foo' """)[0][0]) incookie = Cookie() incookie['trac_session'] = 'john' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) self.assert_('foo' not in session) session['foo'] = 'baz' session.save() rows = self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND authenticated=1 AND name='foo' """) self.assertEqual(1, len(rows)) self.assertEqual('bar', rows[0][0]) rows = self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND authenticated=0 AND name='foo' """) self.assertEqual(1, len(rows)) self.assertEqual('baz', rows[0][0]) def test_delete_authenticated_session_var(self): """ Verify that deleting a variable updates the 'session' table accordingly for an authenticated session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')") req = Mock(authname='john', base_path='/', incookie=Cookie()) session = Session(self.env, req) self.assertEqual('bar', session['foo']) del session['foo'] session.save() self.assertEqual(0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_update_session(self): """ Verify that accessing a session after one day updates the sessions 'last_visit' variable so that the session doesn't get purged. """ now = time.time() # Make sure the session has data so that it doesn't get dropped with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 1)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) incookie = Cookie() incookie['trac_session'] = '123456' outcookie = Cookie() req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=outcookie) session = Session(self.env, req) session['modified'] = True session.save() # updating does require modifications self.assertEqual(PURGE_AGE, outcookie['trac_session']['expires']) self.assertAlmostEqual(now, int(self.env.db_query(""" SELECT last_visit FROM session WHERE sid='123456' AND authenticated=0 """)[0][0]), -1) def test_modify_detached_session(self): """ Verify that modifying a variable in a session not associated with a request updates the database accordingly. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')") session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) session['foo'] = 'baz' session.save() self.assertEqual('baz', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_delete_detached_session_var(self): """ Verify that removing a variable in a session not associated with a request deletes the variable from the database. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')") session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) del session['foo'] session.save() self.assertEqual(0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_session_set(self): """Verify that setting a variable in a session to the default value removes it from the session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')") session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) # Setting the variable to the default value removes the variable with self.env.db_transaction as db: session.set('foo', 'default', 'default') session.save() self.assertEqual(0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) # Setting the variable to a value different from the default sets it with self.env.db_transaction as db: session.set('foo', 'something', 'default') session.save() self.assertEqual('something', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_session_admin_list(self): auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) # Verify the empty case self.assertRaises(StopIteration, sess_admin._get_list([]).next) self.assertEqual([i for i in sess_admin._get_list(['authenticated'])], auth_list) self.assertEqual([i for i in sess_admin._get_list(['anonymous'])], anon_list) self.assertEqual([i for i in sess_admin._get_list(['*'])], all_list) self.assertEqual([i for i in sess_admin._get_list(['name00'])][0], auth_list[0]) self.assertEqual([i for i in sess_admin._get_list(['name10:0'])][0], anon_list[0]) self.assertEqual([i for i in sess_admin._get_list(['name00', 'name01', 'name02'])], all_list[:3]) def test_session_admin_add(self): auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) self.assertRaises(Exception, sess_admin._do_add, 'name00') sess_admin._do_add('john') result = get_session_info(self.env, 'john') self.assertEqual(result, ('john', None, None)) sess_admin._do_add('john1', 'John1') result = get_session_info(self.env, 'john1') self.assertEqual(result, ('john1', 'John1', None)) sess_admin._do_add('john2', 'John2', '*****@*****.**') result = get_session_info(self.env, 'john2') self.assertEqual(result, ('john2', 'John2', '*****@*****.**')) def test_session_admin_set(self): auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) self.assertRaises(TracError, sess_admin._do_set, 'name', 'nothere', 'foo') sess_admin._do_set('name', 'name00', 'john') result = get_session_info(self.env, 'name00') self.assertEqual(result, ('name00', 'john', 'val00')) sess_admin._do_set('email', 'name00', '*****@*****.**') result = get_session_info(self.env, 'name00') self.assertEqual(result, ('name00', 'john', '*****@*****.**')) def test_session_admin_delete(self): auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) sess_admin._do_delete('name00') result = get_session_info(self.env, 'name00') self.assertEqual(result, (None, None, None)) sess_admin._do_delete('nothere') result = get_session_info(self.env, 'nothere') self.assertEqual(result, (None, None, None)) auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin._do_delete('anonymous') result = [i for i in sess_admin._get_list(['*'])] self.assertEqual(result, auth_list) def test_session_admin_purge(self): sess_admin = SessionAdmin(self.env) auth_list, anon_list, all_list = \ _prep_session_table(self.env, spread_visits=True) sess_admin._do_purge('2010-01-02') result = [i for i in sess_admin._get_list(['*'])] self.assertEqual(result, auth_list + anon_list) result = get_session_info(self.env, anon_list[0][0]) self.assertEqual(result, ('name10', 'val10', 'val10')) result = get_session_info(self.env, anon_list[1][0]) self.assertEqual(result, ('name11', 'val11', 'val11')) auth_list, anon_list, all_list = \ _prep_session_table(self.env, spread_visits=True) sess_admin._do_purge('2010-01-12') result = [i for i in sess_admin._get_list(['*'])] self.assertEqual(result, auth_list + anon_list[1:]) rows = self.env.db_query(""" SELECT name, value FROM session_attribute WHERE sid = %s """, (anon_list[0][0],)) self.assertEqual([], rows) result = get_session_info(self.env, anon_list[1][0]) self.assertEqual(result, ('name11', 'val11', 'val11'))
class EnvironmentUpgradeTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() def tearDown(self): self.env.reset_db() def test_multiple_upgrade_participants(self): class Participant1(Component): implements(IEnvironmentSetupParticipant) def environment_created(self): pass def environment_needs_upgrade(self, db): return True def upgrade_environment(self, db): insert_value('value1', 1) class Participant2(Component): implements(IEnvironmentSetupParticipant) def environment_created(self): pass def environment_needs_upgrade(self, db): return True def upgrade_environment(self, db): insert_value('value2', 2) def insert_value(name, value): self.env.db_transaction( """ INSERT INTO system (name, value) VALUES (%s, %s) """, (name, value)) def select_value(name): for value, in self.env.db_query( """ SELECT value FROM system WHERE name=%s """, (name, )): return value self.env.enable_component(Participant1) self.env.enable_component(Participant2) self.assertTrue(self.env.needs_upgrade()) self.assertTrue(self.env.upgrade()) self.assertEqual('1', select_value('value1')) self.assertEqual('2', select_value('value2')) def test_needs_upgrade_legacy_participant(self): """For backward compatibility with plugin, environment_needs_upgrade with a `db` argument is deprecated but still allowed.""" participants = self.env.setup_participants needs_upgrade = self.env.needs_upgrade() class LegacyParticipant(Component): implements(IEnvironmentSetupParticipant) def environment_created(self): pass def environment_needs_upgrade(self, db): return True def upgrade_environment(self, db): pass self.env.enable_component(LegacyParticipant) self.assertFalse(needs_upgrade) self.assertEqual( len(participants) + 1, len(self.env.setup_participants)) self.assertTrue(self.env.needs_upgrade()) def test_upgrade_legacy_participant(self): """For backward compatibility with plugin, upgrade with a `db` argument is deprecated but still allowed.""" participants = self.env.setup_participants class LegacyParticipant(Component): implements(IEnvironmentSetupParticipant) def environment_created(self): pass def environment_needs_upgrade(self, db): return True def upgrade_environment(self, db): pass self.env.enable_component(LegacyParticipant) self.assertEqual( len(participants) + 1, len(self.env.setup_participants)) self.assertTrue(self.env.needs_upgrade()) self.assertTrue(self.env.upgrade())
class PostgresConnectionTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.schema = [ Table('test_simple', key='id')[Column('id', auto_increment=True), Column('username'), Column('email'), Column('enabled', type='int'), Column('extra'), Index(['username'], unique=True), Index(['email'], unique=False), ], Table('test_composite', key=['id', 'name'])[Column('id', type='int'), Column('name'), Column('value'), Column('enabled', type='int'), Index(['name', 'value'], unique=False), Index(['enabled', 'name'], unique=True), ], ] self.dbm = DatabaseManager(self.env) self.dbm.drop_tables(self.schema) self.dbm.create_tables(self.schema) self.dbm.insert_into_tables([ ('test_simple', ('username', 'email', 'enabled'), [('joe', '*****@*****.**', 1), (u'joé', '*****@*****.**', 0)]), ('test_composite', ('id', 'name', 'value', 'enabled'), [(1, 'foo', '42', 1), (1, 'bar', '42', 1), (2, 'foo', '43', 0), (2, 'bar', '43', 0)]), ]) def tearDown(self): DatabaseManager(self.env).drop_tables(self.schema) self.env.reset_db() def _drop_column(self, table, column): with self.env.db_transaction as db: db.drop_column(table, column) def _get_indices(self, table): with self.env.db_query: tab_oid = self._query( """ SELECT tab.oid FROM pg_class tab INNER JOIN pg_namespace ns ON ns.oid = tab.relnamespace AND ns.nspname = ANY (current_schemas(false)) WHERE tab.relname=%s AND tab.relkind = 'r' """, table)[0][0] column_names = self._query( """ SELECT attnum, attname FROM pg_attribute WHERE attrelid=%s AND attnum >= 0 AND NOT attisdropped """, tab_oid) column_names = dict((row[0], row[1]) for row in column_names) indices = self._query( """ SELECT ind.relname, d.indisprimary, d.indisunique, d.indkey FROM pg_index d INNER JOIN pg_class ind ON d.indexrelid = ind.oid AND ind.relkind = 'i' WHERE d.indrelid=%s """, tab_oid) results = {} for index, pk, unique, indkey in indices: columns = [column_names[int(i)] for i in indkey.split()] results[index] = { 'pk': bool(pk), 'unique': bool(unique), 'columns': columns } return results def _query(self, stmt, *args): return self.env.db_query(stmt, args) def test_remove_simple_keys(self): indices_0 = self._get_indices('test_simple') self.assertEqual([ 'test_simple_email_idx', 'test_simple_pkey', 'test_simple_username_idx' ], sorted(indices_0)) self.assertEqual({ 'pk': False, 'unique': True, 'columns': ['username'] }, indices_0['test_simple_username_idx']) self.assertEqual({ 'pk': True, 'unique': True, 'columns': ['id'] }, indices_0['test_simple_pkey']) self.assertEqual({ 'pk': False, 'unique': False, 'columns': ['email'] }, indices_0['test_simple_email_idx']) self._drop_column('test_simple', 'enabled') self.assertEqual(indices_0, self._get_indices('test_simple')) self._drop_column('test_simple', 'username') indices_1 = self._get_indices('test_simple') self.assertEqual(['test_simple_email_idx', 'test_simple_pkey'], sorted(indices_1)) self._drop_column('test_simple', 'email') indices_2 = self._get_indices('test_simple') self.assertEqual(['test_simple_pkey'], sorted(indices_2)) self._drop_column('test_simple', 'id') indices_3 = self._get_indices('test_simple') self.assertEqual({}, indices_3) def test_remove_composite_keys(self): indices_0 = self._get_indices('test_composite') self.assertEqual([ 'test_composite_enabled_name_idx', 'test_composite_name_value_idx', 'test_composite_pk' ], sorted(indices_0)) self.assertEqual( { 'pk': False, 'unique': False, 'columns': ['name', 'value'] }, indices_0['test_composite_name_value_idx']) self.assertEqual( { 'pk': False, 'unique': True, 'columns': ['enabled', 'name'] }, indices_0['test_composite_enabled_name_idx']) self.assertEqual( { 'pk': True, 'unique': True, 'columns': ['id', 'name'] }, indices_0['test_composite_pk']) self._drop_column('test_composite', 'id') indices_1 = self._get_indices('test_composite') self.assertEqual([ 'test_composite_enabled_name_idx', 'test_composite_name_value_idx' ], sorted(indices_1)) self.assertEqual(indices_0['test_composite_name_value_idx'], indices_1['test_composite_name_value_idx']) self.assertEqual(indices_0['test_composite_enabled_name_idx'], indices_1['test_composite_enabled_name_idx']) rows = self._query("""SELECT * FROM test_composite ORDER BY name, value, enabled""") self.assertEqual([('bar', '42', 1), ('bar', '43', 0), ('foo', '42', 1), ('foo', '43', 0)], rows) self._drop_column('test_composite', 'name') self.assertEqual({}, self._get_indices('test_composite')) rows = self._query("""SELECT * FROM test_composite ORDER BY value, enabled""") self.assertEqual([('42', 1), ('42', 1), ('43', 0), ('43', 0)], rows)
class SQLiteConnectionTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.schema = [ Table('test_simple', key='id')[Column('id', auto_increment=True), Column('username'), Column('email'), Column('enabled', type='int'), Column('extra'), Index(['username'], unique=True), Index(['email'], unique=False), ], Table('test_composite', key=['id', 'name'])[Column('id', type='int'), Column('name'), Column('value'), Column('enabled', type='int'), Index(['name', 'value'], unique=False), Index(['name', 'enabled'], unique=True), ], ] self.dbm = DatabaseManager(self.env) self.dbm.drop_tables(self.schema) self.dbm.create_tables(self.schema) self.dbm.insert_into_tables([ ('test_simple', ('username', 'email', 'enabled'), [('joe', '*****@*****.**', 1), (u'joé', '*****@*****.**', 0)]), ('test_composite', ('id', 'name', 'value', 'enabled'), [(1, 'foo', '42', 1), (1, 'bar', '42', 1), (2, 'foo', '43', 0), (2, 'bar', '43', 0)]), ]) def tearDown(self): DatabaseManager(self.env).drop_tables(self.schema) self.env.reset_db() def _table_info(self, table): names = ('column', 'type', 'notnull', 'default', 'pk') with self.env.db_query as db: cursor = db.cursor() cursor.execute("PRAGMA table_info(%s)" % db.quote(table)) return [dict(zip(names, row[1:6])) for row in cursor] def _index_info(self, table): with self.env.db_query as db: cursor = db.cursor() cursor.execute("PRAGMA index_list(%s)" % db.quote(table)) results = {row[1]: {'unique': row[2]} for row in cursor} for index, info in results.iteritems(): cursor.execute("PRAGMA index_info(%s)" % db.quote(index)) info['columns'] = [row[2] for row in cursor] return results def _drop_column(self, table, column): with self.env.db_transaction as db: db.drop_column(table, column) def _query(self, stmt, *args): return self.env.db_query(stmt, args) def test_remove_simple_keys(self): coldef = { 'id': { 'column': 'id', 'type': 'integer', 'notnull': 0, 'default': None, 'pk': 1 }, 'username': { 'column': 'username', 'type': 'text', 'notnull': 0, 'default': None, 'pk': 0 }, 'email': { 'column': 'email', 'type': 'text', 'notnull': 0, 'default': None, 'pk': 0 }, 'enabled': { 'column': 'enabled', 'type': 'integer', 'notnull': 0, 'default': None, 'pk': 0 }, 'extra': { 'column': 'extra', 'type': 'text', 'notnull': 0, 'default': None, 'pk': 0 }, } columns_0 = self._table_info('test_simple') self.assertEqual([ coldef['id'], coldef['username'], coldef['email'], coldef['enabled'], coldef['extra'] ], columns_0) indices_0 = self._index_info('test_simple') self.assertEqual(['test_simple_email_idx', 'test_simple_username_idx'], sorted(indices_0)) self._drop_column('test_simple', 'extra') columns_1 = self._table_info('test_simple') indices_1 = self._index_info('test_simple') self.assertEqual([ coldef['id'], coldef['username'], coldef['email'], coldef['enabled'] ], columns_1) self.assertEqual(indices_1, indices_0) self._drop_column('test_simple', 'id') columns_2 = self._table_info('test_simple') indices_2 = self._index_info('test_simple') self.assertEqual( [coldef['username'], coldef['email'], coldef['enabled']], columns_2) self.assertEqual(indices_2, indices_0) self._drop_column('test_simple', 'username') columns_3 = self._table_info('test_simple') indices_3 = self._index_info('test_simple') self.assertEqual([coldef['email'], coldef['enabled']], columns_3) self.assertEqual(['test_simple_email_idx'], sorted(indices_3)) self._drop_column('test_simple', 'email') columns_4 = self._table_info('test_simple') indices_4 = self._index_info('test_simple') self.assertEqual([coldef['enabled']], columns_4) self.assertEqual({}, indices_4) def test_remove_composite_keys(self): indices_0 = self._index_info('test_composite') self.assertEqual([ 'sqlite_autoindex_test_composite_1', 'test_composite_name_enabled_idx', 'test_composite_name_value_idx' ], sorted(indices_0)) self.assertEqual({ 'unique': 1, 'columns': ['id', 'name'] }, indices_0['sqlite_autoindex_test_composite_1']) self.assertEqual({ 'unique': 0, 'columns': ['name', 'value'] }, indices_0['test_composite_name_value_idx']) self.assertEqual({ 'unique': 1, 'columns': ['name', 'enabled'] }, indices_0['test_composite_name_enabled_idx']) self._drop_column('test_composite', 'id') indices_1 = self._index_info('test_composite') self.assertEqual([ 'test_composite_name_enabled_idx', 'test_composite_name_value_idx' ], sorted(indices_1)) self.assertEqual(indices_0['test_composite_name_value_idx'], indices_1['test_composite_name_value_idx']) self.assertEqual(indices_0['test_composite_name_enabled_idx'], indices_1['test_composite_name_enabled_idx']) rows = self._query("""SELECT * FROM test_composite ORDER BY name, value, enabled""") self.assertEqual([('bar', '42', 1), ('bar', '43', 0), ('foo', '42', 1), ('foo', '43', 0)], rows) self._drop_column('test_composite', 'name') self.assertEqual({}, self._index_info('test_composite')) rows = self._query("""SELECT * FROM test_composite ORDER BY value, enabled""") self.assertEqual([('42', 1), ('42', 1), ('43', 0), ('43', 0)], rows)
class NotificationPreferencesTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.prefs_mod = PreferencesModule(self.env) ids = self._add_subscriptions([ {'sid': 'foo', 'class_': 'TicketOwnerSubscriber'}, {'sid': 'foo', 'class_': 'CarbonCopySubscriber'}, {'sid': 'foo', 'class_': 'TicketReporterSubscriber'}, {'sid': 'bar', 'class_': 'CarbonCopySubscriber'}, ]) self.foo_rule0 = ids[0] self.foo_rule1 = ids[1] self.foo_rule2 = ids[2] self.bar_rule0 = ids[3] def tearDown(self): self.env.reset_db() def _add_subscription(self, **kwargs): props = {'sid': None, 'authenticated': 1, 'distributor': 'email', 'format': 'text/plain', 'adverb': 'always', 'class': 'TicketOwnerSubscriber'} props.update((k.rstrip('_'), (v or None) if isinstance(v, basestring) else v) for k, v in kwargs.iteritems()) assert props['sid'] is not None return Subscription.add(self.env, props) def _add_subscriptions(self, rows): with self.env.db_transaction: return [self._add_subscription(**row) for row in rows] def _get_rules(self, sid, authenticated=1): return self.env.db_query(""" SELECT distributor, format, priority, adverb, class FROM notify_subscription WHERE sid=%s AND authenticated=1 ORDER BY distributor, priority """, (sid,)) def _request(self, **kwargs): kwargs.setdefault('method', 'POST') kwargs.setdefault('path_info', '/prefs/notification') return MockRequest(self.env, **kwargs) def _process(self, req): self.assertTrue(self.prefs_mod.match_request(req)) return self.prefs_mod.process_request(req) def test_add_rule(self): args = {'action': 'add-rule_email', 'new-adverb-email': 'never', 'format-email': '', 'new-rule-email': 'TicketOwnerSubscriber'} req = self._request(authname='baz', args=args) self.assertRaises(RequestDone, self._process, req) self.assertEqual( [('email', None, 1, 'never', 'TicketOwnerSubscriber')], self._get_rules('baz')) args = {'action': 'add-rule_email', 'new-adverb-email': 'always', 'format-email': 'text/plain', 'new-rule-email': 'CarbonCopySubscriber'} req = self._request(authname='baz', args=args) self.assertRaises(RequestDone, self._process, req) self.assertEqual( [('email', None, 1, 'never', 'TicketOwnerSubscriber'), ('email', 'text/plain', 2, 'always', 'CarbonCopySubscriber')], self._get_rules('baz')) def test_delete_rule(self): req = self._request(authname='foo', args={'action': 'delete-rule_%d' % self.foo_rule1}) self.assertRaises(RequestDone, self._process, req) self.assertEqual( [('email', 'text/plain', 1, 'always', 'TicketOwnerSubscriber'), ('email', 'text/plain', 2, 'always', 'TicketReporterSubscriber')], self._get_rules('foo')) # try to delete non-existent rule rules = self._get_rules('foo') req = self._request(authname='foo', args={'action': 'delete-rule_%d' % self.foo_rule1}) self.assertRaises(RequestDone, self._process, req) self.assertEqual(rules, self._get_rules('foo')) # try to delete non-owned rules rules = self._get_rules('bar') req = self._request(authname='foo', args={'action': 'delete-rule_%d' % self.bar_rule0}) self.assertRaises(RequestDone, self._process, req) self.assertEqual(rules, self._get_rules('bar')) def test_move_rule(self): # move to last req = self._request(authname='foo', args={'action': 'move-rule_%d-3' % self.foo_rule1}) self.assertRaises(RequestDone, self._process, req) self.assertEqual( [('email', 'text/plain', 1, 'always', 'TicketOwnerSubscriber'), ('email', 'text/plain', 2, 'always', 'TicketReporterSubscriber'), ('email', 'text/plain', 3, 'always', 'CarbonCopySubscriber')], self._get_rules('foo')) self.assertEqual( [('email', 'text/plain', 1, 'always', 'CarbonCopySubscriber')], self._get_rules('bar')) # move to first req = self._request(authname='foo', args={'action': 'move-rule_%d-1' % self.foo_rule1}) self.assertRaises(RequestDone, self._process, req) self.assertEqual( [('email', 'text/plain', 1, 'always', 'CarbonCopySubscriber'), ('email', 'text/plain', 2, 'always', 'TicketOwnerSubscriber'), ('email', 'text/plain', 3, 'always', 'TicketReporterSubscriber')], self._get_rules('foo')) # move to same position rules = self._get_rules('foo') req = self._request(authname='foo', args={'action': 'move-rule_%d-1' % self.foo_rule1}) self.assertRaises(RequestDone, self._process, req) self.assertEqual(rules, self._get_rules('foo')) # try to move to out of range rules = self._get_rules('foo') req = self._request(authname='foo', args={'action': 'move-rule_%d-42' % self.foo_rule1}) self.assertRaises(RequestDone, self._process, req) self.assertEqual(rules, self._get_rules('foo')) # try to move non-owned rules rules = self._get_rules('foo') req = self._request(authname='bar', args={'action': 'move-rule_%d-3' % self.foo_rule1}) self.assertRaises(RequestDone, self._process, req) self.assertEqual(rules, self._get_rules('foo')) rules = self._get_rules('foo') # try to move non-existent rule req = self._request(authname='foo', args={'action': 'move-rule_42-1'}) self.assertRaises(RequestDone, self._process, req) self.assertEqual(rules, self._get_rules('foo')) # invalid id req = self._request(authname='foo', args={'action': 'move-rule_0-1'}) self.assertRaises(RequestDone, self._process, req) self.assertEqual(rules, self._get_rules('foo')) req = self._request(authname='foo', args={'action': 'move-rule_a-1'}) self.assertRaises(RequestDone, self._process, req) self.assertEqual(rules, self._get_rules('foo')) # invalid priority req = self._request(authname='foo', args={'action': 'move-rule_%d-0' % self.foo_rule1}) self.assertRaises(RequestDone, self._process, req) self.assertEqual(rules, self._get_rules('foo')) req = self._request(authname='foo', args={'action': 'move-rule_%d-a' % self.foo_rule1}) self.assertRaises(RequestDone, self._process, req) self.assertEqual(rules, self._get_rules('foo')) def test_set_format(self): # set text/html req = self._request(authname='foo', args={'action': 'set-format_email', 'format-email': 'text/html'}) self.assertNotIn('notification.format.email', req.session) self.assertRaises(RequestDone, self._process, req) self.assertEqual( [('email', 'text/html', 1, 'always', 'TicketOwnerSubscriber'), ('email', 'text/html', 2, 'always', 'CarbonCopySubscriber'), ('email', 'text/html', 3, 'always', 'TicketReporterSubscriber')], self._get_rules('foo')) self.assertEqual( [('email', 'text/plain', 1, 'always', 'CarbonCopySubscriber')], self._get_rules('bar')) self.assertEqual('text/html', req.session['notification.format.email']) # set default format req = self._request(authname='foo', args={'action': 'set-format_email', 'format-email': ''}) self.assertRaises(RequestDone, self._process, req) self.assertEqual( [('email', None, 1, 'always', 'TicketOwnerSubscriber'), ('email', None, 2, 'always', 'CarbonCopySubscriber'), ('email', None, 3, 'always', 'TicketReporterSubscriber')], self._get_rules('foo')) self.assertNotIn('notification.format.email', req.session) def test_replace(self): arg_list = [ ('action', 'replace_all'), ('format-email', 'text/plain'), ('adverb-email', 'always'), ('adverb-email', 'never'), ('adverb-email', 'always'), ('adverb-email', 'always'), ('class-email', 'TicketOwnerSubscriber'), ('class-email', 'CarbonCopySubscriber'), ('class-email', 'TicketReporterSubscriber'), ('class-email', 'TicketUpdaterSubscriber'), ] req = self._request(authname='foo', arg_list=arg_list) self.assertRaises(RequestDone, self._process, req) self.assertEqual( [('email', 'text/plain', 1, 'always', 'TicketOwnerSubscriber'), ('email', 'text/plain', 2, 'never', 'CarbonCopySubscriber'), ('email', 'text/plain', 3, 'always', 'TicketReporterSubscriber'), ('email', 'text/plain', 4, 'always', 'TicketUpdaterSubscriber')], self._get_rules('foo')) self.assertEqual( [('email', 'text/plain', 1, 'always', 'CarbonCopySubscriber')], self._get_rules('bar')) arg_list = [ ('action', 'replace_all'), ('format-email', ''), ('adverb-email', 'always'), ('class-email', 'TicketOwnerSubscriber'), ] req = self._request(authname='foo', arg_list=arg_list) self.assertRaises(RequestDone, self._process, req) self.assertEqual( [('email', None, 1, 'always', 'TicketOwnerSubscriber')], self._get_rules('foo')) arg_list = [('action', 'replace_all'), ('format-email', '')] req = self._request(authname='foo', arg_list=arg_list) self.assertRaises(RequestDone, self._process, req) self.assertEqual([], self._get_rules('foo'))
class CacheTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.log = self.env.log self.env.db_transaction.executemany( "INSERT INTO repository (id, name, value) VALUES (%s, %s, %s)", [(1, 'name', 'test-repos'), (1, 'youngest_rev', '')]) def tearDown(self): self.env.reset_db() # Helpers def get_repos(self, get_changeset=None, youngest_rev=1): if get_changeset is None: def no_changeset(rev): raise NoSuchChangeset(rev) get_changeset = no_changeset return Mock(Repository, 'test-repos', {'name': 'test-repos', 'id': 1}, self.log, get_changeset=get_changeset, get_oldest_rev=lambda: 0, get_youngest_rev=lambda: youngest_rev, normalize_rev=lambda x: get_changeset(x).rev, next_rev=(lambda x: int(x) < youngest_rev and x + 1 \ or None)) def preset_cache(self, *args): """Each arg is a (rev tuple, changes list of tuples) pair.""" with self.env.db_transaction as db: for rev, changes in args: db("""INSERT INTO revision (repos, rev, time, author, message) VALUES (1,%s,%s,%s,%s) """, rev) if changes: db.executemany(""" INSERT INTO node_change (repos, rev, path, node_type, change_type, base_path, base_rev) VALUES (1, %s, %s, %s, %s, %s, %s) """, [(rev[0],) + change for change in changes]) db("""UPDATE repository SET value=%s WHERE id=1 AND name='youngest_rev' """, (args[-1][0][0],)) # Tests def test_initial_sync_with_empty_repos(self): repos = self.get_repos() cache = CachedRepository(self.env, repos, self.log) cache.sync() with self.env.db_query as db: self.assertEqual([], db( "SELECT rev, time, author, message FROM revision")) self.assertEqual(0, db("SELECT COUNT(*) FROM node_change")[0][0]) def test_initial_sync(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=1) changes = [('trunk', Node.DIRECTORY, Changeset.ADD, None, None), ('trunk/README', Node.FILE, Changeset.ADD, None, None)] changesets = [Mock(Changeset, repos, 0, '', '', t1, get_changes=lambda: []), Mock(Changeset, repos, 1, 'Import', 'joe', t2, get_changes=lambda: iter(changes))] cache = CachedRepository(self.env, repos, self.log) cache.sync() with self.env.db_query as db: rows = db("SELECT rev, time, author, message FROM revision") self.assertEqual(len(rows), 2) self.assertEqual(('0', to_utimestamp(t1), '', ''), rows[0]) self.assertEqual(('1', to_utimestamp(t2), 'joe', 'Import'), rows[1]) rows = db(""" SELECT rev, path, node_type, change_type, base_path, base_rev FROM node_change""") self.assertEqual(len(rows), 2) self.assertEqual(('1', 'trunk', 'D', 'A', None, None), rows[0]) self.assertEqual(('1', 'trunk/README', 'F', 'A', None, None), rows[1]) def test_update_sync(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) t3 = datetime(2003, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (('0', to_utimestamp(t1), '', ''), []), (('1', to_utimestamp(t2), 'joe', 'Import'), [('trunk', 'D', 'A', None, None), ('trunk/README', 'F', 'A', None, None)]), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=2) changes = [('trunk/README', Node.FILE, Changeset.EDIT, 'trunk/README', 1)] changesets = [ None, Mock(Changeset, repos, 1, '', '', t2, get_changes=lambda: []), Mock(Changeset, repos, 2, 'Update', 'joe', t3, get_changes=lambda: iter(changes)) ] cache = CachedRepository(self.env, repos, self.log) cache.sync() with self.env.db_query as db: self.assertEqual([(to_utimestamp(t3), 'joe', 'Update')], db("SELECT time, author, message FROM revision WHERE rev='2'")) self.assertEqual([('trunk/README', 'F', 'E', 'trunk/README', '1')], db("""SELECT path, node_type, change_type, base_path, base_rev FROM node_change WHERE rev='2'""")) def test_clean_sync(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) t3 = datetime(2003, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (('0', to_utimestamp(t1), '', ''), []), (('1', to_utimestamp(t2), 'joe', 'Import'), [('trunk', 'D', 'A', None, None), ('trunk/README', 'F', 'A', None, None)]), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=2) changes1 = [('trunk', Node.DIRECTORY, Changeset.ADD, None, None), ('trunk/README', Node.FILE, Changeset.ADD, None, None)] changes2 = [('trunk/README', Node.FILE, Changeset.EDIT, 'trunk/README', 1)] changesets = [ Mock(Changeset, repos, 0, '**empty**', 'joe', t1, get_changes=lambda: []), Mock(Changeset, repos, 1, 'Initial Import', 'joe', t2, get_changes=lambda: iter(changes1)), Mock(Changeset, repos, 2, 'Update', 'joe', t3, get_changes=lambda: iter(changes2)) ] cache = CachedRepository(self.env, repos, self.log) cache.sync(clean=True) rows = self.env.db_query(""" SELECT time, author, message FROM revision ORDER BY rev """) self.assertEqual(3, len(rows)) self.assertEqual((to_utimestamp(t1), 'joe', '**empty**'), rows[0]) self.assertEqual((to_utimestamp(t2), 'joe', 'Initial Import'), rows[1]) self.assertEqual((to_utimestamp(t3), 'joe', 'Update'), rows[2]) rows = self.env.db_query(""" SELECT rev, path, node_type, change_type, base_path, base_rev FROM node_change ORDER BY rev, path""") self.assertEqual(3, len(rows)) self.assertEqual(('1', 'trunk', 'D', 'A', None, None), rows[0]) self.assertEqual(('1', 'trunk/README', 'F', 'A', None, None), rows[1]) self.assertEqual(('2', 'trunk/README', 'F', 'E', 'trunk/README', '1'), rows[2]) def test_sync_changeset(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (('0', to_utimestamp(t1), '', ''), []), (('1', to_utimestamp(t2), 'joe', 'Import'), [('trunk', 'D', 'A', None, None), ('trunk/README', 'F', 'A', None, None)]), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=1) changes1 = [('trunk', Node.DIRECTORY, Changeset.ADD, None, None), ('trunk/README', Node.FILE, Changeset.ADD, None, None)] changesets = [ Mock(Changeset, repos, 0, '**empty**', 'joe', t1, get_changes=lambda: []), Mock(Changeset, repos, 1, 'Initial Import', 'joe', t2, get_changes=lambda: iter(changes1)), ] cache = CachedRepository(self.env, repos, self.log) cache.sync_changeset(0) rows = self.env.db_query( "SELECT time, author, message FROM revision ORDER BY rev") self.assertEqual(2, len(rows)) self.assertEqual((to_utimestamp(t1), 'joe', '**empty**'), rows[0]) self.assertEqual((to_utimestamp(t2), 'joe', 'Import'), rows[1]) def test_sync_changeset_if_not_exists(self): t = [ datetime(2001, 1, 1, 1, 1, 1, 0, utc), # r0 datetime(2002, 1, 1, 1, 1, 1, 0, utc), # r1 datetime(2003, 1, 1, 1, 1, 1, 0, utc), # r2 datetime(2004, 1, 1, 1, 1, 1, 0, utc), # r3 ] self.preset_cache( (('0', to_utimestamp(t[0]), 'joe', '**empty**'), []), (('1', to_utimestamp(t[1]), 'joe', 'Import'), [('trunk', 'D', 'A', None, None), ('trunk/README', 'F', 'A', None, None)]), # not exists r2 (('3', to_utimestamp(t[3]), 'joe', 'Add COPYING'), [('trunk/COPYING', 'F', 'A', None, None)]), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=3) changes = [ None, # r0 [('trunk', Node.DIRECTORY, Changeset.ADD, None, None), # r1 ('trunk/README', Node.FILE, Changeset.ADD, None, None)], [('branches', Node.DIRECTORY, Changeset.ADD, None, None), # r2 ('tags', Node.DIRECTORY, Changeset.ADD, None, None)], [('trunk/COPYING', Node.FILE, Changeset.ADD, None, None)], # r3 ] changesets = [ Mock(Changeset, repos, 0, '**empty**', 'joe', t[0], get_changes=lambda: []), Mock(Changeset, repos, 1, 'Initial Import', 'joe', t[1], get_changes=lambda: iter(changes[1])), Mock(Changeset, repos, 2, 'Created directories', 'john', t[2], get_changes=lambda: iter(changes[2])), Mock(Changeset, repos, 3, 'Add COPYING', 'joe', t[3], get_changes=lambda: iter(changes[3])), ] cache = CachedRepository(self.env, repos, self.log) self.assertRaises(NoSuchChangeset, cache.get_changeset, 2) cache.sync() self.assertRaises(NoSuchChangeset, cache.get_changeset, 2) self.assertIsNone(cache.sync_changeset(2)) cset = cache.get_changeset(2) self.assertEqual('john', cset.author) self.assertEqual('Created directories', cset.message) self.assertEqual(t[2], cset.date) cset_changes = cset.get_changes() self.assertEqual(('branches', Node.DIRECTORY, Changeset.ADD, None, None), cset_changes.next()) self.assertEqual(('tags', Node.DIRECTORY, Changeset.ADD, None, None), cset_changes.next()) self.assertRaises(StopIteration, cset_changes.next) rows = self.env.db_query( "SELECT time,author,message FROM revision ORDER BY rev") self.assertEqual(4, len(rows)) self.assertEqual((to_utimestamp(t[0]), 'joe', '**empty**'), rows[0]) self.assertEqual((to_utimestamp(t[1]), 'joe', 'Import'), rows[1]) self.assertEqual((to_utimestamp(t[2]), 'john', 'Created directories'), rows[2]) self.assertEqual((to_utimestamp(t[3]), 'joe', 'Add COPYING'), rows[3]) def test_sync_changeset_with_string_rev(self): # ticket:11660 class MockCachedRepository(CachedRepository): def db_rev(self, rev): return '%010d' % rev t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=1) changesets = [ Mock(Changeset, repos, 0, 'empty', 'joe', t1, get_changes=lambda: []), Mock(Changeset, repos, 1, 'first', 'joe', t2, get_changes=lambda: []), ] cache = MockCachedRepository(self.env, repos, self.log) cache.sync_changeset('0') # not cached yet cache.sync_changeset(u'1') # not cached yet rows = self.env.db_query( "SELECT rev,author FROM revision ORDER BY rev") self.assertEqual(2, len(rows)) self.assertEquals(('0000000000', 'joe'), rows[0]) self.assertEquals(('0000000001', 'joe'), rows[1]) cache.sync_changeset(u'0') # cached cache.sync_changeset('1') # cached rows = self.env.db_query( "SELECT rev,author FROM revision ORDER BY rev") self.assertEqual(2, len(rows)) self.assertEquals(('0000000000', 'joe'), rows[0]) self.assertEquals(('0000000001', 'joe'), rows[1]) def test_get_changes(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (('0', to_utimestamp(t1), '', ''), []), (('1', to_utimestamp(t2), 'joe', 'Import'), [('trunk', 'D', 'A', None, None), ('trunk/RDME', 'F', 'A', None, None)]), ) repos = self.get_repos() cache = CachedRepository(self.env, repos, self.log) self.assertEqual('1', cache.youngest_rev) changeset = cache.get_changeset(1) self.assertEqual('joe', changeset.author) self.assertEqual('Import', changeset.message) self.assertEqual(t2, changeset.date) changes = changeset.get_changes() self.assertEqual(('trunk', Node.DIRECTORY, Changeset.ADD, None, None), changes.next()) self.assertEqual(('trunk/RDME', Node.FILE, Changeset.ADD, None, None), changes.next()) self.assertRaises(StopIteration, changes.next)
class WikiTagProviderTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True, enable=['trac.*', 'tractags.*']) self.env.path = tempfile.mkdtemp() setup = TagSetup(self.env) # Current tractags schema is partially setup with enabled component. # Revert these changes for getting a clean setup. self._revert_tractags_schema_init() setup.upgrade_environment() self.perms = PermissionSystem(self.env) self.tag_s = TagSystem(self.env) self.tag_wp = WikiTagProvider(self.env) # Populate table with initial test data. self.env.db_transaction(""" INSERT INTO tags (tagspace, name, tag) VALUES ('wiki', 'WikiStart', 'tag1') """) self.req = Mock(authname='editor') # Mock an anonymous request. self.req.perm = PermissionCache(self.env) self.realm = 'wiki' self.tags = ['tag1'] def tearDown(self): self.env.shutdown() shutil.rmtree(self.env.path) # Helpers def _revert_tractags_schema_init(self): _revert_tractags_schema_init(self.env) # Tests def test_get_tags(self): resource = Resource('wiki', 'WikiStart') self.assertEquals([tag for tag in self.tag_wp.get_resource_tags(self.req, resource)], self.tags) def test_exclude_template_tags(self): # Populate table with more test data. self.env.db_transaction(""" INSERT INTO tags (tagspace, name, tag) VALUES ('wiki', 'PageTemplates/Template', 'tag2') """) tags = ['tag1', 'tag2'] self.assertEquals(self.tag_s.get_all_tags(self.req).keys(), self.tags) self.env.config.set('tags', 'query_exclude_wiki_templates', False) self.assertEquals(self.tag_s.get_all_tags(self.req).keys(), tags) def test_set_tags_no_perms(self): resource = Resource('wiki', 'TaggedPage') self.assertRaises(PermissionError, self.tag_wp.set_resource_tags, self.req, resource, self.tags) def test_set_tags(self): resource = Resource('wiki', 'TaggedPage') self.req.perm = PermissionCache(self.env, username='******') # Shouldn't raise an error with appropriate permission. self.tag_wp.set_resource_tags(self.req, resource, self.tags) self.tag_wp.set_resource_tags(self.req, resource, ['tag2']) # Check change records. rows = self.env.db_query(""" SELECT author,oldtags,newtags FROM tags_change WHERE tagspace=%s AND name=%s ORDER by time DESC """, ('wiki', 'TaggedPage')) self.assertEqual(rows[0], ('editor', 'tag1', 'tag2')) self.assertEqual(rows[1], ('editor', '', 'tag1'))
class SessionTestCase(unittest.TestCase): """Unit tests for the persistent session support.""" def setUp(self): self.env = EnvironmentStub() def tearDown(self): self.env.reset_db() def test_new_session(self): """ Verify that a session cookie gets sent back to the client for a new session. """ req = MockRequest(self.env, authname='anonymous') session = Session(self.env, req) self.assertEqual(session.sid, req.outcookie['trac_session'].value) self.assertEqual( 0, self.env.db_query("SELECT COUNT(*) FROM session")[0][0]) def test_anonymous_session(self): """ Verify that session variables are stored in the database. """ req = MockRequest(self.env, authname='anonymous') req.incookie['trac_session'] = '123456' session = Session(self.env, req) self.assertEqual('123456', session.sid) self.assertNotIn('trac_session', req.outcookie) def _test_authenticated_session(self, username): """ Verifies that a session cookie does not get used if the user is logged in, and that Trac expires the cookie. """ req = MockRequest(self.env, authname=username) req.incookie['trac_session'] = '123456' session = Session(self.env, req) self.assertEqual(username, session.sid) session['foo'] = 'bar' session.save() self.assertEqual(0, req.outcookie['trac_session']['expires']) def test_authenticated_session(self): self._test_authenticated_session('john') self._test_authenticated_session('j.smith') self._test_authenticated_session(u'Jöhn') # non-ascii username self._test_authenticated_session('*****@*****.**') # LDAP username def _test_session_promotion(self, username): """ Verifies that an existing anonymous session gets promoted to an authenticated session when the user logs in. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") req = MockRequest(self.env, authname=username) req.incookie['trac_session'] = '123456' session = Session(self.env, req) self.assertEqual(username, session.sid) session.save() self.assertEqual([(username, 1)], self.env.db_query( """SELECT sid, authenticated FROM session WHERE sid=%s""", (username, ))) def test_session_promotion(self): self._test_session_promotion('john') self._test_session_promotion('j.smith') self._test_session_promotion(u'Jöhn') # non-ascii username self._test_session_promotion('*****@*****.**') # LDAP username sessions = self.env.db_query("SELECT sid, authenticated FROM session") self.assertEqual( set([('john', 1), ('j.smith', 1), (u'Jöhn', 1), ('*****@*****.**', 1)]), set(sessions)) def _test_new_session_promotion(self, username): """ Verifies that even without a preexisting anonymous session, an authenticated session will be created when the user logs in. (same test as above without the initial INSERT) """ with self.env.db_transaction: req = MockRequest(self.env, authname=username) req.incookie['trac_session'] = '123456' session = Session(self.env, req) self.assertEqual(username, session.sid) session.save() self.assertEqual([(username, 1)], self.env.db_query( """SELECT sid, authenticated FROM session WHERE sid=%s""", (username, ))) def test_new_session_promotion(self): self._test_new_session_promotion('john') self._test_new_session_promotion('j.smith') self._test_new_session_promotion(u'Jöhn') # non-ascii username self._test_new_session_promotion('*****@*****.**') # LDAP username sessions = self.env.db_query("SELECT sid, authenticated FROM session") self.assertEqual( set([('john', 1), ('j.smith', 1), (u'Jöhn', 1), ('*****@*****.**', 1)]), set(sessions)) def test_as_int(self): with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 1, 0)") db.executemany( """ INSERT INTO session_attribute VALUES (%s,%s,%s,%s) """, (('123456', 1, 'foo', 'bar'), ('123456', 1, 'baz', 3))) req = MockRequest(self.env, authname='123456') session = Session(self.env, req) self.assertEqual('bar', session.get('foo')) self.assertEqual(2, session.as_int('foo', 2)) self.assertEqual(3, session.as_int('baz', 1)) self.assertEqual(2, session.as_int('baz', 1, max=2)) self.assertEqual(4, session.as_int('baz', 1, min=4)) self.assertIsNone(session.as_int('bat')) def test_as_bool(self): with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 1, 0)") db.executemany( """ INSERT INTO session_attribute VALUES (%s,%s,%s,%s) """, (('123456', 1, 'foo', 'bar'), ('123456', 1, 'baz', 1))) req = MockRequest(self.env, authname='123456') session = Session(self.env, req) self.assertEqual('bar', session.get('foo')) self.assertIsNone(session.as_bool('foo', None)) self.assertTrue(session.as_int('baz')) self.assertTrue(session.as_int('baz', False)) self.assertIsNone(session.as_bool('bat')) def test_add_anonymous_session_var(self): """ Verify that new variables are inserted into the 'session' table in the database for an anonymous session. """ req = MockRequest(self.env, authname='anonymous') req.incookie['trac_session'] = '123456' session = Session(self.env, req) session['foo'] = 'bar' session.save() self.assertEqual( 'bar', self.env.db_query( "SELECT value FROM session_attribute WHERE sid='123456'")[0] [0]) def test_modify_anonymous_session_var(self): """ Verify that modifying an existing variable updates the 'session' table accordingly for an anonymous session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) req = MockRequest(self.env, authname='anonymous') req.incookie['trac_session'] = '123456' session = Session(self.env, req) self.assertEqual('bar', session['foo']) session['foo'] = 'baz' session.save() self.assertEqual( 'baz', self.env.db_query( "SELECT value FROM session_attribute WHERE sid='123456'")[0] [0]) def test_delete_anonymous_session_var(self): """ Verify that modifying a variable updates the 'session' table accordingly for an anonymous session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) req = MockRequest(self.env, authname='anonymous') req.incookie['trac_session'] = '123456' session = Session(self.env, req) self.assertEqual('bar', session['foo']) del session['foo'] session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='123456' AND name='foo' """)[0][0]) def test_purge_anonymous_session(self): """ Verify that old sessions get purged. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, %s)", (0, )) db("INSERT INTO session VALUES ('987654', 0, %s)", (int(time_now() - PURGE_AGE - 3600), )) db(""" INSERT INTO session_attribute VALUES ('987654', 0, 'foo', 'bar') """) # We need to modify a different session to trigger the purging req = MockRequest(self.env, authname='anonymous') req.incookie['trac_session'] = '123456' session = Session(self.env, req) session['foo'] = 'bar' session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session WHERE sid='987654' AND authenticated=0 """)[0][0]) def test_delete_empty_session(self): """ Verify that a session gets deleted when it doesn't have any data except for the 'last_visit' timestamp. """ now = time_now() # Make sure the session has data so that it doesn't get dropped with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, %s)", (int(now - UPDATE_INTERVAL - 3600), )) db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) req = MockRequest(self.env, authname='anonymous') req.incookie['trac_session'] = '123456' session = Session(self.env, req) del session['foo'] session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session WHERE sid='123456' AND authenticated=0 """)[0][0]) def test_change_anonymous_session(self): """ Verify that changing from one anonymous session to an inexisting anonymous session creates the new session and doesn't carry over variables from the previous session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) req = MockRequest(self.env, authname='anonymous') req.incookie['trac_session'] = '123456' session = Session(self.env, req) self.assertEqual({'foo': 'bar'}, session) session.get_session('7890') session['baz'] = 'moo' session.save() self.assertEqual({'baz': 'moo'}, session) with self.env.db_query as db: self.assertEqual( 1, db(""" SELECT COUNT(*) FROM session WHERE sid='7890' AND authenticated=0 """)[0][0]) self.assertEqual([('baz', 'moo')], db(""" SELECT name, value FROM session_attribute WHERE sid='7890' AND authenticated=0 """)) def test_add_authenticated_session_var(self): """ Verify that new variables are inserted into the 'session' table in the database for an authenticated session. """ req = MockRequest(self.env, authname='john') session = Session(self.env, req) session['foo'] = 'bar' session.save() self.assertEqual( 'bar', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_modify_authenticated_session_var(self): """ Verify that modifying an existing variable updates the 'session' table accordingly for an authenticated session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john',1,'foo','bar')") req = MockRequest(self.env, authname='john') session = Session(self.env, req) self.assertEqual('bar', session['foo']) session['foo'] = 'baz' session.save() self.assertEqual( 'baz', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_authenticated_session_independence_var(self): """ Verify that an anonymous session with the same name as an authenticated session doesn't interfere with the latter. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john',1,'foo','bar')") self.assertEqual( 'bar', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND authenticated=1 AND name='foo' """)[0][0]) req = MockRequest(self.env, authname='anonymous') req.incookie['trac_session'] = 'john' session = Session(self.env, req) self.assertTrue('foo' not in session) session['foo'] = 'baz' session.save() rows = self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND authenticated=1 AND name='foo' """) self.assertEqual(1, len(rows)) self.assertEqual('bar', rows[0][0]) rows = self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND authenticated=0 AND name='foo' """) self.assertEqual(1, len(rows)) self.assertEqual('baz', rows[0][0]) def test_delete_authenticated_session_var(self): """ Verify that deleting a variable updates the 'session' table accordingly for an authenticated session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')" ) req = MockRequest(self.env, authname='john') session = Session(self.env, req) self.assertEqual('bar', session['foo']) del session['foo'] session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_update_session(self): """ Verify that accessing a session after one day updates the sessions 'last_visit' variable so that the session doesn't get purged. """ now = time_now() # Make sure the session has data so that it doesn't get dropped with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 1)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) req = MockRequest(self.env, authname='anonymous') req.incookie['trac_session'] = '123456' session = Session(self.env, req) session['modified'] = True session.save() # updating does require modifications self.assertEqual(PURGE_AGE, req.outcookie['trac_session']['expires']) self.assertAlmostEqual( now, int( self.env.db_query(""" SELECT last_visit FROM session WHERE sid='123456' AND authenticated=0 """)[0][0]), -1) def test_modify_detached_session(self): """ Verify that modifying a variable in a session not associated with a request updates the database accordingly. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')" ) session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) session['foo'] = 'baz' session.save() self.assertEqual( 'baz', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_delete_detached_session_var(self): """ Verify that removing a variable in a session not associated with a request deletes the variable from the database. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')" ) session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) del session['foo'] session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_session_set(self): """Verify that setting a variable in a session to the default value removes it from the session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')" ) session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) # Setting the variable to the default value removes the variable with self.env.db_transaction as db: session.set('foo', 'default', 'default') session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) # Setting the variable to a value different from the default sets it with self.env.db_transaction as db: session.set('foo', 'something', 'default') session.save() self.assertEqual( 'something', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_session_set_email(self): """Setting session email invalidates known_users cache.""" session = DetachedSession(self.env, 'user') session['email'] = '*****@*****.**' self.assertEqual([], list(self.env.get_known_users())) session.save() email = None for sid, name, email in self.env.get_known_users(): if sid == 'user': break self.assertEqual(session['email'], email) def test_session_set_name(self): """Setting session name invalidates known_users cache.""" session = DetachedSession(self.env, 'user') session['name'] = 'The User' self.assertEqual([], list(self.env.get_known_users())) session.save() name = None for sid, name, email in self.env.get_known_users(): if sid == 'user': break self.assertEqual(session['name'], name) def test_session_admin_list(self): auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) # Verify the empty case self.assertRaises(StopIteration, sess_admin._get_list([]).next) self.assertEqual([i for i in sess_admin._get_list(['authenticated'])], auth_list) self.assertEqual([i for i in sess_admin._get_list(['anonymous'])], anon_list) self.assertEqual([i for i in sess_admin._get_list(['*'])], all_list) self.assertEqual([i for i in sess_admin._get_list(['name00'])][0], auth_list[0]) self.assertEqual([i for i in sess_admin._get_list(['name10:0'])][0], anon_list[0]) self.assertEqual( [i for i in sess_admin._get_list(['name00', 'name01', 'name02'])], all_list[:3]) def test_session_admin_add(self): _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) self.assertRaises(Exception, sess_admin._do_add, 'name00') sess_admin._do_add('john') self.assertEqual({}, get_session_attrs(self.env, 'john')) self.assertIn(('john', None, None), list(self.env.get_known_users())) sess_admin._do_add('john1', 'John1') self.assertEqual({'name': 'John1'}, get_session_attrs(self.env, 'john1')) self.assertIn(('john1', 'John1', None), list(self.env.get_known_users())) sess_admin._do_add('john2', 'John2', '*****@*****.**') self.assertEqual({ 'name': 'John2', 'email': '*****@*****.**' }, get_session_attrs(self.env, 'john2')) self.assertIn(('john2', 'John2', '*****@*****.**'), list(self.env.get_known_users())) sess_admin._do_add('alice1', None, '*****@*****.**') self.assertEqual({'email': '*****@*****.**'}, get_session_attrs(self.env, 'alice1')) sess_admin._do_add('alice2', '', '*****@*****.**') self.assertEqual({'email': '*****@*****.**'}, get_session_attrs(self.env, 'alice2')) sess_admin._do_add('bob1', 'Bob 1', None) self.assertEqual({'name': 'Bob 1'}, get_session_attrs(self.env, 'bob1')) sess_admin._do_add('bob2', 'Bob 2', '') self.assertEqual({'name': 'Bob 2'}, get_session_attrs(self.env, 'bob2')) sess_admin._do_add('charlie1', '', '') self.assertEqual({}, get_session_attrs(self.env, 'charlie1')) sess_admin._do_add('charlie2', None, None) self.assertEqual({}, get_session_attrs(self.env, 'charlie2')) def test_session_admin_set(self): _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) self.assertRaises(TracError, sess_admin._do_set, 'name', 'nothere', 'foo') self.env.get_known_users() # Prep the cache self.assertIn(('name00', 'val00', 'val00'), list(self.env.get_known_users())) sess_admin._do_set('name', 'name00', 'john') self.assertEqual({ 'name': 'john', 'email': 'val00' }, get_session_attrs(self.env, 'name00')) self.assertIn(('name00', 'john', 'val00'), list(self.env.get_known_users())) sess_admin._do_set('email', 'name00', '*****@*****.**') self.assertEqual({ 'name': 'john', 'email': '*****@*****.**' }, get_session_attrs(self.env, 'name00')) self.assertIn(('name00', 'john', '*****@*****.**'), list(self.env.get_known_users())) sess_admin._do_set('default_handler', 'name00', 'SearchModule') self.assertEqual( { 'name': 'john', 'email': '*****@*****.**', 'default_handler': 'SearchModule' }, get_session_attrs(self.env, 'name00')) sess_admin._do_set('name', 'name00', '') self.assertEqual( { 'email': '*****@*****.**', 'default_handler': 'SearchModule' }, get_session_attrs(self.env, 'name00')) sess_admin._do_set('email', 'name00', '') self.assertEqual({'default_handler': 'SearchModule'}, get_session_attrs(self.env, 'name00')) sess_admin._do_set('default_handler', 'name00', '') self.assertEqual({}, get_session_attrs(self.env, 'name00')) def test_session_admin_delete(self): _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) self.assertIn(('name00', 'val00', 'val00'), list(self.env.get_known_users())) sess_admin._do_delete('name00') self.assertEqual(None, get_session_attrs(self.env, 'name00')) self.assertNotIn(('name00', 'val00', 'val00'), list(self.env.get_known_users())) sess_admin._do_delete('nothere') self.assertEqual(None, get_session_attrs(self.env, 'nothere')) auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin._do_delete('anonymous') result = [i for i in sess_admin._get_list(['*'])] self.assertEqual(result, auth_list) def test_session_admin_purge(self): sess_admin = SessionAdmin(self.env) auth_list, anon_list, all_list = \ _prep_session_table(self.env, spread_visits=True) sess_admin._do_purge('2010-01-02') result = [i for i in sess_admin._get_list(['*'])] self.assertEqual(result, auth_list + anon_list) self.assertEqual({ 'name': 'val10', 'email': 'val10' }, get_session_attrs(self.env, anon_list[0][0])) self.assertEqual({ 'name': 'val11', 'email': 'val11' }, get_session_attrs(self.env, anon_list[1][0])) auth_list, anon_list, all_list = \ _prep_session_table(self.env, spread_visits=True) sess_admin._do_purge('2010-01-12') result = [i for i in sess_admin._get_list(['*'])] self.assertEqual(result, auth_list + anon_list[1:]) rows = self.env.db_query( """ SELECT name, value FROM session_attribute WHERE sid = %s """, (anon_list[0][0], )) self.assertEqual([], rows) self.assertEqual(None, get_session_attrs(self.env, anon_list[0][0])) self.assertEqual({ 'name': 'val11', 'email': 'val11' }, get_session_attrs(self.env, anon_list[1][0])) def test_session_get_session_with_invalid_sid(self): req = MockRequest(self.env, authname='anonymous') session = Session(self.env, req) session.get_session('0123456789') self.assertEqual('0123456789', session.sid) session.get_session('abcxyz') self.assertEqual('abcxyz', session.sid) session.get_session('abc123xyz') self.assertEqual('abc123xyz', session.sid) self.assertRaises(TracError, session.get_session, 'abc 123 xyz') self.assertRaises(TracError, session.get_session, 'abc-123-xyz') self.assertRaises(TracError, session.get_session, 'abc<i>123</i>xyz') self.assertRaises(TracError, session.get_session, u'abc123xÿz') self.assertRaises(TracError, session.get_session, u'abc¹₂³xyz') # Unicode digits def test_session_change_id_with_invalid_sid(self): req = MockRequest(self.env, authname='anonymous', base_path='/') session = Session(self.env, req) session.change_sid('0123456789') self.assertEqual('0123456789', session.sid) session.change_sid('abcxyz') self.assertEqual('abcxyz', session.sid) session.change_sid('abc123xyz') self.assertEqual('abc123xyz', session.sid) self.assertRaises(TracError, session.change_sid, 'abc 123 xyz') self.assertRaises(TracError, session.change_sid, 'abc-123-xyz') self.assertRaises(TracError, session.change_sid, 'abc<i>123</i>xyz') self.assertRaises(TracError, session.change_sid, u'abc123xÿz') self.assertRaises(TracError, session.change_sid, u'abc¹₂³xyz') # Unicode digits
class DatabaseManagerTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True) self.dbm = DatabaseManager(self.env) def tearDown(self): self.env.reset_db() def test_destroy_db(self): """Database doesn't exist after calling destroy_db.""" self.env.db_query("SELECT name FROM system") self.assertIsNotNone(self.dbm._cnx_pool) self.dbm.destroy_db() self.assertIsNone(self.dbm._cnx_pool) # No connection pool scheme, params = parse_connection_uri(get_dburi()) if scheme != 'postgres' or params.get('schema', 'public') != 'public': self.assertFalse(self.dbm.db_exists()) else: self.assertEqual([], self.dbm.get_table_names()) def test_get_column_names(self): """Get column names for the default database.""" for table in default_schema: column_names = [col.name for col in table.columns] self.assertEqual(column_names, self.dbm.get_column_names(table.name)) def test_get_default_database_version(self): """Get database version for the default entry named `database_version`. """ self.assertEqual(default_db_version, self.dbm.get_database_version()) def test_get_table_names(self): """Get table names for the default database.""" self.assertEqual(sorted(table.name for table in default_schema), sorted(self.dbm.get_table_names())) def test_has_table(self): self.assertIs(True, self.dbm.has_table('system')) self.assertIs(True, self.dbm.has_table('wiki')) self.assertIs(False, self.dbm.has_table('trac')) self.assertIs(False, self.dbm.has_table('blah.blah')) def test_set_default_database_version(self): """Set database version for the default entry named `database_version`. """ new_db_version = default_db_version + 1 self.dbm.set_database_version(new_db_version) self.assertEqual(new_db_version, self.dbm.get_database_version()) # Restore the previous version to avoid destroying the database # on teardown self.dbm.set_database_version(default_db_version) self.assertEqual(default_db_version, self.dbm.get_database_version()) def test_set_get_plugin_database_version(self): """Get and set database version for an entry with an arbitrary name. """ name = 'a_trac_plugin_version' db_ver = 1 self.assertFalse(self.dbm.get_database_version(name)) self.dbm.set_database_version(db_ver, name) self.assertEqual(db_ver, self.dbm.get_database_version(name)) def test_get_sequence_names(self): sequence_names = [] if self.dbm.connection_uri.startswith('postgres'): for table in default_schema: for column in table.columns: if column.name == 'id' and column.auto_increment: sequence_names.append(table.name) sequence_names.sort() self.assertEqual(sequence_names, self.dbm.get_sequence_names())
class MilestoneTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True) self.env.path = tempfile.mkdtemp(prefix='trac-tempenv-') self.created_at = datetime(2001, 1, 1, tzinfo=utc) self.updated_at = self.created_at + timedelta(seconds=1) def tearDown(self): shutil.rmtree(self.env.path) self.env.reset_db() def _create_milestone(self, **values): milestone = Milestone(self.env) for k, v in values.iteritems(): setattr(milestone, k, v) return milestone def _insert_ticket(self, when=None, **kwargs): ticket = Ticket(self.env) for name, value in kwargs.iteritems(): ticket[name] = value ticket.insert(when or self.created_at) return ticket def _update_ticket(self, ticket, author=None, comment=None, when=None, **kwargs): for name, value in kwargs.iteritems(): ticket[name] = value ticket.save_changes(author, comment, when or self.updated_at) def test_new_milestone(self): milestone = Milestone(self.env) self.assertFalse(milestone.exists) self.assertIsNone(milestone.name) self.assertIsNone(milestone.due) self.assertIsNone(milestone.completed) self.assertEqual('', milestone.description) def test_new_milestone_empty_name(self): """ Verifies that specifying an empty milestone name results in the milestone being correctly detected as non-existent. """ milestone = Milestone(self.env, '') self.assertFalse(milestone.exists) self.assertIsNone(milestone.name) self.assertIsNone(milestone.due) self.assertIsNone(milestone.completed) self.assertEqual('', milestone.description) def test_existing_milestone(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") milestone = Milestone(self.env, 'Test') self.assertTrue(milestone.exists) self.assertEqual('Test', milestone.name) self.assertIsNone(milestone.due) self.assertIsNone(milestone.completed) self.assertEqual('', milestone.description) def test_create_and_update_milestone(self): milestone = Milestone(self.env) milestone.name = 'Test' milestone.insert() self.assertEqual([('Test', 0, 0, '')], self.env.db_query(""" SELECT name, due, completed, description FROM milestone WHERE name='Test' """)) # Use the same model object to update the milestone milestone.description = 'Some text' milestone.update() self.assertEqual([('Test', 0, 0, 'Some text')], self.env.db_query(""" SELECT name, due, completed, description FROM milestone WHERE name='Test' """)) def test_move_tickets(self): self.env.db_transaction.executemany( "INSERT INTO milestone (name) VALUES (%s)", [('Test',), ('Testing',)]) tkt1 = self._insert_ticket(status='new', summary='Foo', milestone='Test') tkt2 = self._insert_ticket(status='new', summary='Bar', milestone='Test') self._update_ticket(tkt2, status='closed', resolution='fixed') milestone = Milestone(self.env, 'Test') milestone.move_tickets('Testing', 'anonymous', 'Move tickets') tkt1 = Ticket(self.env, tkt1.id) tkt2 = Ticket(self.env, tkt2.id) self.assertEqual('Testing', tkt1['milestone']) self.assertEqual('Testing', tkt2['milestone']) self.assertEqual(tkt1['changetime'], tkt2['changetime']) self.assertNotEqual(self.updated_at, tkt1['changetime']) def test_move_tickets_exclude_closed(self): self.env.db_transaction.executemany( "INSERT INTO milestone (name) VALUES (%s)", [('Test',), ('Testing',)]) tkt1 = self._insert_ticket(status='new', summary='Foo', milestone='Test') tkt2 = self._insert_ticket(status='new', summary='Bar', milestone='Test') self._update_ticket(tkt2, status='closed', resolution='fixed') milestone = Milestone(self.env, 'Test') milestone.move_tickets('Testing', 'anonymous', 'Move tickets', exclude_closed=True) tkt1 = Ticket(self.env, tkt1.id) tkt2 = Ticket(self.env, tkt2.id) self.assertEqual('Testing', tkt1['milestone']) self.assertEqual('Test', tkt2['milestone']) self.assertNotEqual(self.updated_at, tkt1['changetime']) self.assertEqual(self.updated_at, tkt2['changetime']) def test_move_tickets_target_doesnt_exist(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") tkt1 = self._insert_ticket(status='new', summary='Foo', milestone='Test') tkt2 = self._insert_ticket(status='new', summary='Bar', milestone='Test') milestone = Milestone(self.env, 'Test') self.assertRaises(ResourceNotFound, milestone.move_tickets, 'Testing', 'anonymous') tkt1 = Ticket(self.env, tkt1.id) tkt2 = Ticket(self.env, tkt2.id) self.assertEqual('Test', tkt1['milestone']) self.assertEqual('Test', tkt2['milestone']) self.assertNotEqual(self.updated_at, tkt1['changetime']) self.assertNotEqual(self.updated_at, tkt2['changetime']) def test_create_milestone_without_name(self): milestone = Milestone(self.env) self.assertRaises(TracError, milestone.insert) def test_delete_milestone(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") tkt1 = self._insert_ticket(status='new', summary='Foo', milestone='Test') tkt2 = self._insert_ticket(status='new', summary='Bar', milestone='Test') self._update_ticket(tkt2, status='closed', resolution='fixed') milestone = Milestone(self.env, 'Test') milestone.delete() self.assertFalse(milestone.exists) self.assertEqual([], self.env.db_query("SELECT * FROM milestone WHERE name='Test'")) tkt1 = Ticket(self.env, tkt1.id) tkt2 = Ticket(self.env, tkt2.id) self.assertEqual('', tkt1['milestone']) self.assertEqual('', tkt2['milestone']) self.assertEqual(tkt1['changetime'], tkt2['changetime']) self.assertNotEqual(self.updated_at, tkt1['changetime']) def test_delete_milestone_with_attachment(self): milestone = Milestone(self.env) milestone.name = 'MilestoneWithAttachment' milestone.insert() attachment = Attachment(self.env, 'milestone', milestone.name) attachment.insert('foo.txt', StringIO(), 0, 1) milestone.delete() self.assertEqual(False, milestone.exists) attachments = Attachment.select(self.env, 'milestone', milestone.name) self.assertRaises(StopIteration, attachments.next) def test_delete_milestone_retarget_tickets(self): self.env.db_transaction.executemany( "INSERT INTO milestone (name) VALUES (%s)", [('Test',), ('Other',)]) tkt1 = self._insert_ticket(status='new', summary='Foo', milestone='Test') tkt2 = self._insert_ticket(status='new', summary='Bar', milestone='Test') self._update_ticket(tkt2, status='closed', resolution='fixed') milestone = Milestone(self.env, 'Test') milestone.delete(retarget_to='Other') self.assertFalse(milestone.exists) tkt1 = Ticket(self.env, tkt1.id) tkt2 = Ticket(self.env, tkt2.id) self.assertEqual('Other', tkt1['milestone']) self.assertEqual('Other', tkt2['milestone']) self.assertEqual(tkt1['changetime'], tkt2['changetime']) self.assertNotEqual(self.updated_at, tkt1['changetime']) def test_update_milestone(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") milestone = Milestone(self.env, 'Test') t1 = datetime(2001, 01, 01, tzinfo=utc) t2 = datetime(2002, 02, 02, tzinfo=utc) milestone.due = t1 milestone.completed = t2 milestone.description = 'Foo bar' milestone.update() self.assertEqual( [('Test', to_utimestamp(t1), to_utimestamp(t2), 'Foo bar')], self.env.db_query("SELECT * FROM milestone WHERE name='Test'")) def test_update_milestone_without_name(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") milestone = Milestone(self.env, 'Test') milestone.name = None self.assertRaises(TracError, milestone.update) def test_rename_milestone(self): milestone = Milestone(self.env) milestone.name = 'OldName' milestone.insert() attachment = Attachment(self.env, 'milestone', 'OldName') attachment.insert('foo.txt', StringIO(), 0, 1) milestone = Milestone(self.env, 'OldName') milestone.name = 'NewName' milestone.update() self.assertRaises(ResourceNotFound, Milestone, self.env, 'OldName') self.assertEqual('NewName', Milestone(self.env, 'NewName').name) attachments = Attachment.select(self.env, 'milestone', 'OldName') self.assertRaises(StopIteration, attachments.next) attachments = Attachment.select(self.env, 'milestone', 'NewName') self.assertEqual('foo.txt', attachments.next().filename) self.assertRaises(StopIteration, attachments.next) def test_rename_milestone_retarget_tickets(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") tkt1 = self._insert_ticket(status='new', summary='Foo', milestone='Test') tkt2 = self._insert_ticket(status='new', summary='Bar', milestone='Test') self._update_ticket(tkt2, status='closed', resolution='fixed') milestone = Milestone(self.env, 'Test') milestone.name = 'Testing' milestone.update() tkt1 = Ticket(self.env, tkt1.id) tkt2 = Ticket(self.env, tkt2.id) self.assertEqual('Testing', tkt1['milestone']) self.assertEqual('Testing', tkt2['milestone']) self.assertEqual(tkt1['changetime'], tkt2['changetime']) self.assertNotEqual(self.updated_at, tkt1['changetime']) def test_select_milestones(self): self.env.db_transaction.executemany( "INSERT INTO milestone (name) VALUES (%s)", [('1.0',), ('2.0',)]) milestones = list(Milestone.select(self.env)) self.assertEqual('1.0', milestones[0].name) self.assertTrue(milestones[0].exists) self.assertEqual('2.0', milestones[1].name) self.assertTrue(milestones[1].exists) def test_change_listener_created(self): listener = TestMilestoneChangeListener(self.env) milestone = self._create_milestone(name='Milestone 1') milestone.insert() self.assertEqual('created', listener.action) self.assertEqual(milestone, listener.milestone) def test_change_listener_changed(self): listener = TestMilestoneChangeListener(self.env) milestone = self._create_milestone( name='Milestone 1', due=datetime(2001, 01, 01, tzinfo=utc), description='The milestone description') milestone.insert() milestone.name = 'Milestone 2' milestone.completed = datetime(2001, 02, 03, tzinfo=utc) milestone.description = 'The changed description' milestone.update() self.assertEqual('changed', listener.action) self.assertEqual(milestone, listener.milestone) self.assertEqual({'name': 'Milestone 1', 'completed': None, 'description': 'The milestone description'}, listener.old_values) def test_change_listener_deleted(self): listener = TestMilestoneChangeListener(self.env) milestone = self._create_milestone(name='Milestone 1') milestone.insert() self.assertTrue(milestone.exists) milestone.delete() self.assertEqual('Milestone 1', milestone.name) self.assertFalse(milestone.exists) self.assertEqual('deleted', listener.action) self.assertEqual(milestone, listener.milestone)
class TicketCommentDeleteTestCase(TicketCommentTestCase): def setUp(self): self.env = EnvironmentStub(default_data=True) self.env.config.set('ticket-custom', 'foo', 'text') self.created = datetime(2001, 1, 1, 1, 0, 0, 0, utc) self._insert_ticket('Test ticket', self.created, owner='john', keywords='a, b, c', foo='initial') self.t1 = self.created + timedelta(seconds=1) self._modify_ticket('jack', 'Comment 1', self.t1, '1', foo='change 1') self.t2 = self.created + timedelta(seconds=2) self._modify_ticket('john', 'Comment 2', self.t2, '1.2', owner='jack', foo='change2') self.t3 = self.created + timedelta(seconds=3) self._modify_ticket('jim', 'Comment 3', self.t3, '3', keywords='a, b', foo='change3') self.t4 = self.created + timedelta(seconds=4) self._modify_ticket('joe', 'Comment 4', self.t4, '4', keywords='a', foo='change4') def tearDown(self): self.env.reset_db() def test_delete_last_comment(self): ticket = Ticket(self.env, self.id) self.assertEqual('a', ticket['keywords']) self.assertEqual('change4', ticket['foo']) t = datetime.now(utc) ticket.delete_change(cnum=4, when=t) self.assertEqual('a, b', ticket['keywords']) self.assertEqual('change3', ticket['foo']) self.assertIsNone(ticket.get_change(cnum=4)) self.assertIsNotNone(ticket.get_change(cnum=3)) self.assertEqual(t, ticket.time_changed) def test_delete_last_comment_when_custom_field_gone(self): """Regression test for http://trac.edgewall.org/ticket/10858""" ticket = Ticket(self.env, self.id) self.assertEqual('a', ticket['keywords']) self.assertEqual('change4', ticket['foo']) # we simulate the removal of the definition of the 'foo' custom field self.env.config.remove('ticket-custom', 'foo') del TicketSystem(self.env).fields del TicketSystem(self.env).custom_fields ticket = Ticket(self.env, self.id) # t = datetime.now(utc) ticket.delete_change(cnum=4, when=t) self.assertEqual('a, b', ticket['keywords']) # 'foo' is no longer defined for the ticket self.assertIsNone(ticket['foo']) # however, 'foo=change3' is still in the database self.assertEqual([('change3',)], self.env.db_query(""" SELECT value FROM ticket_custom WHERE ticket=%s AND name='foo' """, (self.id,))) self.assertIsNone(ticket.get_change(cnum=4)) self.assertIsNotNone(ticket.get_change(cnum=3)) self.assertEqual(t, ticket.time_changed) def test_delete_last_comment_by_date(self): ticket = Ticket(self.env, self.id) self.assertEqual('a', ticket['keywords']) self.assertEqual('change4', ticket['foo']) t = datetime.now(utc) ticket.delete_change(cdate=self.t4, when=t) self.assertEqual('a, b', ticket['keywords']) self.assertEqual('change3', ticket['foo']) self.assertIsNone(ticket.get_change(cdate=self.t4)) self.assertIsNotNone(ticket.get_change(cdate=self.t3)) self.assertEqual(t, ticket.time_changed) def test_delete_mid_comment(self): ticket = Ticket(self.env, self.id) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='a, b', new='a'), foo=dict(author='joe', old='change3', new='change4')) t = datetime.now(utc) ticket.delete_change(cnum=3, when=t) self.assertIsNone(ticket.get_change(cnum=3)) self.assertEqual('a', ticket['keywords']) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='a, b, c', new='a'), foo=dict(author='joe', old='change2', new='change4')) self.assertEqual(t, ticket.time_changed) def test_delete_mid_comment_by_date(self): ticket = Ticket(self.env, self.id) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='a, b', new='a'), foo=dict(author='joe', old='change3', new='change4')) t = datetime.now(utc) ticket.delete_change(cdate=self.t3, when=t) self.assertIsNone(ticket.get_change(cdate=self.t3)) self.assertEqual('a', ticket['keywords']) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='a, b, c', new='a'), foo=dict(author='joe', old='change2', new='change4')) self.assertEqual(t, ticket.time_changed) def test_delete_mid_comment_inconsistent(self): # Make oldvalue on keywords for change 4 inconsistent. This should # result in no change in oldvalue when deleting change 3. The # oldvalue of foo should change normally. self.env.db_transaction(""" UPDATE ticket_change SET oldvalue='1, 2' WHERE field='keywords' AND oldvalue='a, b' """) ticket = Ticket(self.env, self.id) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='1, 2', new='a'), foo=dict(author='joe', old='change3', new='change4')) ticket.delete_change(3) self.assertIsNone(ticket.get_change(3)) self.assertEqual('a', ticket['keywords']) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='1, 2', new='a'), foo=dict(author='joe', old='change2', new='change4')) def test_delete_all_comments(self): ticket = Ticket(self.env, self.id) ticket.delete_change(4) ticket.delete_change(3) ticket.delete_change(2) t = datetime.now(utc) ticket.delete_change(1, when=t) self.assertEqual(t, ticket.time_changed)
class TagModelTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True, enable=['trac.*', 'tractags.*']) self.env.path = tempfile.mkdtemp() self.perms = PermissionSystem(self.env) self.req = Mock(authname='editor') self.check_perm = WikiTagProvider(self.env).check_permission setup = TagSetup(self.env) # Current tractags schema is setup with enabled component anyway. # Revert these changes for getting default permissions inserted. self._revert_tractags_schema_init() setup.upgrade_environment() # Populate table with initial test data. self.env.db_transaction(""" INSERT INTO tags (tagspace, name, tag) VALUES ('wiki', 'WikiStart', 'tag1') """) self.realm = 'wiki' def tearDown(self): self.env.shutdown() shutil.rmtree(self.env.path) # Helpers def _revert_tractags_schema_init(self): with self.env.db_transaction as db: db("DROP TABLE IF EXISTS tags") db("DROP TABLE IF EXISTS tags_change") db("DELETE FROM system WHERE name='tags_version'") db("DELETE FROM permission WHERE action %s" % db.like(), ('TAGS_%',)) def _tags(self): tags = {} for name, tag in self.env.db_query(""" SELECT name,tag FROM tags """): if name in tags: tags[name].add(tag) else: tags[name] = set([tag]) return tags # Tests def test_get_tags(self): resource = Resource(self.realm, 'WikiStart') self.assertEquals([tag for tag in resource_tags(self.env, resource)], ['tag1']) def test_get_tagged_resource_no_perm(self): self.perms.revoke_permission('anonymous', 'WIKI_VIEW') perm = PermissionCache(self.env) tags = set(['tag1']) # Don't yield resource without permission - 'WIKI_VIEW' here. self.assertEqual([(res, tags) for res, tags in tagged_resources(self.env, self.check_perm, perm, self.realm, tags)], []) def test_get_tagged_resource(self): perm = PermissionCache(self.env) resource = Resource(self.realm, 'WikiStart') tags = set(['tag1']) self.assertEqual([(res, tags) for res, tags in tagged_resources(self.env, self.check_perm, perm, self.realm, tags)], [(resource, tags)]) def test_reparent(self): resource = Resource(self.realm, 'TaggedPage') old_name = 'WikiStart' tag_resource(self.env, resource, 'WikiStart', self.req.authname) self.assertEquals(dict(TaggedPage=set(['tag1'])), self._tags()) def test_tag_changes(self): # Add previously untagged resource. resource = Resource(self.realm, 'TaggedPage') tags = set(['tag1']) tag_resource(self.env, resource, author=self.req.authname, tags=tags) self.assertEquals(dict(TaggedPage=tags, WikiStart=tags), self._tags()) # Add new tag to already tagged resource. resource = Resource(self.realm, 'WikiStart') tags = set(['tag1', 'tag2']) tag_resource(self.env, resource, author=self.req.authname, tags=tags) self.assertEquals(dict(TaggedPage=set(['tag1']), WikiStart=tags), self._tags()) # Exchange tags for already tagged resource. tags = set(['tag1', 'tag3']) tag_resource(self.env, resource, author=self.req.authname, tags=tags) self.assertEquals(dict(TaggedPage=set(['tag1']), WikiStart=tags), self._tags()) # Delete a subset of tags for already tagged resource. tags = set(['tag3']) tag_resource(self.env, resource, author=self.req.authname, tags=tags) self.assertEquals(dict(TaggedPage=set(['tag1']), WikiStart=tags), self._tags()) # Empty tag iterable deletes all resource tag references. tags = tuple() tag_resource(self.env, resource, author=self.req.authname, tags=tags) self.assertEquals(dict(TaggedPage=set(['tag1'])), self._tags())
class ConnectionTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.schema = [ Table('HOURS', key='ID')[ Column('ID', auto_increment=True), Column('AUTHOR')], Table('blog', key='bid')[ Column('bid', auto_increment=True), Column('author') ] ] self.env.global_databasemanager.drop_tables(self.schema) self.env.global_databasemanager.create_tables(self.schema) def tearDown(self): self.env.global_databasemanager.drop_tables(self.schema) self.env.reset_db() def test_get_last_id(self): q = "INSERT INTO report (author) VALUES ('anonymous')" with self.env.db_transaction as db: cursor = db.cursor() cursor.execute(q) # Row ID correct before... id1 = db.get_last_id(cursor, 'report') db.commit() cursor.execute(q) # ... and after commit() db.commit() id2 = db.get_last_id(cursor, 'report') self.assertNotEqual(0, id1) self.assertEqual(id1 + 1, id2) def test_update_sequence_default_column(self): with self.env.db_transaction as db: db("INSERT INTO report (id, author) VALUES (42, 'anonymous')") cursor = db.cursor() db.update_sequence(cursor, 'report', 'id') self.env.db_transaction( "INSERT INTO report (author) VALUES ('next-id')") self.assertEqual(43, self.env.db_query( "SELECT id FROM report WHERE author='next-id'")[0][0]) def test_update_sequence_nondefault_column(self): with self.env.db_transaction as db: cursor = db.cursor() cursor.execute( "INSERT INTO blog (bid, author) VALUES (42, 'anonymous')") db.update_sequence(cursor, 'blog', 'bid') self.env.db_transaction( "INSERT INTO blog (author) VALUES ('next-id')") self.assertEqual(43, self.env.db_query( "SELECT bid FROM blog WHERE author='next-id'")[0][0]) def test_identifiers_need_quoting(self): """Test for regression described in comment:4:ticket:11512.""" with self.env.db_transaction as db: db("INSERT INTO %s (%s, %s) VALUES (42, 'anonymous')" % (db.quote('HOURS'), db.quote('ID'), db.quote('AUTHOR'))) cursor = db.cursor() db.update_sequence(cursor, 'HOURS', 'ID') with self.env.db_transaction as db: cursor = db.cursor() cursor.execute( "INSERT INTO %s (%s) VALUES ('next-id')" % (db.quote('HOURS'), db.quote('AUTHOR'))) last_id = db.get_last_id(cursor, 'HOURS', 'ID') self.assertEqual(43, last_id) def test_get_table_names(self): schema = default_schema + self.schema with self.env.db_query as db: self.assertEqual(sorted(table.name for table in schema), sorted(db.get_table_names())) def test_get_column_names(self): schema = default_schema + self.schema with self.env.db_transaction as db: for table in schema: db_columns = db.get_column_names(table.name) self.assertEqual(len(table.columns), len(db_columns)) for column in table.columns: self.assertIn(column.name, db_columns)
class ConnectionTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.schema = [ Table('HOURS', key='ID')[Column('ID', auto_increment=True), Column('AUTHOR')], Table('blog', key='bid')[Column('bid', auto_increment=True), Column('author'), Column('comment')] ] self.dbm = DatabaseManager(self.env) self.dbm.drop_tables(self.schema) self.dbm.create_tables(self.schema) def tearDown(self): DatabaseManager(self.env).drop_tables(self.schema) self.env.reset_db() def test_drop_column(self): """Data is preserved when column is dropped.""" table_data = [ ('blog', ('author', 'comment'), (('author1', 'comment one'), ('author2', 'comment two'))), ] self.dbm.insert_into_tables(table_data) with self.env.db_transaction as db: db.drop_column('blog', 'comment') data = list(self.env.db_query("SELECT * FROM blog")) self.assertEqual((1, 'author1'), data[0]) self.assertEqual((2, 'author2'), data[1]) def test_drop_column_no_exists(self): """Error is not raised when dropping non-existent column.""" table_data = [ ('blog', ('author', 'comment'), (('author1', 'comment one'), ('author2', 'comment two'))), ] self.dbm.insert_into_tables(table_data) with self.env.db_transaction as db: db.drop_column('blog', 'tags') data = list(self.env.db_query("SELECT * FROM blog")) self.assertEqual((1, 'author1', 'comment one'), data[0]) self.assertEqual((2, 'author2', 'comment two'), data[1]) def test_get_last_id(self): q = "INSERT INTO report (author) VALUES ('anonymous')" with self.env.db_transaction as db: cursor = db.cursor() cursor.execute(q) # Row ID correct before... id1 = db.get_last_id(cursor, 'report') db.commit() cursor.execute(q) # ... and after commit() db.commit() id2 = db.get_last_id(cursor, 'report') self.assertNotEqual(0, id1) self.assertEqual(id1 + 1, id2) def test_update_sequence_default_column_name(self): with self.env.db_transaction as db: db("INSERT INTO report (id, author) VALUES (42, 'anonymous')") cursor = db.cursor() db.update_sequence(cursor, 'report') self.env.db_transaction( "INSERT INTO report (author) VALUES ('next-id')") self.assertEqual( 43, self.env.db_query("SELECT id FROM report WHERE author='next-id'") [0][0]) def test_update_sequence_nondefault_column_name(self): with self.env.db_transaction as db: cursor = db.cursor() cursor.execute( "INSERT INTO blog (bid, author) VALUES (42, 'anonymous')") db.update_sequence(cursor, 'blog', 'bid') self.env.db_transaction("INSERT INTO blog (author) VALUES ('next-id')") self.assertEqual( 43, self.env.db_query("SELECT bid FROM blog WHERE author='next-id'")[0] [0]) def test_identifiers_need_quoting(self): """Test for regression described in comment:4:ticket:11512.""" with self.env.db_transaction as db: db("INSERT INTO %s (%s, %s) VALUES (42, 'anonymous')" % (db.quote('HOURS'), db.quote('ID'), db.quote('AUTHOR'))) cursor = db.cursor() db.update_sequence(cursor, 'HOURS', 'ID') with self.env.db_transaction as db: cursor = db.cursor() cursor.execute("INSERT INTO %s (%s) VALUES ('next-id')" % (db.quote('HOURS'), db.quote('AUTHOR'))) last_id = db.get_last_id(cursor, 'HOURS', 'ID') self.assertEqual(43, last_id) def test_get_table_names(self): schema = default_schema + self.schema with self.env.db_query as db: # Some DB (e.g. MariaDB) normalize the table names to lower case self.assertEqual( sorted(table.name.lower() for table in schema), sorted(name.lower() for name in db.get_table_names())) def test_get_column_names(self): schema = default_schema + self.schema with self.env.db_query as db: for table in schema: column_names = [col.name for col in table.columns] self.assertEqual(column_names, db.get_column_names(table.name))
class TicketCommentDeleteTestCase(TicketCommentTestCase): def setUp(self): self.env = EnvironmentStub(default_data=True) self.env.config.set('ticket-custom', 'foo', 'text') self.created = datetime(2001, 1, 1, 1, 0, 0, 0, utc) self._insert_ticket('Test ticket', self.created, owner='john', keywords='a, b, c', foo='initial') self.t1 = self.created + timedelta(seconds=1) self._modify_ticket('jack', 'Comment 1', self.t1, '1', foo='change 1') self.t2 = self.created + timedelta(seconds=2) self._modify_ticket('john', 'Comment 2', self.t2, '1.2', owner='jack', foo='change2') self.t3 = self.created + timedelta(seconds=3) self._modify_ticket('jim', 'Comment 3', self.t3, '3', keywords='a, b', foo='change3') self.t4 = self.created + timedelta(seconds=4) self._modify_ticket('joe', 'Comment 4', self.t4, '4', keywords='a', foo='change4') def tearDown(self): self.env.reset_db() def test_delete_last_comment(self): ticket = Ticket(self.env, self.id) self.assertEqual('a', ticket['keywords']) self.assertEqual('change4', ticket['foo']) t = datetime.now(utc) ticket.delete_change(cnum=4, when=t) self.assertEqual('a, b', ticket['keywords']) self.assertEqual('change3', ticket['foo']) self.assertEqual(None, ticket.get_change(cnum=4)) self.assertNotEqual(None, ticket.get_change(cnum=3)) self.assertEqual(t, ticket.time_changed) def test_delete_last_comment_when_custom_field_gone(self): """Regression test for http://trac.edgewall.org/ticket/10858""" ticket = Ticket(self.env, self.id) self.assertEqual('a', ticket['keywords']) self.assertEqual('change4', ticket['foo']) # we simulate the removal of the definition of the 'foo' custom field self.env.config.remove('ticket-custom', 'foo') del TicketSystem(self.env).fields del TicketSystem(self.env).custom_fields ticket = Ticket(self.env, self.id) # t = datetime.now(utc) ticket.delete_change(cnum=4, when=t) self.assertEqual('a, b', ticket['keywords']) # 'foo' is no longer defined for the ticket self.assertEqual(None, ticket['foo']) # however, 'foo=change3' is still in the database self.assertEqual([('change3',)], self.env.db_query(""" SELECT value FROM ticket_custom WHERE ticket=%s AND name='foo' """, (self.id,))) self.assertEqual(None, ticket.get_change(cnum=4)) self.assertNotEqual(None, ticket.get_change(cnum=3)) self.assertEqual(t, ticket.time_changed) def test_delete_last_comment_by_date(self): ticket = Ticket(self.env, self.id) self.assertEqual('a', ticket['keywords']) self.assertEqual('change4', ticket['foo']) t = datetime.now(utc) ticket.delete_change(cdate=self.t4, when=t) self.assertEqual('a, b', ticket['keywords']) self.assertEqual('change3', ticket['foo']) self.assertEqual(None, ticket.get_change(cdate=self.t4)) self.assertNotEqual(None, ticket.get_change(cdate=self.t3)) self.assertEqual(t, ticket.time_changed) def test_delete_mid_comment(self): ticket = Ticket(self.env, self.id) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='a, b', new='a'), foo=dict(author='joe', old='change3', new='change4')) t = datetime.now(utc) ticket.delete_change(cnum=3, when=t) self.assertEqual(None, ticket.get_change(cnum=3)) self.assertEqual('a', ticket['keywords']) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='a, b, c', new='a'), foo=dict(author='joe', old='change2', new='change4')) self.assertEqual(t, ticket.time_changed) def test_delete_mid_comment_by_date(self): ticket = Ticket(self.env, self.id) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='a, b', new='a'), foo=dict(author='joe', old='change3', new='change4')) t = datetime.now(utc) ticket.delete_change(cdate=self.t3, when=t) self.assertEqual(None, ticket.get_change(cdate=self.t3)) self.assertEqual('a', ticket['keywords']) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='a, b, c', new='a'), foo=dict(author='joe', old='change2', new='change4')) self.assertEqual(t, ticket.time_changed) def test_delete_mid_comment_inconsistent(self): # Make oldvalue on keywords for change 4 inconsistent. This should # result in no change in oldvalue when deleting change 3. The # oldvalue of foo should change normally. self.env.db_transaction(""" UPDATE ticket_change SET oldvalue='1, 2' WHERE field='keywords' AND oldvalue='a, b' """) ticket = Ticket(self.env, self.id) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='1, 2', new='a'), foo=dict(author='joe', old='change3', new='change4')) ticket.delete_change(3) self.assertEqual(None, ticket.get_change(3)) self.assertEqual('a', ticket['keywords']) self.assertChange(ticket, 4, self.t4, 'joe', comment=dict(author='joe', old='4', new='Comment 4'), keywords=dict(author='joe', old='1, 2', new='a'), foo=dict(author='joe', old='change2', new='change4')) def test_delete_all_comments(self): ticket = Ticket(self.env, self.id) ticket.delete_change(4) ticket.delete_change(3) ticket.delete_change(2) t = datetime.now(utc) ticket.delete_change(1, when=t) self.assertEqual(t, ticket.time_changed)
class ModifyTableTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.dbm = DatabaseManager(self.env) self.schema = [ Table('table1', key='col1')[Column('col1', auto_increment=True), Column('col2'), Column('col3'), ], Table('table2', key='col1')[Column('col1'), Column('col2'), ], Table('table3', key='col2')[Column('col1'), Column('col2', type='int'), Column('col3')] ] self.dbm.create_tables(self.schema) self.new_schema = copy.deepcopy([self.schema[0], self.schema[2]]) self.new_schema[0].remove_columns(('col2', )) self.new_schema[1].columns.append(Column('col4')) def tearDown(self): self.dbm.drop_tables(self.schema) self.env.reset_db() def _insert_data(self): table_data = [ ('table1', ('col2', 'col3'), (('data1', 'data2'), ('data3', 'data4'))), ('table2', ('col1', 'col2'), (('data5', 'data6'), ('data7', 'data8'))), ('table3', ('col1', 'col2', 'col3'), (('data9', 10, 'data11'), ('data12', 13, 'data14'))), ] self.dbm.insert_into_tables(table_data) def test_drop_columns(self): """Data is preserved when column is dropped.""" self._insert_data() self.dbm.drop_columns('table1', ('col2', )) self.assertEqual(['col1', 'col3'], self.dbm.get_column_names('table1')) data = list(self.env.db_query("SELECT * FROM table1")) self.assertEqual((1, 'data2'), data[0]) self.assertEqual((2, 'data4'), data[1]) def test_drop_columns_multiple_columns(self): """Data is preserved when columns are dropped.""" self._insert_data() self.dbm.drop_columns('table3', ('col1', 'col3')) self.assertEqual(['col2'], self.dbm.get_column_names('table3')) data = list(self.env.db_query("SELECT * FROM table3")) self.assertEqual((10, ), data[0]) self.assertEqual((13, ), data[1]) def test_upgrade_tables_have_new_schema(self): """The upgraded tables have the new schema.""" self.dbm.upgrade_tables(self.new_schema) for table in self.new_schema: self.assertEqual([col.name for col in table.columns], self.dbm.get_column_names(table.name)) def test_upgrade_tables_data_is_migrated(self): """The data is migrated to the upgraded tables.""" self._insert_data() self.dbm.upgrade_tables(self.new_schema) self.env.db_transaction(""" INSERT INTO table1 (col3) VALUES ('data12') """) data = list(self.env.db_query("SELECT * FROM table1")) self.assertEqual((1, 'data2'), data[0]) self.assertEqual((2, 'data4'), data[1]) self.assertEqual( 3, self.env.db_query(""" SELECT col1 FROM table1 WHERE col3='data12'""")[0][0]) data = list(self.env.db_query("SELECT * FROM table2")) self.assertEqual(('data5', 'data6'), data[0]) self.assertEqual(('data7', 'data8'), data[1]) data = list(self.env.db_query("SELECT * FROM table3")) self.assertEqual(('data9', 10, 'data11', None), data[0]) self.assertEqual(('data12', 13, 'data14', None), data[1]) def test_upgrade_tables_no_common_columns(self): schema = [ Table('table1', key='id')[Column('id', auto_increment=True), Column('name'), Column('value'), ], ] self.dbm.upgrade_tables(schema) self.assertEqual(['id', 'name', 'value'], self.dbm.get_column_names('table1')) self.assertEqual([], list(self.env.db_query("SELECT * FROM table1")))
class CacheTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.log = self.env.log self.env.db_transaction.executemany( "INSERT INTO repository (id, name, value) VALUES (%s, %s, %s)", [(1, 'name', 'test-repos'), (1, 'youngest_rev', '')]) def tearDown(self): self.env.reset_db() # Helpers def get_repos(self, get_changeset=None, youngest_rev=1): if get_changeset is None: def no_changeset(rev): raise NoSuchChangeset(rev) get_changeset = no_changeset return Mock(Repository, 'test-repos', {'name': 'test-repos', 'id': 1}, self.log, get_changeset=get_changeset, get_oldest_rev=lambda: 0, get_youngest_rev=lambda: youngest_rev, normalize_rev=lambda x: get_changeset(x).rev, next_rev=(lambda x: int(x) < youngest_rev and x + 1 \ or None)) def preset_cache(self, *args): """Each arg is a (rev tuple, changes list of tuples) pair.""" with self.env.db_transaction as db: for rev, changes in args: db( """INSERT INTO revision (repos, rev, time, author, message) VALUES (1,%s,%s,%s,%s) """, rev) if changes: db.executemany( """ INSERT INTO node_change (repos, rev, path, node_type, change_type, base_path, base_rev) VALUES (1, %s, %s, %s, %s, %s, %s) """, [(rev[0], ) + change for change in changes]) db( """UPDATE repository SET value=%s WHERE id=1 AND name='youngest_rev' """, (args[-1][0][0], )) # Tests def test_initial_sync_with_empty_repos(self): repos = self.get_repos() cache = CachedRepository(self.env, repos, self.log) cache.sync() with self.env.db_query as db: self.assertEquals( [], db("SELECT rev, time, author, message FROM revision")) self.assertEquals(0, db("SELECT COUNT(*) FROM node_change")[0][0]) def test_initial_sync(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=1) changes = [('trunk', Node.DIRECTORY, Changeset.ADD, None, None), ('trunk/README', Node.FILE, Changeset.ADD, None, None)] changesets = [ Mock(Changeset, repos, 0, '', '', t1, get_changes=lambda: []), Mock(Changeset, repos, 1, 'Import', 'joe', t2, get_changes=lambda: iter(changes)) ] cache = CachedRepository(self.env, repos, self.log) cache.sync() with self.env.db_query as db: rows = db("SELECT rev, time, author, message FROM revision") self.assertEquals(len(rows), 2) self.assertEquals(('0', to_utimestamp(t1), '', ''), rows[0]) self.assertEquals(('1', to_utimestamp(t2), 'joe', 'Import'), rows[1]) rows = db(""" SELECT rev, path, node_type, change_type, base_path, base_rev FROM node_change""") self.assertEquals(len(rows), 2) self.assertEquals(('1', 'trunk', 'D', 'A', None, None), rows[0]) self.assertEquals(('1', 'trunk/README', 'F', 'A', None, None), rows[1]) def test_update_sync(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) t3 = datetime(2003, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (('0', to_utimestamp(t1), '', ''), []), (('1', to_utimestamp(t2), 'joe', 'Import'), [ ('trunk', 'D', 'A', None, None), ('trunk/README', 'F', 'A', None, None) ]), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=2) changes = [('trunk/README', Node.FILE, Changeset.EDIT, 'trunk/README', 1)] changesets = [ None, Mock(Changeset, repos, 1, '', '', t2, get_changes=lambda: []), Mock(Changeset, repos, 2, 'Update', 'joe', t3, get_changes=lambda: iter(changes)) ] cache = CachedRepository(self.env, repos, self.log) cache.sync() with self.env.db_query as db: self.assertEquals( [(to_utimestamp(t3), 'joe', 'Update')], db("SELECT time, author, message FROM revision WHERE rev='2'")) self.assertEquals( [('trunk/README', 'F', 'E', 'trunk/README', '1')], db("""SELECT path, node_type, change_type, base_path, base_rev FROM node_change WHERE rev='2'""")) def test_clean_sync(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) t3 = datetime(2003, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (('0', to_utimestamp(t1), '', ''), []), (('1', to_utimestamp(t2), 'joe', 'Import'), [ ('trunk', 'D', 'A', None, None), ('trunk/README', 'F', 'A', None, None) ]), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=2) changes1 = [('trunk', Node.DIRECTORY, Changeset.ADD, None, None), ('trunk/README', Node.FILE, Changeset.ADD, None, None)] changes2 = [('trunk/README', Node.FILE, Changeset.EDIT, 'trunk/README', 1)] changesets = [ Mock(Changeset, repos, 0, '**empty**', 'joe', t1, get_changes=lambda: []), Mock(Changeset, repos, 1, 'Initial Import', 'joe', t2, get_changes=lambda: iter(changes1)), Mock(Changeset, repos, 2, 'Update', 'joe', t3, get_changes=lambda: iter(changes2)) ] cache = CachedRepository(self.env, repos, self.log) cache.sync(clean=True) rows = self.env.db_query(""" SELECT time, author, message FROM revision ORDER BY rev """) self.assertEquals(3, len(rows)) self.assertEquals((to_utimestamp(t1), 'joe', '**empty**'), rows[0]) self.assertEquals((to_utimestamp(t2), 'joe', 'Initial Import'), rows[1]) self.assertEquals((to_utimestamp(t3), 'joe', 'Update'), rows[2]) rows = self.env.db_query(""" SELECT rev, path, node_type, change_type, base_path, base_rev FROM node_change ORDER BY rev, path""") self.assertEquals(3, len(rows)) self.assertEquals(('1', 'trunk', 'D', 'A', None, None), rows[0]) self.assertEquals(('1', 'trunk/README', 'F', 'A', None, None), rows[1]) self.assertEquals(('2', 'trunk/README', 'F', 'E', 'trunk/README', '1'), rows[2]) def test_sync_changeset(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (('0', to_utimestamp(t1), '', ''), []), (('1', to_utimestamp(t2), 'joe', 'Import'), [ ('trunk', 'D', 'A', None, None), ('trunk/README', 'F', 'A', None, None) ]), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=1) changes1 = [('trunk', Node.DIRECTORY, Changeset.ADD, None, None), ('trunk/README', Node.FILE, Changeset.ADD, None, None)] changesets = [ Mock(Changeset, repos, 0, '**empty**', 'joe', t1, get_changes=lambda: []), Mock(Changeset, repos, 1, 'Initial Import', 'joe', t2, get_changes=lambda: iter(changes1)), ] cache = CachedRepository(self.env, repos, self.log) cache.sync_changeset(0) rows = self.env.db_query( "SELECT time, author, message FROM revision ORDER BY rev") self.assertEquals(2, len(rows)) self.assertEquals((to_utimestamp(t1), 'joe', '**empty**'), rows[0]) self.assertEquals((to_utimestamp(t2), 'joe', 'Import'), rows[1]) def test_sync_changeset_if_not_exists(self): t = [ datetime(2001, 1, 1, 1, 1, 1, 0, utc), # r0 datetime(2002, 1, 1, 1, 1, 1, 0, utc), # r1 datetime(2003, 1, 1, 1, 1, 1, 0, utc), # r2 datetime(2004, 1, 1, 1, 1, 1, 0, utc), # r3 ] self.preset_cache( (('0', to_utimestamp(t[0]), 'joe', '**empty**'), []), (('1', to_utimestamp(t[1]), 'joe', 'Import'), [ ('trunk', 'D', 'A', None, None), ('trunk/README', 'F', 'A', None, None) ]), # not exists r2 (('3', to_utimestamp(t[3]), 'joe', 'Add COPYING'), [ ('trunk/COPYING', 'F', 'A', None, None) ]), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=3) changes = [ None, # r0 [ ('trunk', Node.DIRECTORY, Changeset.ADD, None, None), # r1 ('trunk/README', Node.FILE, Changeset.ADD, None, None) ], [ ('branches', Node.DIRECTORY, Changeset.ADD, None, None), # r2 ('tags', Node.DIRECTORY, Changeset.ADD, None, None) ], [('trunk/COPYING', Node.FILE, Changeset.ADD, None, None)], # r3 ] changesets = [ Mock(Changeset, repos, 0, '**empty**', 'joe', t[0], get_changes=lambda: []), Mock(Changeset, repos, 1, 'Initial Import', 'joe', t[1], get_changes=lambda: iter(changes[1])), Mock(Changeset, repos, 2, 'Created directories', 'john', t[2], get_changes=lambda: iter(changes[2])), Mock(Changeset, repos, 3, 'Add COPYING', 'joe', t[3], get_changes=lambda: iter(changes[3])), ] cache = CachedRepository(self.env, repos, self.log) self.assertRaises(NoSuchChangeset, cache.get_changeset, 2) cache.sync() self.assertRaises(NoSuchChangeset, cache.get_changeset, 2) self.assertEqual(None, cache.sync_changeset(2)) cset = cache.get_changeset(2) self.assertEqual('john', cset.author) self.assertEqual('Created directories', cset.message) self.assertEqual(t[2], cset.date) cset_changes = cset.get_changes() self.assertEqual( ('branches', Node.DIRECTORY, Changeset.ADD, None, None), cset_changes.next()) self.assertEqual(('tags', Node.DIRECTORY, Changeset.ADD, None, None), cset_changes.next()) self.assertRaises(StopIteration, cset_changes.next) rows = self.env.db_query( "SELECT time,author,message FROM revision ORDER BY rev") self.assertEquals(4, len(rows)) self.assertEquals((to_utimestamp(t[0]), 'joe', '**empty**'), rows[0]) self.assertEquals((to_utimestamp(t[1]), 'joe', 'Import'), rows[1]) self.assertEquals((to_utimestamp(t[2]), 'john', 'Created directories'), rows[2]) self.assertEquals((to_utimestamp(t[3]), 'joe', 'Add COPYING'), rows[3]) def test_get_changes(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (('0', to_utimestamp(t1), '', ''), []), (('1', to_utimestamp(t2), 'joe', 'Import'), [ ('trunk', 'D', 'A', None, None), ('trunk/RDME', 'F', 'A', None, None) ]), ) repos = self.get_repos() cache = CachedRepository(self.env, repos, self.log) self.assertEqual('1', cache.youngest_rev) changeset = cache.get_changeset(1) self.assertEqual('joe', changeset.author) self.assertEqual('Import', changeset.message) self.assertEqual(t2, changeset.date) changes = changeset.get_changes() self.assertEqual(('trunk', Node.DIRECTORY, Changeset.ADD, None, None), changes.next()) self.assertEqual(('trunk/RDME', Node.FILE, Changeset.ADD, None, None), changes.next()) self.assertRaises(StopIteration, changes.next)
class UpgradeTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(path=mkdtemp()) self.dbm = DatabaseManager(self.env) with self.env.db_transaction: self.dbm.drop_tables(new_schema) self.dbm.create_tables(old_schema) self.dbm.set_database_version(VERSION - 1) def tearDown(self): self.env.reset_db_and_disk() def test_attachment_table_upgraded(self): """The ipnr column is removed from the attachment table.""" db42.do_upgrade(self.env, VERSION, None) column_names = [col.name for col in new_attachment_schema.columns] self.assertEqual(column_names, self.dbm.get_column_names('attachment')) def test_wiki_table_upgraded(self): """The ipnr column is removed from the wiki table.""" db42.do_upgrade(self.env, VERSION, None) column_names = [col.name for col in new_wiki_schema.columns] self.assertEqual(column_names, self.dbm.get_column_names('wiki')) def test_attachments_data_migrated(self): """Attachment data is migrated on table upgrade.""" now = to_utimestamp(datetime_now(utc)) attachment_column_names = \ [col.name for col in old_attachment_schema.columns] attachment_data = (('ticket', '1', 'file1', 10, now, 'desc1', 'user1', '::1'), ('wiki', 'WikiStart', 'file2', 20, now, 'desc2', 'user2', '::2')) self.dbm.insert_into_tables( (('attachment', attachment_column_names, attachment_data), )) db42.do_upgrade(self.env, VERSION, None) ipnr_col = attachment_column_names.index('ipnr') i = 0 for i, data in enumerate( self.env.db_query(""" SELECT * FROM attachment ORDER BY type """)): self.assertEqual( attachment_data[i][:ipnr_col] + attachment_data[i][ipnr_col + 1:], data) self.assertEqual(len(attachment_data), i + 1) def test_wiki_data_migrated(self): """Wiki data is migrated on table upgrade.""" now = to_utimestamp(datetime_now(utc)) wiki_column_names = \ [col.name for col in old_wiki_schema.columns] wiki_data = (('TracGuide', 2, now, 'user2', '::4', 'The guide', 'Edit', 0), ('WikiStart', 1, now, 'user1', '::3', 'The page', 'Init', 1)) self.dbm.insert_into_tables((('wiki', wiki_column_names, wiki_data), )) db42.do_upgrade(self.env, VERSION, None) ipnr_col = wiki_column_names.index('ipnr') i = 0 for i, data in enumerate( self.env.db_query(""" SELECT * FROM wiki ORDER BY name """)): self.assertEqual( wiki_data[i][:ipnr_col] + wiki_data[i][ipnr_col + 1:], data) self.assertEqual(len(wiki_data), i + 1)
class SubscriptionTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() def tearDown(self): self.env.reset_db() def _add_subscriber(self, req, class_, distributor='email', format='text/plain', adverb='always'): session = req.session args = { 'sid': session.sid, 'authenticated': session.authenticated, 'distributor': distributor, 'format': format, 'adverb': adverb, 'class': class_ } return Subscription.add(self.env, args) def _insert_rows(self): rows = [ ('joe', 1, 'email', 'text/plain', 1, 'always', 'EmailSubscriber1'), ('joe', 1, 'email', 'text/html', 2, 'always', 'EmailSubscriber2'), ('joe', 1, 'email', 'text/plain', 3, 'always', 'EmailSubscriber3'), ('joe', 1, 'xmpp', 'text/html', 1, 'always', 'XmppSubscriber1'), ('joe', 1, 'xmpp', 'text/plain', 2, 'never', 'XmppSubscriber2'), ('joe', 1, 'xmpp', 'text/html', 3, 'never', 'XmppSubscriber3'), ('joe', 1, 'irc', 'text/plain', 1, 'never', 'IrcSubscriber1'), ('joe', 1, 'irc', 'text/plain', 2, 'never', 'IrcSubscriber2'), ('joe', 1, 'irc', 'text/plain', 3, 'never', 'IrcSubscriber3'), ('jes', 1, 'email', 'text/html', 1, 'always', 'EmailSubscriber1'), ('jes', 1, 'email', 'text/plain', 2, 'never', 'EmailSubscriber2'), ('jes', 1, 'email', 'text/html', 3, 'always', 'EmailSubscriber3'), ('jan', 1, 'xmpp', 'text/plain', 1, 'always', 'XmppSubscriber1'), ('jan', 1, 'xmpp', 'text/html', 2, 'never', 'XmppSubscriber2'), ('jan', 1, 'xmpp', 'text/plain', 3, 'never', 'XmppSubscriber3'), ('jim', 1, 'irc', 'text/html', 1, 'always', 'IrcSubscriber1'), ('jim', 1, 'irc', 'text/plain', 2, 'never', 'IrcSubscriber2'), ('jim', 1, 'irc', 'text/html', 3, 'always', 'IrcSubscriber3'), ] ts = to_utimestamp(datetime(2016, 2, 3, 12, 34, 56, 987654, utc)) with self.env.db_transaction as db: cursor = db.cursor() cursor.executemany( """ INSERT INTO notify_subscription ( time, changetime, sid, authenticated, distributor, format, priority, adverb, class) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)""", [(ts + idx, ts + idx * 2) + row for idx, row in enumerate(rows)]) def _props(self, items, name): return [item[name] for item in items] def test_add(self): req = MockRequest(self.env, authname='joe') with self.env.db_transaction: self._add_subscriber(req, 'TicketSubscriber1', format='text/html') self._add_subscriber(req, 'TicketSubscriber2') self._add_subscriber(req, 'TicketSubscriber3', format='text/html') self._add_subscriber(req, 'XmppSubscriber1', distributor='xmpp', adverb='never') self.assertEqual([(u'joe', 1, u'email', u'text/html', 1, u'always', u'TicketSubscriber1'), (u'joe', 1, u'email', u'text/plain', 2, u'always', u'TicketSubscriber2'), (u'joe', 1, u'email', u'text/html', 3, u'always', u'TicketSubscriber3'), (u'joe', 1, u'xmpp', u'text/plain', 1, u'never', u'XmppSubscriber1')], self.env.db_query( """\ SELECT sid, authenticated, distributor, format, priority, adverb, class FROM notify_subscription WHERE sid=%s AND authenticated=%s ORDER BY distributor, priority""", ('joe', 1))) def test_delete(self): req = MockRequest(self.env, authname='joe') with self.env.db_transaction: ids = [ self._add_subscriber(req, 'TicketSubscriber1'), self._add_subscriber(req, 'TicketSubscriber2'), self._add_subscriber(req, 'TicketSubscriber3'), self._add_subscriber(req, 'XmppSubscriber1', distributor='xmpp', adverb='never'), self._add_subscriber(req, 'XmppSubscriber2', distributor='xmpp') ] self.assertEqual( 5, self.env.db_query( """\ SELECT COUNT(*) FROM notify_subscription WHERE sid=%s AND authenticated=%s""", ('joe', 1))[0][0]) Subscription.delete(self.env, ids[1]) rows = self.env.db_query( """\ SELECT id, distributor, priority, class FROM notify_subscription WHERE sid=%s AND authenticated=%s ORDER BY distributor, priority""", ('joe', 1)) self.assertEqual((ids[0], 'email', 1, 'TicketSubscriber1'), rows[0]) self.assertEqual((ids[2], 'email', 2, 'TicketSubscriber3'), rows[1]) self.assertEqual((ids[3], 'xmpp', 1, 'XmppSubscriber1'), rows[2]) self.assertEqual((ids[4], 'xmpp', 2, 'XmppSubscriber2'), rows[3]) self.assertEqual(4, len(rows)) def test_find_by_sid_and_distributor(self): self._insert_rows() items = Subscription.find_by_sid_and_distributor( self.env, 'joe', True, 'xmpp') self.assertEqual(['joe'] * 3, self._props(items, 'sid')) self.assertEqual([1] * 3, self._props(items, 'authenticated')) self.assertEqual(['xmpp'] * 3, self._props(items, 'distributor')) self.assertEqual(['text/html', 'text/plain', 'text/html'], self._props(items, 'format')) self.assertEqual([1, 2, 3], self._props(items, 'priority')) self.assertEqual(['always', 'never', 'never'], self._props(items, 'adverb')) self.assertEqual( ['XmppSubscriber1', 'XmppSubscriber2', 'XmppSubscriber3'], self._props(items, 'class')) def test_find_by_sids_and_class(self): self._insert_rows() sids = [('joe', True), ('jes', True), ('jan', True), ('jim', True)] items = Subscription.find_by_sids_and_class(self.env, sids, 'IrcSubscriber3') self.assertEqual(['joe', 'jim'], self._props(items, 'sid')) self.assertEqual([1] * 2, self._props(items, 'authenticated')) self.assertEqual(['irc'] * 2, self._props(items, 'distributor')) self.assertEqual(['text/plain', 'text/html'], self._props(items, 'format')) self.assertEqual([3, 3], self._props(items, 'priority')) self.assertEqual(['never', 'always'], self._props(items, 'adverb')) self.assertEqual(['IrcSubscriber3', 'IrcSubscriber3'], self._props(items, 'class')) def test_move(self): def query_subs(): return self.env.db_query( """\ SELECT distributor, priority, class FROM notify_subscription WHERE sid=%s AND authenticated=%s ORDER BY distributor, priority""", ('joe', 1)) req = MockRequest(self.env, authname='joe') with self.env.db_transaction: rule_ids = {} for class_, distributor in [('EmailSubscriber1', 'email'), ('EmailSubscriber2', 'email'), ('EmailSubscriber3', 'email'), ('EmailSubscriber4', 'email'), ('XmppSubscriber1', 'xmpp'), ('XmppSubscriber2', 'xmpp')]: rule_ids[(class_, distributor)] = \ self._add_subscriber(req, class_, distributor) self.assertEqual([ ('email', 1, 'EmailSubscriber1'), ('email', 2, 'EmailSubscriber2'), ('email', 3, 'EmailSubscriber3'), ('email', 4, 'EmailSubscriber4'), ('xmpp', 1, 'XmppSubscriber1'), ('xmpp', 2, 'XmppSubscriber2'), ], query_subs()) Subscription.move(self.env, rule_ids[('EmailSubscriber3', 'email')], 1) self.assertEqual([ ('email', 1, 'EmailSubscriber3'), ('email', 2, 'EmailSubscriber1'), ('email', 3, 'EmailSubscriber2'), ('email', 4, 'EmailSubscriber4'), ('xmpp', 1, 'XmppSubscriber1'), ('xmpp', 2, 'XmppSubscriber2'), ], query_subs()) Subscription.move(self.env, rule_ids[('EmailSubscriber1', 'email')], 4) self.assertEqual([ ('email', 1, 'EmailSubscriber3'), ('email', 2, 'EmailSubscriber2'), ('email', 3, 'EmailSubscriber4'), ('email', 4, 'EmailSubscriber1'), ('xmpp', 1, 'XmppSubscriber1'), ('xmpp', 2, 'XmppSubscriber2'), ], query_subs()) Subscription.move(self.env, rule_ids[('EmailSubscriber3', 'email')], 3) self.assertEqual([ ('email', 1, 'EmailSubscriber2'), ('email', 2, 'EmailSubscriber4'), ('email', 3, 'EmailSubscriber3'), ('email', 4, 'EmailSubscriber1'), ('xmpp', 1, 'XmppSubscriber1'), ('xmpp', 2, 'XmppSubscriber2'), ], query_subs()) def test_replace_all(self): def query(sid, authenticated): return self.env.db_query( """\ SELECT distributor, format, priority, adverb, class FROM notify_subscription WHERE sid=%s AND authenticated=%s ORDER BY distributor, priority""", (sid, authenticated)) req = MockRequest(self.env, authname='joe') sess = req.session items = [ ('email', 'text/plain', 'always', 'TicketSubscriber1'), ('email', 'text/html', 'always', 'TicketSubscriber2'), ('email', 'text/html', 'always', 'TicketSubscriber3'), ('xmpp', 'text/html', 'never', 'XmppSubscriber1'), ('xmpp', 'text/plain', 'always', 'XmppSubscriber2'), ] items = [ dict(zip(('distributor', 'format', 'adverb', 'class'), item)) for item in items ] Subscription.replace_all(self.env, sess.sid, sess.authenticated, items) rows = query('joe', 1) expected = [ ('email', 'text/plain', 1, 'always', 'TicketSubscriber1'), ('email', 'text/html', 2, 'always', 'TicketSubscriber2'), ('email', 'text/html', 3, 'always', 'TicketSubscriber3'), ('xmpp', 'text/html', 1, 'never', 'XmppSubscriber1'), ('xmpp', 'text/plain', 2, 'always', 'XmppSubscriber2'), ] self.assertEqual(expected, rows) items = [ ('email', 'text/plain', 'never', 'TicketSubscriber3'), ('xmpp', 'text/html', 'always', 'XmppSubscriber1'), ] items = [ dict(zip(('distributor', 'format', 'adverb', 'class'), item)) for item in items ] Subscription.replace_all(self.env, sess.sid, sess.authenticated, items) rows = query('joe', 1) expected = [ ('email', 'text/plain', 1, 'never', 'TicketSubscriber3'), ('xmpp', 'text/html', 1, 'always', 'XmppSubscriber1'), ] self.assertEqual(expected, rows) Subscription.replace_all(self.env, sess.sid, sess.authenticated, []) self.assertEqual([], query('joe', 1)) def test_update_format_by_distributor_and_sid(self): self._insert_rows() Subscription.update_format_by_distributor_and_sid( self.env, 'email', 'joe', True, 'application/pdf') rows = self.env.db_query( """\ SELECT distributor, format, priority, adverb, class FROM notify_subscription WHERE sid=%s AND authenticated=%s ORDER BY distributor, priority""", ('joe', 1)) expected = [ ('email', 'application/pdf', 1, 'always', 'EmailSubscriber1'), ('email', 'application/pdf', 2, 'always', 'EmailSubscriber2'), ('email', 'application/pdf', 3, 'always', 'EmailSubscriber3'), ('irc', 'text/plain', 1, 'never', 'IrcSubscriber1'), ('irc', 'text/plain', 2, 'never', 'IrcSubscriber2'), ('irc', 'text/plain', 3, 'never', 'IrcSubscriber3'), ('xmpp', 'text/html', 1, 'always', 'XmppSubscriber1'), ('xmpp', 'text/plain', 2, 'never', 'XmppSubscriber2'), ('xmpp', 'text/html', 3, 'never', 'XmppSubscriber3'), ] self.assertEqual(expected, rows)
class MilestoneTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True) self.env.path = os.path.join(tempfile.gettempdir(), 'trac-tempenv') os.mkdir(self.env.path) def tearDown(self): shutil.rmtree(self.env.path) self.env.reset_db() def _create_milestone(self, **values): milestone = Milestone(self.env) for k, v in values.iteritems(): setattr(milestone, k, v) return milestone def test_new_milestone(self): milestone = Milestone(self.env) self.assertEqual(False, milestone.exists) self.assertEqual(None, milestone.name) self.assertEqual(None, milestone.due) self.assertEqual(None, milestone.completed) self.assertEqual('', milestone.description) def test_new_milestone_empty_name(self): """ Verifies that specifying an empty milestone name results in the milestone being correctly detected as non-existent. """ milestone = Milestone(self.env, '') self.assertEqual(False, milestone.exists) self.assertEqual(None, milestone.name) self.assertEqual(None, milestone.due) self.assertEqual(None, milestone.completed) self.assertEqual('', milestone.description) def test_existing_milestone(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") milestone = Milestone(self.env, 'Test') self.assertEqual(True, milestone.exists) self.assertEqual('Test', milestone.name) self.assertEqual(None, milestone.due) self.assertEqual(None, milestone.completed) self.assertEqual('', milestone.description) def test_create_and_update_milestone(self): milestone = Milestone(self.env) milestone.name = 'Test' milestone.insert() self.assertEqual([('Test', 0, 0, '')], self.env.db_query(""" SELECT name, due, completed, description FROM milestone WHERE name='Test' """)) # Use the same model object to update the milestone milestone.description = 'Some text' milestone.update() self.assertEqual([('Test', 0, 0, 'Some text')], self.env.db_query(""" SELECT name, due, completed, description FROM milestone WHERE name='Test' """)) def test_create_milestone_without_name(self): milestone = Milestone(self.env) self.assertRaises(TracError, milestone.insert) def test_delete_milestone(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") milestone = Milestone(self.env, 'Test') milestone.delete() self.assertEqual(False, milestone.exists) self.assertEqual([], self.env.db_query("SELECT * FROM milestone WHERE name='Test'")) def test_delete_milestone_retarget_tickets(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") tkt1 = Ticket(self.env) tkt1.populate({'summary': 'Foo', 'milestone': 'Test'}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({'summary': 'Bar', 'milestone': 'Test'}) tkt2.insert() milestone = Milestone(self.env, 'Test') milestone.delete(retarget_to='Other') self.assertEqual(False, milestone.exists) self.assertEqual('Other', Ticket(self.env, tkt1.id)['milestone']) self.assertEqual('Other', Ticket(self.env, tkt2.id)['milestone']) def test_update_milestone(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") milestone = Milestone(self.env, 'Test') t1 = datetime(2001, 01, 01, tzinfo=utc) t2 = datetime(2002, 02, 02, tzinfo=utc) milestone.due = t1 milestone.completed = t2 milestone.description = 'Foo bar' milestone.update() self.assertEqual( [('Test', to_utimestamp(t1), to_utimestamp(t2), 'Foo bar')], self.env.db_query("SELECT * FROM milestone WHERE name='Test'")) def test_update_milestone_without_name(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") milestone = Milestone(self.env, 'Test') milestone.name = None self.assertRaises(TracError, milestone.update) def test_update_milestone_update_tickets(self): self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')") tkt1 = Ticket(self.env) tkt1.populate({'summary': 'Foo', 'milestone': 'Test'}) tkt1.insert() tkt2 = Ticket(self.env) tkt2.populate({'summary': 'Bar', 'milestone': 'Test'}) tkt2.insert() milestone = Milestone(self.env, 'Test') milestone.name = 'Testing' milestone.update() self.assertEqual('Testing', Ticket(self.env, tkt1.id)['milestone']) self.assertEqual('Testing', Ticket(self.env, tkt2.id)['milestone']) def test_rename_milestone(self): milestone = Milestone(self.env) milestone.name = 'OldName' milestone.insert() attachment = Attachment(self.env, 'milestone', 'OldName') attachment.insert('foo.txt', StringIO(), 0, 1) milestone = Milestone(self.env, 'OldName') milestone.name = 'NewName' milestone.update() self.assertRaises(ResourceNotFound, Milestone, self.env, 'OldName') self.assertEqual('NewName', Milestone(self.env, 'NewName').name) attachments = Attachment.select(self.env, 'milestone', 'OldName') self.assertRaises(StopIteration, attachments.next) attachments = Attachment.select(self.env, 'milestone', 'NewName') self.assertEqual('foo.txt', attachments.next().filename) self.assertRaises(StopIteration, attachments.next) def test_select_milestones(self): self.env.db_transaction.executemany( "INSERT INTO milestone (name) VALUES (%s)", [('1.0',), ('2.0',)]) milestones = list(Milestone.select(self.env)) self.assertEqual('1.0', milestones[0].name) assert milestones[0].exists self.assertEqual('2.0', milestones[1].name) assert milestones[1].exists def test_change_listener_created(self): listener = TestMilestoneChangeListener(self.env) milestone = self._create_milestone(name='Milestone 1') milestone.insert() self.assertEqual('created', listener.action) self.assertEqual(milestone, listener.milestone) def test_change_listener_changed(self): listener = TestMilestoneChangeListener(self.env) milestone = self._create_milestone( name='Milestone 1', due=datetime(2001, 01, 01, tzinfo=utc), description='The milestone description') milestone.insert() milestone.name = 'Milestone 2' milestone.completed = datetime(2001, 02, 03, tzinfo=utc) milestone.description = 'The changed description' milestone.update() self.assertEqual('changed', listener.action) self.assertEqual(milestone, listener.milestone) self.assertEqual({'name': 'Milestone 1', 'completed': None, 'description': 'The milestone description'}, listener.old_values) def test_change_listener_deleted(self): listener = TestMilestoneChangeListener(self.env) milestone = self._create_milestone(name='Milestone 1') milestone.insert() self.assertEqual(True, milestone.exists) milestone.delete() self.assertEqual('Milestone 1', milestone.name) self.assertEqual(False, milestone.exists) self.assertEqual('deleted', listener.action) self.assertEqual(milestone, listener.milestone)
class TicketTagProviderTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True, enable=['trac.*', 'tractags.*']) self.env.path = tempfile.mkdtemp() self.perms = PermissionSystem(self.env) setup = TagSetup(self.env) # Current tractags schema is setup with enabled component anyway. # Revert these changes for getting default permissions inserted. self._revert_tractags_schema_init() setup.upgrade_environment() self.provider = TicketTagProvider(self.env) self.realm = 'ticket' self.tag_sys = TagSystem(self.env) self.tags = ['tag1', 'tag2'] # Populate tables with initial test data. self._create_ticket(self.tags) # Mock an anonymous request. self.anon_req = Mock() self.anon_req.perm = PermissionCache(self.env) self.req = Mock(authname='editor') self.req.authname = 'editor' self.req.perm = PermissionCache(self.env, username='******') def tearDown(self): self.env.shutdown() shutil.rmtree(self.env.path) # Helpers def _create_ticket(self, tags, **kwargs): ticket = Ticket(self.env) ticket['keywords'] = u' '.join(sorted(map(to_unicode, tags))) ticket['summary'] = 'summary' ticket['reporter'] = 'admin' for name, value in kwargs.iteritems(): ticket[name] = value ticket.insert() return ticket def _revert_tractags_schema_init(self): with self.env.db_transaction as db: db("DROP TABLE IF EXISTS tags") db("DROP TABLE IF EXISTS tags_change") db("DELETE FROM system WHERE name='tags_version'") db("DELETE FROM permission WHERE action %s" % db.like(), ('TAGS_%',)) def _tags(self): tags = {} for name, tag in self.env.db_query(""" SELECT name,tag FROM tags """): if name in tags: tags[name].add(tag) else: tags[name] = set([tag]) return tags # Tests def test_get_tagged_resources(self): # No tags, no restrictions, all resources. self.assertEquals( [r for r in self.provider.get_tagged_resources(self.req, None)][0][1], set(self.tags)) # Force fine-grained perm-check check for all tags, not just the one # from query. self.provider.fast_permcheck = False self.assertEquals( [r for r in self.provider.get_tagged_resources(self.req, set(self.tags[:1]))][0][1], set(self.tags)) def test_get_tags(self): resource = Resource('ticket', 2) self.assertRaises(ResourceNotFound, self.provider.get_resource_tags, self.req, resource) self._create_ticket(self.tags) self.assertEquals( [tag for tag in self.provider.get_resource_tags(self.req, resource)], self.tags) #ignore_closed_tickets def test_set_tags(self): tags = ['tag3'] ticket = Ticket(self.env, 1) ticket['keywords'] = tags[0] # Tags get updated by TicketChangeListener method. ticket.save_changes(self.req.authname) self.assertEquals(self.tag_sys.get_all_tags(self.req).keys(), tags) def test_remove_tags(self): resource = Resource('ticket', 1) # Anonymous lacks required permissions. self.assertRaises(PermissionError, self.provider.remove_resource_tags, self.anon_req, resource) # Shouldn't raise an error with appropriate permission. self.provider.remove_resource_tags(self.req, resource, 'comment') ticket = Ticket(self.env, 1) self.assertEquals(ticket['keywords'], '') def test_describe_tagged_resource(self): resource = Resource('ticket', 1) self.assertEquals( self.provider.describe_tagged_resource(self.req, resource), 'defect: summary') def test_create_ticket_by_anonymous(self): ticket = self._create_ticket(self.tags, reporter='anonymous') tags = self.provider.get_resource_tags(self.req, ticket.resource) self.assertEquals(tags, set(self.tags)) def test_update_ticket_by_anonymous(self): ticket = self._create_ticket([]) tags = self.provider.get_resource_tags(self.req, ticket.resource) self.assertEquals(tags, set([])) ticket['keywords'] = ', '.join(self.tags) ticket.save_changes('anonymous', comment='Adding keywords') tags = self.provider.get_resource_tags(self.req, ticket.resource) self.assertEquals(tags, set(self.tags))
class WikiPageTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.env.path = tempfile.mkdtemp(prefix='trac-tempenv-') def tearDown(self): shutil.rmtree(self.env.path) self.env.reset_db() def test_new_page(self): page = WikiPage(self.env) self.assertFalse(page.exists) self.assertIsNone(page.name) self.assertEqual(0, page.version) self.assertEqual('', page.text) self.assertEqual(0, page.readonly) self.assertEqual('', page.author) self.assertEqual('', page.comment) self.assertIsNone(page.time) def test_existing_page(self): t = datetime(2001, 1, 1, 1, 1, 1, 0, utc) self.env.db_transaction( "INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", ('TestPage', 1, to_utimestamp(t), 'joe', '::1', 'Bla bla', 'Testing', 0)) page = WikiPage(self.env, 'TestPage') self.assertTrue(page.exists) self.assertEqual('TestPage', page.name) self.assertEqual(1, page.version) self.assertIsNone(page.resource.version) # FIXME: Intentional? self.assertEqual('Bla bla', page.text) self.assertEqual(0, page.readonly) self.assertEqual('joe', page.author) self.assertEqual('Testing', page.comment) self.assertEqual(t, page.time) history = list(page.get_history()) self.assertEqual(1, len(history)) self.assertEqual((1, t, 'joe', 'Testing', '::1'), history[0]) page = WikiPage(self.env, 'TestPage', 1) self.assertEqual(1, page.resource.version) self.assertEqual(1, page.version) resource = Resource('wiki', 'TestPage') page = WikiPage(self.env, resource, 1) self.assertEqual(1, page.version) def test_create_page(self): page = WikiPage(self.env) page.name = 'TestPage' page.text = 'Bla bla' t = datetime(2001, 1, 1, 1, 1, 1, 0, utc) page.save('joe', 'Testing', '::1', t) self.assertTrue(page.exists) self.assertEqual(1, page.version) self.assertEqual(1, page.resource.version) self.assertEqual(0, page.readonly) self.assertEqual('joe', page.author) self.assertEqual('Testing', page.comment) self.assertEqual(t, page.time) self.assertEqual( [(1, to_utimestamp(t), 'joe', '::1', 'Bla bla', 'Testing', 0)], self.env.db_query(""" SELECT version, time, author, ipnr, text, comment, readonly FROM wiki WHERE name=%s """, ('TestPage',))) listener = TestWikiChangeListener(self.env) self.assertEqual(page, listener.added[0]) def test_update_page(self): t = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) self.env.db_transaction( "INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", ('TestPage', 1, to_utimestamp(t), 'joe', '::1', 'Bla bla', 'Testing', 0)) page = WikiPage(self.env, 'TestPage') page.text = 'Bla' page.save('kate', 'Changing', '192.168.0.101', t2) self.assertEqual(2, page.version) self.assertEqual(2, page.resource.version) self.assertEqual(0, page.readonly) self.assertEqual('kate', page.author) self.assertEqual('Changing', page.comment) self.assertEqual(t2, page.time) with self.env.db_query as db: rows = db(""" SELECT version, time, author, ipnr, text, comment, readonly FROM wiki WHERE name=%s ORDER BY version """, ('TestPage',)) self.assertEqual(2, len(rows)) self.assertEqual((1, to_utimestamp(t), 'joe', '::1', 'Bla bla', 'Testing', 0), rows[0]) self.assertEqual((2, to_utimestamp(t2), 'kate', '192.168.0.101', 'Bla', 'Changing', 0), rows[1]) listener = TestLegacyWikiChangeListener(self.env) self.assertEqual((page, 2, t2, 'Changing', 'kate', '192.168.0.101'), listener.changed[0]) listener = TestWikiChangeListener(self.env) self.assertEqual((page, 2, t2, 'Changing', 'kate'), listener.changed[0]) page = WikiPage(self.env, 'TestPage') history = list(page.get_history()) self.assertEqual(2, len(history)) self.assertEqual((2, t2, 'kate', 'Changing', '192.168.0.101'), history[0]) self.assertEqual((1, t, 'joe', 'Testing', '::1'), history[1]) def test_delete_page(self): self.env.db_transaction( "INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", ('TestPage', 1, 42, 'joe', '::1', 'Bla bla', 'Testing', 0)) page = WikiPage(self.env, 'TestPage') page.delete() self.assertFalse(page.exists) self.assertEqual([], self.env.db_query(""" SELECT version, time, author, ipnr, text, comment, readonly FROM wiki WHERE name=%s """, ('TestPage',))) listener = TestWikiChangeListener(self.env) self.assertEqual(page, listener.deleted[0]) def test_delete_page_version(self): self.env.db_transaction.executemany( "INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", [('TestPage', 1, 42, 'joe', '::1', 'Bla bla', 'Testing', 0), ('TestPage', 2, 43, 'kate', '192.168.0.11', 'Bla', 'Changing', 0)]) page = WikiPage(self.env, 'TestPage') page.delete(version=2) self.assertTrue(page.exists) self.assertEqual( [(1, 42, 'joe', '::1', 'Bla bla', 'Testing', 0)], self.env.db_query(""" SELECT version, time, author, ipnr, text, comment, readonly FROM wiki WHERE name=%s """, ('TestPage',))) listener = TestWikiChangeListener(self.env) self.assertEqual(page, listener.deleted_version[0]) def test_delete_page_last_version(self): self.env.db_transaction( "INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", ('TestPage', 1, 42, 'joe', '::1', 'Bla bla', 'Testing', 0)) page = WikiPage(self.env, 'TestPage') page.delete(version=1) self.assertFalse(page.exists) self.assertEqual([], self.env.db_query(""" SELECT version, time, author, ipnr, text, comment, readonly FROM wiki WHERE name=%s """, ('TestPage',))) listener = TestWikiChangeListener(self.env) self.assertEqual(page, listener.deleted[0]) def test_rename_page(self): data = (1, 42, 'joe', '::1', 'Bla bla', 'Testing', 0) self.env.db_transaction( "INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", ('TestPage',) + data) attachment = Attachment(self.env, 'wiki', 'TestPage') attachment.insert('foo.txt', StringIO(), 0, 1) page = WikiPage(self.env, 'TestPage') page.rename('PageRenamed') self.assertEqual('PageRenamed', page.name) self.assertEqual('PageRenamed', page.resource.id) self.assertEqual([data], self.env.db_query(""" SELECT version, time, author, ipnr, text, comment, readonly FROM wiki WHERE name=%s """, ('PageRenamed',))) attachments = Attachment.select(self.env, 'wiki', 'PageRenamed') self.assertEqual('foo.txt', attachments.next().filename) self.assertRaises(StopIteration, attachments.next) Attachment.delete_all(self.env, 'wiki', 'PageRenamed') old_page = WikiPage(self.env, 'TestPage') self.assertFalse(old_page.exists) self.assertEqual([], self.env.db_query(""" SELECT version, time, author, ipnr, text, comment, readonly FROM wiki WHERE name=%s """, ('TestPage',))) listener = TestWikiChangeListener(self.env) self.assertEqual((page, 'TestPage'), listener.renamed[0]) def test_invalid_page_name(self): invalid_names = ('../Page', 'Page/..', 'Page/////SubPage', 'Page/./SubPage', '/PagePrefix', 'PageSuffix/') for name in invalid_names: page = WikiPage(self.env) page.name = name page.text = 'Bla bla' t = datetime(2001, 1, 1, 1, 1, 1, 0, utc) self.assertRaises(TracError, page.save, 'joe', 'Testing', '::1', t) page = WikiPage(self.env) page.name = 'TestPage' page.text = 'Bla bla' t = datetime(2001, 1, 1, 1, 1, 1, 0, utc) page.save('joe', 'Testing', '::1', t) for name in invalid_names: page = WikiPage(self.env, 'TestPage') self.assertRaises(TracError, page.rename, name) def test_invalid_version(self): data = (1, 42, 'joe', '::1', 'Bla bla', 'Testing', 0) self.env.db_transaction( "INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", ('TestPage',) + data) self.assertRaises(ValueError, WikiPage, self.env, 'TestPage', '1abc') resource = Resource('wiki', 'TestPage') self.assertRaises(ValueError, WikiPage, self.env, resource, '1abc') resource = Resource('wiki', 'TestPage', '1abc') page = WikiPage(self.env, resource) self.assertEqual(1, page.version)
class TicketTagProviderTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True, enable=['trac.*', 'tractags.*']) self.env.path = tempfile.mkdtemp() self.perms = PermissionSystem(self.env) setup = TagSetup(self.env) # Current tractags schema is setup with enabled component anyway. # Revert these changes for getting default permissions inserted. self._revert_tractags_schema_init() setup.upgrade_environment() self.provider = TicketTagProvider(self.env) self.realm = 'ticket' self.tag_sys = TagSystem(self.env) self.tags = ['tag1', 'tag2'] # Populate tables with initial test data. self._create_ticket(self.tags) def tearDown(self): self.env.shutdown() shutil.rmtree(self.env.path) # Helpers def _create_ticket(self, tags, **kwargs): ticket = Ticket(self.env) ticket['keywords'] = u' '.join(sorted(map(to_unicode, tags))) ticket['summary'] = 'summary' ticket['reporter'] = 'admin' for name, value in kwargs.iteritems(): ticket[name] = value ticket.insert() return ticket def _revert_tractags_schema_init(self): with self.env.db_transaction as db: db("DROP TABLE IF EXISTS tags") db("DROP TABLE IF EXISTS tags_change") db("DELETE FROM system WHERE name='tags_version'") db("DELETE FROM permission WHERE action %s" % db.like(), ('TAGS_%', )) def _tags(self): tags = {} for name, tag in self.env.db_query(""" SELECT name,tag FROM tags """): if name in tags: tags[name].add(tag) else: tags[name] = set([tag]) return tags # Tests def test_get_tagged_resources(self): # No tags, no restrictions, all resources. req = MockRequest(self.env, authname='editor') self.assertEquals( [r for r in self.provider.get_tagged_resources(req, None)][0][1], set(self.tags)) # Force fine-grained perm-check check for all tags, not just the one # from query. self.provider.fast_permcheck = False self.assertEquals([ r for r in self.provider.get_tagged_resources( req, set(self.tags[:1])) ][0][1], set(self.tags)) def test_get_tags(self): req = MockRequest(self.env, authname='editor') resource = Resource('ticket', 2) self.assertRaises(ResourceNotFound, self.provider.get_resource_tags, req, resource) self._create_ticket(self.tags) self.assertEquals( [tag for tag in self.provider.get_resource_tags(req, resource)], self.tags) def test_set_tags(self): req = MockRequest(self.env, authname='editor') tags = ['tag3'] ticket = Ticket(self.env, 1) ticket['keywords'] = tags[0] # Tags get updated by TicketChangeListener method. ticket.save_changes(req.authname) self.assertEquals(self.tag_sys.get_all_tags(req).keys(), tags) def test_remove_tags(self): req = MockRequest(self.env, authname='editor') anon_req = MockRequest(self.env, authname='anonymous') resource = Resource('ticket', 1) # Anonymous lacks required permissions. self.assertRaises(PermissionError, self.provider.remove_resource_tags, anon_req, resource) # Shouldn't raise an error with appropriate permission. self.provider.remove_resource_tags(req, resource, 'comment') ticket = Ticket(self.env, 1) self.assertEquals(ticket['keywords'], '') def test_describe_tagged_resource(self): req = MockRequest(self.env, authname='editor') resource = Resource('ticket', 1) self.assertEquals( self.provider.describe_tagged_resource(req, resource), 'defect: summary') def test_create_ticket_by_anonymous(self): req = MockRequest(self.env, authname='editor') ticket = self._create_ticket(self.tags, reporter='anonymous') tags = self.provider.get_resource_tags(req, ticket.resource) self.assertEquals(tags, set(self.tags)) def test_update_ticket_by_anonymous(self): req = MockRequest(self.env, authname='editor') ticket = self._create_ticket([]) tags = self.provider.get_resource_tags(req, ticket.resource) self.assertEquals(tags, set([])) ticket['keywords'] = ', '.join(self.tags) ticket.save_changes('anonymous', comment='Adding keywords') tags = self.provider.get_resource_tags(req, ticket.resource) self.assertEquals(tags, set(self.tags))
class StringsTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() def tearDown(self): self.env.reset_db() def test_insert_unicode(self): self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-unicode', u'ünicöde')) self.assertEqual([(u'ünicöde',)], self.env.db_query( "SELECT value FROM system WHERE name='test-unicode'")) def test_insert_empty(self): from trac.util.text import empty self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-empty', empty)) self.assertEqual([(u'',)], self.env.db_query( "SELECT value FROM system WHERE name='test-empty'")) def test_insert_markup(self): from genshi.core import Markup self.env.db_transaction( "INSERT INTO system (name,value) VALUES (%s,%s)", ('test-markup', Markup(u'<em>märkup</em>'))) self.assertEqual([(u'<em>märkup</em>',)], self.env.db_query( "SELECT value FROM system WHERE name='test-markup'")) def test_quote(self): with self.env.db_query as db: cursor = db.cursor() cursor.execute('SELECT 1 AS %s' % \ db.quote(r'alpha\`\"\'\\beta``gamma""delta')) self.assertEqual(r'alpha\`\"\'\\beta``gamma""delta', get_column_names(cursor)[0]) def test_quoted_id_with_percent(self): name = """%?`%s"%'%%""" def test(logging=False): with self.env.db_query as db: cursor = db.cursor() if logging: cursor.log = self.env.log cursor.execute('SELECT 1 AS ' + db.quote(name)) self.assertEqual(name, get_column_names(cursor)[0]) cursor.execute('SELECT %s AS ' + db.quote(name), (42,)) self.assertEqual(name, get_column_names(cursor)[0]) cursor.executemany("UPDATE system SET value=%s WHERE " "1=(SELECT 0 AS " + db.quote(name) + ")", []) cursor.executemany("UPDATE system SET value=%s WHERE " "1=(SELECT 0 AS " + db.quote(name) + ")", [('42',), ('43',)]) test() test(True) def test_prefix_match_case_sensitive(self): with self.env.db_transaction as db: db.executemany("INSERT INTO system (name,value) VALUES (%s,1)", [('blahblah',), ('BlahBlah',), ('BLAHBLAH',), (u'BlähBlah',), (u'BlahBläh',)]) with self.env.db_query as db: names = sorted(name for name, in db( "SELECT name FROM system WHERE name %s" % db.prefix_match(), (db.prefix_match_value('Blah'),))) self.assertEqual('BlahBlah', names[0]) self.assertEqual(u'BlahBläh', names[1]) self.assertEqual(2, len(names)) def test_prefix_match_metachars(self): def do_query(prefix): with self.env.db_query as db: return [name for name, in db( "SELECT name FROM system WHERE name %s " "ORDER BY name" % db.prefix_match(), (db.prefix_match_value(prefix),))] values = ['foo*bar', 'foo*bar!', 'foo?bar', 'foo?bar!', 'foo[bar', 'foo[bar!', 'foo]bar', 'foo]bar!', 'foo%bar', 'foo%bar!', 'foo_bar', 'foo_bar!', 'foo/bar', 'foo/bar!', 'fo*ob?ar[fo]ob%ar_fo/obar'] with self.env.db_transaction as db: db.executemany("INSERT INTO system (name,value) VALUES (%s,1)", [(value,) for value in values]) self.assertEqual(['foo*bar', 'foo*bar!'], do_query('foo*')) self.assertEqual(['foo?bar', 'foo?bar!'], do_query('foo?')) self.assertEqual(['foo[bar', 'foo[bar!'], do_query('foo[')) self.assertEqual(['foo]bar', 'foo]bar!'], do_query('foo]')) self.assertEqual(['foo%bar', 'foo%bar!'], do_query('foo%')) self.assertEqual(['foo_bar', 'foo_bar!'], do_query('foo_')) self.assertEqual(['foo/bar', 'foo/bar!'], do_query('foo/')) self.assertEqual(['fo*ob?ar[fo]ob%ar_fo/obar'], do_query('fo*')) self.assertEqual(['fo*ob?ar[fo]ob%ar_fo/obar'], do_query('fo*ob?ar[fo]ob%ar_fo/obar'))
class MySQLConnectionTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.schema = [ Table('test_simple', key='id')[Column('id', auto_increment=True), Column('username'), Column('email'), Column('enabled', type='int'), Column('extra'), Index(['username'], unique=True), Index(['email'], unique=False), ], Table('test_composite', key=['id', 'name'])[Column('id', type='int'), Column('name'), Column('value'), Column('enabled', type='int'), Index(['name', 'value'], unique=False), Index(['name', 'enabled'], unique=True), ], ] self.dbm = DatabaseManager(self.env) self.dbm.drop_tables(self.schema) self.dbm.create_tables(self.schema) self.dbm.insert_into_tables([ ('test_simple', ('username', 'email', 'enabled'), [('joe', '*****@*****.**', 1), ('joé', '*****@*****.**', 0)]), ('test_composite', ('id', 'name', 'value', 'enabled'), [(1, 'foo', '42', 1), (1, 'bar', '42', 1), (2, 'foo', '43', 0), (2, 'bar', '43', 0)]), ]) def tearDown(self): DatabaseManager(self.env).drop_tables(self.schema) self.env.reset_db() def _show_index(self, table): with self.env.db_query as db: cursor = db.cursor() cursor.execute("SHOW INDEX FROM " + db.quote(table)) columns = get_column_names(cursor) rows = [dict(zip(columns, row)) for row in cursor] results = {} for index, group in itertools.groupby(rows, lambda v: v['Key_name']): group = list(group) results[index] = { 'unique': not group[0]['Non_unique'], 'columns': [row['Column_name'] for row in group], } return results def _drop_column(self, table, column): with self.env.db_transaction as db: db.drop_column(table, column) def _query(self, stmt, *args): return self.env.db_query(stmt, args) def test_remove_simple_keys(self): indices_0 = self._show_index('test_simple') self.assertEqual( ['PRIMARY', 'test_simple_email_idx', 'test_simple_username_idx'], sorted(indices_0)) self.assertEqual({ 'unique': True, 'columns': ['id'] }, indices_0['PRIMARY']) self.assertEqual({ 'unique': True, 'columns': ['username'] }, indices_0['test_simple_username_idx']) self.assertEqual({ 'unique': False, 'columns': ['email'] }, indices_0['test_simple_email_idx']) self._drop_column('test_simple', 'enabled') self.assertEqual(indices_0, self._show_index('test_simple')) self._drop_column('test_simple', 'username') indices_1 = self._show_index('test_simple') self.assertEqual(['PRIMARY', 'test_simple_email_idx'], sorted(indices_1)) self._drop_column('test_simple', 'email') indices_2 = self._show_index('test_simple') self.assertEqual(['PRIMARY'], sorted(indices_2)) self._drop_column('test_simple', 'id') indices_3 = self._show_index('test_simple') self.assertEqual({}, indices_3) def test_remove_composite_keys(self): indices_0 = self._show_index('test_composite') self.assertEqual([ 'PRIMARY', 'test_composite_name_enabled_idx', 'test_composite_name_value_idx' ], sorted(indices_0)) self.assertEqual({ 'unique': True, 'columns': ['id', 'name'] }, indices_0['PRIMARY']) self.assertEqual({ 'unique': False, 'columns': ['name', 'value'] }, indices_0['test_composite_name_value_idx']) self.assertEqual({ 'unique': True, 'columns': ['name', 'enabled'] }, indices_0['test_composite_name_enabled_idx']) self._drop_column('test_composite', 'id') indices_1 = self._show_index('test_composite') self.assertEqual([ 'test_composite_name_enabled_idx', 'test_composite_name_value_idx' ], sorted(indices_1)) self.assertEqual(indices_0['test_composite_name_value_idx'], indices_1['test_composite_name_value_idx']) self.assertEqual(indices_0['test_composite_name_enabled_idx'], indices_1['test_composite_name_enabled_idx']) rows = self._query("""SELECT * FROM test_composite ORDER BY name, value, enabled""") self.assertEqual([('bar', '42', 1), ('bar', '43', 0), ('foo', '42', 1), ('foo', '43', 0)], rows) self._drop_column('test_composite', 'name') self.assertEqual({}, self._show_index('test_composite')) rows = self._query("""SELECT * FROM test_composite ORDER BY value, enabled""") self.assertEqual([('42', 1), ('42', 1), ('43', 0), ('43', 0)], rows)
class UpgradeTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(path=mkdtemp()) self.env.config.filename = os.path.join(self.env.path, 'trac.ini') self.env.config.set('trac', 'repository_sync_per_request', 'repos1, repos3, repos5') def tearDown(self): self.env.reset_db_and_disk() def test_saves_backup(self): """Backup file is saved during upgrade.""" config = self.env.config db32.do_upgrade(self.env, VERSION, None) self.assertTrue(os.path.exists(config.filename + '.db32.bak')) def test_repository_sync_per_request_default_value(self): """The default repository sync_per_request attribute is set to true when repository_sync_per_request is not set in trac.ini. """ self.env.config.remove('trac', 'repository_sync_per_request') repositories = self.env.config['repositories'] repositories.set('.dir', '/var/svn') repositories.set('.type', 'svn') repositories.set('git.dir', '/var/git') repositories.set('git.type', 'git') db32.do_upgrade(self.env, VERSION, None) repositories = self.env.config['repositories'] self.assertIn('.sync_per_request', repositories) self.assertTrue(repositories.getbool('.sync_per_request')) self.assertNotIn('git.sync_per_request', repositories) self.assertFalse(repositories.getbool('git.sync_per_request')) def test_repository_sync_per_request_default_value_with_db(self): """The default repository sync_per_request attribute is set to true when repository_sync_per_request is not set in trac.ini. """ self.env.config.remove('trac', 'repository_sync_per_request') # directly insert repository records instead of DbRepositoryProvider # to avoid a TracError "The repository type 'svn' is not supported" with self.env.db_transaction as db: db.executemany( """INSERT INTO repository (id,name,value) VALUES (%s,%s,%s)""", [(1, 'name', ''), (1, 'dir', '/var/svn'), (1, 'type', 'svn'), (2, 'name', 'git'), (2, 'dir', '/var/git'), (2, 'type', 'git')]) db32.do_upgrade(self.env, VERSION, None) repos = RepositoryManager(self.env).get_all_repositories() self.assertIn('', repos) self.assertTrue(repos['']['sync_per_request']) self.assertEqual( '1', self.env.db_query(""" SELECT value FROM repository WHERE id=1 AND name='sync_per_request'""")[0][0]) self.assertIn('git', repos) self.assertFalse(repos['git']['sync_per_request']) self.assertIsNone( self.env.db_query(""" SELECT value FROM repository WHERE id=2 AND name='sync_per_request'""")[0][0]) def test_gitweb_configuration_moved(self): """The Gitweb configuration is moved from the [git] section to the [gitweb-repositories] section. """ projects_list = os.path.join(self.env.path, 'projects_list') projects_base = os.path.dirname(projects_list) projects_url = 'http://localhost/%s' with open(projects_list, 'w') as f: f.write(""" repos1 user1+<*****@*****.**> repos2 """) config = self.env.config['git'] config.set('projects_list', projects_list) config.set('projects_base', projects_base) config.set('projects_url', projects_url) repos1_dir = os.path.join(projects_base, 'repos1') repos2_dir = os.path.join(projects_base, 'repos2') db32.do_upgrade(self.env, VERSION, None) repos = RepositoryManager(self.env).get_all_repositories() self.assertIn('repos1', repos) self.assertTrue(repos['repos1']['sync_per_request']) self.assertEqual(repos1_dir, repos['repos1']['dir']) self.assertEqual('http://localhost/repos1', repos['repos1']['url']) self.assertIn('repos2', repos) self.assertFalse(repos['repos2']['sync_per_request']) self.assertEqual(repos2_dir, repos['repos2']['dir']) self.assertEqual('http://localhost/repos2', repos['repos2']['url']) config = self.env.config['gitweb-repositories'] self.assertNotIn('projects_list', self.env.config) self.assertNotIn('projects_base', self.env.config) self.assertNotIn('projects_url', self.env.config) self.assertNotIn('repository_sync_per_request', self.env.config) self.assertEqual(projects_list, config.get('projects_list')) self.assertEqual(projects_base, config.get('projects_base')) self.assertEqual(projects_url, config.get('projects_url')) self.assertEqual('repos1', config.get('sync_per_request')) def test_repository_providers_disabled(self): """Repository configuration is rewritten when repository providers are disabled. """ projects_list = os.path.join(self.env.path, 'projects_list') projects_base = os.path.dirname(projects_list) projects_url = 'http://localhost/%s' with open(projects_list, 'w') as f: f.write(""" repos1 user1+<*****@*****.**> repos2 """) config = self.env.config['git'] config.set('projects_list', projects_list) config.set('projects_base', projects_base) config.set('projects_url', projects_url) db_provider = DbRepositoryProvider(self.env) db_provider.add_repository('repos3', '/var/git/repos3', 'git') db_provider.add_repository('repos4', '/var/git/repos4', 'git') config = self.env.config['repositories'] config.set('repos5.dir', '/var/svn/repos4') config.set('repos5.type', 'svn') config.set('repos6.dir', '/var/svn/repos5') config.set('repos6.type', 'svn') self.env.disable_component(GitwebProjectsRepositoryProvider) self.env.disable_component(DbRepositoryProvider) self.env.disable_component(RepositoryManager) db32.do_upgrade(self.env, VERSION, None) self.env.enable_component(GitwebProjectsRepositoryProvider) self.env.enable_component(DbRepositoryProvider) self.env.enable_component(RepositoryManager) repos = RepositoryManager(self.env).get_all_repositories() config = self.env.config['gitweb-repositories'] self.assertEqual(projects_list, config.get('projects_list')) self.assertEqual(projects_base, config.get('projects_base')) self.assertEqual(projects_url, config.get('projects_url')) self.assertEqual('repos1', config.get('sync_per_request')) self.assertIn('repos1', repos) self.assertTrue(repos['repos1']['sync_per_request']) self.assertIn('repos2', repos) self.assertFalse(repos['repos2']['sync_per_request']) self.assertIn('repos3', repos) self.assertTrue(repos['repos3']['sync_per_request']) self.assertIn('repos4', repos) self.assertFalse(repos['repos4']['sync_per_request']) self.assertIn('repos5', repos) self.assertTrue(repos['repos5']['sync_per_request']) self.assertIn('repos6', repos) self.assertFalse(repos['repos6']['sync_per_request'])
class LoginModuleTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.module = LoginModule(self.env) def tearDown(self): self.env.reset_db() def test_anonymous_access(self): req = Mock(incookie=Cookie(), href=Href('/trac.cgi'), remote_addr='127.0.0.1', remote_user=None, base_path='/trac.cgi') self.assertEqual(None, self.module.authenticate(req)) def test_unknown_cookie_access(self): incookie = Cookie() incookie['trac_auth'] = '123' req = Mock(cgi_location='/trac', href=Href('/trac.cgi'), incookie=incookie, outcookie=Cookie(), remote_addr='127.0.0.1', remote_user=None, base_path='/trac.cgi') self.assertEqual(None, self.module.authenticate(req)) def test_known_cookie_access(self): self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") incookie = Cookie() incookie['trac_auth'] = '123' outcookie = Cookie() req = Mock(incookie=incookie, outcookie=outcookie, href=Href('/trac.cgi'), base_path='/trac.cgi', remote_addr='127.0.0.1', remote_user=None) self.assertEqual('john', self.module.authenticate(req)) self.failIf('auth_cookie' in req.outcookie) def test_known_cookie_ip_check_enabled(self): self.env.config.set('trac', 'check_auth_ip', 'yes') self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") incookie = Cookie() incookie['trac_auth'] = '123' outcookie = Cookie() req = Mock(cgi_location='/trac', href=Href('/trac.cgi'), incookie=incookie, outcookie=outcookie, remote_addr='192.168.0.100', remote_user=None, base_path='/trac.cgi') self.assertEqual(None, self.module.authenticate(req)) self.failIf('trac_auth' not in req.outcookie) def test_known_cookie_ip_check_disabled(self): self.env.config.set('trac', 'check_auth_ip', 'no') self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") incookie = Cookie() incookie['trac_auth'] = '123' outcookie = Cookie() req = Mock(incookie=incookie, outcookie=outcookie, href=Href('/trac.cgi'), base_path='/trac.cgi', remote_addr='192.168.0.100', remote_user=None) self.assertEqual('john', self.module.authenticate(req)) self.failIf('auth_cookie' in req.outcookie) def test_login(self): outcookie = Cookie() # remote_user must be upper case to test that by default, case is # preserved. req = Mock(cgi_location='/trac', href=Href('/trac.cgi'), incookie=Cookie(), outcookie=outcookie, remote_addr='127.0.0.1', remote_user='******', authname='john', base_path='/trac.cgi') self.module._do_login(req) assert outcookie.has_key('trac_auth'), '"trac_auth" Cookie not set' auth_cookie = outcookie['trac_auth'].value self.assertEquals([('john', '127.0.0.1')], self.env.db_query( "SELECT name, ipnr FROM auth_cookie WHERE cookie=%s", (auth_cookie,))) def test_login_ignore_case(self): """ Test that login is succesful when the usernames differ in case, but case is ignored. """ self.env.config.set('trac', 'ignore_auth_case', 'yes') outcookie = Cookie() req = Mock(cgi_location='/trac', href=Href('/trac.cgi'), incookie=Cookie(), outcookie=outcookie, remote_addr='127.0.0.1', remote_user='******', authname='anonymous', base_path='/trac.cgi') self.module._do_login(req) assert outcookie.has_key('trac_auth'), '"trac_auth" Cookie not set' auth_cookie = outcookie['trac_auth'].value self.assertEquals([('john', '127.0.0.1')], self.env.db_query( "SELECT name, ipnr FROM auth_cookie WHERE cookie=%s", (auth_cookie,))) def test_login_no_username(self): req = Mock(incookie=Cookie(), href=Href('/trac.cgi'), remote_addr='127.0.0.1', remote_user=None, base_path='/trac.cgi') self.assertRaises(TracError, self.module._do_login, req) def test_already_logged_in_same_user(self): self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") incookie = Cookie() incookie['trac_auth'] = '123' req = Mock(incookie=incookie, outcookie=Cookie(), href=Href('/trac.cgi'), base_path='/trac.cgi', remote_addr='127.0.0.1', remote_user='******', authname='john') self.module._do_login(req) # this shouldn't raise an error def test_already_logged_in_different_user(self): self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") incookie = Cookie() incookie['trac_auth'] = '123' req = Mock(incookie=incookie, authname='john', href=Href('/trac.cgi'), base_path='/trac.cgi', remote_addr='127.0.0.1', remote_user='******') self.assertRaises(AssertionError, self.module._do_login, req) def test_logout(self): self.env.db_transaction(""" INSERT INTO auth_cookie (cookie, name, ipnr) VALUES ('123', 'john', '127.0.0.1')""") incookie = Cookie() incookie['trac_auth'] = '123' outcookie = Cookie() req = Mock(cgi_location='/trac', href=Href('/trac.cgi'), incookie=incookie, outcookie=outcookie, remote_addr='127.0.0.1', remote_user=None, authname='john', base_path='/trac.cgi') self.module._do_logout(req) self.failIf('trac_auth' not in outcookie) self.failIf(self.env.db_query( "SELECT name, ipnr FROM auth_cookie WHERE name='john'")) def test_logout_not_logged_in(self): req = Mock(cgi_location='/trac', href=Href('/trac.cgi'), incookie=Cookie(), outcookie=Cookie(), remote_addr='127.0.0.1', remote_user=None, authname='anonymous', base_path='/trac.cgi') self.module._do_logout(req) # this shouldn't raise an error
class CacheTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub() self.log = self.env.log self.env.db_transaction.executemany( "INSERT INTO repository (id, name, value) VALUES (%s, %s, %s)", [(1, "name", "test-repos"), (1, "youngest_rev", "")], ) def tearDown(self): self.env.reset_db() # Helpers def get_repos(self, get_changeset=None, youngest_rev=1): if get_changeset is None: def no_changeset(rev): raise NoSuchChangeset(rev) get_changeset = no_changeset return Mock( Repository, "test-repos", {"name": "test-repos", "id": 1}, self.log, get_changeset=get_changeset, get_oldest_rev=lambda: 0, get_youngest_rev=lambda: youngest_rev, normalize_rev=lambda x: get_changeset(x).rev, next_rev=(lambda x: int(x) < youngest_rev and x + 1 or None), ) def preset_cache(self, *args): """Each arg is a (rev tuple, changes list of tuples) pair.""" with self.env.db_transaction as db: for rev, changes in args: db( """INSERT INTO revision (repos, rev, time, author, message) VALUES (1,%s,%s,%s,%s) """, rev, ) if changes: db.executemany( """ INSERT INTO node_change (repos, rev, path, node_type, change_type, base_path, base_rev) VALUES (1, %s, %s, %s, %s, %s, %s) """, [(rev[0],) + change for change in changes], ) db( """UPDATE repository SET value=%s WHERE id=1 AND name='youngest_rev' """, (args[-1][0][0],), ) # Tests def test_initial_sync_with_empty_repos(self): repos = self.get_repos() cache = CachedRepository(self.env, repos, self.log) cache.sync() with self.env.db_query as db: self.assertEquals([], db("SELECT rev, time, author, message FROM revision")) self.assertEquals(0, db("SELECT COUNT(*) FROM node_change")[0][0]) def test_initial_sync(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=1) changes = [ ("trunk", Node.DIRECTORY, Changeset.ADD, None, None), ("trunk/README", Node.FILE, Changeset.ADD, None, None), ] changesets = [ Mock(Changeset, repos, 0, "", "", t1, get_changes=lambda: []), Mock(Changeset, repos, 1, "Import", "joe", t2, get_changes=lambda: iter(changes)), ] cache = CachedRepository(self.env, repos, self.log) cache.sync() with self.env.db_query as db: rows = db("SELECT rev, time, author, message FROM revision") self.assertEquals(len(rows), 2) self.assertEquals(("0", to_utimestamp(t1), "", ""), rows[0]) self.assertEquals(("1", to_utimestamp(t2), "joe", "Import"), rows[1]) rows = db( """ SELECT rev, path, node_type, change_type, base_path, base_rev FROM node_change""" ) self.assertEquals(len(rows), 2) self.assertEquals(("1", "trunk", "D", "A", None, None), rows[0]) self.assertEquals(("1", "trunk/README", "F", "A", None, None), rows[1]) def test_update_sync(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) t3 = datetime(2003, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (("0", to_utimestamp(t1), "", ""), []), ( ("1", to_utimestamp(t2), "joe", "Import"), [("trunk", "D", "A", None, None), ("trunk/README", "F", "A", None, None)], ), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=2) changes = [("trunk/README", Node.FILE, Changeset.EDIT, "trunk/README", 1)] changesets = [ None, Mock(Changeset, repos, 1, "", "", t2, get_changes=lambda: []), Mock(Changeset, repos, 2, "Update", "joe", t3, get_changes=lambda: iter(changes)), ] cache = CachedRepository(self.env, repos, self.log) cache.sync() with self.env.db_query as db: self.assertEquals( [(to_utimestamp(t3), "joe", "Update")], db("SELECT time, author, message FROM revision WHERE rev='2'") ) self.assertEquals( [("trunk/README", "F", "E", "trunk/README", "1")], db( """SELECT path, node_type, change_type, base_path, base_rev FROM node_change WHERE rev='2'""" ), ) def test_clean_sync(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) t3 = datetime(2003, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (("0", to_utimestamp(t1), "", ""), []), ( ("1", to_utimestamp(t2), "joe", "Import"), [("trunk", "D", "A", None, None), ("trunk/README", "F", "A", None, None)], ), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=2) changes1 = [ ("trunk", Node.DIRECTORY, Changeset.ADD, None, None), ("trunk/README", Node.FILE, Changeset.ADD, None, None), ] changes2 = [("trunk/README", Node.FILE, Changeset.EDIT, "trunk/README", 1)] changesets = [ Mock(Changeset, repos, 0, "**empty**", "joe", t1, get_changes=lambda: []), Mock(Changeset, repos, 1, "Initial Import", "joe", t2, get_changes=lambda: iter(changes1)), Mock(Changeset, repos, 2, "Update", "joe", t3, get_changes=lambda: iter(changes2)), ] cache = CachedRepository(self.env, repos, self.log) cache.sync(clean=True) rows = self.env.db_query( """ SELECT time, author, message FROM revision ORDER BY rev """ ) self.assertEquals(3, len(rows)) self.assertEquals((to_utimestamp(t1), "joe", "**empty**"), rows[0]) self.assertEquals((to_utimestamp(t2), "joe", "Initial Import"), rows[1]) self.assertEquals((to_utimestamp(t3), "joe", "Update"), rows[2]) rows = self.env.db_query( """ SELECT rev, path, node_type, change_type, base_path, base_rev FROM node_change ORDER BY rev, path""" ) self.assertEquals(3, len(rows)) self.assertEquals(("1", "trunk", "D", "A", None, None), rows[0]) self.assertEquals(("1", "trunk/README", "F", "A", None, None), rows[1]) self.assertEquals(("2", "trunk/README", "F", "E", "trunk/README", "1"), rows[2]) def test_sync_changeset(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (("0", to_utimestamp(t1), "", ""), []), ( ("1", to_utimestamp(t2), "joe", "Import"), [("trunk", "D", "A", None, None), ("trunk/README", "F", "A", None, None)], ), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=1) changes1 = [ ("trunk", Node.DIRECTORY, Changeset.ADD, None, None), ("trunk/README", Node.FILE, Changeset.ADD, None, None), ] changesets = [ Mock(Changeset, repos, 0, "**empty**", "joe", t1, get_changes=lambda: []), Mock(Changeset, repos, 1, "Initial Import", "joe", t2, get_changes=lambda: iter(changes1)), ] cache = CachedRepository(self.env, repos, self.log) cache.sync_changeset(0) rows = self.env.db_query("SELECT time, author, message FROM revision ORDER BY rev") self.assertEquals(2, len(rows)) self.assertEquals((to_utimestamp(t1), "joe", "**empty**"), rows[0]) self.assertEquals((to_utimestamp(t2), "joe", "Import"), rows[1]) def test_sync_changeset_if_not_exists(self): t = [ datetime(2001, 1, 1, 1, 1, 1, 0, utc), # r0 datetime(2002, 1, 1, 1, 1, 1, 0, utc), # r1 datetime(2003, 1, 1, 1, 1, 1, 0, utc), # r2 datetime(2004, 1, 1, 1, 1, 1, 0, utc), # r3 ] self.preset_cache( (("0", to_utimestamp(t[0]), "joe", "**empty**"), []), ( ("1", to_utimestamp(t[1]), "joe", "Import"), [("trunk", "D", "A", None, None), ("trunk/README", "F", "A", None, None)], ), # not exists r2 (("3", to_utimestamp(t[3]), "joe", "Add COPYING"), [("trunk/COPYING", "F", "A", None, None)]), ) repos = self.get_repos(get_changeset=lambda x: changesets[int(x)], youngest_rev=3) changes = [ None, # r0 [ ("trunk", Node.DIRECTORY, Changeset.ADD, None, None), # r1 ("trunk/README", Node.FILE, Changeset.ADD, None, None), ], [ ("branches", Node.DIRECTORY, Changeset.ADD, None, None), # r2 ("tags", Node.DIRECTORY, Changeset.ADD, None, None), ], [("trunk/COPYING", Node.FILE, Changeset.ADD, None, None)], # r3 ] changesets = [ Mock(Changeset, repos, 0, "**empty**", "joe", t[0], get_changes=lambda: []), Mock(Changeset, repos, 1, "Initial Import", "joe", t[1], get_changes=lambda: iter(changes[1])), Mock(Changeset, repos, 2, "Created directories", "john", t[2], get_changes=lambda: iter(changes[2])), Mock(Changeset, repos, 3, "Add COPYING", "joe", t[3], get_changes=lambda: iter(changes[3])), ] cache = CachedRepository(self.env, repos, self.log) self.assertRaises(NoSuchChangeset, cache.get_changeset, 2) cache.sync() self.assertRaises(NoSuchChangeset, cache.get_changeset, 2) self.assertEqual(None, cache.sync_changeset(2)) cset = cache.get_changeset(2) self.assertEqual("john", cset.author) self.assertEqual("Created directories", cset.message) self.assertEqual(t[2], cset.date) cset_changes = cset.get_changes() self.assertEqual(("branches", Node.DIRECTORY, Changeset.ADD, None, None), cset_changes.next()) self.assertEqual(("tags", Node.DIRECTORY, Changeset.ADD, None, None), cset_changes.next()) self.assertRaises(StopIteration, cset_changes.next) rows = self.env.db_query("SELECT time,author,message FROM revision ORDER BY rev") self.assertEquals(4, len(rows)) self.assertEquals((to_utimestamp(t[0]), "joe", "**empty**"), rows[0]) self.assertEquals((to_utimestamp(t[1]), "joe", "Import"), rows[1]) self.assertEquals((to_utimestamp(t[2]), "john", "Created directories"), rows[2]) self.assertEquals((to_utimestamp(t[3]), "joe", "Add COPYING"), rows[3]) def test_get_changes(self): t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) self.preset_cache( (("0", to_utimestamp(t1), "", ""), []), ( ("1", to_utimestamp(t2), "joe", "Import"), [("trunk", "D", "A", None, None), ("trunk/RDME", "F", "A", None, None)], ), ) repos = self.get_repos() cache = CachedRepository(self.env, repos, self.log) self.assertEqual("1", cache.youngest_rev) changeset = cache.get_changeset(1) self.assertEqual("joe", changeset.author) self.assertEqual("Import", changeset.message) self.assertEqual(t2, changeset.date) changes = changeset.get_changes() self.assertEqual(("trunk", Node.DIRECTORY, Changeset.ADD, None, None), changes.next()) self.assertEqual(("trunk/RDME", Node.FILE, Changeset.ADD, None, None), changes.next()) self.assertRaises(StopIteration, changes.next)
class WikiTagProviderTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True, path=tempfile.mkdtemp(), enable=['trac.*', 'tractags.*']) setup = TagSetup(self.env) # Current tractags schema is partially setup with enabled component. # Revert these changes for getting a clean setup. self._revert_tractags_schema_init() setup.upgrade_environment() self.perms = PermissionSystem(self.env) self.tag_s = TagSystem(self.env) self.tag_wp = WikiTagProvider(self.env) # Populate table with initial test data. self.env.db_transaction(""" INSERT INTO tags (tagspace, name, tag) VALUES ('wiki', 'WikiStart', 'tag1') """) self.realm = 'wiki' self.tags = ['tag1'] def tearDown(self): self.env.shutdown() shutil.rmtree(self.env.path) # Helpers def _revert_tractags_schema_init(self): _revert_tractags_schema_init(self.env) # Tests def test_get_tags(self): resource = Resource('wiki', 'WikiStart') req = MockRequest(self.env, authname='editor') self.assertEquals([tag for tag in self.tag_wp.get_resource_tags(req, resource)], self.tags) def test_exclude_template_tags(self): # Populate table with more test data. req = MockRequest(self.env, authname='editor') self.env.db_transaction(""" INSERT INTO tags (tagspace, name, tag) VALUES ('wiki', 'PageTemplates/Template', 'tag2') """) tags = ['tag1', 'tag2'] self.assertEquals(self.tag_s.get_all_tags(req).keys(), self.tags) self.env.config.set('tags', 'query_exclude_wiki_templates', False) self.assertEquals(self.tag_s.get_all_tags(req).keys(), tags) def test_set_tags_no_perms(self): resource = Resource('wiki', 'TaggedPage') req = MockRequest(self.env, authname='anonymous') self.assertRaises(PermissionError, self.tag_wp.set_resource_tags, req, resource, self.tags) def test_set_tags(self): resource = Resource('wiki', 'TaggedPage') req = MockRequest(self.env, authname='editor') # Shouldn't raise an error with appropriate permission. self.tag_wp.set_resource_tags(req, resource, self.tags) self.tag_wp.set_resource_tags(req, resource, ['tag2']) # Check change records. rows = self.env.db_query(""" SELECT author,oldtags,newtags FROM tags_change WHERE tagspace=%s AND name=%s ORDER by time DESC """, ('wiki', 'TaggedPage')) self.assertEqual(rows[0], ('editor', 'tag1', 'tag2')) self.assertEqual(rows[1], ('editor', '', 'tag1'))
class SessionTestCase(unittest.TestCase): """Unit tests for the persistent session support.""" def setUp(self): self.env = EnvironmentStub() def tearDown(self): self.env.reset_db() def test_new_session(self): """ Verify that a session cookie gets sent back to the client for a new session. """ cookie = Cookie() req = Mock(incookie=Cookie(), outcookie=cookie, authname='anonymous', base_path='/') session = Session(self.env, req) self.assertEqual(session.sid, cookie['trac_session'].value) self.assertEqual( 0, self.env.db_query("SELECT COUNT(*) FROM session")[0][0]) def test_anonymous_session(self): """ Verify that session variables are stored in the database. """ incookie = Cookie() incookie['trac_session'] = '123456' outcookie = Cookie() req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=outcookie) session = Session(self.env, req) self.assertEquals('123456', session.sid) self.failIf(outcookie.has_key('trac_session')) def test_authenticated_session(self): """ Verifies that a session cookie does not get used if the user is logged in, and that Trac expires the cookie. """ incookie = Cookie() incookie['trac_session'] = '123456' outcookie = Cookie() req = Mock(authname='john', base_path='/', incookie=incookie, outcookie=outcookie) session = Session(self.env, req) self.assertEqual('john', session.sid) session['foo'] = 'bar' session.save() self.assertEquals(0, outcookie['trac_session']['expires']) def test_session_promotion(self): """ Verifies that an existing anonymous session gets promoted to an authenticated session when the user logs in. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") incookie = Cookie() incookie['trac_session'] = '123456' outcookie = Cookie() req = Mock(authname='john', base_path='/', incookie=incookie, outcookie=outcookie) session = Session(self.env, req) self.assertEqual('john', session.sid) session.save() self.assertEqual( [('john', 1)], self.env.db_query("SELECT sid, authenticated FROM session")) def test_new_session_promotion(self): """ Verifies that even without a preexisting anonymous session, an authenticated session will be created when the user logs in. (same test as above without the initial INSERT) """ with self.env.db_transaction as db: incookie = Cookie() incookie['trac_session'] = '123456' outcookie = Cookie() req = Mock(authname='john', base_path='/', incookie=incookie, outcookie=outcookie) session = Session(self.env, req) self.assertEqual('john', session.sid) session.save() self.assertEqual( [('john', 1)], self.env.db_query("SELECT sid, authenticated FROM session")) def test_add_anonymous_session_var(self): """ Verify that new variables are inserted into the 'session' table in the database for an anonymous session. """ incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) session['foo'] = 'bar' session.save() self.assertEqual( 'bar', self.env.db_query( "SELECT value FROM session_attribute WHERE sid='123456'")[0] [0]) def test_modify_anonymous_session_var(self): """ Verify that modifying an existing variable updates the 'session' table accordingly for an anonymous session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) self.assertEqual('bar', session['foo']) session['foo'] = 'baz' session.save() self.assertEqual( 'baz', self.env.db_query( "SELECT value FROM session_attribute WHERE sid='123456'")[0] [0]) def test_delete_anonymous_session_var(self): """ Verify that modifying a variable updates the 'session' table accordingly for an anonymous session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) self.assertEqual('bar', session['foo']) del session['foo'] session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='123456' AND name='foo' """)[0][0]) def test_purge_anonymous_session(self): """ Verify that old sessions get purged. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, %s)", (0, )) db("INSERT INTO session VALUES ('987654', 0, %s)", (int(time.time() - PURGE_AGE - 3600), )) db(""" INSERT INTO session_attribute VALUES ('987654', 0, 'foo', 'bar') """) # We need to modify a different session to trigger the purging incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) session['foo'] = 'bar' session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session WHERE sid='987654' AND authenticated=0 """)[0][0]) def test_delete_empty_session(self): """ Verify that a session gets deleted when it doesn't have any data except for the 'last_visit' timestamp. """ now = time.time() # Make sure the session has data so that it doesn't get dropped with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, %s)", (int(now - UPDATE_INTERVAL - 3600), )) db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) del session['foo'] session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session WHERE sid='123456' AND authenticated=0 """)[0][0]) def test_change_anonymous_session(self): """ Verify that changing from one anonymous session to an inexisting anonymous session creates the new session and doesn't carry over variables from the previous session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 0)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) incookie = Cookie() incookie['trac_session'] = '123456' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) self.assertEqual({'foo': 'bar'}, session) session.get_session('7890') session['baz'] = 'moo' session.save() self.assertEqual({'baz': 'moo'}, session) with self.env.db_query as db: self.assertEqual( 1, db(""" SELECT COUNT(*) FROM session WHERE sid='7890' AND authenticated=0 """)[0][0]) self.assertEqual([('baz', 'moo')], db(""" SELECT name, value FROM session_attribute WHERE sid='7890' AND authenticated=0 """)) def test_add_authenticated_session_var(self): """ Verify that new variables are inserted into the 'session' table in the database for an authenticated session. """ req = Mock(authname='john', base_path='/', incookie=Cookie()) session = Session(self.env, req) session['foo'] = 'bar' session.save() self.assertEqual( 'bar', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_modify_authenticated_session_var(self): """ Verify that modifying an existing variable updates the 'session' table accordingly for an authenticated session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john',1,'foo','bar')") req = Mock(authname='john', base_path='/', incookie=Cookie()) session = Session(self.env, req) self.assertEqual('bar', session['foo']) session['foo'] = 'baz' session.save() self.assertEqual( 'baz', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_authenticated_session_independence_var(self): """ Verify that an anonymous session with the same name as an authenticated session doesn't interfere with the latter. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john',1,'foo','bar')") self.assertEqual( 'bar', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND authenticated=1 AND name='foo' """)[0][0]) incookie = Cookie() incookie['trac_session'] = 'john' req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=Cookie()) session = Session(self.env, req) self.assert_('foo' not in session) session['foo'] = 'baz' session.save() rows = self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND authenticated=1 AND name='foo' """) self.assertEqual(1, len(rows)) self.assertEqual('bar', rows[0][0]) rows = self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND authenticated=0 AND name='foo' """) self.assertEqual(1, len(rows)) self.assertEqual('baz', rows[0][0]) def test_delete_authenticated_session_var(self): """ Verify that deleting a variable updates the 'session' table accordingly for an authenticated session. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')" ) req = Mock(authname='john', base_path='/', incookie=Cookie()) session = Session(self.env, req) self.assertEqual('bar', session['foo']) del session['foo'] session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_update_session(self): """ Verify that accessing a session after one day updates the sessions 'last_visit' variable so that the session doesn't get purged. """ now = time.time() # Make sure the session has data so that it doesn't get dropped with self.env.db_transaction as db: db("INSERT INTO session VALUES ('123456', 0, 1)") db(""" INSERT INTO session_attribute VALUES ('123456', 0, 'foo', 'bar') """) incookie = Cookie() incookie['trac_session'] = '123456' outcookie = Cookie() req = Mock(authname='anonymous', base_path='/', incookie=incookie, outcookie=outcookie) session = Session(self.env, req) session['modified'] = True session.save() # updating does require modifications self.assertEqual(PURGE_AGE, outcookie['trac_session']['expires']) self.assertAlmostEqual( now, int( self.env.db_query(""" SELECT last_visit FROM session WHERE sid='123456' AND authenticated=0 """)[0][0]), -1) def test_modify_detached_session(self): """ Verify that modifying a variable in a session not associated with a request updates the database accordingly. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')" ) session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) session['foo'] = 'baz' session.save() self.assertEqual( 'baz', self.env.db_query(""" SELECT value FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_delete_detached_session_var(self): """ Verify that removing a variable in a session not associated with a request deletes the variable from the database. """ with self.env.db_transaction as db: db("INSERT INTO session VALUES ('john', 1, 0)") db("INSERT INTO session_attribute VALUES ('john', 1, 'foo', 'bar')" ) session = DetachedSession(self.env, 'john') self.assertEqual('bar', session['foo']) del session['foo'] session.save() self.assertEqual( 0, self.env.db_query(""" SELECT COUNT(*) FROM session_attribute WHERE sid='john' AND name='foo' """)[0][0]) def test_session_admin_list(self): auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) # Verify the empty case self.assertRaises(StopIteration, sess_admin._get_list([]).next) self.assertEqual([i for i in sess_admin._get_list(['authenticated'])], auth_list) self.assertEqual([i for i in sess_admin._get_list(['anonymous'])], anon_list) self.assertEqual([i for i in sess_admin._get_list(['*'])], all_list) self.assertEqual([i for i in sess_admin._get_list(['name00'])][0], auth_list[0]) self.assertEqual([i for i in sess_admin._get_list(['name10:0'])][0], anon_list[0]) self.assertEqual( [i for i in sess_admin._get_list(['name00', 'name01', 'name02'])], all_list[:3]) def test_session_admin_add(self): auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) self.assertRaises(Exception, sess_admin._do_add, 'name00') sess_admin._do_add('john') result = get_session_info(self.env, 'john') self.assertEqual(result, ('john', None, None)) sess_admin._do_add('john1', 'John1') result = get_session_info(self.env, 'john1') self.assertEqual(result, ('john1', 'John1', None)) sess_admin._do_add('john2', 'John2', '*****@*****.**') result = get_session_info(self.env, 'john2') self.assertEqual(result, ('john2', 'John2', '*****@*****.**')) def test_session_admin_set(self): auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) self.assertRaises(TracError, sess_admin._do_set, 'name', 'nothere', 'foo') sess_admin._do_set('name', 'name00', 'john') result = get_session_info(self.env, 'name00') self.assertEqual(result, ('name00', 'john', 'val00')) sess_admin._do_set('email', 'name00', '*****@*****.**') result = get_session_info(self.env, 'name00') self.assertEqual(result, ('name00', 'john', '*****@*****.**')) def test_session_admin_delete(self): auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin = SessionAdmin(self.env) sess_admin._do_delete('name00') result = get_session_info(self.env, 'name00') self.assertEqual(result, (None, None, None)) sess_admin._do_delete('nothere') result = get_session_info(self.env, 'nothere') self.assertEqual(result, (None, None, None)) auth_list, anon_list, all_list = _prep_session_table(self.env) sess_admin._do_delete('anonymous') result = [i for i in sess_admin._get_list(['*'])] self.assertEqual(result, auth_list) def test_session_admin_purge(self): sess_admin = SessionAdmin(self.env) auth_list, anon_list, all_list = \ _prep_session_table(self.env, spread_visits=True) sess_admin._do_purge('2010-01-02') result = [i for i in sess_admin._get_list(['*'])] self.assertEqual(result, auth_list + anon_list) result = get_session_info(self.env, anon_list[0][0]) self.assertEqual(result, ('name10', 'val10', 'val10')) result = get_session_info(self.env, anon_list[1][0]) self.assertEqual(result, ('name11', 'val11', 'val11')) auth_list, anon_list, all_list = \ _prep_session_table(self.env, spread_visits=True) sess_admin._do_purge('2010-01-12') result = [i for i in sess_admin._get_list(['*'])] self.assertEqual(result, auth_list + anon_list[1:]) rows = self.env.db_query( """ SELECT name, value FROM session_attribute WHERE sid = %s """, (anon_list[0][0], )) self.assertEqual([], rows) result = get_session_info(self.env, anon_list[1][0]) self.assertEqual(result, ('name11', 'val11', 'val11'))
class TagSetupTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(enable=['trac.*']) self.env.path = tempfile.mkdtemp() self.db_mgr = DatabaseManager(self.env) def tearDown(self): self.env.shutdown() shutil.rmtree(self.env.path) # Helpers def _get_cursor_description(self, cursor): # Cursors don't look the same across Trac versions if trac_version < '0.12': return cursor.description else: return cursor.cursor.description def _revert_tractags_schema_init(self): with self.env.db_transaction as db: db("DROP TABLE IF EXISTS tags") db("DROP TABLE IF EXISTS tags_change") db("DELETE FROM system WHERE name='tags_version'") db("DELETE FROM permission WHERE action %s" % db.like(), ('TAGS_%',)) def get_db_version(self): for version, in self.env.db_query(""" SELECT value FROM system WHERE name='tags_version' """): return int(version) # Tests def test_new_install(self): setup = TagSetup(self.env) # Current tractags schema is setup with enabled component anyway. # Revert these changes for clean install testing. self._revert_tractags_schema_init() self.assertEquals(0, setup.get_schema_version()) self.assertTrue(setup.environment_needs_upgrade()) setup.upgrade_environment() self.assertFalse(setup.environment_needs_upgrade()) with self.env.db_query as db: cursor = db.cursor() cursor.execute("SELECT * FROM tags") cols = [col[0] for col in self._get_cursor_description(cursor)] self.assertEquals([], cursor.fetchall()) self.assertEquals(['tagspace', 'name', 'tag'], cols) self.assertEquals(db_default.schema_version, self.get_db_version()) def test_upgrade_schema_v1(self): # Ancient, unversioned schema - wiki only. schema = [ Table('wiki_namespace')[ Column('name'), Column('namespace'), Index(['name', 'namespace']), ] ] setup = TagSetup(self.env) # Current tractags schema is setup with enabled component anyway. # Revert these changes for clean install testing. self._revert_tractags_schema_init() connector = self.db_mgr._get_connector()[0] with self.env.db_transaction as db: for table in schema: for stmt in connector.to_sql(table): db(stmt) # Populate table with migration test data. db("""INSERT INTO wiki_namespace (name, namespace) VALUES ('WikiStart', 'tag')""") tags = self.env.db_query("SELECT * FROM wiki_namespace") self.assertEquals([('WikiStart', 'tag')], tags) self.assertEquals(1, setup.get_schema_version()) self.assertTrue(setup.environment_needs_upgrade()) setup.upgrade_environment() self.assertFalse(setup.environment_needs_upgrade()) with self.env.db_query as db: cursor = db.cursor() cursor.execute("SELECT * FROM tags") tags = cursor.fetchall() cols = [col[0] for col in self._get_cursor_description(cursor)] # Db content should be migrated. self.assertEquals([('wiki', 'WikiStart', 'tag')], tags) self.assertEquals(['tagspace', 'name', 'tag'], cols) self.assertEquals(db_default.schema_version, self.get_db_version()) def test_upgrade_schema_v2(self): # Just register a current, but unversioned schema. schema = [ Table('tags', key=('tagspace', 'name', 'tag'))[ Column('tagspace'), Column('name'), Column('tag'), Index(['tagspace', 'name']), Index(['tagspace', 'tag']), ] ] setup = TagSetup(self.env) # Current tractags schema is setup with enabled component anyway. # Revert these changes for clean install testing. self._revert_tractags_schema_init() connector = self.db_mgr._get_connector()[0] with self.env.db_transaction as db: for table in schema: for stmt in connector.to_sql(table): db(stmt) # Populate table with test data. db("""INSERT INTO tags (tagspace, name, tag) VALUES ('wiki', 'WikiStart', 'tag')""") tags = self.env.db_query("SELECT * FROM tags") self.assertEquals([('wiki', 'WikiStart', 'tag')], tags) self.assertEquals(2, setup.get_schema_version()) self.assertTrue(setup.environment_needs_upgrade()) setup.upgrade_environment() self.assertFalse(setup.environment_needs_upgrade()) with self.env.db_query as db: cursor = db.cursor() cursor.execute("SELECT * FROM tags") tags = cursor.fetchall() cols = [col[0] for col in self._get_cursor_description(cursor)] # Db should be unchanged. self.assertEquals([('wiki', 'WikiStart', 'tag')], tags) self.assertEquals(['tagspace', 'name', 'tag'], cols) self.assertEquals(db_default.schema_version, self.get_db_version()) def test_upgrade_schema_v3(self): # Add table for tag change records to the schema. schema = [ Table('tags', key=('tagspace', 'name', 'tag'))[ Column('tagspace'), Column('name'), Column('tag'), Index(['tagspace', 'name']), Index(['tagspace', 'tag']), ] ] setup = TagSetup(self.env) # Current tractags schema is setup with enabled component anyway. # Revert these changes for clean install testing. self._revert_tractags_schema_init() connector = self.db_mgr._get_connector()[0] with self.env.db_transaction as db: for table in schema: for stmt in connector.to_sql(table): db(stmt) # Preset system db table with old version. db("""INSERT INTO system (name, value) VALUES ('tags_version', '3')""") self.assertEquals(3, setup.get_schema_version()) self.assertTrue(setup.environment_needs_upgrade()) setup.upgrade_environment() self.assertFalse(setup.environment_needs_upgrade()) with self.env.db_query as db: cursor = db.cursor() cursor.execute("SELECT * FROM tags_change") cols = [col[0] for col in self._get_cursor_description(cursor)] self.assertEquals(['tagspace', 'name', 'time', 'author', 'oldtags', 'newtags'], cols) self.assertEquals(db_default.schema_version, self.get_db_version())