Ejemplo n.º 1
0
def test_multi_default_1(init_world, get_client):
    '''
    friends - always childred 1 & 2
    '''
    init_world(family.dm)
    client = get_client()

    #   1.  Add best_friend field.
    child = dm().object('child')
    child.add_field(Rel('friends', stores=child, multi=True, default=[1, 2]))

    #   If this is PG storage, we need to update database schema
    if issubclass(type(world().storage), PGStorage):
        conn = world().storage._conn
        conn.cursor().execute('ALTER TABLE child ADD COLUMN friends integer[]')
        conn.commit()

    #   2.  Post a child without friends
    data, status, headers = client.post('child', {'name': 'c'})
    assert status == 201
    assert data['friends'] == [1, 2]

    #   3.  Post a child with friends
    data, status, headers = client.post('child', {
        'name': 'c',
        'friends': [2, 3]
    })
    assert status == 201
    assert data['friends'] == [2, 3]
Ejemplo n.º 2
0
    def delete(self, propagated_from=None):
        '''
        Delete SELF:
            *   make other instances forget (by updating relationship fields)
            *   make world forget

        PROPAGATED_FROM - other instance, connected to SELF via a Rel field with cascade=True,
        that was deleted - and this way initialized deletion of SELF. None if SELF.delete() was
        called in a different context.
        '''
        #   Not usable -> exception
        if not self.usable:
            raise exceptions.ProgrammingError("This instance is no longer usable")

        #   Delete started
        self.deleted = True

        for field, val in self.field_values():
            if field.rel and val is not None:
                field.propagate_delete(self)

        #   And the world will forget
        world().remove_instance(self)

        #   Instance should not be used any longer
        self.usable = False
Ejemplo n.º 3
0
def test_one_dir_1(init_world, get_client):
    #   1.  Init world
    init_cookies_with_shelf_1(init_world)
    client = get_client()

    #   2.  Save current world state, for further comparisions
    world_data = world().data()

    #   3.  Create a shelf with both jars
    data, status, headers = client.put('shelf', 1, {'jars': [1, 2]})
    assert status == 201

    #   4.  Check if world data didn't change (except shelf)
    new_world_data = world().data()
    assert new_world_data['cookie'] == world_data['cookie']
    assert new_world_data['jar'] == world_data['jar']

    #   5.  Check if GET works as expected
    jar_1, status_1, headers = client.get('jar', 1, depth=1)
    jar_2, status_2, headers = client.get('jar', 2, depth=1)
    shelf, status_3, headers = client.get('shelf', 1, depth=2)
    assert status_1 == 200
    assert status_2 == 200
    assert status_3 == 200
    assert shelf['jars'] == [jar_1, jar_2]

    #   6.  Test 404
    shelf, status, headers = client.put('shelf', 2, {'jars': [3]})
    assert status == 404
Ejemplo n.º 4
0
def test_single_default_1(init_world, get_client):
    '''
    Best Friend - always child 1
    '''
    init_world(family.dm)
    client = get_client()

    #   1.  Add best_friend field.
    child = dm().object('child')
    child.add_field(Rel('best_friend', stores=child, multi=False, default=1))

    #   If this is PG storage, we need to update database schema
    if issubclass(type(world().storage), PGStorage):
        conn = world().storage._conn
        conn.cursor().execute(
            'ALTER TABLE child ADD COLUMN best_friend integer REFERENCES child(id) DEFERRABLE'
        )
        conn.commit()

    #   2.  Post a child without best friend
    data, status, headers = client.post('child', {'name': 'c'})
    assert status == 201
    assert data['best_friend'] == 1

    #   3.  Post a child with best friend
    data, status, headers = client.post('child', {
        'name': 'c',
        'best_friend': 2
    })
    assert status == 201
    assert data['best_friend'] == 2
Ejemplo n.º 5
0
def test_3(init_world):
    init_world(cookies.dm, get_instance_class=get_instance_class)
    world().begin()
    cookie = world().get_instance('cookie', 1)
    cookie.update({'jar': 2})
    assert type(cookie) is FirstJarCookieInstance
    assert cookie.repr(1)['jar'] == 1
Ejemplo n.º 6
0
    def add_field_closed(dm):
        jar = dm.object('jar')
        jar.add_field(Scalar('closed', default=False))

        #   Add database column if PGStorage
        if issubclass(type(engine.world().storage), engine.PGStorage):
            engine.world().storage._conn.cursor().execute('''
                ALTER TABLE jar ADD COLUMN closed boolean;
            ''')
Ejemplo n.º 7
0
def test_usable_4(init_world):
    '''attempt to update not usable instance should raise an exception'''
    init_world(family.dm)

    world().begin()
    child = world().get_instance('child', 1)
    world().write()

    with pytest.raises(exceptions.ProgrammingError):
        child.update(dict(name='aaa'))
Ejemplo n.º 8
0
def test_usable_5(init_world):
    '''attempt to delete not usable instance should raise an exception'''
    init_world(family.dm)

    world().begin()
    child = world().get_instance('child', 1)
    world().write()

    with pytest.raises(Exception):
        child.delete()
Ejemplo n.º 9
0
def test_usable_3(init_world):
    init_world(family.dm)
    world().begin()
    child = world().new_instance('child')

    #   fresh instance is fine ...
    assert child.usable

    #   ... until written
    world().write()
    assert not child.usable
Ejemplo n.º 10
0
def test_delete_3(init_world):
    init_world(cookies.dm)

    cookie_id = 2
    world().begin()
    cookie = world().get_instance('cookie', cookie_id)
    jar = related(cookie, 'jar')

    cookie.delete()

    assert cookie not in related(jar, 'cookies')
    assert cookie_id not in world().data()['cookie']
Ejemplo n.º 11
0
def test_delete_1(init_world):
    init_world(family.dm)
    child_id = 1
    world().begin()
    child = world().get_instance('child', child_id)
    father = related(child, 'father')
    mother = related(child, 'mother')

    child.delete()

    assert child not in related(father, 'children')
    assert child not in related(mother, 'children')
    assert child_id not in world().data()['child']
Ejemplo n.º 12
0
def test_delete_4(init_world):
    init_world(cookies.dm)

    jar_id = 1
    world().begin()
    jar = world().get_instance('jar', jar_id)
    jar_1_cookies = related(jar, 'cookies')

    jar.delete()

    for cookie in jar_1_cookies:
        assert related(cookie, 'jar') is None
    assert jar_id not in world().data()['jar']
Ejemplo n.º 13
0
def test_delete_2(init_world):
    init_world(family.dm)
    male_id = 1
    world().begin()
    male = world().get_instance('male', male_id)
    wife = related(male, 'wife')
    children = related(male, 'children')

    male.delete()

    assert related(wife, 'husband') is None
    for child in children:
        assert related(child, 'father') is None
    assert male_id not in world().data()['male']
Ejemplo n.º 14
0
    def propagate_delete(self, deleted_instance):
        '''
        DELETED_INSTANCE is being deleted.
        If SELF.other is not None, we need to remove all connections.
        '''
        other = self.other
        if other is None:
            return

        ids = deleted_instance.get_val(self).ids()

        for id_ in ids:
            this_id = deleted_instance.id()

            #   Other instance could have already been deleted, if we have multiple
            #   cascade fields - we just ignore it here
            try:
                other_instance = world().get_instance(self.stores.name, id_)
            except exceptions.e404:
                continue
            other_instance_ids = other_instance.get_val(other).ids()
            if this_id in other_instance_ids:
                #   If other.cascade, instance should also be deleted,
                #   if not - connection should be removed
                #
                #   Note: if other.cascade is True, other.multi must be False (-> check Rel.init())
                #   so this is the only connection.
                #
                #   We also check instance.deleted to avoid recursion on recursive cascades
                if other.cascade and not other_instance.deleted:
                    other_instance.delete(propagated_from=deleted_instance)
                else:
                    new_ids = other_instance_ids
                    new_ids.remove(this_id)
                    other_instance.set_value(other, other.val(new_ids))
Ejemplo n.º 15
0
def init_cookies_with_shelf_1(init_world):
    '''Add shelf to cookies datamodel. 
    Shelf knows it's jars, jar has no idea about shelf
    '''
    dm = deepcopy(cookies.dm)
    shelf = dm.create_object('shelf')
    shelf.add_field(Scalar('id', pkey=True, type_=int))
    shelf.add_field(Rel('jars', stores=dm.object('jar'), multi=True))

    init_world(dm)

    #   If this is PG storage, we need to update database schema
    if issubclass(type(world().storage), PGStorage):
        conn = world().storage._conn
        conn.cursor().execute('CREATE TABLE shelf (id serial PRIMARY KEY, jars integer[])')
        conn.commit()
Ejemplo n.º 16
0
def test_implicit_fields_post_put(get_client, method, args):
    '''
    Check if fields set in a implicit way after POST/PUT (i.e. database defaults) are returned
    '''
    #   INIT
    init_pg_world(cookies.dm)
    client = get_client()

    #   Add default column value
    world().storage._conn.cursor().execute('''
        ALTER TABLE cookie
            ALTER COLUMN jar SET DEFAULT 1;
    ''')
    data, status, headers = getattr(client, method)(*args)
    assert status == 201
    assert data['jar'] == 1
Ejemplo n.º 17
0
def init_cookies_with_shelf_2(init_world):
    '''Add shelf to cookies datamodel. 
    Jar knows it's shelf, shelf has no idea about jars.
    '''
    dm = deepcopy(cookies.dm)
    shelf = dm.create_object('shelf')
    shelf.add_field(Scalar('id', pkey=True, type_=int))
    jar = dm.object('jar')
    jar.add_field(Rel('shelf', stores=shelf, multi=False))
    init_world(dm)

    #   If this is PG storage, we need to update database schema
    if issubclass(type(world().storage), PGStorage):
        conn = world().storage._conn
        conn.cursor().execute('CREATE TABLE shelf (id serial PRIMARY KEY)')
        conn.cursor().execute('ALTER TABLE jar ADD COLUMN shelf integer REFERENCES shelf(id)')
        conn.commit()
Ejemplo n.º 18
0
def world_data():
    '''
    Returns expected data for current world, based on
        *   world's datamodel name
        *   example.*.world_data() function
    '''
    d = eval(engine.dm().name)
    return d.world_data(type(engine.world().storage).__name__)
Ejemplo n.º 19
0
def test_rollback_delete_1(init_world, finalize, equal, operation):
    init_world(cookies.dm)

    data_before = deepcopy(world().data())

    world().begin()
    operation(world())
    world().write()
    getattr(world(), finalize)()

    assert (data_before == world().data()) is equal
Ejemplo n.º 20
0
def test_world_commit_3(init_world):
    '''Test remove_instance()'''
    init_world(cookies.dm)
    w = world()
    w.begin()
    i = w.get_instance('cookie', 1)
    w.write()
    w.commit()
    with pytest.raises(exceptions.ProgrammingError):
        w.remove_instance(i)
Ejemplo n.º 21
0
def test_not_null_put(get_client, status, args):
    init_pg_world(cookies.dm)
    client = get_client()

    #   from now on, we no longer accept cookies of unknown type
    conn = world().storage._conn
    conn.cursor().execute('ALTER TABLE cookie ALTER COLUMN type SET NOT NULL')
    conn.commit()

    assert client.put('cookie', 4, args)[1] == status
Ejemplo n.º 22
0
def _get_not_null_msg():
    '''Ugly fix to make the tests work on both tested versions of PostgreSQL'''
    init_pg_world(cookies.dm)
    cursor = world().storage._conn.cursor()
    cursor.execute('SELECT version()')
    version = cursor.fetchone()[0]
    if 'PostgreSQL 9' in version:
        return 'null value in column "type" violates not-null constraint'
    else:
        return 'null value in column "type" of relation "cookie" violates not-null constraint'
Ejemplo n.º 23
0
def test_api_delete_cookies(init_world, get_client, deletions, data_id):
    init_world(cookies.dm)
    client = get_client()

    for name, id_ in deletions:
        client.delete(name, id_)

    expected_data = expected_delete_data(data_id)

    assert world().data() == expected_data
Ejemplo n.º 24
0
def test_implicit_fields_patch(get_client):
    '''
    Check if fields set in a implicit way after PATCH (i.e. by database triggers) are returned
    '''
    #   INIT
    init_pg_world(cookies.dm)
    client = get_client()

    #   Add trigger changing type after update
    world().storage._conn.cursor().execute('''
        CREATE FUNCTION pg_temp.new_cookie_type() RETURNS trigger AS $new_cookie_type$
        BEGIN
            NEW.type = 'type_set_by_trigger';
            RETURN NEW;
        END;
        $new_cookie_type$ LANGUAGE plpgsql;

        CREATE TRIGGER change_cookie_type
        BEFORE UPDATE ON pg_temp.cookie
        FOR EACH ROW EXECUTE PROCEDURE pg_temp.new_cookie_type();
    ''')

    #   Create fresh cookie
    data, status, headers = client.put('cookie', 4, {'type': 'donut'})
    assert status == 201
    assert data['type'] == 'donut'

    #   Make sure it's still a donut
    data, status, headers = client.get('cookie', 4)
    assert status == 200
    assert data['type'] == 'donut'

    #   Patch it with some data and check if we got triggered type
    data, status, headers = client.patch('cookie', 4,
                                         {'type': 'doesnt matter'})
    assert status == 200
    assert data['type'] == 'type_set_by_trigger'

    #   Make sure it's still a triggered type
    data, status, headers = client.get('cookie', 4)
    assert status == 200
    assert data['type'] == 'type_set_by_trigger'
Ejemplo n.º 25
0
    def _extract_ids(self, values):
        if not self.multi and len(values) > 1:
            raise exceptions.ProgrammingError('More than one instance on a Single related field')

        ids = []
        for val in values:
            type_ = type(val)
            if val is None:
                raise exceptions.ProgrammingError("None is not accepted for {}".format(type(self)))
            elif type_ in (int, float, str):
                ids.append(val)
            elif issubclass(type_, world().get_instance_class(self.name)):
                ids.append(val.id())
            elif issubclass(type_, dict):
                inst = world().new_instance(self.name)
                inst.update(val)
                ids.append(inst.id())
            else:
                raise exceptions.ProgrammingError('could not create {}'.format(type(self)))
        return sorted(ids)
Ejemplo n.º 26
0
    def _int_update(self, this_instance, value, ext):
        old_val = this_instance.get_val(self)
        new_val = self.val(value)

        if ext:
            #   If ext is True, we need to validate if new value objects really exist,
            #   the most straightforward way is to create instances stored in new_val.
            #   This would work well, but slow - we might end up creating many redundant instances.
            #   Instead here we extract new ids and validate only those.
            new_ids = set(new_val.ids()) - set(old_val.ids())
            new_ids_val = self.val(list(new_ids))
            new_ids_val.inst()

        this_instance.set_value(self, new_val)

        other = self.other
        if other:
            this_id = this_instance.id()
            added = set(new_val.ids()) - set(old_val.ids())
            removed = set(old_val.ids()) - set(new_val.ids())

            for added_id in added:
                other_instance = world().get_instance(self.stores.name, added_id)
                other_instance_ids = other_instance.get_val(other).ids()
                if this_id not in other_instance_ids:
                    if other.multi:
                        new_ids = sorted(other_instance_ids + [this_id])
                    else:
                        new_ids = [this_id]
                        if other_instance_ids:
                            disconnected_instance = world().get_instance(other.stores.name, other_instance_ids[0])
                            disconnected_instance.set_value(self, self.val(self.default))
                    other_instance.set_value(other, other.val(new_ids))

            for removed_id in removed:
                other_instance = world().get_instance(self.stores.name, removed_id)
                other_instance_ids = other_instance.get_val(other).ids()
                if this_id in other_instance_ids:
                    new_ids = other_instance_ids
                    new_ids.remove(this_id)
                    other_instance.set_value(other, other.val(new_ids))
Ejemplo n.º 27
0
def test_ext_name_storage(init_world, get_client):
    '''
    ext_name should have no influence on stored data
    '''
    #   1.  Init
    init_world(cookies.dm)
    client = get_client()

    #   2.  Post
    data, status, headers = client.post('cookie', {'type': 'tasty'})
    id_ = data['id']
    stored_before = world().data()

    #   4.  Set an ext_name
    dm().object('cookie').field('type').ext_name = 'Cookie Type'

    #   5.  Put the same data, but using ext_name
    data, status, headers = client.put('cookie', id_, {'Cookie Type': 'tasty'})

    #   6.  Check
    assert stored_before == world().data()
Ejemplo n.º 28
0
def test_single_multi_2(init_world, get_client):
    '''
    Best Friend - new female named BLARGH (note: new anonymous child creates infinite recursion)
    '''
    init_world(family.dm)
    client = get_client()

    #   1.  Add best_friend field. New child's best friend is always child no 1.
    child = dm().object('child')
    child.add_field(
        Rel('best_friend',
            stores=dm().object('female'),
            multi=False,
            default={'name': 'BLARGH'}))

    #   If this is PG storage, we need to update database schema
    if issubclass(type(world().storage), PGStorage):
        conn = world().storage._conn
        conn.cursor().execute(
            'ALTER TABLE child ADD COLUMN best_friend integer REFERENCES child(id) DEFERRABLE'
        )
        conn.commit()

    #   2.  Post a child without best friend
    data, status, headers = client.post('child', {'name': 'c'})
    assert status == 201
    assert data['best_friend'] == 3

    #   3.  Check if name was saved
    data, status, headers = client.get('female', 3)
    assert status == 200
    assert data['name'] == 'BLARGH'

    #   4.  Post a child with best friend
    data, status, headers = client.post('child', {
        'name': 'c',
        'best_friend': 2
    })
    assert status == 201
    assert data['best_friend'] == 2
Ejemplo n.º 29
0
def cleanup():
    yield

    #   Drop schema
    conn = engine.world().storage._conn
    conn.close()
    conn = get_connection()
    conn.cursor().execute(
        'DROP SCHEMA IF EXISTS {} CASCADE'.format(schema_name))
    conn.commit()

    #   Reload PGStorage - remove code changes
    importlib.reload(pg_storage)
Ejemplo n.º 30
0
def test_not_null_patch(get_client, expected_status, cookie_id, args, expected_data):
    init_pg_world(cookies.dm)
    client = get_client()

    #   from now on, we no longer accept cookies of unknown type
    conn = world().storage._conn
    conn.cursor().execute('ALTER TABLE cookie ALTER COLUMN type SET NOT NULL')
    conn.commit()

    data, status, headers = client.patch('cookie', cookie_id, args)
    assert status == expected_status
    print(data)
    if expected_data is not None:
        assert data == expected_data