def test_api_dustbin_show(): # Specific file in dustbin eff = execute_cmd('dustbin_show', {'path': '/foo'}) sequence = [ (EDustbinShow('/foo'), const([{ 'path': '/foo' }])), ] resp = perform_sequence(sequence, eff) assert resp == {'status': 'ok', 'dustbin': [{'path': '/foo'}]} # All files in dustbin eff = execute_cmd('dustbin_show', {}) sequence = [ (EDustbinShow(), const([{ 'path': '/foo' }, { 'path': '/bar' }])), ] resp = perform_sequence(sequence, eff) assert resp == { 'status': 'ok', 'dustbin': [{ 'path': '/foo' }, { 'path': '/bar' }] }
def test_perform_undelete(app, alice_identity, file): vlob = {'id': '2345', 'read_trust_seed': '42', 'write_trust_seed': '43'} blob = [{ 'blocks': [{ 'block': '4567', 'digest': digest(b''), 'size': 0 }], 'key': to_jsonb64(b'<dummy-key-00000000000000000001>') }] blob = ejson_dumps(blob).encode() blob = to_jsonb64(blob) eff = app.perform_delete(EDelete('/foo')) sequence = [ (EIdentityGet(), const(alice_identity)), (EVlobRead(vlob['id'], vlob['read_trust_seed']), const({ 'id': vlob['id'], 'blob': blob, 'version': 1 })), (EVlobList(), const([])), (EVlobRead(vlob['id'], vlob['read_trust_seed'], 1), const({ 'id': vlob['id'], 'blob': blob, 'version': 1 })), (EBlockDelete('4567'), conste(BlockNotFound('Block not found.'))), (EVlobDelete('2345'), conste(VlobNotFound('Vlob not found.'))) ] ret = perform_sequence(sequence, eff) eff = app.perform_undelete(EUndelete('2345')) sequence = [(EIdentityGet(), const(alice_identity))] ret = perform_sequence(sequence, eff) assert ret is None
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)
def test_discard(self, file): 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-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) # Already synchronized sequence = [ (EVlobRead('1234', '42', 1), const({ 'id': '1234', 'blob': blob, 'version': 1 })), (EBlockDelete('4567'), conste(BlockNotFound('Block not found.'))), (EBlockDelete('5678'), noop), (EBlockDelete('6789'), noop), (EVlobDelete('1234'), conste(VlobNotFound('Block not found.')) ) # TODO vlob OR block exceptin ] ret = perform_sequence(sequence, file.discard()) assert ret is False # Not already synchronized file.dirty = True file.version = 0 sequence = [(EVlobRead('1234', '42', 1), const({ 'id': '1234', 'blob': blob, 'version': 1 })), (EBlockDelete('4567'), noop), (EBlockDelete('5678'), noop), (EBlockDelete('6789'), noop), (EVlobDelete('1234'), noop)] ret = perform_sequence(sequence, file.discard()) assert ret is True assert file.dirty is False
def app(mock_crypto_passthrough, alice_identity): # app = FSComponent() # identity_component = IdentityComponent() fs_component = FSComponent() # synchronizer_component = SynchronizerComponent() # identity_component = IdentityComponent() # app = app_factory( # fs_component.get_dispatcher(), # synchronizer_component.get_dispatcher(), # identity_component.get_dispatcher() # ) blob = { 'dustbin': [], 'entries': { '/': None }, 'groups': {}, 'versions': {} } blob = ejson_dumps(blob).encode() blob = to_jsonb64(blob) sequence = [(EIdentityGet(), const(alice_identity)), (EUserVlobRead(), const({ 'blob': '', 'version': 0 })), (EUserVlobUpdate(1, blob), noop)] perform_sequence(sequence, fs_component._get_manifest()) return fs_component
def test_load_file(self, file): vlob_id = '1234' other_vlob_id = '5678' read_trust_seed = '42' version = 1 # Load from open files file2 = perform_sequence( [], File.load(vlob_id, to_jsonb64(b'<dummy-key-00000000000000000001>'), read_trust_seed, '43')) assert file == file2 File.files = {} # Test reloading commited and not commited file for synchronizer_vlob_list in [[vlob_id, other_vlob_id], [other_vlob_id]]: key = to_jsonb64(b'<dummy-key-00000000000000000001>') sequence = [ (EVlobRead(vlob_id, read_trust_seed, None), const({ 'id': vlob_id, 'blob': 'foo', 'version': version })), (EVlobList(), const(synchronizer_vlob_list)), ] file = perform_sequence( sequence, File.load(vlob_id, key, read_trust_seed, '43')) assert file.dirty is (vlob_id in synchronizer_vlob_list) assert file.version == (version - 1 if file.dirty else version) File.files = {}
def test_perform_file_history(app, file, alice_identity): vlob = {'id': '2345', 'read_trust_seed': '42', 'write_trust_seed': '43'} blob = [{ 'blocks': [{ 'block': '4567', '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_history(EFileHistory('/foo', 1, 1)) sequence = [ (EIdentityGet(), const(alice_identity)), (EIdentityGet(), const(alice_identity)), (EVlobRead(vlob['id'], vlob['read_trust_seed']), const({ 'id': vlob['id'], 'blob': blob, 'version': 1 })), (EVlobList(), const([])), ] perform_sequence(sequence, eff)
def test_handshake(component, mock_crypto_passthrough, alice): with patch('parsec.backend.session._generate_challenge' ) as generate_challenge: generate_challenge.return_value = 'my-challenge' eff = component.handshake() sequence = [ (EHandshakeSend( payload= '{"challenge": "my-challenge", "handshake": "challenge"}'), noop), (EHandshakeRecv(), const( '{"handshake": "answer", "answer": "bXktY2hhbGxlbmdlLWFuc3dlcg==", "identity": "*****@*****.**"}' )), (EPubKeyGet('*****@*****.**', raw=False), const(alice.pub_key)), (EHandshakeSend(payload='{"status": "ok", "handshake": "done"}'), noop) ] perform_sequence(sequence, eff) # Now identity can retrieve authenticated user intent = EGetAuthenticatedUser() eff = component.perform_get_authenticated_user(intent) sequence = [] ret = perform_sequence(sequence, eff) assert ret == '*****@*****.**'
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
def test_perform_delete(app, alice_identity): eff = app.perform_folder_create(EFolderCreate('/dir')) sequence = [(EIdentityGet(), const(alice_identity))] ret = perform_sequence(sequence, eff) eff = app.perform_delete(EDelete('/dir')) sequence = [(EIdentityGet(), const(alice_identity))] ret = perform_sequence(sequence, eff) assert ret is None
def test_perform_stat(app, alice_identity, file): eff = app.perform_folder_create(EFolderCreate('/dir')) sequence = [(EIdentityGet(), const(alice_identity))] ret = perform_sequence(sequence, eff) eff = app.perform_stat(EStat('/dir')) sequence = [(EIdentityGet(), const(alice_identity))] ret = perform_sequence(sequence, eff) assert ret == {'children': [], 'type': 'folder'}
async def test_user_vlob_update_ok(self, component): intent = EUserVlobUpdate(1, b'Next version.') eff = component.perform_user_vlob_update(intent) sequence = [(EGetAuthenticatedUser(), const('*****@*****.**')), (EEvent('user_vlob_updated', '*****@*****.**'), noop)] await asyncio_perform_sequence(sequence, eff) # Check back the value intent = EUserVlobRead(version=1) eff = component.perform_user_vlob_read(intent) sequence = [(EGetAuthenticatedUser(), const('*****@*****.**'))] ret = await asyncio_perform_sequence(sequence, eff) assert ret == UserVlobAtom('*****@*****.**', 1, b'Next version.')
def test_reencrypt(self, file): old_vlob = file.get_vlob() 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) sequence = [ (EVlobRead('1234', '42', 1), const({ 'id': '1234', 'blob': blob, 'version': 1 })), ( EVlobCreate(blob), # TODO check re-encryption const({ 'id': '2345', 'read_trust_seed': '21', 'write_trust_seed': '22' })) ] ret = perform_sequence(sequence, file.reencrypt()) assert ret is None file.reencrypt() new_vlob = file.get_vlob() for property in old_vlob.keys(): assert old_vlob[property] != new_vlob[property]
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)
def test_perform_group_create(app, alice_identity): blob = {'dustbin': [], 'entries': {'/': None}, 'versions': {}} blob = ejson_dumps(blob).encode() blob = to_jsonb64(blob) eff = app.perform_group_create(EGroupCreate('share')) sequence = [(EIdentityGet(), const(alice_identity)), (EVlobCreate(), const({ 'id': '1234', 'read_trust_seed': '42', 'write_trust_seed': '43' })), (EVlobUpdate('1234', '43', 1, blob), noop)] ret = perform_sequence(sequence, eff) assert ret is None
async def test_user_vlob_read_previous_version(self, component): # Update user vlob intent = EUserVlobUpdate(1, b'Next version.') eff = component.perform_user_vlob_update(intent) sequence = [(EGetAuthenticatedUser(), const('*****@*****.**')), (EEvent('user_vlob_updated', '*****@*****.**'), noop)] await asyncio_perform_sequence(sequence, eff) # Read previous version intent = EUserVlobRead(version=0) eff = component.perform_user_vlob_read(intent) sequence = [ (EGetAuthenticatedUser(), const('*****@*****.**')), ] ret = await asyncio_perform_sequence(sequence, eff) assert ret == UserVlobAtom('*****@*****.**', 0, b'')
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
def test_vlob_create_ok(self, id_and_blob): id, blob = id_and_blob intent = EVlobCreate(id, blob) intent_ret = VlobAtom(id, 'readtrustseed-123', 'writetrustseed-123', blob, 1) payload = {} if id: payload['id'] = id else: intent_ret.id = '123' if blob: payload['blob'] = to_jsonb64(blob) else: intent.blob = b'' intent_ret.blob = b'' eff = execute_cmd('vlob_create', payload) sequence = [ (intent, const(intent_ret)), ] ret = perform_sequence(sequence, eff) assert ret == { 'status': 'ok', 'id': intent_ret.id, 'read_trust_seed': 'readtrustseed-123', 'write_trust_seed': 'writetrustseed-123' }
def test_api_file_read(): eff = execute_cmd('file_read', {'path': '/foo'}) sequence = [ (EFileRead('/foo', 0, None), const('foo')), ] resp = perform_sequence(sequence, eff) assert resp == {'status': 'ok', 'content': 'foo'}
def test_perform_group_create(): eff = perform_group_create(EBackendGroupCreate('teamA')) backend_response = {'status': 'ok'} sequence = [(BackendCmd('group_create', {'name': 'teamA'}), const(backend_response))] ret = perform_sequence(sequence, eff) assert ret is None
def test_api_manifest_history(): summary_history = { 'summary_history': { 'entries': { 'added': {}, 'changed': {}, 'removed': {} }, 'dustbin': { 'added': [], 'removed': [] }, 'versions': { 'added': {}, 'changed': {}, 'removed': {} } } } eff = execute_cmd('history', {}) sequence = [ (EManifestHistory(1, None, False), const(summary_history)), ] resp = perform_sequence(sequence, eff) summary_history['status'] = 'ok' assert resp == summary_history
async def _update(user): intent = EUserVlobUpdate(1, b'Next version for %s.' % user.encode()) eff = component.perform_user_vlob_update(intent) sequence = [(EGetAuthenticatedUser(), const(user)), (EEvent('user_vlob_updated', user), noop)] await asyncio_perform_sequence(sequence, eff)
def test_api_identity_load(): eff = execute_cmd('identity_load', {'id': 'JohnDoe', 'key': 'MTIzNDU=\n'}) sequence = [ (EIdentityLoad('JohnDoe', b'12345', None), const(Identity('JohnDoe', Mock(), Mock()))), ] resp = perform_sequence(sequence, eff) assert resp == {'status': 'ok'}
def test_perform_vlob_read(app, app_no_cache): local_blob = 'foo' eff = app.perform_vlob_update(EVlobUpdate('123', '43', 1, local_blob)) perform_sequence([], eff) # Read remote vlob assert app.vlob_cache.currsize == 0 remote_blob = b'bar' eff = app.perform_vlob_read(EVlobRead('123', 'ABC', 2)) sequence = [(EBackendVlobRead('123', 'ABC', 2), const(VlobAtom('123', 2, remote_blob)))] vlob = perform_sequence(sequence, eff) assert sorted(list(vlob.keys())) == ['blob', 'id', 'version'] assert vlob['id'] == '123' assert vlob['blob'] == remote_blob.decode() # TODO decode? assert vlob['version'] == 2 assert app.vlob_cache.currsize == 1 # Read remote vlob with cache disabled assert app_no_cache.vlob_cache.currsize == 0 remote_blob = b'bar' eff = app_no_cache.perform_vlob_read(EVlobRead('123', 'ABC', 2)) sequence = [(EBackendVlobRead('123', 'ABC', 2), const(VlobAtom('123', 2, remote_blob)))] vlob = perform_sequence(sequence, eff) assert sorted(list(vlob.keys())) == ['blob', 'id', 'version'] assert vlob['id'] == '123' assert vlob['blob'] == remote_blob.decode() # TODO decode? assert vlob['version'] == 2 assert app_no_cache.vlob_cache.currsize == 0 # Read vlob in cache remote_blob = b'bar' eff = app.perform_vlob_read(EVlobRead('123', 'ABC', 2)) vlob = perform_sequence([], eff) assert sorted(list(vlob.keys())) == ['blob', 'id', 'version'] assert vlob['id'] == '123' assert vlob['blob'] == remote_blob.decode() # TODO decode? assert vlob['version'] == 2 # Delete vlob from cache eff = app.perform_vlob_delete(EVlobDelete('123', 2)) perform_sequence([], eff) # Read local vlob eff = app.perform_vlob_read(EVlobRead('123', '43', 1)) vlob = perform_sequence([], eff) assert sorted(list(vlob.keys())) == ['blob', 'id', 'version'] assert vlob['id'] == '123' assert vlob['blob'] == local_blob # TODO decode? assert vlob['version'] == 1
async def test_user_vlob_update_wrong_version(self, component): intent = EUserVlobUpdate(42, b'Next version.') with pytest.raises(UserVlobError): eff = component.perform_user_vlob_update(intent) sequence = [ (EGetAuthenticatedUser(), const('*****@*****.**')), ] await asyncio_perform_sequence(sequence, eff)
def test_perform_synchronize(app, alice_identity): blob = { 'dustbin': [], 'entries': { '/': None }, 'groups': {}, 'versions': {} } blob = ejson_dumps(blob).encode() blob = to_jsonb64(blob) eff = app.perform_synchronize(ESynchronize()) sequence = [(EIdentityGet(), const(alice_identity)), (EVlobList(), const([])), (EUserVlobUpdate(1, blob), noop), (EUserVlobSynchronize(), noop)] ret = perform_sequence(sequence, eff) assert ret is None
def test_api_stat(): stat = {'type': 'file', 'id': '123'} eff = execute_cmd('stat', {'path': '/foo'}) sequence = [ (EStat('/foo'), const(stat)), ] resp = perform_sequence(sequence, eff) stat['status'] = 'ok' assert resp == stat
def test_perform_vlob_update(): eff = perform_user_vlob_update(EBackendUserVlobUpdate(3, b'bar')) backend_response = {'status': 'ok'} sequence = [(BackendCmd('user_vlob_update', { 'version': 3, 'blob': to_jsonb64(b'bar') }), const(backend_response))] ret = perform_sequence(sequence, eff) assert ret is None
def test_perform_message_new(): eff = perform_message_new(EBackendMessageNew('John', b'my body')) backend_response = {'status': 'ok'} sequence = [ (BackendCmd('message_new', {'recipient': 'John', 'body': to_jsonb64(b'my body')}), const(backend_response)) ] ret = perform_sequence(sequence, eff) assert ret is None
def test_perform_dustbin_show(app, alice_identity, file): with freeze_time('2012-01-01') as frozen_datetime: vlob = { 'id': '2345', 'read_trust_seed': '42', 'write_trust_seed': '43' } blob = [{ 'blocks': [{ 'block': '4567', 'digest': digest(b''), 'size': 0 }], 'key': to_jsonb64(b'<dummy-key-00000000000000000001>') }] blob = ejson_dumps(blob).encode() blob = to_jsonb64(blob) eff = app.perform_delete(EDelete('/foo')) sequence = [ (EIdentityGet(), const(alice_identity)), (EVlobRead(vlob['id'], vlob['read_trust_seed']), const({ 'id': vlob['id'], 'blob': blob, 'version': 1 })), (EVlobList(), const([])), (EVlobRead(vlob['id'], vlob['read_trust_seed'], 1), const({ 'id': vlob['id'], 'blob': blob, 'version': 1 })), (EBlockDelete('4567'), conste(BlockNotFound('Block not found.'))), (EVlobDelete('2345'), conste(VlobNotFound('Vlob not found.'))), ] perform_sequence(sequence, eff) eff = app.perform_dustbin_show(EDustbinShow()) sequence = [(EIdentityGet(), const(alice_identity))] dustbin = perform_sequence(sequence, eff) vlob['path'] = '/foo' vlob['removed_date'] = frozen_datetime().isoformat() vlob['key'] = to_jsonb64(b'<dummy-key-00000000000000000002>') assert dustbin == [vlob]