Beispiel #1
0
async def test_write_blob_data(db, guillotina_main):
    db = await get_database("db")
    login()

    async with transaction(db=db):
        container = await db.async_get("container")
        if container is None:
            container = await create_content_in_container(db,
                                                          "Container",
                                                          "container",
                                                          title="Container")

        blob = Blob(container)
        container.blob = blob

        blobfi = blob.open("w")
        await blobfi.async_write(b"foobar")

    async with transaction(db=db):
        container = await db.async_get("container")
        assert await container.blob.open().async_read() == b"foobar"
        assert container.blob.size == 6
        assert container.blob.chunks == 1

        await db.async_del("container")
Beispiel #2
0
async def test_bucket_dict_value(db, guillotina_main):
    db = await get_database("db")
    login()
    bucket = BucketDictValue(bucket_len=10)

    async with transaction(db=db):
        container = await create_content_in_container(db, "Container", "container", title="Container")
        ob = await create_content_in_container(container, "Item", "foobar")
        for index in range(50):
            await bucket.assign(ob, str(index), index)

        assert len(bucket) == 50
        assert await bucket.get(ob, "1") == 1

    async with transaction(db=db):
        container = await db.async_get("container")
        ob = await container.async_get("foobar")
        await bucket.clear(ob)
        assert len(bucket) == 0
        assert await bucket.get(ob, "1") is None

    async with transaction(db=db):
        container = await db.async_get("container")
        ob = await container.async_get("foobar")
        for index in range(50, 100):
            await bucket.assign(ob, str(index), index)

        assert len(bucket) == 50
        assert await bucket.get(ob, "50") == 50
Beispiel #3
0
async def test_create_annotation(db, guillotina_main):
    db = await get_database("db")
    login()

    async with transaction(db=db):
        container = await create_content_in_container(db, "Container", "container", title="Container")
        ob = await create_content_in_container(container, "Item", "foobar")

        annotations = IAnnotations(ob)
        data = AnnotationData()
        data["foo"] = "bar"
        await annotations.async_set("foobar", data)

    async with transaction(db=db):
        container = await db.async_get("container")
        ob = await container.async_get("foobar")
        annotations = IAnnotations(ob)
        assert "foobar" in (await annotations.async_keys())
        await annotations.async_del("foobar")

    async with transaction(db=db):
        container = await db.async_get("container")
        ob = await container.async_get("foobar")
        annotations = IAnnotations(ob)
        assert "foobar" not in (await annotations.async_keys())
        await container.async_del("foobar")
        await db.async_del("container")
Beispiel #4
0
async def test_bucket_dict_value(db, guillotina_main):
    db = await get_database("db")
    login()
    bucket = BucketDictValue(bucket_len=10)

    async with transaction(db=db):
        container = await create_content_in_container(db,
                                                      "Container",
                                                      "container",
                                                      title="Container")
        ob = await create_content_in_container(container, "Item", "foobar")
        for index in range(50):
            await bucket.assign(ob, str(index), index)

        assert len(bucket) == 50
        assert await bucket.get(ob, "1") == 1

    async with transaction(db=db):
        container = await db.async_get("container")
        ob = await container.async_get("foobar")
        await bucket.clear(ob)
        assert len(bucket) == 0
        assert await bucket.get(ob, "1") is None

    async with transaction(db=db):
        container = await db.async_get("container")
        ob = await container.async_get("foobar")
        for index in range(50, 100):
            await bucket.assign(ob, str(index), index)

        assert len(bucket) == 50
        assert await bucket.get(ob, "50") == 50

    # Test iter keys, values and items
    async with transaction(db=db):
        # Test iterating on a None object
        assert [k async for k in bucket.iter_keys(None)] == []
        assert [v async for v in bucket.iter_values(None)] == []
        assert [(k, v) async for k, v in bucket.iter_items(None)] == []

        # Test iterate on empty values
        container = await db.async_get("container")
        ob = await create_content_in_container(container, "Item", "foobar2")
        assert [k async for k in bucket.iter_keys(ob)] == []
        assert [v async for v in bucket.iter_values(ob)] == []
        assert [(k, v) async for k, v in bucket.iter_items(ob)] == []

        # Add some data now
        _range = range(50, 100)
        for index in range(50, 100):
            await bucket.assign(ob, str(index), index)

        assert [k async for k in bucket.iter_keys(ob)
                ] == [str(i) for i in _range]
        assert [v async for v in bucket.iter_values(ob)] == [i for i in _range]
        assert [(k, v) async for k, v in bucket.iter_items(ob)
                ] == [(str(i), i) for i in _range]
Beispiel #5
0
    async def create(self, arguments, db):

        if arguments.force:
            async with transaction(db=db) as txn:
                tm = task_vars.tm.get()
                root = await tm.get_root(txn=txn)
                if await root.async_contains(arguments.name):
                    context = await root.async_get(arguments.name)
                    content_id = context.id
                    parent = context.__parent__
                    await notify(
                        BeforeObjectRemovedEvent(context, parent, content_id))
                    txn.delete(context)
                    await notify(
                        ObjectRemovedEvent(context, parent, content_id))

        site = None
        async with transaction(db=db) as txn:
            tm = task_vars.tm.get()
            root = await tm.get_root(txn=txn)
            try:
                site = await create_content_in_container(root,
                                                         "Site",
                                                         arguments.name,
                                                         check_security=False)
                await addons.install(site, "cms")
                await addons.install(site, "dbusers")

                workflow = IWorkflow(site)
                await workflow.do_action("publish", "Initial setup")
            except ConflictIdOnContainer:
                pass

        if site is None:
            return

        async with transaction(db=db) as txn:
            await txn.refresh(site)
            groups = await site.async_get("groups")
            obj = await create_content_in_container(groups,
                                                    "Group",
                                                    "Managers",
                                                    check_security=False)
            obj.user_roles = [
                "guillotina.Manager",
                "guillotina.ContainerAdmin",
                "guillotina.Owner",
            ]
            obj.register()

        async with transaction(db=db) as txn:
            await txn.refresh(site)
            users = await site.async_get("users")
            obj: IUser = await create_content_in_container(
                users, "User", "admin", check_security=False)
            await obj.set_password("admin")
            obj.groups = ["Managers"]
Beispiel #6
0
async def test_read_data_ranges(db, guillotina_main, dummy_request):
    db = await get_database("db")
    login()

    async with transaction(db=db):
        container = await db.async_get("container")
        if container is None:
            container = await create_content_in_container(db,
                                                          "Container",
                                                          "container",
                                                          title="Container")

        container.add_behavior(IAttachment)
        beh = await get_behavior(container, IAttachment, create=True)

        fm = get_multi_adapter(
            (container, dummy_request, IAttachment["file"].bind(beh)),
            IFileManager)

        async def file_generator():
            yield (b"X" * (1024 * 1024 * 1)) + (b"Y" * 512)
            yield b"Z" * (1024 * 1024 * 10)

        await fm.save_file(file_generator)

    async with transaction(db=db):
        container = await db.async_get("container")
        container.add_behavior(IAttachment)
        beh = await get_behavior(container, IAttachment)
        fm = get_multi_adapter(
            (container, dummy_request, IAttachment["file"].bind(beh)),
            IFileManager)
        data = b""
        async for chunk in fm.iter_data():
            data += chunk
        assert len(data) == ((1024 * 1024 * 11) + 512)

        assert await fm.file_storage_manager.range_supported()

        assert await _gather_all(
            fm.file_storage_manager.read_range(0, 1024 * 1024)
        ) == b"X" * 1024 * 1024
        assert (await _gather_all(
            fm.file_storage_manager.read_range(1024 * 1024,
                                               (1024 * 1024) + 512)
        ) == b"Y" * 512)
        assert await _gather_all(
            fm.file_storage_manager.read_range(1024 * 1023,
                                               (1024 * 1024) + 1024)
        ) == (b"X" * 1024) + (b"Y" * 512) + (b"Z" * 512)
        assert await _gather_all(
            fm.file_storage_manager.read_range(0, (1024 * 1024 * 11) + 512)
        ) == (b"X" * (1024 * 1024 * 1)) + (b"Y" * 512) + (b"Z" *
                                                          (1024 * 1024 * 10))
async def test_registry(my_requester):
    async with ctx(my_requester):
        utility = get_utility(IEvolutionUtility)

        async with transaction():
            assert await utility._get_curr_gen() == 0

        async with transaction():
            await utility._update_curr_gen(5)

        registry = await get_registry()
        assert registry[GENERATION_KEY] == 5
Beispiel #8
0
async def test_managed_transaction_aborted_on_exception(container_requester):
    async with container_requester as requester:
        with pytest.raises(Exception):
            async with transaction(db=requester.db) as txn:
                root = await txn.get(ROOT_ID)
                container = await root.async_get("guillotina")
                container.title = "changed title"
                container.register()

                raise Exception()

        async with transaction(db=requester.db) as txn:
            root = await txn.get(ROOT_ID)
            container = await root.async_get("guillotina")
            assert container.title == "Guillotina Container"
Beispiel #9
0
async def test_large_upload_chunks(manager_type, redis_container,
                                   container_requester):
    async with container_requester as requester:
        response, status = await requester(
            "POST",
            "/db/guillotina/",
            data=json.dumps({
                "@type": "Item",
                "@behaviors": [IAttachment.__identifier__],
                "id": "foobar"
            }),
        )
        assert status == 201

        response, status = await requester(
            "PATCH",
            "/db/guillotina/foobar/@upload/file",
            data=b"X" * 1024 * 1024 * 10,
            headers={"x-upload-size": str(1024 * 1024 * 10)},
        )
        assert status == 200

        response, status = await requester(
            "GET", "/db/guillotina/foobar/@download/file")
        assert status == 200
        assert len(response) == (1024 * 1024 * 10)

        root = await utils.get_root(db=requester.db)
        async with transaction(db=requester.db, abort_when_done=True):
            container = await root.async_get("guillotina")
            obj = await container.async_get("foobar")
            behavior = IAttachment(obj)
            await behavior.load()
            assert behavior.file.chunks == 2
Beispiel #10
0
 async def run(self):
     if self._request is not None:
         async with transaction(abort_when_done=False), self._request:
             await self._func(*self._args or [], **self._kwargs or {})
     else:
         # if no request, we do it without transaction
         await self._func(*self._args or [], **self._kwargs or {})
Beispiel #11
0
async def test_creator_used_from_content_creation(dummy_guillotina):
    utils.login()

    async with transaction(db=await get_database("db")):
        container = await create_content("Container", id="guillotina", title="Guillotina")
        container.__name__ = "guillotina"
        utils.register(container)

        import guillotina.tests

        configure.register_configuration(
            Folder,
            dict(
                type_name="TestType2", behaviors=[], module=guillotina.tests
            ),  # for registration initialization
            "contenttype",
        )
        root = get_utility(IApplication, name="root")

        configure.load_configuration(root.app.config, "guillotina.tests", "contenttype")
        root.app.config.execute_actions()
        load_cached_schema()

        obj = await create_content_in_container(
            container, "TestType2", "foobar", creators=("root",), contributors=("root",)
        )

        assert obj.creators == ("root",)
        assert obj.contributors == ("root",)

        behavior = IDublinCore(obj)
        assert behavior.creators == ("root",)
        assert behavior.contributors == ("root",)
Beispiel #12
0
async def test_large_upload_chunks(manager_type, redis_container,
                                   container_requester):
    async with container_requester as requester:
        response, status = await requester('POST',
                                           '/db/guillotina/',
                                           data=json.dumps({
                                               '@type':
                                               'Item',
                                               '@behaviors':
                                               [IAttachment.__identifier__],
                                               'id':
                                               'foobar'
                                           }))
        assert status == 201

        response, status = await requester(
            'PATCH',
            '/db/guillotina/foobar/@upload/file',
            data=b'X' * 1024 * 1024 * 10,
            headers={'x-upload-size': str(1024 * 1024 * 10)})
        assert status == 200

        response, status = await requester(
            'GET', '/db/guillotina/foobar/@download/file')
        assert status == 200
        assert len(response) == (1024 * 1024 * 10)

        request = utils.get_mocked_request(db=requester.db)
        root = await utils.get_root(request)
        async with transaction(db=requester.db, abort_when_done=True):
            container = await root.async_get('guillotina')
            obj = await container.async_get('foobar')
            behavior = IAttachment(obj)
            await behavior.load()
            assert behavior.file.chunks == 2
Beispiel #13
0
async def test_create_contenttype_with_date(container_requester):
    async with container_requester as requester:
        _, status = await requester(
            'POST',
            '/db/guillotina/',
            data=json.dumps({
                "@type": "Item",
                "title": "Item1",
                "id": "item1",
            })
        )
        assert status == 201
        date_to_test = "2016-11-30T14:39:07.394273+01:00"
        _, status = await requester(
            'PATCH',
            '/db/guillotina/item1',
            data=json.dumps({
                "guillotina.behaviors.dublincore.IDublinCore": {
                    "creation_date": date_to_test,
                    "expiration_date": date_to_test
                }
            })
        )

        request = utils.get_mocked_request(db=requester.db)
        root = await utils.get_root(request)
        async with transaction(db=requester.db, abort_when_done=True):
            container = await root.async_get('guillotina')
            obj = await container.async_get('item1')
            from guillotina.behaviors.dublincore import IDublinCore
            behavior = IDublinCore(obj)
            await behavior.load()
            assert behavior.creation_date.isoformat() == date_to_test  # pylint: disable=E1101
            assert behavior.expiration_date.isoformat() == date_to_test  # pylint: disable=E1101
Beispiel #14
0
async def test_add_behavior(dummy_guillotina):
    utils.login()

    async with transaction(db=await get_database("db")):
        container = await create_content("Container",
                                         id="guillotina",
                                         title="Guillotina")
        container.__name__ = "guillotina"
        utils.register(container)

        item = await create_content_in_container(container,
                                                 "Item",
                                                 id_="foobar")
        with pytest.raises(AttributeError):
            item.add_behavior(123)

        with pytest.raises(ComponentLookupError):
            item.add_behavior("foo")

        all_behaviors = await get_all_behaviors(item)
        assert len(all_behaviors) == 1
        assert all_behaviors[0][0] == IDublinCore

        # IDublinCore already exists and check it is not added
        item.add_behavior(IDublinCore.__identifier__)
        assert len(item.__behaviors__) == 0
        assert len(await get_all_behaviors(item)) == 1

        # Manually add IDublinCore and check it is not returned twice
        item.__behaviors__ |= {IDublinCore.__identifier__}
        assert len(await get_all_behaviors(item)) == 1

        item.add_behavior(IAttachment)
        assert len(await get_all_behaviors(item)) == 2
Beispiel #15
0
async def test_tus_multi(manager_type, redis_container, container_requester):
    async with container_requester as requester:
        response, status = await requester(
            'POST',
            '/db/guillotina/',
            data=json.dumps({
                '@type': 'Item',
                '@behaviors': [IMultiAttachment.__identifier__],
                'id': 'foobar'
            }))
        assert status == 201

        response, status = await requester(
            'OPTIONS',
            '/db/guillotina/foobar/@tusupload/files/file',
            headers={
                'Origin': 'http://foobar.com',
                'Access-Control-Request-Method': 'POST'
            })
        assert status == 200

        response, status = await requester(
            'POST',
            '/db/guillotina/foobar/@tusupload/files/file',
            headers={
                'UPLOAD-LENGTH': str(1024 * 1024 * 10),
                'TUS-RESUMABLE': '1.0.0'
            })
        assert status == 201

        response, status = await requester(
            'HEAD', '/db/guillotina/foobar/@tusupload/files/file')
        assert status == 200

        for idx in range(10):
            # 10, 1mb chunks
            response, status = await requester(
                'PATCH',
                '/db/guillotina/foobar/@tusupload/files/file',
                headers={
                    'CONTENT-LENGTH': str(1024 * 1024 * 1),
                    'TUS-RESUMABLE': '1.0.0',
                    'upload-offset': str(1024 * 1024 * idx)
                },
                data=b'X' * 1024 * 1024 * 1)
            assert status == 200

        response, status = await requester(
            'GET', '/db/guillotina/foobar/@download/files/file')
        assert status == 200
        assert len(response) == (1024 * 1024 * 10)

        request = utils.get_mocked_request(db=requester.db)
        root = await utils.get_root(request)
        async with transaction(db=requester.db, abort_when_done=True):
            container = await root.async_get('guillotina')
            obj = await container.async_get('foobar')
            behavior = IMultiAttachment(obj)
            await behavior.load()
            assert behavior.files['file'].chunks == 10
Beispiel #16
0
async def test_allowed_types(dummy_guillotina):
    utils.login()

    async with transaction(db=await get_database("db")):
        container = await create_content("Container", id="guillotina", title="Guillotina")
        container.__name__ = "guillotina"
        utils.register(container)

        import guillotina.tests

        configure.register_configuration(
            Folder,
            dict(
                type_name="TestType",
                allowed_types=["Item"],
                module=guillotina.tests,  # for registration initialization
            ),
            "contenttype",
        )
        root = get_utility(IApplication, name="root")

        configure.load_configuration(root.app.config, "guillotina.tests", "contenttype")
        root.app.config.execute_actions()
        load_cached_schema()

        obj = await create_content_in_container(container, "TestType", "foobar")

        constrains = IConstrainTypes(obj, None)
        assert constrains.get_allowed_types() == ["Item"]
        assert constrains.is_type_allowed("Item")

        with pytest.raises(NotAllowedContentType):
            await create_content_in_container(obj, "TestType", "foobar")
        await create_content_in_container(obj, "Item", "foobar")
Beispiel #17
0
async def test_create_blob(db, guillotina_main):
    root = get_utility(IApplication, name='root')
    db = root['db']
    login()

    async with transaction(db=db):
        container = await create_content_in_container(
            db, 'Container', 'container', title='Container')

        blob = Blob(container)
        container.blob = blob

    async with transaction(db=db):
        container = await db.async_get('container')
        assert blob.bid == container.blob.bid
        assert blob.resource_uid == container.__uuid__
        await db.async_del('container')
Beispiel #18
0
async def test_allowed_to_create_content(dummy_guillotina):
    utils.login()

    async with transaction(db=await get_database("db")):
        container = await create_content("Container", id="guillotina", title="Guillotina")
        container.__name__ = "guillotina"
        utils.register(container)

        await create_content_in_container(container, "Item", id_="foobar")
Beispiel #19
0
async def test_txn_reuse_delete(container_requester):
    async with container_requester as requester:
        async with transaction(db=requester.db) as txn:
            root = await txn.get(ROOT_ID)
            container1 = await root.async_get("guillotina")
            assert container1 is not None
            await root.async_del("guillotina")
            container2 = await root.async_get("guillotina")
            assert container2 is None
Beispiel #20
0
async def get_container(*, requester=None, tm=None, container_id='guillotina'):
    kw = {'tm': tm}
    if requester is not None:
        kw['db'] = requester.db
    root = await get_root(**kw)
    async with transaction(**kw):
        container = await root.async_get(container_id)
        task_vars.container.set(container)
        return container
Beispiel #21
0
async def test_create_blob(db, guillotina_main):
    db = await get_database('db')
    login()

    async with transaction(db=db):
        container = await create_content_in_container(db,
                                                      'Container',
                                                      'container',
                                                      title='Container')

        blob = Blob(container)
        container.blob = blob

    async with transaction(db=db):
        container = await db.async_get('container')
        assert blob.bid == container.blob.bid
        assert blob.resource_uid == container.__uuid__
        await db.async_del('container')
Beispiel #22
0
 async def run(self):
     if self._task_vars is not None:
         load_task_vars(self._task_vars)
     tm = task_vars.tm.get()
     if tm is not None:
         async with transaction(tm=tm):
             await self._func(*self._args or [], **self._kwargs or {})
     else:
         # if no request, we do it without transaction
         await self._func(*self._args or [], **self._kwargs or {})
Beispiel #23
0
async def test_txn_reuse(container_requester):
    async with container_requester as requester:
        async with transaction(db=requester.db) as txn:
            root = await txn.get(ROOT_ID)
            container1 = await root.async_get("guillotina")
            container1.title = "My new title"
            container1.register()
            container2 = await root.async_get("guillotina")
            assert container1 == container2
            assert container2.title == "My new title"
async def test_multiple_evolutions_at_once(loop, my_requester):
    async with ctx(my_requester) as container:
        # Create content in the container
        async with transaction():
            await create_content_in_container(container, "Item", id_="foobar")
            ob = await container.async_get("foobar")
            assert hasattr(ob, "title") is False

        # Register a new evolution
        async def ensure_all_items_have_attribute_title(container):
            async for item in container.async_values():
                item.title = ""
                item.register()

        async def ensure_all_items_have_attribute_description(container):
            async for item in container.async_values():
                item.description = "patata"
                item.register()

        utility = get_utility(IEvolutionUtility)

        async with transaction():
            await utility._update_curr_gen(0)

        utility.register(1, ensure_all_items_have_attribute_title)
        utility.register(2, ensure_all_items_have_attribute_description)

        # Evolve
        await utility.evolve(container)

        # Assert generation was updated on container registry
        async with transaction():
            registry = await get_registry()
            assert registry[GENERATION_KEY] == 2

        # Check objects after evolution
        async with transaction():
            ob = await container.async_get("foobar")
            assert ob is not None
            assert hasattr(ob, "title") is True
            assert hasattr(ob, "description") is True
            assert ob.title == ""
            assert ob.description == "patata"
Beispiel #25
0
async def test_txn_refresh(container_requester):
    async with container_requester as requester:
        async with transaction(db=requester.db) as txn:
            root = await txn.get(ROOT_ID)
            container1 = await root.async_get("guillotina")
            container1.title = "changed title"
            container1.register()

        async with transaction(db=requester.db) as txn:
            container2 = await root.async_get("guillotina")
            container2.title = "changed title2"
            container2.register()

        async with transaction(db=requester.db) as txn:
            assert container1.__serial__ != container2.__serial__
            assert container1.title != container2.title
            await txn.refresh(container1)
            assert container1.__serial__ == container2.__serial__
            assert container1.title == container2.title
Beispiel #26
0
    async def create(self, arguments, db):
        async with transaction(db=db) as txn:
            tm = task_vars.tm.get()
            root = await tm.get_root(txn=txn)
            obj = await create_content_in_container(root,
                                                    arguments.type,
                                                    arguments.name,
                                                    check_security=False)

            await addons.install(obj, "cms")
            await addons.install(obj, "dbusers")
Beispiel #27
0
 async def create_next_index(self):
     async with transaction(adopt_parent_txn=True) as txn:
         await txn.refresh(await self.index_manager.get_registry())
         next_index_name = await self.index_manager.start_migration()
     if await self.conn.indices.exists(next_index_name):
         if self.force:
             # delete and recreate
             self.response.write("Clearing index")
             resp = await self.conn.indices.delete(next_index_name)
             assert resp["acknowledged"]
     await self.utility.create_index(next_index_name, self.index_manager)
     return next_index_name
Beispiel #28
0
async def test_txn_reuse_add(container_requester):
    async with container_requester as requester:
        async with transaction(db=requester.db) as txn:
            root = await txn.get(ROOT_ID)
            container1 = await root.async_get("guillotina")
            obj = await create_content_in_container(container1,
                                                    "Folder",
                                                    "myobj",
                                                    check_security=False)
            obj2 = await container1.async_get("myobj")
            assert obj2 is not None
            assert obj == obj2
Beispiel #29
0
async def test_read_data_range_errors(db, guillotina_main, dummy_request):
    db = await get_database("db")
    login()

    async with transaction(db=db):
        container = await db.async_get("container")
        if container is None:
            container = await create_content_in_container(db,
                                                          "Container",
                                                          "container",
                                                          title="Container")

        container.add_behavior(IAttachment)
        beh = await get_behavior(container, IAttachment, create=True)

        fm = get_multi_adapter(
            (container, dummy_request, IAttachment["file"].bind(beh)),
            IFileManager)

        with pytest.raises(RangeNotSupported):
            # raise before we've added anything
            async for c in fm.file_storage_manager.read_range(1, 2):
                ...  # pragma: no cover

        async def file_generator():
            yield (b"X" * (1024 * 1024 * 1))

        await fm.save_file(file_generator)

    async with transaction(db=db):
        container = await db.async_get("container")
        container.add_behavior(IAttachment)
        beh = await get_behavior(container, IAttachment)
        fm = get_multi_adapter(
            (container, dummy_request, IAttachment["file"].bind(beh)),
            IFileManager)

        with pytest.raises(RangeNotFound):
            assert await _gather_all(
                fm.file_storage_manager.read_range(0, 1024 * 1024 * 3))
async def test_managed_transaction_with_adoption(container_requester):
    async with container_requester as requester:
        async with transaction(db=requester.db, abort_when_done=True) as txn:
            root = await txn.get(ROOT_ID)
            container = await root.async_get('guillotina')
            container.title = 'changed title'
            container.register()

            assert container.__uuid__ in container.__txn__.modified

            # nest it with adoption
            async with transaction(adopt_parent_txn=True):
                # this should commit, take on parent txn for container
                pass

            # no longer modified, adopted in previous txn
            assert container.__uuid__ not in container.__txn__.modified

        # finally, retrieve it again and make sure it's updated
        async with transaction(abort_when_done=True):
            container = await root.async_get('guillotina')
            assert container.title == 'changed title'