def setUp(self): print('setup') if hasattr(UserTable, 'test_column1'): try: delattr(UserTable, 'test_column1') except: pass sa.inspect(UserTable).mapper._expire_memoizations() del sa.inspect(UserTable).mapper.columns['test_column1'] del sa.inspect(UserTable).mapper._props['test_column1'] if hasattr(UserTable, 'test_column3'): try: delattr(UserTable, 'test_column3') except: pass sa.inspect(UserTable).mapper._expire_memoizations() del sa.inspect(UserTable).mapper.columns['test_column3'] del sa.inspect(UserTable).mapper._props['test_column3'] try: delete_cmd = 'drop table {}' self.engine.execute(delete_cmd.format(UserTable.__tablename__)) except Exception as e: pass Base.metadata.create_all(self.engine) UserTable.register(ArchiveTable, self.engine) MultiColumnUserTable.register(MultiColumnArchiveTable, self.engine) self.p1 = dict(product_id=10, col1='foobar', col2=10, col3=1) self.p2 = dict(product_id=11, col1='baz', col2=11, col3=1) self.p3 = dict(product_id=2546, col1='test', col2=12, col3=0) self.session = self.Session()
def test_insert_new_product_with_user(self): p = UserTable(**self.p1) p.updated_by('test_user') self._add_and_test_version(p, 0) self._verify_row(self.p1, 0) self._verify_archive(self.p1, 0, log_id=p.va_id, user='******')
def test_va_diff_new_column_and_del_column(self): p = UserTable(**self.p1) p._updated_by = '1' self._add_and_test_version(p, 0) print("111", p.__table__.c) self.addTestNullableColumn() p = self.session.query(UserTable).get(p.id) print("222", p.__table__.c) p.col1 = 'test' p.test_column1 = 'tc1' p._updated_by = '2' self.session.commit() res = UserTable.va_diff(self.session, va_id=p.va_id) print("RESULT", res) self.assertEqual( res, { 'va_prev_version': 0, 'va_version': 1, 'prev_user_id': '1', 'user_id': '2', 'change': { 'col1': { 'this': 'test', 'prev': 'foobar' }, 'test_column1': { 'prev': None, 'this': 'tc1', } } }) print("PASSED") self.deleteTestNullableColumn() print("DELETED") p = self.session.query(UserTable).get(p.id) print("P COLS", p.__table__.c) p.col1 = 'test2' p._updated_by = '1' self.session.commit() res = UserTable.va_diff(self.session, va_id=p.va_id) print("ANOTHER RES", res) self.assertEqual( res, { 'va_prev_version': 1, 'va_version': 2, 'prev_user_id': '2', 'user_id': '1', 'change': { 'col1': { 'this': 'test2', 'prev': 'test' }, 'test_column1': { 'prev': 'tc1', 'this': None, } } })
def test_va_list_by_pk_fail(self): p = UserTable(**self.p1) self._add_and_test_version(p, 0) p = self.session.query(UserTable).get(p.id) p.col1 = 'test' self.session.commit() with self.assertRaises(LogIdentifyError): UserTable.va_list_by_pk(self.session)
def setUp(self): Base.metadata.create_all(self.engine) UserTable.register(ArchiveTable, self.engine) MultiColumnUserTable.register(MultiColumnArchiveTable, self.engine) self.p1 = dict(product_id=10, col1='foobar', col2=10, col3=1) self.p2 = dict(product_id=11, col1='baz', col2=11, col3=1) self.p3 = dict(product_id=2546, col1='test', col2=12, col3=0) self.session = self.Session()
def test_insert_new_product(self): self.assertTrue(versionalchemy.is_initialized()) p = UserTable(**self.p1) p.col4 = 11 self._add_and_test_version(p, 0) expected = dict(other_name=11, **self.p1) self._verify_row(expected, 0) self._verify_archive(expected, 0, log_id=p.va_id)
def test_update_no_changes(self): ''' Add an unchanged row and make sure the version does not get bumped. ''' p = UserTable(**self.p1) self._add_and_test_version(p, 0) p.col1 = self.p1['col1'] self.session.add(p) self.session.commit() self._verify_archive(self.p1, 0) self.assertEqual(len(self.session.query(ArchiveTable).all()), 1)
def engine(): """Session-wide test database engine.""" drop_test_db() create_test_db() savage.init() _engine = create_engine(get_test_database_url(), json_serializer=savage_json_serializer) Base.metadata.create_all(_engine) UserTable.register(ArchiveTable, _engine) MultiColumnUserTable.register(MultiColumnArchiveTable, _engine) yield _engine _engine.dispose() drop_test_db()
def test_va_get_by_va_id(self): p = UserTable(**self.p1) self._add_and_test_version(p, 0) p = self.session.query(UserTable).get(p.id) res = p.va_get(self.session, va_id=p.va_id) self.assertEqual( res, { 'other_name': None, 'id': p.id, 'product_id': p.product_id, 'col1': p.col1, 'col2': p.col2, 'col3': p.col3, 'va_id': p.va_id })
def test_delete_with_user(self): p = UserTable(**self.p1) p.updated_by('test_user') self._add_and_test_version(p, 0) self.session.delete(p) self.session.flush() self.assertEquals( len( self.session.execute( sa.select([UserTable ]).where(UserTable.product_id == self.p1['product_id'])).fetchall()), 0) self._verify_archive(self.p1, 0) self._verify_archive(self.p1, 1, deleted=True, user='******')
def test_restore_row_with_non_default_column(self): p = UserTable(**self.p1) self._add_and_test_version(p, 0) p = self.session.query(UserTable).get(p.id) first_version = p.va_id p.col1 = 'test' self.session.commit() p = self.session.query(UserTable).get(p.id) self.assertEqual(p.col1, 'test') self.assertEqual(p.va_id, first_version + 1, 'Version should be increased') self.addTestNoDefaultNoNullColumn() p = self.session.query(UserTable).get(p.id) with self.assertRaises(RestoreError): p.va_restore(self.session, first_version)
def test_product_update(self): p = UserTable(**self.p1) self._add_and_test_version(p, 0) p.col1 = 'new' p.col2 = -1 self._add_and_test_version(p, 1) self._verify_row(dict(self.p1, **{'col1': 'new', 'col2': -1}), 1) self._verify_archive(self.p1, 0) self._verify_archive(dict(self.p1, **{ 'col1': 'new', 'col2': -1, }), 1, log_id=p.va_id)
def test_archive_table_collision_fails_1(self): """ Try to insert two records with the same version and foreign key in the same transaction and ensure the write fails. In other words, ensure the unique constraint is correctly imposed on the archive table. """ # Insert an element so it exists in the archive table p = UserTable(**self.p1) self._add_and_test_version(p, 0) to_insert = { 'va_version': 1, 'va_deleted': False, 'user_id': 'bar', 'va_updated_at': datetime.now(), 'va_data': {}, 'product_id': p.product_id, } self.session.add(ArchiveTable(**to_insert)) to_insert = { 'va_version': 1, 'va_deleted': True, 'user_id': 'foo', 'va_updated_at': datetime.now(), 'va_data': {}, 'product_id': p.product_id, } self.session.add(ArchiveTable(**to_insert)) with self.assertRaises(IntegrityError): self.session.flush()
def __init__(self, methodName='runTest'): self.engine1 = sa.create_engine(self.DATABASE_URL, isolation_level='READ UNCOMMITTED', echo='debug', logging_name='engine1') self.engine2 = sa.create_engine(self.DATABASE_URL, isolation_level='READ UNCOMMITTED', echo='debug', logging_name='engine2') self.Session1 = sessionmaker(bind=self.engine1) self.Session2 = sessionmaker(bind=self.engine2) Base.metadata.create_all(self.engine1) UserTable.register(ArchiveTable, self.engine1) UserTable.register(ArchiveTable, self.engine1) self.p1 = dict(product_id=10, col1='foobar', col2=10, col3=True) super(TestConcurrentUpdate, self).__init__(methodName)
def test_product_update_fails(self): """ Insert a product. Construct a new ORM object with the same id as the inserted object and make sure the insertion fails. """ # Initial product insert p = UserTable(**self.p1) self._add_and_test_version(p, 0) # Create a new row with the same primary key and try to insert it p_up = dict( col1='newcol', col2=5, col3=False, product_id=10, ) p_up_row = UserTable(**p_up) with self.assertRaises(IntegrityError): self._add_and_test_version(p_up_row, 1)
def test_insert_multiple_products(self): p1 = UserTable(**self.p1) p2 = UserTable(**self.p2) p3 = UserTable(**self.p3) self.session.add_all([p1, p2, p3]) self.session.flush() self.assertEquals(p1.version(self.session), 0) self.assertEquals(p2.version(self.session), 0) self.assertEquals(p3.version(self.session), 0) # Assert the columns match expected = [self.p1, self.p2, self.p3] ids = [p1.va_id, p2.va_id, p3.va_id] for i, p in enumerate(expected): self._verify_row(p, 0) self._verify_archive(p, 0, log_id=ids[i])
def setUp(self): super(TestGetAPI, self).setUp() self.p1_history, self.p2_history, self.p3_history = [], [], [] self.t1 = datetime.utcfromtimestamp(10) p1 = UserTable(**self.p1) p3 = UserTable(**self.p3) with mock.patch('versionalchemy.models.datetime') as p: p.now.return_value = self.t1 self.session.add_all([p1, p3]) self.session.flush() self.p1_history.append(self._history(p1, self.t1, 0)) self.p3_history.append(self._history(p3, self.t1, 0)) self.t2 = datetime.utcfromtimestamp(20) p1.col1 = 'change1' p2 = UserTable(**self.p2) with mock.patch('versionalchemy.models.datetime') as p: p.now.return_value = self.t2 self.session.add_all([p1, p2]) self.session.flush() self.p1_history.append(self._history(p1, self.t2, 1)) self.p2_history.append(self._history(p2, self.t2, 0)) self.t3 = datetime.utcfromtimestamp(30) p1.col3 = False p1.col1 = 'change2' with mock.patch('versionalchemy.models.datetime') as p: p.now.return_value = self.t3 self.session.add(p1) self.session.flush() self.p1_history.append(self._history(p1, self.t3, 2)) self.t4 = datetime.utcfromtimestamp(40) p1.col2 = 15 p2.col2 = 12 with mock.patch('versionalchemy.models.datetime') as p: p.now.return_value = self.t4 self.session.add_all([p1, p2]) self.session.flush() self.p1_history.append(self._history(p1, self.t4, 3)) self.p2_history.append(self._history(p2, self.t4, 1))
def test_va_diff_basic_va_version(self): p = UserTable(**self.p1) p._updated_by = '1' self._add_and_test_version(p, 0) p = self.session.query(UserTable).get(p.id) p.col1 = 'test' p._updated_by = '2' self.session.commit() log = self.session.query(ArchiveTable).get(p.va_id) res = UserTable.va_diff(self.session, log.va_version) self.assertEqual( res, { 'va_prev_version': 0, 'va_version': 1, 'prev_user_id': '1', 'user_id': '2', 'change': { 'col1': { 'this': 'test', 'prev': 'foobar' } } })
def setUp(self): super(TestDeleteAPI, self).setUp() p1 = UserTable(**self.p1) p3 = UserTable(**self.p3) self.session.add_all([p1, p3]) self.session.flush() p1.col1 = 'change1' p2 = UserTable(**self.p2) self.session.add_all([p1, p2]) self.session.flush() p1.col3 = False p1.col1 = 'change2' self.session.add(p1) self.session.flush() p1.col2 = 15 p2.col2 = 12 self.session.add_all([p1, p2]) self.session.flush()
def test_insert_after_delete(self): """ Inserting a row that has already been deleted should version where it left off (not at 0). """ p = UserTable(**self.p1) self._add_and_test_version(p, 0) self.session.delete(p) self.session.flush() p_new = dict(self.p1, **{ 'col1': 'changed', 'col2': 139, }) q = UserTable(**p_new) self._add_and_test_version(q, 2) self._verify_row(p_new, 2) self._verify_archive(self.p1, 0) self._verify_archive(self.p1, 1, deleted=True) self._verify_archive(p_new, 2)
def test_concurrent_product_updates(self): """ Assert that if two separate sessions try to update a product row, one succeeds and the other fails. """ p1 = UserTable(**self.p1) # Create two sessions session1 = self.Session1() session2 = self.Session2() # Add the initial row and flush it to the table session1.add(p1) session1.commit() # Update 1 in session1 p1.col1 = 'changed col 1' session1.add(p1) # Update 2 in session 2 p2 = session2.query(UserTable).all()[0] p2.col2 = 1245600 session2.add(p2) # this flush should succeed session2.commit() session2.close() # this flush should fail session1.commit() session1.close() final = dict(self.p1, **{'col1': 'changed col 1', 'col2': 1245600}) self._verify_row(final, 2, session=session1) history = [self.p1, dict(self.p1, **{'col2': 1245600}), final] for i, expected in enumerate(history): self._verify_archive(expected, i, session=session1)
def test_va_diff_all(self): p = UserTable(**self.p1) p._updated_by = '1' self._add_and_test_version(p, 0) p = self.session.query(UserTable).get(p.id) p.col1 = 'test' p._updated_by = '2' self.session.commit() res = UserTable.va_diff_all_by_pk(self.session, product_id=p.product_id) expected_result = [{ 'va_prev_version': None, 'va_version': 0, 'prev_user_id': None, 'user_id': '1', 'change': { 'col1': { 'this': 'foobar', 'prev': None }, 'col2': { 'this': 10, 'prev': None }, 'col3': { 'prev': None, 'this': 1 }, 'product_id': { 'prev': None, 'this': 10 }, 'id': { 'this': 1, 'prev': None } } }, { 'va_prev_version': 0, 'va_version': 1, 'prev_user_id': '1', 'user_id': '2', 'change': { 'col1': { 'this': 'test', 'prev': 'foobar' } } }] self.assertEqual(res, expected_result) res = p.va_diff_all(self.session) self.assertEqual(res, expected_result)
def test_va_list(self): p = UserTable(**self.p1) self._add_and_test_version(p, 0) p = self.session.query(UserTable).get(p.id) first_version = p.va_id p.col1 = 'test' self.session.commit() res = p.va_list(self.session) expected_response = [ { 'va_id': first_version, 'user_id': None, 'va_version': 0 }, { 'va_id': first_version + 1, 'user_id': None, 'va_version': 1 }, ] self.assertEqual(res, expected_response) res = UserTable.va_list_by_pk(self.session, product_id=p.product_id) self.assertEqual(res, expected_response)
def test_va_diff_first_version(self): p = UserTable(**self.p1) p._updated_by = '1' self._add_and_test_version(p, 0) p = self.session.query(UserTable).get(p.id) res = UserTable.va_diff(self.session, va_id=p.va_id) self.assertEqual( res, { 'va_prev_version': None, 'va_version': 0, 'prev_user_id': None, 'user_id': '1', 'change': { 'col1': { 'this': 'foobar', 'prev': None }, 'col2': { 'this': 10, 'prev': None }, 'col3': { 'prev': None, 'this': 1 }, 'product_id': { 'prev': None, 'this': 10 }, 'id': { 'this': 1, 'prev': None } } })
def test_product_update_with_user(self): p = UserTable(**self.p1) p.updated_by('test_user1') self._add_and_test_version(p, 0) p.col1 = 'new' p.col2 = -1 p.updated_by('test_user2') self._add_and_test_version(p, 1) self._verify_row(dict(self.p1, **{'col1': 'new', 'col2': -1}), 1) self._verify_archive(self.p1, 0, user='******') self._verify_archive(dict(self.p1, **{ 'col1': 'new', 'col2': -1, }), 1, user='******', log_id=p.va_id)
def test_restore_row_with_new_nullable_column_by_va_version(self): p = UserTable(**self.p1) self._add_and_test_version(p, 0) p = self.session.query(UserTable).get(p.id) log = self.session.query(ArchiveTable).get(p.va_id) first_va_version = log.va_version p.col1 = 'test' p.col2 = 10 self.session.commit() p = self.session.query(UserTable).get(p.id) self.assertEqual(p.col1, 'test') self.assertEqual(p.col2, 10) self.addTestNullableColumn() p = self.session.query(UserTable).get(p.id) p.va_restore(self.session, first_va_version) p = self.session.query(UserTable).get(p.id) self.assertEqual(p.col1, self.p1['col1']) self.assertEqual(p.col2, self.p1['col2']) self.assertEqual(p.test_column1, None)
def test_va_get_all(self): p = UserTable(**self.p1) p._updated_by = '1' self._add_and_test_version(p, 0) p = self.session.query(UserTable).get(p.id) p.col1 = 'test' p._updated_by = '2' self.session.commit() expected_result = [{ 'record': { 'col1': 'foobar', 'col2': 10, 'col3': 1, 'id': 1, 'other_name': None, 'product_id': 10 }, 'user_id': '1', 'va_id': 1, 'va_version': 0 }, { 'record': { 'col1': 'test', 'col2': 10, 'col3': 1, 'id': 1, 'other_name': None, 'product_id': 10 }, 'user_id': '2', 'va_id': 2, 'va_version': 1 }] res = p.va_get_all(self.session) self.assertEqual(res, expected_result) res = UserTable.va_get_all_by_pk(self.session, product_id=p.product_id) self.assertEqual(res, expected_result)
def test_restore_row_with_new_nullable_column_by_va_id(self): p = UserTable(**self.p1) self._add_and_test_version(p, 0) p = self.session.query(UserTable).get(p.id) first_va_id = p.va_id p.col1 = 'test' p.col2 = 10 self.session.commit() p = self.session.query(UserTable).get(p.id) self.assertEqual(p.col1, 'test') self.assertEqual(p.col2, 10) self.assertEqual(p.va_id, first_va_id + 1, 'va_id should be increased') self.addTestNullableColumn() p = self.session.query(UserTable).get(p.id) p.va_restore(self.session, va_id=first_va_id) p = self.session.query(UserTable).get(p.id) self.assertEqual(p.col1, self.p1['col1']) self.assertEqual(p.col2, self.p1['col2']) self.assertEqual(p.test_column1, None) self.assertEqual(p.va_id, first_va_id + 2)
def test_multiple_product_updates(self): """ Update a product multiple times and ensure each one gets correctly versioned. """ p = UserTable(**self.p1) self._add_and_test_version(p, 0) p.col1 = 'new' p.col2 = -1 self._add_and_test_version(p, 1) p.col1 = 'third change' p.col2 = 139 p.col3 = False self._add_and_test_version(p, 2) self._verify_row( dict(self.p1, **{ 'col1': 'third change', 'col2': 139, 'col3': False, }), 1) self._verify_archive(self.p1, 0) self._verify_archive(dict(self.p1, **{ 'col1': 'new', 'col2': -1, }), 1) self._verify_archive(dict( self.p1, **{ 'col1': 'third change', 'col2': 139, 'col3': False, }), 2, log_id=p.va_id)
def test_paging_results(self): self.session.execute('delete from {}'.format(UserTable.__tablename__)) self.session.execute('delete from {}'.format(ArchiveTable.__tablename__)) t = datetime.utcfromtimestamp(10000) with mock.patch('versionalchemy.models.datetime') as p: p.now.return_value = t history = [] self.p1['col2'] = 0 p1 = UserTable(**self.p1) self.session.add(p1) self.session.commit() history.append(self._history(p1, t, self.p1['col2'])) # make 500 changes for i in xrange(500): self.p1['col2'] += 1 self.p1['col3'] = int(i < 250) self.p1['col1'] = 'foobar' + '1' * ((i + 1) / 10) [setattr(p1, k, v) for k, v in self.p1.iteritems()] self.session.add(p1) self.session.commit() history.append(self._history(p1, t, self.p1['col2'])) result = get( UserTable, self.session, t1=datetime.utcfromtimestamp(0), t2=datetime.utcfromtimestamp(10000000000), page=1, page_size=1000, ) self._assert_result(result, history) result = get( UserTable, self.session, t1=datetime.utcfromtimestamp(0), t2=datetime.utcfromtimestamp(10000000000), page=1, page_size=100 ) self._assert_result(result, history[:100]) result = get( UserTable, self.session, t1=datetime.utcfromtimestamp(0), t2=datetime.utcfromtimestamp(10000000000), page=3, page_size=100 ) self._assert_result(result, history[200:300]) result = get( UserTable, self.session, t1=datetime.utcfromtimestamp(0), t2=datetime.utcfromtimestamp(10000000000), page=5, page_size=100 ) self._assert_result(result, history[400:500]) result = get( UserTable, self.session, t1=datetime.utcfromtimestamp(0), t2=datetime.utcfromtimestamp(10000000000), fields=['col1'], page=1, page_size=80 ) self._assert_result(result, history[0:80:10], fields=['col1'])