diff options
Diffstat (limited to 'examples/versioned_history/test_versioning.py')
-rw-r--r-- | examples/versioned_history/test_versioning.py | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/examples/versioned_history/test_versioning.py b/examples/versioned_history/test_versioning.py new file mode 100644 index 000000000..906280555 --- /dev/null +++ b/examples/versioned_history/test_versioning.py @@ -0,0 +1,490 @@ +"""Unit tests illustrating usage of the ``history_meta.py`` module functions.""" + +from unittest import TestCase +from sqlalchemy.ext.declarative import declarative_base +from .history_meta import Versioned, versioned_session +from sqlalchemy import create_engine, Column, Integer, String, ForeignKey +from sqlalchemy.orm import clear_mappers, Session, deferred, relationship +from sqlalchemy.testing import AssertsCompiledSQL, eq_, assert_raises +from sqlalchemy.testing.entities import BasicEntity, ComparableEntity +from sqlalchemy.orm import exc as orm_exc + +engine = None + + +def setup(): + global engine + engine = create_engine('sqlite://', echo=True) + +class TestVersioning(TestCase, AssertsCompiledSQL): + __dialect__ = 'default' + + def setUp(self): + self.session = Session(engine) + self.Base = declarative_base() + versioned_session(self.session) + + def tearDown(self): + self.session.close() + clear_mappers() + self.Base.metadata.drop_all(engine) + + def create_tables(self): + self.Base.metadata.create_all(engine) + + def test_plain(self): + class SomeClass(Versioned, self.Base, ComparableEntity): + __tablename__ = 'sometable' + + id = Column(Integer, primary_key=True) + name = Column(String(50)) + + self.create_tables() + sess = self.session + sc = SomeClass(name='sc1') + sess.add(sc) + sess.commit() + + sc.name = 'sc1modified' + sess.commit() + + assert sc.version == 2 + + SomeClassHistory = SomeClass.__history_mapper__.class_ + + eq_( + sess.query(SomeClassHistory).filter(SomeClassHistory.version == 1).all(), + [SomeClassHistory(version=1, name='sc1')] + ) + + sc.name = 'sc1modified2' + + eq_( + sess.query(SomeClassHistory).order_by(SomeClassHistory.version).all(), + [ + SomeClassHistory(version=1, name='sc1'), + SomeClassHistory(version=2, name='sc1modified') + ] + ) + + assert sc.version == 3 + + sess.commit() + + sc.name = 'temp' + sc.name = 'sc1modified2' + + sess.commit() + + eq_( + sess.query(SomeClassHistory).order_by(SomeClassHistory.version).all(), + [ + SomeClassHistory(version=1, name='sc1'), + SomeClassHistory(version=2, name='sc1modified') + ] + ) + + sess.delete(sc) + sess.commit() + + eq_( + sess.query(SomeClassHistory).order_by(SomeClassHistory.version).all(), + [ + SomeClassHistory(version=1, name='sc1'), + SomeClassHistory(version=2, name='sc1modified'), + SomeClassHistory(version=3, name='sc1modified2') + ] + ) + + def test_w_mapper_versioning(self): + class SomeClass(Versioned, self.Base, ComparableEntity): + __tablename__ = 'sometable' + + id = Column(Integer, primary_key=True) + name = Column(String(50)) + + SomeClass.__mapper__.version_id_col = SomeClass.__table__.c.version + + self.create_tables() + sess = self.session + sc = SomeClass(name='sc1') + sess.add(sc) + sess.commit() + + s2 = Session(sess.bind) + sc2 = s2.query(SomeClass).first() + sc2.name = 'sc1modified' + + sc.name = 'sc1modified_again' + sess.commit() + + eq_(sc.version, 2) + + assert_raises( + orm_exc.StaleDataError, + s2.flush + ) + + def test_from_null(self): + class SomeClass(Versioned, self.Base, ComparableEntity): + __tablename__ = 'sometable' + + id = Column(Integer, primary_key=True) + name = Column(String(50)) + + self.create_tables() + sess = self.session + sc = SomeClass() + sess.add(sc) + sess.commit() + + sc.name = 'sc1' + sess.commit() + + assert sc.version == 2 + + def test_deferred(self): + """test versioning of unloaded, deferred columns.""" + + class SomeClass(Versioned, self.Base, ComparableEntity): + __tablename__ = 'sometable' + + id = Column(Integer, primary_key=True) + name = Column(String(50)) + data = deferred(Column(String(25))) + + self.create_tables() + sess = self.session + sc = SomeClass(name='sc1', data='somedata') + sess.add(sc) + sess.commit() + sess.close() + + sc = sess.query(SomeClass).first() + assert 'data' not in sc.__dict__ + + sc.name = 'sc1modified' + sess.commit() + + assert sc.version == 2 + + SomeClassHistory = SomeClass.__history_mapper__.class_ + + eq_( + sess.query(SomeClassHistory).filter(SomeClassHistory.version == 1).all(), + [SomeClassHistory(version=1, name='sc1', data='somedata')] + ) + + + def test_joined_inheritance(self): + class BaseClass(Versioned, self.Base, ComparableEntity): + __tablename__ = 'basetable' + + id = Column(Integer, primary_key=True) + name = Column(String(50)) + type = Column(String(20)) + + __mapper_args__ = {'polymorphic_on':type, 'polymorphic_identity':'base'} + + class SubClassSeparatePk(BaseClass): + __tablename__ = 'subtable1' + + id = Column(Integer, primary_key=True) + base_id = Column(Integer, ForeignKey('basetable.id')) + subdata1 = Column(String(50)) + + __mapper_args__ = {'polymorphic_identity':'sep'} + + class SubClassSamePk(BaseClass): + __tablename__ = 'subtable2' + + id = Column(Integer, ForeignKey('basetable.id'), primary_key=True) + subdata2 = Column(String(50)) + + __mapper_args__ = {'polymorphic_identity':'same'} + + self.create_tables() + sess = self.session + + sep1 = SubClassSeparatePk(name='sep1', subdata1='sep1subdata') + base1 = BaseClass(name='base1') + same1 = SubClassSamePk(name='same1', subdata2='same1subdata') + sess.add_all([sep1, base1, same1]) + sess.commit() + + base1.name = 'base1mod' + same1.subdata2 = 'same1subdatamod' + sep1.name ='sep1mod' + sess.commit() + + BaseClassHistory = BaseClass.__history_mapper__.class_ + SubClassSeparatePkHistory = SubClassSeparatePk.__history_mapper__.class_ + SubClassSamePkHistory = SubClassSamePk.__history_mapper__.class_ + eq_( + sess.query(BaseClassHistory).order_by(BaseClassHistory.id).all(), + [ + SubClassSeparatePkHistory(id=1, name='sep1', type='sep', version=1), + BaseClassHistory(id=2, name='base1', type='base', version=1), + SubClassSamePkHistory(id=3, name='same1', type='same', version=1) + ] + ) + + same1.subdata2 = 'same1subdatamod2' + + eq_( + sess.query(BaseClassHistory).order_by(BaseClassHistory.id, BaseClassHistory.version).all(), + [ + SubClassSeparatePkHistory(id=1, name='sep1', type='sep', version=1), + BaseClassHistory(id=2, name='base1', type='base', version=1), + SubClassSamePkHistory(id=3, name='same1', type='same', version=1), + SubClassSamePkHistory(id=3, name='same1', type='same', version=2) + ] + ) + + base1.name = 'base1mod2' + eq_( + sess.query(BaseClassHistory).order_by(BaseClassHistory.id, BaseClassHistory.version).all(), + [ + SubClassSeparatePkHistory(id=1, name='sep1', type='sep', version=1), + BaseClassHistory(id=2, name='base1', type='base', version=1), + BaseClassHistory(id=2, name='base1mod', type='base', version=2), + SubClassSamePkHistory(id=3, name='same1', type='same', version=1), + SubClassSamePkHistory(id=3, name='same1', type='same', version=2) + ] + ) + + def test_joined_inheritance_multilevel(self): + class BaseClass(Versioned, self.Base, ComparableEntity): + __tablename__ = 'basetable' + + id = Column(Integer, primary_key=True) + name = Column(String(50)) + type = Column(String(20)) + + __mapper_args__ = {'polymorphic_on': type, + 'polymorphic_identity': 'base'} + + class SubClass(BaseClass): + __tablename__ = 'subtable' + + id = Column(Integer, primary_key=True) + base_id = Column(Integer, ForeignKey('basetable.id')) + subdata1 = Column(String(50)) + + __mapper_args__ = {'polymorphic_identity': 'sub'} + + class SubSubClass(SubClass): + __tablename__ = 'subsubtable' + + id = Column(Integer, ForeignKey('subtable.id'), primary_key=True) + subdata2 = Column(String(50)) + + __mapper_args__ = {'polymorphic_identity': 'subsub'} + + self.create_tables() + + SubSubHistory = SubSubClass.__history_mapper__.class_ + sess = self.session + q = sess.query(SubSubHistory) + self.assert_compile( + q, + "SELECT subsubtable_history.id AS subsubtable_history_id, " + "subtable_history.id AS subtable_history_id, " + "basetable_history.id AS basetable_history_id, " + "basetable_history.name AS basetable_history_name, " + "basetable_history.type AS basetable_history_type, " + "subsubtable_history.version AS subsubtable_history_version, " + "subtable_history.version AS subtable_history_version, " + "basetable_history.version AS basetable_history_version, " + "subtable_history.base_id AS subtable_history_base_id, " + "subtable_history.subdata1 AS subtable_history_subdata1, " + "subsubtable_history.subdata2 AS subsubtable_history_subdata2 " + "FROM basetable_history " + "JOIN subtable_history " + "ON basetable_history.id = subtable_history.base_id " + "AND basetable_history.version = subtable_history.version " + "JOIN subsubtable_history ON subtable_history.id = " + "subsubtable_history.id AND subtable_history.version = subsubtable_history.version" + ) + + ssc = SubSubClass(name='ss1', subdata1='sd1', subdata2='sd2') + sess.add(ssc) + sess.commit() + eq_( + sess.query(SubSubHistory).all(), + [] + ) + ssc.subdata1 = 'sd11' + ssc.subdata2 = 'sd22' + sess.commit() + eq_( + sess.query(SubSubHistory).all(), + [SubSubHistory(name='ss1', subdata1='sd1', + subdata2='sd2', type='subsub', version=1)] + ) + eq_(ssc, SubSubClass(name='ss1', subdata1='sd11', + subdata2='sd22', version=2)) + + + + def test_single_inheritance(self): + class BaseClass(Versioned, self.Base, ComparableEntity): + __tablename__ = 'basetable' + + id = Column(Integer, primary_key=True) + name = Column(String(50)) + type = Column(String(50)) + __mapper_args__ = {'polymorphic_on':type, 'polymorphic_identity':'base'} + + class SubClass(BaseClass): + + subname = Column(String(50), unique=True) + __mapper_args__ = {'polymorphic_identity':'sub'} + + self.create_tables() + sess = self.session + + b1 = BaseClass(name='b1') + sc = SubClass(name='s1', subname='sc1') + + sess.add_all([b1, sc]) + + sess.commit() + + b1.name='b1modified' + + BaseClassHistory = BaseClass.__history_mapper__.class_ + SubClassHistory = SubClass.__history_mapper__.class_ + + eq_( + sess.query(BaseClassHistory).order_by(BaseClassHistory.id, BaseClassHistory.version).all(), + [BaseClassHistory(id=1, name='b1', type='base', version=1)] + ) + + sc.name ='s1modified' + b1.name='b1modified2' + + eq_( + sess.query(BaseClassHistory).order_by(BaseClassHistory.id, BaseClassHistory.version).all(), + [ + BaseClassHistory(id=1, name='b1', type='base', version=1), + BaseClassHistory(id=1, name='b1modified', type='base', version=2), + SubClassHistory(id=2, name='s1', type='sub', version=1) + ] + ) + + # test the unique constraint on the subclass + # column + sc.name ="modifyagain" + sess.flush() + + def test_unique(self): + class SomeClass(Versioned, self.Base, ComparableEntity): + __tablename__ = 'sometable' + + id = Column(Integer, primary_key=True) + name = Column(String(50), unique=True) + data = Column(String(50)) + + self.create_tables() + sess = self.session + sc = SomeClass(name='sc1', data='sc1') + sess.add(sc) + sess.commit() + + sc.data = 'sc1modified' + sess.commit() + + assert sc.version == 2 + + sc.data = 'sc1modified2' + sess.commit() + + assert sc.version == 3 + + def test_relationship(self): + + class SomeRelated(self.Base, ComparableEntity): + __tablename__ = 'somerelated' + + id = Column(Integer, primary_key=True) + + class SomeClass(Versioned, self.Base, ComparableEntity): + __tablename__ = 'sometable' + + id = Column(Integer, primary_key=True) + name = Column(String(50)) + related_id = Column(Integer, ForeignKey('somerelated.id')) + related = relationship("SomeRelated", backref='classes') + + SomeClassHistory = SomeClass.__history_mapper__.class_ + + self.create_tables() + sess = self.session + sc = SomeClass(name='sc1') + sess.add(sc) + sess.commit() + + assert sc.version == 1 + + sr1 = SomeRelated() + sc.related = sr1 + sess.commit() + + assert sc.version == 2 + + eq_( + sess.query(SomeClassHistory).filter(SomeClassHistory.version == 1).all(), + [SomeClassHistory(version=1, name='sc1', related_id=None)] + ) + + sc.related = None + + eq_( + sess.query(SomeClassHistory).order_by(SomeClassHistory.version).all(), + [ + SomeClassHistory(version=1, name='sc1', related_id=None), + SomeClassHistory(version=2, name='sc1', related_id=sr1.id) + ] + ) + + assert sc.version == 3 + + def test_backref_relationship(self): + + class SomeRelated(self.Base, ComparableEntity): + __tablename__ = 'somerelated' + + id = Column(Integer, primary_key=True) + name = Column(String(50)) + related_id = Column(Integer, ForeignKey('sometable.id')) + related = relationship("SomeClass", backref='related') + + class SomeClass(Versioned, self.Base, ComparableEntity): + __tablename__ = 'sometable' + + id = Column(Integer, primary_key=True) + + self.create_tables() + sess = self.session + sc = SomeClass() + sess.add(sc) + sess.commit() + + assert sc.version == 1 + + sr = SomeRelated(name='sr', related=sc) + sess.add(sr) + sess.commit() + + assert sc.version == 1 + + sr.name = 'sr2' + sess.commit() + + assert sc.version == 1 + + sess.delete(sr) + sess.commit() + + assert sc.version == 1 |