Esempio n. 1
0
def test_perform_block_synchronize(app, app_no_cache):
    content = 'foo'
    eff = app.perform_block_create(EBlockCreate(content))
    block_id = perform_sequence([], eff)
    eff = app_no_cache.perform_block_create(EBlockCreate(content))
    block_2_id = perform_sequence([], eff)
    # With cache enabled
    assert app.block_cache.currsize == 0
    eff = app.perform_block_synchronize(EBlockSynchronize(block_id))
    sequence = [(EBackendBlockCreate(block_id,
                                     content), const(Block(block_id,
                                                           content)))]
    synchronization = perform_sequence(sequence, eff)
    assert synchronization is True
    assert block_id not in app.blocks
    assert app.block_cache.currsize == 1
    # With cache disabled
    assert app_no_cache.block_cache.currsize == 0
    eff = app_no_cache.perform_block_synchronize(EBlockSynchronize(block_2_id))
    sequence = [(EBackendBlockCreate(block_2_id, content),
                 const(Block(block_2_id, content)))]
    synchronization = perform_sequence(sequence, eff)
    assert synchronization is True
    assert block_2_id not in app_no_cache.blocks
    assert app_no_cache.block_cache.currsize == 0
    # Do nothing
    eff = app.perform_block_synchronize(EBlockSynchronize(block_2_id))
    synchronization = perform_sequence([], eff)
    assert synchronization is False
Esempio n. 2
0
 def test_build_file_blocks(self, file, length):
     file.dirty = False
     block_size = 4096
     content = b''.join(
         [str(random.randint(1, 9)).encode() for i in range(0, length)])
     chunks = [
         content[i:i + block_size]
         for i in range(0, len(content), block_size)
     ]
     if not chunks:
         chunks = [b'']
     sequence = []
     for chunk in chunks:
         sequence.append((EBlockCreate(to_jsonb64(chunk)), const('4567')))
     blocks = perform_sequence(sequence, file._build_file_blocks(content))
     assert sorted(blocks.keys()) == ['blocks', 'key']
     assert isinstance(blocks['blocks'], list)
     required_blocks = int(len(content) / block_size)
     if not len(content) or len(content) % block_size:
         required_blocks += 1
     assert len(blocks['blocks']) == required_blocks
     for index, block in enumerate(blocks['blocks']):
         assert sorted(block.keys()) == ['block', 'digest', 'size']
         assert block['block']
         length = len(content) - index * block_size
         length = block_size if length > block_size else length
         assert block['size'] == length
         assert block['digest'] == digest(content[index * block_size:index +
                                                  1 * block_size])
     assert file.dirty is True
Esempio n. 3
0
def test_perform_file_create(app, alice_identity, file):
    vlob = {'id': '2345', 'read_trust_seed': '42', 'write_trust_seed': '43'}
    block_id = '4567'
    # Already exist
    blob = [{
        'blocks': [{
            'block': block_id,
            'digest': digest(b''),
            'size': 0
        }],
        'key': to_jsonb64(b'<dummy-key-00000000000000000003>')
    }]
    blob = ejson_dumps(blob).encode()
    blob = to_jsonb64(blob)
    eff = app.perform_file_create(EFileCreate('/foo'))
    sequence = [
        (EBlockCreate(''), const(block_id)),
        (EVlobCreate(blob), const(vlob)),
        (EIdentityGet(), const(alice_identity)),
        (EVlobRead(vlob['id'], vlob['read_trust_seed'],
                   1), const({
                       'id': vlob['id'],
                       'blob': blob,
                       'version': 1
                   })),
        (EBlockDelete(block_id), noop),
        (EVlobDelete(vlob['id']), noop),
    ]
    with pytest.raises(ManifestError):
        perform_sequence(sequence, eff)
Esempio n. 4
0
def test_perform_block_create(app):
    content = 'foo'
    with freeze_time('2012-01-01') as frozen_datetime:
        eff = app.perform_block_create(EBlockCreate(content))
        block_id = perform_sequence([], eff)
        assert app.last_modified == Arrow.fromdatetime(frozen_datetime())
    eff = app.perform_block_read(EBlockRead(block_id))
    block = perform_sequence([], eff)
    assert block['content'] == content
Esempio n. 5
0
def test_perform_block_list(app):
    content = 'foo'
    eff = app.perform_block_create(EBlockCreate(content))
    block_id = perform_sequence([], eff)
    eff = app.perform_block_create(EBlockCreate(content))
    block_2_id = perform_sequence([], eff)
    eff = app.perform_block_list(EBlockList())
    block_list = perform_sequence([], eff)
    assert isinstance(block_list, list)
    assert set(block_list) == set([block_id, block_2_id])
    # Synchronized blocks are excluded
    eff = app.perform_block_synchronize(EBlockSynchronize(block_2_id))
    sequence = [(EBackendBlockCreate(block_2_id, content),
                 const(Block(block_2_id, content)))]
    perform_sequence(sequence, eff)
    eff = app.perform_block_list(EBlockList())
    block_list = perform_sequence([], eff)
    assert isinstance(block_list, list)
    assert set(block_list) == set([block_id])
Esempio n. 6
0
def test_perform_synchronize(app):
    content = 'foo'
    eff = app.perform_block_create(EBlockCreate(content))
    block_id = perform_sequence([], eff)
    eff = app.perform_block_create(EBlockCreate(content))
    block_2_id = perform_sequence([], eff)
    block_ids = sorted([block_id, block_2_id])
    blob = 'foo'
    eff = app.perform_vlob_create(EVlobCreate(blob))
    perform_sequence([], eff)
    eff = app.perform_vlob_create(EVlobCreate(blob))
    perform_sequence([], eff)
    eff = app.perform_synchronize(ESynchronize())
    sequence = [
        (EBackendBlockCreate(block_ids[0],
                             content), const(Block(block_ids[0], content))),
        (EBackendBlockCreate(block_ids[1],
                             content), const(Block(block_ids[1], content))),
        (
            EBackendVlobCreate(blob.encode()),  # TODO encode correct?
            const(VlobAccess('345', 'ABC', 'DEF'))),
        (
            EBackendVlobCreate(blob.encode()),  # TODO encode correct?
            const(VlobAccess('678', 'ABC', 'DEF'))),
    ]
    synchronization = perform_sequence(sequence, eff)
    assert synchronization is True
    eff = app.perform_block_list(EBlockList())
    block_list = perform_sequence([], eff)
    assert block_list == []
    eff = app.perform_user_vlob_exist(EUserVlobExist())
    exist = perform_sequence([], eff)
    assert exist is False
    eff = app.perform_vlob_list(EVlobList())
    vlob_list = perform_sequence([], eff)
    assert vlob_list == []
    # Do nothing
    eff = app.perform_synchronize(ESynchronize())
    synchronization = perform_sequence([], eff)
    assert synchronization is False
Esempio n. 7
0
def test_perform_block_read(app, app_no_cache):
    local_content = 'foo'
    eff = app.perform_block_create(EBlockCreate(local_content))
    block_id = perform_sequence([], eff)
    # Read block in new blocks
    eff = app.perform_block_read(EBlockRead(block_id))
    block = perform_sequence([], eff)
    assert sorted(list(block.keys())) == ['content', 'id']
    assert block['id']
    assert block['content'] == local_content
    remote_content = b'bar'
    # Read remote block
    assert app.block_cache.currsize == 0
    eff = app.perform_block_read(EBlockRead('123'))
    sequence = [(EBackendBlockRead('123'), const(Block('123',
                                                       remote_content)))]
    block = perform_sequence(sequence, eff)
    assert sorted(list(block.keys())) == ['content', 'id']
    assert block['id']
    assert block['content'] == remote_content
    assert app.block_cache.currsize == 1
    # Read remote block with cache disabled
    assert app_no_cache.block_cache.currsize == 0
    eff = app_no_cache.perform_block_read(EBlockRead('123'))
    sequence = [(EBackendBlockRead('123'), const(Block('123',
                                                       remote_content)))]
    block = perform_sequence(sequence, eff)
    assert sorted(list(block.keys())) == ['content', 'id']
    assert block['id']
    assert block['content'] == remote_content
    assert app_no_cache.block_cache.currsize == 0
    # Read block in cache
    eff = app.perform_block_read(EBlockRead('123'))
    block = perform_sequence([], eff)
    assert sorted(list(block.keys())) == ['content', 'id']
    assert block['id']
    assert block['content'] == remote_content
    # Delete block from cache
    eff = app.perform_block_delete(EBlockDelete('123'))
    perform_sequence([], eff)
    # Not found
    eff = app.perform_block_read(EBlockRead('123'))
    sequence = [(EBackendBlockRead('123'),
                 conste(BlockNotFound('Block not found.')))]
    with pytest.raises(BlockNotFound):
        block = perform_sequence(sequence, eff)
    eff = app.perform_block_read(EBlockRead('123'))
    sequence = [(EBackendBlockRead('123'), conste(BlockError('Block error.'))
                 )  # TODO keep it? usefull with multiple backends...
                ]
    with pytest.raises(BlockNotFound):
        block = perform_sequence(sequence, eff)
Esempio n. 8
0
def file(app, alice_identity, mock_crypto_passthrough):
    vlob = {'id': '2345', 'read_trust_seed': '42', 'write_trust_seed': '43'}
    block_id = '4567'
    blob = [{
        'blocks': [{
            'block': block_id,
            'digest': digest(b''),
            'size': 0
        }],
        'key': to_jsonb64(b'<dummy-key-00000000000000000001>')
    }]
    blob = ejson_dumps(blob).encode()
    blob = to_jsonb64(blob)
    eff = app.perform_file_create(EFileCreate('/foo'))
    sequence = [(EBlockCreate(''), const(block_id)),
                (EVlobCreate(blob), const(vlob)),
                (EIdentityGet(), const(alice_identity))]
    ret = perform_sequence(sequence, eff)
    assert ret is None
    File.files = {}
Esempio n. 9
0
def test_perform_block_delete(app):
    content = 'foo'
    eff = app.perform_block_create(EBlockCreate(content))
    block_id = perform_sequence([], eff)
    # Delete from new blocks
    with freeze_time('2012-01-01') as frozen_datetime:
        eff = app.perform_block_delete(EBlockDelete(block_id))
        perform_sequence([], eff)
        assert app.last_modified == Arrow.fromdatetime(frozen_datetime())
    # Delete in cache
    app.block_cache[block_id] = {'foo': 'bar'}
    assert app.block_cache.currsize == 1
    with freeze_time('2012-01-01') as frozen_datetime:
        eff = app.perform_block_delete(EBlockDelete(block_id))
        perform_sequence([], eff)
        assert app.last_modified == Arrow.fromdatetime(frozen_datetime())
    assert app.block_cache.currsize == 0
    # Not found
    with pytest.raises(BlockNotFound):
        eff = app.perform_block_delete(EBlockDelete(block_id))
        perform_sequence([], eff)
Esempio n. 10
0
 def _build_file_blocks(self, data):
     # Create chunks
     chunk_size = 4096  # TODO modify size
     chunks = [data[i:i + chunk_size] for i in range(0, len(data), chunk_size)]
     # Force a chunk even if the data is empty
     if not chunks:
         chunks = [b'']
     encryptor = generate_sym_key()
     blocks = []
     for chunk in chunks:
         cypher_chunk = encryptor.encrypt(chunk)
         cypher_chunk = to_jsonb64(cypher_chunk)
         block_id = yield Effect(EBlockCreate(cypher_chunk))
         blocks.append({'block': block_id,
                        'digest': digest(chunk),
                        'size': len(chunk)})
     # New vlob atom
     block_key = to_jsonb64(encryptor.key)
     blob = {'blocks': blocks,
             'key': block_key}
     self.dirty = True
     return blob
Esempio n. 11
0
def file(mock_crypto_passthrough):
    block_id = '4567'
    blob = [{
        'blocks': [{
            'block': block_id,
            'digest': digest(b''),
            'size': 0
        }],
        'key': to_jsonb64(b'<dummy-key-00000000000000000001>')
    }]
    blob = ejson_dumps(blob).encode()
    blob = to_jsonb64(blob)
    sequence = [
        (EBlockCreate(''), const(block_id)),
        (EVlobCreate(blob),
         const({
             'id': '1234',
             'read_trust_seed': '42',
             'write_trust_seed': '43'
         })),
    ]
    return perform_sequence(sequence, File.create())
Esempio n. 12
0
    def test_find_matching_blocks(self, file):
        vlob_id = '1234'
        block_size = 4096
        # Contents
        contents = {}
        total_length = 0
        for index, length in enumerate([
                block_size + 1, block_size - 1, block_size, 2 * block_size + 2,
                2 * block_size - 2, 2 * block_size
        ]):
            content = b''.join(
                [str(random.randint(1, 9)).encode() for i in range(0, length)])
            contents[index] = content
            total_length += length
        # Blocks

        def generator():
            i = 2000
            while True:
                yield str(i)
                i += 1

        gen = generator()

        blocks = {}
        block_contents = {}
        block_id = 2000
        for index, content in contents.items():
            chunks = [
                content[i:i + block_size]
                for i in range(0, len(content), block_size)
            ]
            if not chunks:
                chunks = [b'']
            sequence = []
            for chunk in chunks:
                encoded_chunk = to_jsonb64(chunk)
                sequence.append((EBlockCreate(encoded_chunk),
                                 lambda id=id: next(gen)))  # TODO dirty
                block_contents[str(block_id)] = encoded_chunk
                block_id += 1
            blocks[index] = perform_sequence(sequence,
                                             file._build_file_blocks(content))
        # Create file
        blob = ejson_dumps([blocks[i] for i in range(0, len(blocks))]).encode()
        blob = to_jsonb64(blob)
        # All matching blocks
        sequence = [(EVlobRead(vlob_id, '42', 1),
                     const({
                         'id': vlob_id,
                         'blob': blob,
                         'version': 1
                     }))]
        matching_blocks = perform_sequence(sequence,
                                           file._find_matching_blocks())
        assert matching_blocks == {
            'pre_excluded_blocks': [],
            'pre_excluded_data': b'',
            'pre_included_data': b'',
            'included_blocks': [blocks[i] for i in range(0, len(blocks))],
            'post_included_data': b'',
            'post_excluded_data': b'',
            'post_excluded_blocks': []
        }
        # With offset
        delta = 10
        offset = (blocks[0]['blocks'][0]['size'] +
                  blocks[0]['blocks'][1]['size'] +
                  blocks[1]['blocks'][0]['size'] +
                  blocks[2]['blocks'][0]['size'] - delta)
        sequence = [(EVlobRead(vlob_id, '42', 1),
                     const({
                         'id': vlob_id,
                         'blob': blob,
                         'version': 1
                     })),
                    (EBlockRead('2003'),
                     const({
                         'content': block_contents['2003'],
                         'creation_date': '2012-01-01T00:00:00'
                     }))]
        matching_blocks = perform_sequence(
            sequence, file._find_matching_blocks(None, offset))
        pre_excluded_data = contents[2][:blocks[2]['blocks'][0]['size'] -
                                        delta]
        pre_included_data = contents[2][-delta:]
        assert matching_blocks == {
            'pre_excluded_blocks': [blocks[0], blocks[1]],
            'pre_excluded_data': pre_excluded_data,
            'pre_included_data': pre_included_data,
            'included_blocks': [blocks[i] for i in range(3, 6)],
            'post_included_data': b'',
            'post_excluded_data': b'',
            'post_excluded_blocks': []
        }
        # With small size
        delta = 10
        size = 5
        offset = (blocks[0]['blocks'][0]['size'] +
                  blocks[0]['blocks'][1]['size'] +
                  blocks[1]['blocks'][0]['size'] +
                  blocks[2]['blocks'][0]['size'] - delta)
        sequence = [(EVlobRead(vlob_id, '42', 1),
                     const({
                         'id': vlob_id,
                         'blob': blob,
                         'version': 1
                     })),
                    (EBlockRead(id='2003'),
                     const({
                         'content': block_contents['2003'],
                         'creation_date': '2012-01-01T00:00:00'
                     }))]
        matching_blocks = perform_sequence(
            sequence, file._find_matching_blocks(size, offset))
        pre_excluded_data = contents[2][:blocks[2]['blocks'][0]['size'] -
                                        delta]
        pre_included_data = contents[2][-delta:][:size]
        post_excluded_data = contents[2][-delta:][size:]
        assert matching_blocks == {
            'pre_excluded_blocks': [blocks[0], blocks[1]],
            'pre_excluded_data': pre_excluded_data,
            'pre_included_data': pre_included_data,
            'included_blocks': [],
            'post_included_data': b'',
            'post_excluded_data': post_excluded_data,
            'post_excluded_blocks': [blocks[i] for i in range(3, 6)]
        }
        # With big size
        delta = 10
        size = delta
        size += blocks[3]['blocks'][0]['size']
        size += blocks[3]['blocks'][1]['size']
        size += blocks[3]['blocks'][2]['size']
        size += 2 * delta
        offset = (blocks[0]['blocks'][0]['size'] +
                  blocks[0]['blocks'][1]['size'] +
                  blocks[1]['blocks'][0]['size'] +
                  blocks[2]['blocks'][0]['size'] - delta)
        sequence = [(EVlobRead(vlob_id, '42', 1),
                     const({
                         'id': vlob_id,
                         'blob': blob,
                         'version': 1
                     })),
                    (EBlockRead('2003'),
                     const({
                         'content': block_contents['2003'],
                         'creation_date': '2012-01-01T00:00:00'
                     })),
                    (EBlockRead('2007'),
                     const({
                         'content': block_contents['2007'],
                         'creation_date': '2012-01-01T00:00:00'
                     }))]
        matching_blocks = perform_sequence(
            sequence, file._find_matching_blocks(size, offset))
        pre_excluded_data = contents[2][:-delta]
        pre_included_data = contents[2][-delta:]
        post_included_data = contents[4][:2 * delta]
        post_excluded_data = contents[4][:block_size][2 * delta:]
        partial_block_4 = deepcopy(blocks[4])
        del partial_block_4['blocks'][0]
        assert matching_blocks == {
            'pre_excluded_blocks': [blocks[0], blocks[1]],
            'pre_excluded_data': pre_excluded_data,
            'pre_included_data': pre_included_data,
            'included_blocks': [blocks[3]],
            'post_included_data': post_included_data,
            'post_excluded_data': post_excluded_data,
            'post_excluded_blocks': [partial_block_4, blocks[5]]
        }
        # With big size and no delta
        size = blocks[3]['blocks'][0]['size']
        size += blocks[3]['blocks'][1]['size']
        size += blocks[3]['blocks'][2]['size']
        offset = (blocks[0]['blocks'][0]['size'] +
                  blocks[0]['blocks'][1]['size'] +
                  blocks[1]['blocks'][0]['size'] +
                  blocks[2]['blocks'][0]['size'])
        sequence = [
            (EVlobRead(vlob_id, '42',
                       1), const({
                           'id': vlob_id,
                           'blob': blob,
                           'version': 1
                       })),
        ]
        matching_blocks = perform_sequence(
            sequence, file._find_matching_blocks(size, offset))
        assert matching_blocks == {
            'pre_excluded_blocks': [blocks[0], blocks[1], blocks[2]],
            'pre_excluded_data': b'',
            'pre_included_data': b'',
            'included_blocks': [blocks[3]],
            'post_included_data': b'',
            'post_excluded_data': b'',
            'post_excluded_blocks': [blocks[4], blocks[5]]
        }
        # With total size
        sequence = [
            (EVlobRead(vlob_id, '42',
                       1), const({
                           'id': vlob_id,
                           'blob': blob,
                           'version': 1
                       })),
        ]
        matching_blocks = perform_sequence(
            sequence, file._find_matching_blocks(total_length, 0))
        assert matching_blocks == {
            'pre_excluded_blocks': [],
            'pre_excluded_data': b'',
            'pre_included_data': b'',
            'included_blocks': [blocks[i] for i in range(0, 6)],
            'post_included_data': b'',
            'post_excluded_data': b'',
            'post_excluded_blocks': []
        }
Esempio n. 13
0
 def test_commit(self, file):
     vlob_id = '1234'
     content = b'This is a test content.'
     block_ids = ['4567', '5678', '6789']
     new_vlob = {
         'id': '2345',
         'read_trust_seed': 'ABC',
         'write_trust_seed': 'DEF'
     }
     # Original content
     chunk_1 = content[:5]
     chunk_2 = content[5:14]
     chunk_3 = content[14:]
     blob = [{
         'blocks': [{
             'block': block_ids[0],
             'digest': digest(chunk_1),
             'size': len(chunk_1)
         }, {
             'block': block_ids[1],
             'digest': digest(chunk_2),
             'size': len(chunk_2)
         }],
         'key':
         to_jsonb64(b'<dummy-key-00000000000000000003>')
     }, {
         'blocks': [{
             'block': block_ids[2],
             'digest': digest(chunk_3),
             'size': len(chunk_3)
         }],
         'key':
         to_jsonb64(b'<dummy-key-00000000000000000004>')
     }]
     blob = ejson_dumps(blob).encode()
     blob = to_jsonb64(blob)
     # New content after truncate
     new_chuck_2 = b'is a'
     new_block_id = '7654'
     new_blob = [{
         'blocks': [{
             'block': block_ids[0],
             'digest': digest(chunk_1),
             'size': len(chunk_1)
         }],
         'key':
         to_jsonb64(b'<dummy-key-00000000000000000003>')
     }, {
         'blocks': [{
             'block': new_block_id,
             'digest': digest(new_chuck_2),
             'size': len(new_chuck_2)
         }],
         'key':
         to_jsonb64(b'<dummy-key-00000000000000000003>')
     }]
     new_blob = ejson_dumps(new_blob).encode()
     new_blob = to_jsonb64(new_blob)
     file.truncate(9)
     sequence = [
         (EVlobRead('1234', '42',
                    1), const({
                        'id': '1234',
                        'blob': blob,
                        'version': 1
                    })),
         (EVlobRead('1234', '42',
                    1), const({
                        'id': '1234',
                        'blob': blob,
                        'version': 1
                    })),
         (EBlockRead(block_ids[1]),
          const({
              'content': to_jsonb64(chunk_2),
              'creation_date': '2012-01-01T00:00:00'
          })), (EBlockCreate(to_jsonb64(new_chuck_2)), const(new_block_id)),
         (EVlobUpdate(vlob_id, '43', 1, new_blob), noop),
         (EVlobRead('1234', '42',
                    1), const({
                        'id': '1234',
                        'blob': new_blob,
                        'version': 1
                    })),
         (EBlockDelete('5678'), conste(BlockNotFound('Block not found.'))),
         (EBlockDelete('6789'), noop),
         (EVlobRead('1234', '42',
                    1), const({
                        'id': '1234',
                        'blob': new_blob,
                        'version': 1
                    })), (EBlockSynchronize('4567'), const(True)),
         (EBlockSynchronize('7654'), const(False)),
         (EVlobSynchronize('1234'), const(new_vlob))
     ]
     ret = perform_sequence(sequence, file.commit())
     new_vlob['key'] = to_jsonb64(b'<dummy-key-00000000000000000002>')
     assert ret == new_vlob
     assert file.dirty is False
     assert file.version == 1
Esempio n. 14
0
 def test_flush(self, file):
     file.truncate(9)
     file.write(b'IS', 5)
     file.write(b'IS a nice test content.', 5)
     file.dirty = False
     file.version = 2
     vlob_id = '1234'
     content = b'This is a test content.'
     block_ids = ['4567', '5678', '6789']
     # Original content
     chunk_1 = content[:5]
     chunk_2 = content[5:14]
     chunk_3 = content[14:]
     blob = [{
         'blocks': [{
             'block': block_ids[0],
             'digest': digest(chunk_1),
             'size': len(chunk_1)
         }, {
             'block': block_ids[1],
             'digest': digest(chunk_2),
             'size': len(chunk_2)
         }],
         'key':
         to_jsonb64(b'<dummy-key-00000000000000000001>')
     }, {
         'blocks': [{
             'block': block_ids[2],
             'digest': digest(chunk_3),
             'size': len(chunk_3)
         }],
         'key':
         to_jsonb64(b'<dummy-key-00000000000000000002>')
     }]
     blob = ejson_dumps(blob).encode()
     blob = to_jsonb64(blob)
     # New content after truncate
     new_chuck_2 = b'is a'
     new_block_id = '7654'
     new_blob = [{
         'blocks': [{
             'block': block_ids[0],
             'digest': digest(chunk_1),
             'size': len(chunk_1)
         }],
         'key':
         to_jsonb64(b'<dummy-key-00000000000000000001>')
     }, {
         'blocks': [{
             'block': new_block_id,
             'digest': digest(new_chuck_2),
             'size': len(new_chuck_2)
         }],
         'key':
         to_jsonb64(b'<dummy-key-00000000000000000003>')
     }]
     new_blob = ejson_dumps(new_blob).encode()
     new_blob = to_jsonb64(new_blob)
     # New content after write
     new_block_2_id = '6543'
     new_chunk_4 = b'IS a nice test content.'
     new_blob_2 = [{
         'blocks': [{
             'block': block_ids[0],
             'digest': digest(chunk_1),
             'size': len(chunk_1)
         }],
         'key':
         to_jsonb64(b'<dummy-key-00000000000000000001>')
     }, {
         'blocks': [{
             'block': new_block_2_id,
             'digest': digest(new_chunk_4),
             'size': len(new_chunk_4)
         }],
         'key':
         to_jsonb64(b'<dummy-key-00000000000000000004>')
     }]
     new_blob_2 = ejson_dumps(new_blob_2).encode()
     new_blob_2 = to_jsonb64(new_blob_2)
     sequence = [
         (
             EVlobRead(vlob_id, '42', 2),  # Get blocks
             const({
                 'id': vlob_id,
                 'blob': blob,
                 'version': 2
             })),
         (
             EVlobRead(vlob_id, '42', 2),  # Matching blocks
             const({
                 'id': vlob_id,
                 'blob': blob,
                 'version': 2
             })),
         (EBlockRead(block_ids[1]),
          const({
              'content': to_jsonb64(chunk_2),
              'creation_date': '2012-01-01T00:00:00'
          })),
         (EBlockCreate(to_jsonb64(new_chuck_2)), const(new_block_id)),
         (EVlobUpdate(vlob_id, '43', 3, new_blob), noop),
         (
             EVlobRead(vlob_id, '42', 3),  # Matching blocks
             const({
                 'id': vlob_id,
                 'blob': new_blob,
                 'version': 3
             })),
         (EBlockCreate(to_jsonb64(new_chunk_4)), const(new_block_2_id)),
         (EVlobUpdate(vlob_id, '43', 3, new_blob_2), noop),
         (EVlobRead(vlob_id, '42', 3),
          const({
              'id': vlob_id,
              'blob': new_blob_2,
              'version': 3
          })),
         (EBlockDelete('5678'), conste(BlockNotFound('Block not found.'))),
         (EBlockDelete('6789'), noop),
     ]
     ret = perform_sequence(sequence, file.flush())
     assert ret is None
     assert file.dirty is True
     assert file.version == 2