def test_alice_writes_bob_receives_multiple(magic_folder): """ When Alice does a series of updates, Bob should just receive them with no .backup or .conflict files being produced. """ alice_dir, bob_dir = magic_folder unwanted_files = [ join(bob_dir, "multiple.backup"), join(bob_dir, "multiple.conflict") ] # first update with open(join(alice_dir, "multiple"), "w") as f: f.write("alice wrote this") util.await_file_contents( join(bob_dir, "multiple"), "alice wrote this", error_if=unwanted_files, ) # second update with open(join(alice_dir, "multiple"), "w") as f: f.write("someone changed their mind") util.await_file_contents( join(bob_dir, "multiple"), "someone changed their mind", error_if=unwanted_files, ) # third update with open(join(alice_dir, "multiple"), "w") as f: f.write("absolutely final version ship it") util.await_file_contents( join(bob_dir, "multiple"), "absolutely final version ship it", error_if=unwanted_files, ) # forth update, but both "at once" so one should conflict time.sleep(2) with open(join(alice_dir, "multiple"), "w") as f: f.write("okay one more attempt") with open(join(bob_dir, "multiple"), "w") as f: f.write("...but just let me add") bob_conflict = join(bob_dir, "multiple.conflict") alice_conflict = join(alice_dir, "multiple.conflict") found = util.await_files_exist([ bob_conflict, alice_conflict, ]) assert len(found) > 0, "Should have found a conflict" print("conflict found (as expected)")
def test_alice_adds_files_while_bob_is_offline(reactor, request, temp_dir, magic_folder, bob): """ Alice can add new files to a magic folder while Bob is offline. When Bob comes back online his copy is updated to reflect the new files. """ alice_magic_dir, bob_magic_dir = magic_folder alice_node_dir = join(temp_dir, "alice") # Take Bob offline. yield bob.stop_magic_folder() # Create a couple files in Alice's local directory. some_files = list((name * 3) + ".added-while-offline" for name in "xyz") for name in some_files: with open(join(alice_magic_dir, name), "w") as f: f.write(name + " some content") agent = Agent(reactor) upload_dircap = load_magic_folders( alice_node_dir)["default"]["upload_dircap"] # Alice's tahoe-lafs web api uri = "http://127.0.0.1:9980/uri/{}?t=json".format(upload_dircap) @pytest_twisted.inlineCallbacks def good_remote_state(agent): response = yield agent.request( b"GET", uri, ) if response.code != 200: returnValue(False) files = json.loads((yield readBody(response))) print(files) returnValue(True) yield poll("good-remote-state", partial(good_remote_state, agent), reactor) # Start Bob up again yield bob.start_magic_folder() yield util.await_files_exist( list(join(bob_magic_dir, name) for name in some_files), await_all=True, ) # Let it settle. It would be nicer to have a readable status output we # could query. Parsing the current text format is more than I want to # deal with right now. time.sleep(1.0) conflict_files = list(name + ".conflict" for name in some_files) assert all( list(not exists(join(bob_magic_dir, name)) for name in conflict_files), )
def test_alice_writes_bob_receives_multiple(magic_folder): """ When Alice does a series of updates, Bob should just receive them with no .backup or .conflict files being produced. """ alice_dir, bob_dir = magic_folder unwanted_files = [ join(bob_dir, "multiple.backup"), join(bob_dir, "multiple.conflict") ] # first update with open(join(alice_dir, "multiple"), "w") as f: f.write("alice wrote this") util.await_file_contents( join(bob_dir, "multiple"), "alice wrote this", error_if=unwanted_files, ) # second update with open(join(alice_dir, "multiple"), "w") as f: f.write("someone changed their mind") util.await_file_contents( join(bob_dir, "multiple"), "someone changed their mind", error_if=unwanted_files, ) # third update with open(join(alice_dir, "multiple"), "w") as f: f.write("absolutely final version ship it") util.await_file_contents( join(bob_dir, "multiple"), "absolutely final version ship it", error_if=unwanted_files, ) # forth update, but both "at once" so one should conflict time.sleep(2) with open(join(alice_dir, "multiple"), "w") as f: f.write("okay one more attempt") with open(join(bob_dir, "multiple"), "w") as f: f.write("...but just let me add") bob_conflict = join(bob_dir, "multiple.conflict") alice_conflict = join(alice_dir, "multiple.conflict") found = util.await_files_exist([ bob_conflict, alice_conflict, ]) assert len(found) > 0, "Should have found a conflict" print("conflict found (as expected)")
def test_alice_adds_files_while_bob_is_offline(reactor, request, temp_dir, magic_folder): """ Alice can add new files to a magic folder while Bob is offline. When Bob comes back online his copy is updated to reflect the new files. """ alice_magic_dir, bob_magic_dir = magic_folder alice_node_dir = join(temp_dir, "alice") bob_node_dir = join(temp_dir, "bob") # Take Bob offline. yield util.cli(request, reactor, bob_node_dir, "stop") # Create a couple files in Alice's local directory. some_files = list((name * 3) + ".added-while-offline" for name in "xyz") for name in some_files: with open(join(alice_magic_dir, name), "w") as f: f.write(name + " some content") good = False for i in range(15): status = yield util.magic_folder_cli(request, reactor, alice_node_dir, "status") good = status.count(".added-while-offline (36 B): good, version=0" ) == len(some_files) * 2 if good: # We saw each file as having a local good state and a remote good # state. That means we're ready to involve Bob. break else: time.sleep(1.0) assert good, ("Timed out waiting for good Alice state. Last status:\n{}". format(status)) # Start Bob up again magic_text = 'Completed initial Magic Folder scan successfully' yield util._run_node(reactor, bob_node_dir, request, magic_text) yield util.await_files_exist( list(join(bob_magic_dir, name) for name in some_files), await_all=True, ) # Let it settle. It would be nicer to have a readable status output we # could query. Parsing the current text format is more than I want to # deal with right now. time.sleep(1.0) conflict_files = list(name + ".conflict" for name in some_files) assert all( list(not exists(join(bob_magic_dir, name)) for name in conflict_files), )
def _bob_conflicts_alice_await_conflicts(name, alice_dir, bob_dir): """ shared code between _fresh and _preexisting conflict test """ found = util.await_files_exist( [ join(bob_dir, '{}.conflict'.format(name)), join(alice_dir, '{}.conflict'.format(name)), ], ) assert len(found) >= 1, "should be at least one conflict" assert open(join(bob_dir, name), 'r').read() == "this is bob's {}\n".format(name) assert open(join(alice_dir, name), 'r').read() == "this is alice's {}\n".format(name) alice_conflict = join(alice_dir, '{}.conflict'.format(name)) bob_conflict = join(bob_dir, '{}.conflict'.format(name)) if exists(bob_conflict): assert open(bob_conflict, 'r').read() == "this is alice's {}\n".format(name) if exists(alice_conflict): assert open(alice_conflict, 'r').read() == "this is bob's {}\n".format(name)
def _bob_conflicts_alice_await_conflicts(name, alice_dir, bob_dir): """ shared code between _fresh and _preexisting conflict test """ found = util.await_files_exist( [ join(bob_dir, '{}.conflict'.format(name)), join(alice_dir, '{}.conflict'.format(name)), ], ) assert len(found) >= 1, "should be at least one conflict" assert open(join(bob_dir, name), 'r').read() == "this is bob's {}\n".format(name) assert open(join(alice_dir, name), 'r').read() == "this is alice's {}\n".format(name) alice_conflict = join(alice_dir, '{}.conflict'.format(name)) bob_conflict = join(bob_dir, '{}.conflict'.format(name)) if exists(bob_conflict): assert open(bob_conflict, 'r').read() == "this is alice's {}\n".format(name) if exists(alice_conflict): assert open(alice_conflict, 'r').read() == "this is bob's {}\n".format(name)
def test_alice_adds_files_while_bob_is_offline(reactor, request, temp_dir, magic_folder): """ Alice can add new files to a magic folder while Bob is offline. When Bob comes back online his copy is updated to reflect the new files. """ alice_magic_dir, bob_magic_dir = magic_folder alice_node_dir = join(temp_dir, "alice") bob_node_dir = join(temp_dir, "bob") # Take Bob offline. yield util.cli(reactor, bob_node_dir, "stop") # Create a couple files in Alice's local directory. some_files = list( (name * 3) + ".added-while-offline" for name in "xyz" ) for name in some_files: with open(join(alice_magic_dir, name), "w") as f: f.write(name + " some content") good = False for i in range(15): status = yield util.magic_folder_cli(reactor, alice_node_dir, "status") good = status.count(".added-while-offline (36 B): good, version=0") == len(some_files) * 2 if good: # We saw each file as having a local good state and a remote good # state. That means we're ready to involve Bob. break else: time.sleep(1.0) assert good, ( "Timed out waiting for good Alice state. Last status:\n{}".format(status) ) # Start Bob up again magic_text = 'Completed initial Magic Folder scan successfully' yield util._run_node(reactor, bob_node_dir, request, magic_text) yield util.await_files_exist( list( join(bob_magic_dir, name) for name in some_files ), await_all=True, ) # Let it settle. It would be nicer to have a readable status output we # could query. Parsing the current text format is more than I want to # deal with right now. time.sleep(1.0) conflict_files = list(name + ".conflict" for name in some_files) assert all( list( not exists(join(bob_magic_dir, name)) for name in conflict_files ), )