def test_merge_conflict_basic(self, mock_labbook_lfs_disabled): """ Test a basic merge-conflict scenario with a conflict on one file. First, assert that a MergeConflict is raised when the conflict is detected Second, test the force flag to overwrite the conflict using the incoming branch.""" lb = mock_labbook_lfs_disabled[2] # Insert a text file into the master branch of lb with open('/tmp/s1.txt', 'w') as s1: s1.write('original-file\ndata') FileOperations.insert_file(lb, section='code', src_file=s1.name) # Create a new branch from this point and make a change to s1.txt bm = BranchManager(lb, username=TEST_USER) feature_name = bm.create_branch("example-feature-branch") with open('/tmp/s1.txt', 'w') as s1: s1.write('new-changes-in\nfeature-branch') FileOperations.insert_file(lb, section='code', src_file=s1.name) # Switch back to the main branch and make a new, conflicting change. bm.workon_branch(bm.workspace_branch) assert lb.is_repo_clean assert not os.path.exists(os.path.join(lb.root_dir, 'output/sample')) with open('/tmp/s1.txt', 'w') as s1: s1.write('upstream-changes-from-workspace') FileOperations.insert_file(lb, section='code', src_file=s1.name, dst_path='') # Switch back to feature branch -- make sure that failed merges rollback to state before merge. bm.workon_branch(feature_name) cp = bm.repository.git.commit_hash try: bm.merge_from(bm.workspace_branch) assert False, "merge_from should have thrown conflict" except MergeConflict as m: # Assert that the conflicted file(s) are as expected assert m.file_conflicts == ['code/s1.txt'] assert lb.is_repo_clean # Now try to force merge, and changes are taken from the workspace-branch bm.merge_use_ours(bm.workspace_branch) assert open(os.path.join(lb.root_dir, 'code', 's1.txt')).read(1000) == \ 'new-changes-in\nfeature-branch' assert lb.is_repo_clean # Reset this branch call_subprocess(f'git reset --hard {cp}'.split(), cwd=bm.repository.root_dir) bm.merge_use_theirs(bm.workspace_branch) assert open(os.path.join(lb.root_dir, 'code', 's1.txt')).read(1000) == \ 'upstream-changes-from-workspace' assert lb.is_repo_clean
def resolve_is_local(self, info): lb = InventoryManager().load_labbook(get_logged_in_username(), self.owner, self.name) return self.branch_name in BranchManager(lb).branches_local
def test_success_merge_from(self, mock_labbook_lfs_disabled): """ Test merging with nonconflicting changes. """ lb = mock_labbook_lfs_disabled[2] bm = BranchManager(lb, username=TEST_USER) t = 'my-new-example-feature' feature_branch_name = bm.create_branch(title=t) assert bm.active_branch == feature_branch_name bm.workon_branch(bm.workspace_branch) FileOperations.makedir(lb, 'code/sillyfolder', create_activity_record=True) FileOperations.makedir(lb, 'input/newfolder', create_activity_record=True) bm.workon_branch(feature_branch_name) FileOperations.makedir(lb, 'output/otherdir', create_activity_record=True) bm.merge_from(bm.workspace_branch) # Assert repo state is as we expect assert os.path.isdir(os.path.join(lb.root_dir, 'code/sillyfolder')) assert os.path.isdir(os.path.join(lb.root_dir, 'input/newfolder')) assert os.path.isdir(os.path.join(lb.root_dir, 'output/otherdir')) assert lb.is_repo_clean # Return to original branch and check proper state bm.workon_branch(bm.workspace_branch) assert os.path.isdir(os.path.join(lb.root_dir, 'code/sillyfolder')) assert os.path.isdir(os.path.join(lb.root_dir, 'input/newfolder')) assert not os.path.isdir(os.path.join(lb.root_dir, 'output/otherdir')) assert lb.is_repo_clean
def test_link_unlink_dataset_across_branches(self, mock_labbook): """Test to verify linked Dataset initialization works across branching in Projects - Create a project - Create a dataset - Link dataset on master - Switch to another branch - Unlink dataset: dataset is gone - Switch to master: dataset is available - Switch to other branch: dataset is gone - Switch to master: dataset is available """ inv_manager = InventoryManager(mock_labbook[0]) lb = mock_labbook[2] ds = inv_manager.create_dataset("test", "test", "dataset100", "gigantum_object_v1", description="my dataset") # Fake publish to a local bare repo _MOCK_create_remote_repo2(ds, 'test', None, None) assert os.path.exists(os.path.join(lb.root_dir, '.gitmodules')) is False # link dataset and make sure it's there inv_manager.link_dataset_to_labbook(ds.remote, 'test', 'dataset100', lb) assert os.path.exists(os.path.join(lb.root_dir, '.gitmodules')) is True dataset_submodule_dir = os.path.join(lb.root_dir, '.gigantum', 'datasets', 'test', 'dataset100') assert os.path.exists(dataset_submodule_dir) is True assert os.path.exists(os.path.join(dataset_submodule_dir, '.gigantum')) is True # Create a branch bm = BranchManager(lb, username="******") assert bm.active_branch == 'master' branch_name = bm.create_branch(title="test-branch") assert bm.active_branch == branch_name assert lb.is_repo_clean # Dataset still there assert os.path.exists(os.path.join(lb.root_dir, '.gitmodules')) is True dataset_submodule_dir = os.path.join(lb.root_dir, '.gigantum', 'datasets', 'test', 'dataset100') assert os.path.exists(dataset_submodule_dir) is True assert os.path.exists(os.path.join(dataset_submodule_dir, '.gigantum')) is True # Unlink dataset in branch inv_manager.unlink_dataset_from_labbook('test', 'dataset100', lb) # Dataset gone dataset_submodule_dir = os.path.join(lb.root_dir, '.gigantum', 'datasets', 'test', 'dataset100') assert os.path.exists(dataset_submodule_dir) is False assert os.path.exists(os.path.join(dataset_submodule_dir, '.gigantum')) is False with open(os.path.join(lb.root_dir, '.gitmodules'), 'rt') as mf: data = mf.read() assert len(data) == 0 # Switch back to master bm.workon_branch('master') assert bm.active_branch == 'master' assert lb.active_branch == 'master' assert lb.is_repo_clean # Dataset is back! assert os.path.exists(os.path.join(lb.root_dir, '.gitmodules')) is True dataset_submodule_dir = os.path.join(lb.root_dir, '.gigantum', 'datasets', 'test', 'dataset100') assert os.path.exists(dataset_submodule_dir) is True assert os.path.exists(os.path.join(dataset_submodule_dir, '.gigantum')) is True with open(os.path.join(lb.root_dir, '.gitmodules'), 'rt') as mf: data = mf.read() assert len(data) > 0 # Switch back to branch bm.workon_branch('test-branch') assert bm.active_branch == 'test-branch' assert lb.active_branch == 'test-branch' assert lb.is_repo_clean dataset_submodule_dir = os.path.join(lb.root_dir, '.gigantum', 'datasets', 'test', 'dataset100') assert os.path.exists(dataset_submodule_dir) is False assert os.path.exists(os.path.join(dataset_submodule_dir, '.gigantum')) is False with open(os.path.join(lb.root_dir, '.gitmodules'), 'rt') as mf: data = mf.read() assert len(data) == 0 # Switch back to master bm.workon_branch('master') assert bm.active_branch == 'master' assert lb.active_branch == 'master' assert lb.is_repo_clean # Dataset is back! assert os.path.exists(os.path.join(lb.root_dir, '.gitmodules')) is True dataset_submodule_dir = os.path.join(lb.root_dir, '.gigantum', 'datasets', 'test', 'dataset100') assert os.path.exists(dataset_submodule_dir) is True assert os.path.exists(os.path.join(dataset_submodule_dir, '.gigantum')) is True with open(os.path.join(lb.root_dir, '.gitmodules'), 'rt') as mf: data = mf.read() assert len(data) > 0
def resolve_is_active(self, info): lb = InventoryManager().load_labbook(get_logged_in_username(), self.owner, self.name) return BranchManager(lb).active_branch == self.branch_name
def helper_resolve_commits_behind(dataset): """Temporary Helper to get the commits behind for a dataset. Used for linked datasets to see if they are out of date""" bm = BranchManager(dataset) bm.fetch() return bm.get_commits_behind(branch_name='master')
def helper_resolve_active_branch(self, labbook): active_branch_name = BranchManager(labbook, username=get_logged_in_username()).active_branch return LabbookRef(id=f"{self.owner}&{self.name}&None&{active_branch_name}", owner=self.owner, name=self.name, prefix=None, ref_name=active_branch_name)
def resolve_workspace_branch_name(self, info): return info.context.labbook_loader.load(f"{get_logged_in_username()}&{self.owner}&{self.name}").then( lambda labbook: BranchManager(labbook, username=get_logged_in_username()).workspace_branch)
def resolve_remote_branch_names(self, info): fltr = lambda labbook: \ BranchManager(labbook, username=get_logged_in_username()).branches_remote return info.context.labbook_loader.load(f"{get_logged_in_username()}&{self.owner}&{self.name}").then( fltr)
def test_fail_remove_branch_not_exist(self, mock_labbook_lfs_disabled): """ Test remove branch does raises exception when deleting nonexisting branch """ lb = mock_labbook_lfs_disabled[2] bm = BranchManager(lb, username=TEST_USER) with pytest.raises(InvalidBranchName): bm.remove_branch('branch-that-does-not-exist')
def test_fail_remove_branch_on_active_branch(self, mock_labbook_lfs_disabled): """ Test remove branch does raises exception when deleting current branch """ lb = mock_labbook_lfs_disabled[2] bm = BranchManager(lb, username=TEST_USER) with pytest.raises(BranchException): bm.remove_branch(bm.active_branch)
def test_success_init_and_active_branch(self, mock_labbook_lfs_disabled): """ All newly-created repos should be on master branch. """ bm = BranchManager(mock_labbook_lfs_disabled[2], username=TEST_USER) assert bm.active_branch == 'master' assert bm.workspace_branch == 'master'
def test_fail_create_rollback_to_invalid_revision(self, mock_labbook_lfs_disabled): """ Fail when provided with an invalid Git revision """ test_user_lb = mock_labbook_lfs_disabled[2] bm = BranchManager(test_user_lb, username=TEST_USER) with pytest.raises(InvalidBranchName): bm.create_branch('should-fail', revision='invalidrevision')
def test_commits_ahead_behind(self, fixture_working_dir_lfs_disabled): with responses.RequestsMock() as rsps: rsps.add(responses.GET, 'https://usersrv.gigantum.io/key', json={'key': 'afaketoken'}, status=200) config_file, client = fixture_working_dir_lfs_disabled[0], \ fixture_working_dir_lfs_disabled[2] im = InventoryManager(config_file) lb = im.create_labbook(UT_USERNAME, UT_USERNAME, UT_LBNAME, description="tester") bm = BranchManager(lb, username=UT_USERNAME) bm.create_branch('new-branch-1') bm.create_branch('new-branch-2') bm.workon_branch('master') q = f""" {{ labbook(name: "{UT_LBNAME}", owner: "{UT_USERNAME}") {{ branches {{ branchName isLocal isRemote isActive commitsAhead commitsBehind }} }} }} """ r = client.execute(q) assert 'errors' not in r assert len(r['data']['labbook']['branches']) == 3 assert r['data']['labbook']['branches'][0][ 'branchName'] == 'master' assert r['data']['labbook']['branches'][0][ 'isLocal'] is True, "Should be local" assert r['data']['labbook']['branches'][0][ 'isRemote'] is False, "not published yet" assert r['data']['labbook']['branches'][0]['isActive'] is True assert r['data']['labbook']['branches'][0]['commitsAhead'] == 0 assert r['data']['labbook']['branches'][0]['commitsBehind'] == 0 # Make a remote change! username = '******' wf = LabbookWorkflow(lb) wf.publish(username=username) other_user = '******' wf_other = LabbookWorkflow.import_from_remote( remote_url=wf.remote, username=other_user, config_file=lb.client_config.config_file) with open(os.path.join(wf_other.repository.root_dir, 'testfile'), 'w') as f: f.write('filedata') wf_other.repository.sweep_uncommitted_changes() wf_other.sync(username=other_user) r = client.execute(q) assert 'errors' not in r assert len(r['data']['labbook']['branches']) == 3 assert r['data']['labbook']['branches'][0][ 'branchName'] == 'master' assert r['data']['labbook']['branches'][0][ 'isLocal'] is True, "Should be local" assert r['data']['labbook']['branches'][0][ 'isRemote'] is True, "There should be a remote" assert r['data']['labbook']['branches'][0]['isActive'] is True assert r['data']['labbook']['branches'][0]['commitsAhead'] == 0 assert r['data']['labbook']['branches'][0]['commitsBehind'] == 1 # Make a local change! lb.write_readme("blah") r = client.execute(q) assert 'errors' not in r assert len(r['data']['labbook']['branches']) == 3 assert r['data']['labbook']['branches'][0][ 'branchName'] == 'master' assert r['data']['labbook']['branches'][0][ 'isLocal'] is True, "Should be local" assert r['data']['labbook']['branches'][0][ 'isRemote'] is True, "There should be a remote" assert r['data']['labbook']['branches'][0]['isActive'] is True assert r['data']['labbook']['branches'][0]['commitsAhead'] == 1 assert r['data']['labbook']['branches'][0]['commitsBehind'] == 1 # Sync wf.sync(username=username) r = client.execute(q) assert 'errors' not in r assert len(r['data']['labbook']['branches']) == 3 assert r['data']['labbook']['branches'][0][ 'branchName'] == 'master' assert r['data']['labbook']['branches'][0][ 'isLocal'] is True, "Should be local" assert r['data']['labbook']['branches'][0][ 'isRemote'] is True, "There should be a remote" assert r['data']['labbook']['branches'][0]['isActive'] is True assert r['data']['labbook']['branches'][0]['commitsAhead'] == 0 assert r['data']['labbook']['branches'][0]['commitsBehind'] == 0
def test_no_remote_branches_when_no_remote(self, mock_labbook_lfs_disabled): test_user_lb = mock_labbook_lfs_disabled[2] bm = BranchManager(test_user_lb, username=TEST_USER) assert bm.branches_remote == []
def _mergeable(lb): # TODO(billvb) - Refactor for new branch model. username = get_logged_in_username() bm = BranchManager(lb, username=username) return [b for b in bm.branches_local if bm.active_branch != b]
def test_get_commits_with_remote_changes(self, mock_config_file, remote_labbook_repo, mock_labbook_lfs_disabled): # When the branch is up to date, ensure it doesn't report being behind. lb = mock_labbook_lfs_disabled[2] lb.add_remote("origin", remote_labbook_repo) bm = BranchManager(lb, username='******') bm.workon_branch("testing-branch") from gtmcore.inventory.inventory import InventoryManager remote_lb = InventoryManager( mock_config_file[0]).load_labbook_from_directory( remote_labbook_repo) remote_bm = BranchManager(remote_lb, 'test') remote_bm.workon_branch("testing-branch") FileOperations.makedir(remote_lb, 'code/xyzdir', create_activity_record=True) bm.fetch() behind = bm.get_commits_behind() ahead = bm.get_commits_ahead() assert ahead == 0 assert behind == 2
def helper_resolve_branches(self, lb, kwargs): bm = BranchManager(lb) return [Branch(owner=self.owner, name=self.name, branch_name=b) for b in sorted(set(bm.branches_local + bm.branches_remote))]