def test_validate(): Base, User, Foo, Bar, Baz = models() bar = Bar() bar['lock_version'] = '2' bar['chks'] = ['A', 'B'] assert not bar.is_valid() assert bar.errors == { 'chks': [errors.INVALID], 'created_by': [errors.UNSELECTED], 'foo_id': [errors.UNSELECTED], 'rad': [errors.UNSELECTED], 'updated_by': [errors.UNSELECTED]} bar = instantiate(Bar, id=1, lock_version=1) bar['lock_version'] = '2' assert not bar.is_valid() assert bar.errors == {None: [errors.CONFLICT]} bar = instantiate(Bar, id=1, lock_version=2) bar['lock_version'] = '2' bar['foo_id'] = '10' bar['rad'] = '1' bar['name'] = '?' Base.DB.execute = rs2csr(qw('? ?'), (1, 1)) assert not bar.is_valid() assert bar.errors == {'name': [errors.TAKEN], 'rad': [errors.TAKEN]} assert last_x() == ( 'SELECT ' 'EXISTS (SELECT 1 FROM bars t1 WHERE foo_id = %s AND rad = %s),\n' 'EXISTS (SELECT 1 FROM bars t1 WHERE name = %s)', [10, True, '?'], 1 ) bar.errors.clear() bar['name'] = '' bar.validate_column('name', len=(None, 2)) assert bar.errors == {'name': [errors.BLANK]} bar.errors.clear() bar.validate_column('name', len=(None, 2), nn=False) assert not bar.errors bar['name'] = 'abc' bar.validate_column('name', len=(None, 2)) assert bar.errors == {'name': [errors.TOO_LONG(2, 3)]} bar['xxx'] = '10' bar.errors.clear() assert not bar.validate_item('xxx', type=int, range=(1, 5)) assert bar.errors == {'xxx': [errors.BAD_FORMAT]} bar.errors.clear() assert not bar.validate_item( 'xxx', type=int, range=(1, 5), cast=Util.str2int) assert bar.errors == {'xxx': [errors.TOO_GREAT(5)]}
def test_restore_attrs(): Base, User, Foo, Bar, Baz = models() _ks = {'name', 'foo', 'foo_id'} def _d(x): return {k: v for k, v in x.__dict__.items() if k in _ks} foo = instantiate(Foo, id=1) bar = Bar() assert _d(bar) == {'name': '?'} bar.name = 'bar' bar.foo = foo assert _d(bar) == {'foo': foo, 'foo_id': 1, 'name': 'bar'} assert bar.name == 'bar' bar.restore_attrs() assert _d(bar) == {'name': '?'} assert bar.name == '?' bar = instantiate(Bar, id=2, foo_id=1, name='bar') assert bar.foo_id == 1 assert bar.name == 'bar' assert _d(bar) == {'foo_id': 1} bar.foo_id = 1 bar.name = 'bar' assert _d(bar) == {'foo_id': 1, 'name': 'bar'} bar.restore_attrs() assert _d(bar) == {} assert bar.foo_id == 1 assert bar.name == 'bar' assert _d(bar) == {'foo_id': 1} bar.foo = None assert _d(bar) == {'foo': None, 'foo_id': None} assert bar.foo_id is None bar.restore_attrs() assert _d(bar) == {} assert bar.foo_id == 1 bar.foo = foo assert _d(bar) == {'foo': foo, 'foo_id': 1} assert bar.foo_id == 1 bar.restore_attrs() assert _d(bar) == {'foo': foo} assert bar.foo_id == 1 bar.foo = foo2 = instantiate(Foo, id=2) assert _d(bar) == {'foo': foo2, 'foo_id': 2} assert bar.foo_id == 2 bar.restore_attrs() assert _d(bar) == {} assert bar.foo_id == 1
def test_dict(): Base, User, Foo, Bar, Baz = models() foo = instantiate(Foo, id=1, a=2, b=3) foo['b'] = 4 foo['c'] = '5' assert foo.dict() == {'id': 1} assert foo.dict(True) == {'id': 1, 'a': 2, 'b': 4, 'c': '5'}
def test_attr_in_db(): Base, User, Foo, Bar, Baz = models() bar = instantiate(Bar, id=1, foo_id=2, chks=3) bar['foo'] = Foo(id=3) bar['chks'] = ['1', '4'] assert bar.foo_id == 3 assert bar.chks == [bar.CHKS.A, bar.CHKS.C] assert bar.attr_in_db('foo_id') == 2 assert bar.attr_in_db('chks') == [bar.CHKS.A, bar.CHKS.B] assert bar.chks == [bar.CHKS.A, bar.CHKS.C]
def test_delete(): Base, User, Foo, Bar, Baz = models() bar = instantiate(Bar, id=100) assert bar.id == 100 bar.id = 200 assert bar.id == 200 args = [] Base.DB.execute = lambda *a: args.append(a) or 1 assert bar.delete() == 1 assert args == [('DELETE t1 FROM bars t1 WHERE id = %s', (100,), int)] with pytest.raises(ColumnNotLoaded) as e: Bar().delete() assert e.value.args == ('id',)
def test_choices(): class Base(model.Model): DB = database.Database() class Foo(Base): DB_TABLE = 'foos' abc = INT( choices=XEnum( qw('value label title'), A=(1, 'AA', 'aaa'), B=(2, 'BB', 'bbb'), C=(4, 'CC', 'ccc'), ), multiple=True, default=3, null=True, ) efg = INT(choices={ 1: 'ONE', 2: 'TWO', 4: 'FOUR' }, multiple=True, default=2, null=True) xyz = VARCHAR(choices=XEnum(X='x', Y='y', Z='z'), default='x', null=True) flag = BOOL(choices={ True: 'ON', False: 'OFF' }, default=True, null=True) assert '%r' % Foo.ABC.A == 'Foo.ABC.A' foo = Foo() assert foo.abc_choices() == ['AA', 'BB'] assert foo.abc_choices('title') == ['aaa', 'bbb'] assert foo.efg_choices() == ['TWO'] assert foo.xyz is Foo.XYZ.X assert foo.xyz_choice() == 'X' assert foo.flag_choice() == 'ON' assert foo.is_valid() foo['abc'] = ['A', 'AA', '2'] foo['efg'] = ['4', '3', 'TWO'] foo['xyz'] = 'Z' foo['flag'] = 'f' assert foo.abc == ['A', 'AA', Foo.ABC.B] assert foo.abc_choices() == ['BB'] assert foo.abc_choices('title') == ['bbb'] assert foo.efg == [4, '3', 'TWO'] assert foo.efg_choices() == ['FOUR'] assert foo.xyz == 'Z' assert foo.xyz_choice() is None assert foo.flag == 'f' assert foo.flag_choice() is None assert not foo.is_valid() assert foo.errors == { 'abc': [errors.INVALID], 'efg': [errors.INVALID], 'xyz': [errors.BAD_FORMAT], 'flag': [errors.BAD_FORMAT], } foo = instantiate(Foo, abc=None, efg=None, xyz=None, flag=None) assert foo.abc is None assert foo.abc_choices() == [] assert foo.efg is None assert foo.efg_choices() == [] assert foo.xyz is None assert foo.xyz_choice() is None assert foo.flag is None assert foo.flag_choice() is None foo['flag'] = '' assert foo.is_valid() assert not foo.is_changed() foo = instantiate(Foo, abc=0, efg=0, xyz='x', flag=0) assert foo.abc == foo.efg == [] assert foo.xyz is foo.XYZ.X assert foo.flag is False foo['flag'] = '1' assert foo.flag is True assert foo.is_changed() foo['flag'] = '0' assert not foo.is_changed() foo['xyz'] = '' assert foo.xyz is None assert foo.is_changed() foo['xyz'] = 'y' assert foo.xyz is Foo.XYZ.Y foo = instantiate(Foo, abc=7, efg=7, xyz='z', flag=1) assert foo.abc == [Foo.ABC.A, Foo.ABC.B, Foo.ABC.C] assert foo.efg == [1, 2, 4] assert foo.xyz is foo.XYZ.Z assert foo.flag is True foo['abc'] = foo['efg'] = '' assert foo.abc == '' assert foo.efg == '' assert not foo.is_valid() assert foo.errors == { 'abc': [errors.BAD_FORMAT], 'efg': [errors.BAD_FORMAT], } foo['abc'] = foo['efg'] = [] assert foo.abc == [] assert foo.attr_in_db('abc') == [Foo.ABC.A, Foo.ABC.B, Foo.ABC.C] assert foo.efg == [] assert foo.attr_in_db('efg') == [1, 2, 4] assert foo.is_valid() assert foo.is_changed(txn={}) == {'abc': 0, 'efg': 0} foo['abc'] = foo['efg'] = None assert foo.abc is foo.efg is None assert foo.is_valid() assert foo.is_changed(txn={}) == {'abc': None, 'efg': None} with pytest.raises(TypeError) as e: class E1(Base): DB_TABLE = 'xs' flag = BOOL(choices={1: 1}, multiple=True) assert e.value.args == ('E1.flag: multiple and db2py are incompossible', ) with pytest.raises(TypeError) as e: class E2(Base): DB_TABLE = 'xs' abc = INT(choices=XEnum( A=(1, 'AA'), B=(3, 'BB'), ), multiple=True) assert e.value.args == ('E2.abc: invalid bit E2.ABC.B', ) with pytest.raises(TypeError) as e: class E3(Base): DB_TABLE = 'xs' abc = INT(choices={1: 'AA', 3: 'BB'}, multiple=True) assert e.value.args == ('E3.abc: invalid bit 3', ) with pytest.raises(TypeError) as e: class E4(Base): DB_TABLE = 'xs' flag = BOOL(choices=XEnum(T=1)) assert e.value.args == ('E4.flag: XEnum and db2py are incompossible', ) a = Foo.ABC.A assert a == 1 assert a.name == 'A' assert a.value == 1 assert a.label == 'AA' assert a.title == 'aaa' assert dict(Foo.ABC.items()) == {1: 'AA', 2: 'BB', 4: 'CC'} assert dict(Foo.ABC.items('title')) == {1: 'aaa', 2: 'bbb', 4: 'ccc'} assert [*Foo.ABC] == [Foo.ABC.A, Foo.ABC.B, Foo.ABC.C] assert Foo.abc.choices is Foo.ABC assert Foo.flag.choices == {True: 'ON', False: 'OFF'} foo = instantiate(Foo, abc=3, flag=1) assert foo.abc == [foo.ABC.A, foo.ABC.B] == [1, 2] assert foo.flag is True assert foo.flag_choice() == 'ON' foo['abc'] = ['2', '4'] assert foo.abc == [foo.ABC.B, foo.ABC.C] == [2, 4] assert foo.attr_in_db('abc') == [foo.ABC.A, foo.ABC.B] == [1, 2] assert foo.abc == [foo.ABC.B, foo.ABC.C] == [2, 4] foo['flag'] = '0' assert foo.flag is False assert foo.is_changed(txn={}) == {'abc': 6, 'flag': False} foo = Foo() assert foo.abc == [foo.ABC.A, foo.ABC.B] assert foo.flag is True assert foo.is_valid() foo['abc'] = ['1', '2'] foo['flag'] = 1 assert foo.abc == [foo.ABC.A, foo.ABC.B] == [1, 2] assert foo.flag is True assert foo.is_valid() foo['flag'] = False assert foo.flag is False foo = Foo() foo['xyz'] = ['X'] assert not foo.is_valid() assert foo.errors == {'xyz': [errors.BAD_FORMAT]}
def test_BELONGS_TO(): class Base(model.Model): DB = database.Database() class Foo(Base): DB_TABLE = 'foos' id = INT(auto_increment=True, primary_key=True) class Bar(Base): DB_TABLE = 'bars' id = INT(auto_increment=True, primary_key=True) foo_id = BELONGS_TO(Foo) assert '%r' % Bar.id == 'Bar.id' assert '%r' % Bar.foo_id == 'Bar.foo_id' bar = Bar() assert bar.foo_id is None assert bar.__dict__ == {} assert 'foo_id' not in bar foo = bar.foo = instantiate(Foo, id=10) assert bar.__dict__ == {'foo': foo, 'foo_id': 10} assert 'foo_id' in bar assert bar.foo_id == 10 assert bar.foo is foo foo.id = 11 assert bar.__dict__ == {'foo': foo, 'foo_id': 10} assert bar.foo_id == 10 assert bar.is_changed(txn={}) == {'foo_id': 10} assert bar.__dict__ == {'foo': foo, 'foo_id': 10} bar.foo_id = 11 assert bar.__dict__ == {'foo': foo, 'foo_id': 11} assert bar.foo_id == 11 assert bar.foo is foo bar.foo_id = 12 assert bar.__dict__ == {'foo_id': 12} assert 'foo' not in bar.__dict__ assert bar.foo_id == 12 bar = Bar() assert bar.foo_id is None foo = bar.foo = Foo() foo.id = 13 assert bar.foo_id == 13 assert bar.__dict__ == {'foo': foo} bar = Bar() assert not bar.is_valid() assert 'foo_id' not in bar bar = Bar() foo = bar.foo = Foo() assert bar.__dict__ == {'foo': foo} assert 'foo_id' not in bar assert bar.is_valid() del bar.__dict__['foo'] assert not bar.is_valid() del bar.__dict__['errors'] bar.foo = None assert bar.__dict__ == {'foo': None, 'foo_id': None} assert not bar.is_valid() bar.foo_id = 'abc' assert bar.foo is None assert not bar.is_valid() assert Bar.foo_id.db_type() == 'INT' class DatePk(Base): DB_TABLE = 'date_pks' id = DATE(primary_key=True) class StrPk(Base): DB_TABLE = 'str_pks' id = VARCHAR(primary_key=True) class Foo(Base): DB_TABLE = 'foos' id = SERIAL() date_pk_id = BELONGS_TO(DatePk) str_pk_id = BELONGS_TO(StrPk) assert Foo.ddl_sqls() == [ 'CREATE TABLE foos (\n' ' id SERIAL PRIMARY KEY,\n' ' date_pk_id DATE NOT NULL,\n' ' str_pk_id VARCHAR(255) NOT NULL,\n' ' FOREIGN KEY (date_pk_id) REFERENCES date_pks (id),\n' ' FOREIGN KEY (str_pk_id) REFERENCES str_pks (id)\n' ')', 'CREATE INDEX foos_date_pk_id ON foos (date_pk_id)', 'CREATE INDEX foos_str_pk_id ON foos (str_pk_id)' ] foo = Foo().set_items(date_pk_id='2020-01-27', str_pk_id='aaa') assert foo.date_pk_id == Date('2020-01-27') assert foo.is_valid()
def test_Column(): class Base(model.Model): DB = database.Database() class Foo(Base): DB_TABLE = 'foos' id = INT(auto_increment=True, primary_key=True) name = VARCHAR() # __get__ assert '%r' % Foo.id == 'Foo.id' assert '%r' % Foo.name == 'Foo.name' foo = Foo() assert foo.id is None assert 'id' not in foo.__dict__ assert foo.name is None foo.id = 101 assert foo.id == 101 assert foo.__dict__['id'] == 101 foo = instantiate(Foo, id=100, name='foo') assert set(foo.__dict__) == {'.k2i', '.dbvs'} assert foo.id == 100 assert foo.name == 'foo' assert set(foo.__dict__) == {'.k2i', '.dbvs'} foo = instantiate(Foo) with pytest.raises(ColumnNotLoaded): foo.id cnt = [0] def db2py(v): cnt[0] += 1 return v + 1 Foo.id.db2py = db2py foo = instantiate(Foo, id=100) assert cnt == [0] assert foo.id == 101 assert cnt == [1] assert 'id' in foo.__dict__ assert foo.id == 101 assert cnt == [1] foo = instantiate(Foo, id=100) foo.id = 10 assert 'id' in foo.__dict__ assert foo.id == 10 assert cnt == [1] Foo.id.db2py = None # __init__ with pytest.raises(TypeError): INT(foo=1) # _in_ foo = Foo() assert 'id' not in foo foo.id = 100 assert 'id' in foo foo = instantiate(Foo, id=100) assert 'id' in foo # is_changed foo = Foo() assert not foo.is_changed() assert not foo.is_changed('id') assert foo.__dict__ == {} foo.id = 10 foo.name = 'f' assert foo.is_changed() assert foo.is_changed(txn={}) == dict(id=10, name='f') foo = instantiate(Foo, id=10) assert not foo.is_changed() foo.id = 10 assert not foo.is_changed() foo.id = 11 assert foo.is_changed() foo.name = 'f' assert foo.is_changed(txn={}) == dict(id=11, name='f') Foo.id.py2db = lambda x: x + 1 assert foo.is_changed('id', txn={}) == {'id': 12} assert foo.id == 11 Foo.id.py2db = None class Bar(Base): DB_TABLE = 'bars' foo_id = BELONGS_TO(Foo) foo = instantiate(Foo, id=100, name='foo') assert set(foo.__dict__) == {'.k2i', '.dbvs'} assert foo.id == 100 assert foo.name == 'foo' assert set(foo.__dict__) == {'.k2i', '.dbvs', 'id'} foo = Foo() bar = Bar(foo=foo) assert bar.is_changed(txn={}) == dict(foo_id=None) foo.id = 1 assert bar.is_changed(txn={}) == dict(foo_id=1) # to_be_valid assert not Foo().is_valid() assert Foo(name='?').is_valid() Foo.id.auto_increment = False assert not Foo(name='?').is_valid() assert Foo(id=1, name='?').is_valid() assert not Foo(id=1).is_valid() Foo.name.validate['nn'] = False assert Foo(id=1).is_valid() foo = Foo.instantiate({}, (), None, None) assert foo.is_valid() foo.id = None assert not foo.is_valid() Foo.id.auto_increment = True Foo.name.validate['nn'] = True assert Foo.ddl_sqls() == [ "CREATE TABLE foos (\n" " id INT NOT NULL PRIMARY KEY,\n" " name VARCHAR(255) NOT NULL\n)" ]
def test_save(): Base, User, Foo, Bar, Baz = models() args = [] Base.DB.execute_insert = lambda *a: args.append(a) or 100 assert Foo().save() == 1 assert args == [('INSERT INTO foos () VALUES ()', (), 'id')] args.clear() bar = Bar() assert bar.save() == 1 assert args == [( 'INSERT INTO bars (created_at, updated_at, lock_version, name) ' 'VALUES (%s, %s, %s, %s)', (bar.created_at, bar.updated_at, 1, '?'), 'id')] assert bar.__dict__ == { '.k2i': bar.SHARED_INDEX, '.dbvs': [ 100, None, None, bar.created_at, None, bar.updated_at, 1, '?', None, None], 'created_at': bar.created_at, 'updated_at': bar.updated_at, 'name': '?', } args.clear() Base.DB.execute = lambda *a: args.append(a) or 1 baz = Baz(name='!') baz.insert() assert args == [('INSERT INTO bazs (name) VALUES (%s)', ('!',))] at = DateTime('2019-10-28 18:00:00') u10 = User(id=10) bar = instantiate( Bar, id=1, updated_by=10, updated_at=at, lock_version=1, name='A') assert bar.id == 1 bar.updater = u10 assert not bar.is_changed() args.clear() Base.DB.execute = lambda *a: args.append(a) or 1 assert bar.save(by=User(id=11)) is bar.NO_CHANGES assert bar.NO_CHANGES and True assert not args assert bar.updated_by == 10 assert bar.__dict__['updater'] is u10 assert bar.updated_at == at assert bar.lock_version == 1 assert not bar.is_changed() bar['id'] = '2' bar['name'] = 'B' assert bar.is_changed() assert bar.save(by=User(id=11), lock=False) == 1 assert args == [( 'UPDATE bars t1 SET id = %s, name = %s, updated_at = %s, ' 'updated_by = %s WHERE id = %s', (2, 'B', bar.updated_at, 11, 1), int)] assert bar.updated_by == 11 assert 'updater' not in bar.__dict__ assert bar.updated_at != at assert bar.lock_version == 1 assert not bar.is_changed() args.clear() bar['lock_version'] = '1' assert bar.save(by=12) is bar.NO_CHANGES assert not args bar['lock_version'] = '2' assert bar.save(by=12) == 0 assert bar.errors == {None: [errors.CONFLICT]} assert not args bar['name'] = 'C' bar['lock_version'] = '1' assert bar.save(by=12, timestamp=True) == 1 assert args == [( 'UPDATE bars t1 SET name = %s, lock_version = %s, updated_at = %s, ' 'updated_by = %s WHERE id = %s AND lock_version = %s', ('C', 2, bar.updated_at, 12, 2, 1), int)] assert bar.updated_by == 12 assert bar.lock_version == 2 with pytest.raises(TypeError) as e: bar.name = '!' bar.save(by='?') assert e.value.args == ( "keyword argument 'by' must be int or User, not str",) args.clear() bar = instantiate(Bar, id=1) assert bar.__dict__ == {'.k2i': {'id': 0}, '.dbvs': (1,)} bar['name'] = 'D' assert bar.save(lock=True) == 1 assert args == [( 'UPDATE bars t1 SET name = %s, updated_at = %s, ' 'lock_version = (lock_version + 1) %% 100 WHERE id = %s', ('D', bar.updated_at, 1), int)] assert bar.updated_at is not None assert bar.__dict__ == { 'name': 'D', '.k2i': {'id': 0, 'name': 1, 'updated_at': 2}, '.dbvs': [1, 'D', bar.updated_at], } foo = instantiate(Foo, id=1) assert foo.save() is foo.NO_CHANGES assert foo.save(force=True) is foo.NO_CHANGES
def test_BelongsTo(): Foo, Bar, Bar2, Baz = models() assert Bar.foo.join_key == 'foo' assert Bar.foo.reverse_b2 is False assert Bar2.foo.reverse_b2 is False assert Bar2.bar.reverse_b2 is False assert Bar.foo.simple_joins == (('id', 'foo_id'), ) # __get__ assert '%r' % Bar.foo == 'Bar.foo' bar = Bar() assert bar.foo is None assert bar.__dict__ == {} foo = Foo() bar = Bar(foo=foo) assert bar.foo is foo bar = Bar(foo_id=2) assert 'foo' not in bar with pytest.raises(NotImplementedError): bar.foo assert 'foo' not in bar DB = Foo.DB DB.execute = r2csr(id=2) DB.find_cache = {} foo = bar.foo del DB.execute assert foo.id == 2 assert bar.__dict__['foo'] is foo assert 'foo' in bar # __set__ bar.foo_id = None assert 'foo' not in bar.__dict__ assert bar.__dict__['foo_id'] is None assert bar.foo is None assert 'foo' not in bar with pytest.raises(TypeError) as e: bar.foo = 2 assert e.value.args == ('must be Foo, not int', ) bar.foo_id = 2 assert 'foo' not in bar assert bar.foo is foo assert bar.__dict__['foo'] is foo # foo = instantiate(Foo, id=2) bar = instantiate(Bar, id=3, foo_id=2) bar2 = Bar2(foo=foo, bar=bar) assert 'foo' not in bar assert bar.foo is foo bar = instantiate(Bar, id=3, foo_id=2) bar2 = Bar2(bar=bar, foo=foo) assert 'foo' not in bar assert bar.foo is foo bar2 = Bar2(foo_id=2, bar_id=3) DB.execute = r2csr(id=3) assert bar2.bar.id == 3 del DB.execute bar2 = Bar2(foo_id=foo.id) bar = bar2.bar = Bar(foo=foo) assert 'foo' not in bar2 assert bar2.foo is foo assert 'foo' in bar2 bar2.foo = Foo() assert 'foo_id' not in bar2 assert Bar.bazs.query().sql() == SQL( 'SELECT * FROM bazs bz WHERE bz.bar_id = ba.id') assert Bar.bazs.as_('t1').sql() == SQL( 'SELECT * FROM bazs t1 WHERE t1.bar_id = ba.id') assert Bar.bazs.query('bz').sql() == SQL( 'SELECT * FROM bazs WHERE bar_id = bz.id')