class TestGitPopenMockupMixin: def setup_git_popen(self): # repository mockup (in a temporary place) self.repository = Repo.init(self.tempdir.name) # setup git command mockup self.Popen = MockPopen() def FixPopen(*a, **k): if 'start_new_session' in k: del k['start_new_session'] return self.Popen.Popen(*a, **k) self.Popen.mock.Popen.side_effect = FixPopen self.Popen.mock.Popen_instance.stdin = None self.Popen.mock.Popen_instance.wait = lambda *a, **k: self.Popen.wait() self.Popen.mock.Popen_instance.__enter__ = lambda self: self self.Popen.mock.Popen_instance.__exit__ = lambda self, *a, **k: None def set_mock_popen_commands(self, cmd_list): for cmd, out, err, rc in cmd_list: self.Popen.set_command(cmd, out, err, returncode=rc) def mockup_git(self, namespace, repository, url=None): # disable refspec check from git import remote remote.Remote._assert_refspec = lambda self: None # write FETCH_HEAD ref with open(os.path.join(self.repository.git_dir, 'FETCH_HEAD'), 'w') as f: url = url or "{}:{}/{}".format(self.service.fqdn, namespace, repository) f.write("749656b8b3b282d11a4221bb84e48291ca23ecc6" \ " branch 'master' of {}".format(url)) return Replace('git.cmd.Popen', self.Popen)
class TestGitPopenMockupMixin: def setup_git_popen(self): # repository mockup (in a temporary place) self.repository = Repo.init(self.tempdir.name) # setup git command mockup self.Popen = MockPopen() self.Popen.mock.Popen_instance.stdin = None self.Popen.mock.Popen_instance.wait = lambda *a, **k: self.Popen.wait() self.Popen.mock.Popen_instance.__enter__ = lambda self: self self.Popen.mock.Popen_instance.__exit__ = lambda self, *a, **k: None def set_mock_popen_commands(self, cmd_list): for cmd, out, err, rc in cmd_list: self.Popen.set_command(cmd, out, err, returncode=rc) def mockup_git(self, namespace, repository, url=None): # disable refspec check from git import remote remote.Remote._assert_refspec = lambda self: None # write FETCH_HEAD ref with open(os.path.join(self.repository.git_dir, 'FETCH_HEAD'), 'w') as f: url = url or "{}:{}/{}".format(self.service.fqdn, namespace, repository) f.write("749656b8b3b282d11a4221bb84e48291ca23ecc6" \ " branch 'master' of {}".format(url)) return Replace('git.cmd.Popen', self.Popen)
class GitRepoTestCase(): def setup_method(self, method): self.log.info('GitRepoTestCase.setup_method({})'.format(method)) # build temporary directory self.tempdir = TemporaryDirectory() # repository mockup (in a temporary place) self.repository = Repo.init(self.tempdir.name) # setup git command mockup self.Popen = MockPopen() self.Popen.mock.Popen_instance.stdin = None self.Popen.mock.Popen_instance.wait = lambda *a, **k: self.Popen.wait() self.Popen.mock.Popen_instance.__enter__ = lambda self: self self.Popen.mock.Popen_instance.__exit__ = lambda self, *a, **k: None # when initiating service with no repository, the connection is not triggered self.service = self.get_service() self.service.repository = self.repository # setup http api mockup self.recorder = betamax.Betamax(self.get_requests_session()) self.get_requests_session().headers['Accept-Encoding'] = 'identity' # have git commands logged Git.GIT_PYTHON_TRACE = True FORMAT = '> %(message)s' formatter = logging.Formatter(fmt=FORMAT) handler = logging.StreamHandler() handler.setFormatter(formatter) logging.getLogger('git.cmd').removeHandler(logging.NullHandler()) logging.getLogger('git.cmd').addHandler(handler) logging.getLogger('git.cmd').propagate = True # have HTTP requests logged import http.client http.client.HTTPConnection.debuglevel = 1 logging.getLogger('requests.packages.urllib3').setLevel(logging.DEBUG) logging.getLogger('requests.packages.urllib3').propagate = True def teardown_method(self, method): self.log.info('GitRepoTestCase.teardown_method({})'.format(method)) self.tempdir.cleanup() '''popen helper''' def set_mock_popen_commands(self, cmd_list): for cmd, out, err, rc in cmd_list: self.Popen.set_command(cmd, out, err, returncode=rc) def mockup_git(self, namespace, repository): # disable refspec check from git import remote remote.Remote._assert_refspec = lambda self: None # write FETCH_HEAD ref with open(os.path.join(self.repository.git_dir, 'FETCH_HEAD'), 'w') as f: f.write("749656b8b3b282d11a4221bb84e48291ca23ecc6" \ " branch 'master' of git@{}/{}/{}".format(self.service.fqdn, namespace, repository)) return Replace('git.cmd.Popen', self.Popen) '''assertion helpers''' def assert_repository_exists(self, namespace, repository): try: self.service.get_repository(namespace, repository) except Exception as err: raise AssertionError("Repository {}/{} not found on {}: {}".format( namespace, repository, self.service.name, err)) from err def assert_repository_not_exists(self, namespace, repository): try: self.service.get_repository(namespace, repository) except Exception as err: return #raise AssertionError("Repository {}/{} exists on {}".format(namespace, # repository, # self.service.name, # )) def assert_added_remote(self, remote): try: self.repository.remote(remote) except ValueError as err: raise AssertionError( "Remote {} not in repository".format(remote)) from err def assert_added_remote_defaults(self): self.assert_added_remote(self.service.name) self.assert_added_remote('all') def assert_tracking_remote(self, remote_name=None, branch_name='master'): if not remote_name: remote_name = self.service.name for branch in self.repository.branches: if branch == branch_name: assert remote_name in self.repository.branches[0].tracking_branch().name, \ 'Could not set "{}" as tracking branch master'.format(self.service.name) '''test cases templates''' def action_fork(self, cassette_name, local_namespace, remote_namespace, repository): # hijack subprocess call with self.mockup_git(local_namespace, repository): # prepare output for git commands remote_slug = self.service.format_path(namespace=remote_namespace, repository=repository, rw=True) local_slug = self.service.format_path(namespace=local_namespace, repository=repository, rw=True) self.set_mock_popen_commands([ ('git remote add upstream {}'.format(remote_slug), b'', b'', 0), ('git remote add all {}'.format(local_slug), b'', b'', 0), ('git remote add {} {}'.format(self.service.name, local_slug), b'', b'', 0), ('git version', b'git version 2.8.0', b'', 0), ('git pull --progress -v {} master'.format( self.service.name ), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, local_namespace, repository), ' * branch master -> FETCH_HEAD', ' * [new branch] master -> {}/master'.format( self.service.name) ]).encode('utf-8'), 0) ]) with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() self.service.fork(remote_namespace, repository, clone=True) def action_fork__no_clone(self, cassette_name, local_namespace, remote_namespace, repository): # hijack subprocess call with self.mockup_git(local_namespace, repository): # prepare output for git commands remote_slug = self.service.format_path(namespace=remote_namespace, repository=repository, rw=True) local_slug = self.service.format_path(namespace=local_namespace, repository=repository, rw=True) self.set_mock_popen_commands([ ('git remote add upstream {}'.format(remote_slug), b'', b'', 0), ('git remote add all {}'.format(local_slug), b'', b'', 0), ('git remote add {} {}'.format(self.service.name, local_slug), b'', b'', 0), ('git version', b'git version 2.8.0', b'', 0), ('git pull --progress -v {} master'.format( self.service.name ), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, local_namespace, repository), ' * branch master -> FETCH_HEAD', ' * [new branch] master -> {}/master'.format( self.service.name) ]).encode('utf-8'), 0) ]) with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() self.service.fork(remote_namespace, repository, clone=False) def action_clone(self, cassette_name, namespace, repository): # hijack subprocess call with self.mockup_git(namespace, repository): local_slug = self.service.format_path(namespace=namespace, repository=repository, rw=True) self.set_mock_popen_commands([ ('git remote add all {}'.format(local_slug), b'', b'', 0), ('git remote add {} {}'.format(self.service.name, local_slug), b'', b'', 0), ('git version', b'git version 2.8.0', b'', 0), ('git pull --progress -v {} master'.format( self.service.name ), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, namespace, repository), ' * branch master -> FETCH_HEAD', ' * [new branch] master -> {}/master'.format( self.service.name) ]).encode('utf-8'), 0) ]) with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() self.service.clone(namespace, repository) def action_create(self, cassette_name, namespace, repository): with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() self.service.create(namespace, repository, add=True) # self.assert_repository_exists(namespace, repository) self.assert_added_remote_defaults() def action_create__no_add(self, cassette_name, namespace, repository): with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() self.service.create(namespace, repository, add=False) # self.assert_repository_exists(namespace, repository) self.assert_added_remote_defaults() def action_delete(self, cassette_name, repository, namespace=None): with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() if namespace: self.service.delete(user=namespace, repo=repository) else: self.service.delete(repo=repository) # if not namespace: namespace = self.service.user self.assert_repository_not_exists(namespace, repository) def action_add(self, cassette_name, namespace, repository, alone=False, name=None, tracking='master'): with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): # init git in the repository's destination self.repository.init() self.service.connect() self.service.add(user=namespace, repo=repository, alone=alone, name=name, tracking=tracking) # if not tracking: if not alone and not name: self.assert_added_remote_defaults() elif not alone and name: self.assert_added_remote(name) self.assert_added_remote('all') elif alone and not name: self.assert_added_remote(self.service.name) elif alone and name: self.assert_added_remote(name) else: if not alone and not name: self.assert_added_remote_defaults() self.assert_tracking_remote() elif not alone and name: self.assert_added_remote(name) self.assert_added_remote('all') self.assert_tracking_remote(name) elif alone and not name: self.assert_added_remote(self.service.name) self.assert_tracking_remote(branch_name=tracking) elif alone and name: self.assert_added_remote(name) self.assert_tracking_remote(name, tracking) def action_request_list(self, cassette_name, namespace, repository, rq_list_data=[]): with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() requests = list( self.service.request_list(user=namespace, repo=repository)) for i, rq in enumerate(rq_list_data): assert requests[i] == rq def action_request_fetch(self, cassette_name, namespace, repository, request, pull=False): with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() self.service.clone(namespace, repository, rw=False) self.service.request_fetch(repository, namespace, request) assert self.repository.branches[-1].name == 'request/{}'.format( request) def action_gist_list(self, cassette_name, gist=None, gist_list_data=[]): with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() if gist is None: gists = list(self.service.gist_list()) for i, g in enumerate(gist_list_data): assert gists[i] == g else: gist_files = list(self.service.gist_list()) for i, gf in enumerate(gist_list_data): assert gist_files[i] == gf def action_gist_clone(self, cassette_name, gist): with self.mockup_git(None, None): self.set_mock_popen_commands([ ('git version', b'git version 2.8.0', b'', 0), ('git remote add gist {}.git'.format(gist), b'', b'', 0), ('git pull --progress -v gist master', b'', b'\n'.join([ b'POST git-upload-pack (140 bytes)', b'remote: Counting objects: 8318, done.', b'remote: Compressing objects: 100% (3/3), done.', b'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', b'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', b'Resolving deltas: 100% (5126/5126), done.', bytes('From {}'.format(gist), 'utf-8'), b' * branch master -> FETCH_HEAD' ]), 0), ]) with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() self.service.gist_clone(gist) def action_gist_fetch(self, cassette_name, gist, gist_file=None): with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() content = self.service.gist_fetch(gist, gist_file) return content def action_gist_create(self, cassette_name, description, gist_files, secret): with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() content = self.service.gist_create(gist_files, description, secret) def action_gist_delete(self, cassette_name, gist): with self.recorder.use_cassette('_'.join( ['test', self.service.name, cassette_name])): self.service.connect() content = self.service.gist_delete(gist) def action_open(self, cassette_name, namespace, repository): self.set_mock_popen_commands([ ('xdg-open {}'.format( self.service.format_path(namespace=namespace, repository=repository)), b'', b'', 0), ('open {}'.format( self.service.format_path(namespace=namespace, repository=repository)), b'', b'', 0), ]) with Replace('subprocess.Popen', self.Popen): self.service.open(user=namespace, repo=repository)
class GitRepoTestCase(): def setup_method(self, method): self.log.info('GitRepoTestCase.setup_method({})'.format(method)) # build temporary directory self.tempdir = TemporaryDirectory() # repository mockup (in a temporary place) self.repository = Repo.init(self.tempdir.name) # setup git command mockup self.Popen = MockPopen() self.Popen.mock.Popen_instance.stdin = None self.Popen.mock.Popen_instance.wait = lambda *a, **k: self.Popen.wait() self.Popen.mock.Popen_instance.__enter__ = lambda self: self self.Popen.mock.Popen_instance.__exit__ = lambda self, *a, **k: None # when initiating service with no repository, the connection is not triggered self.service = self.get_service() self.service.repository = self.repository # setup http api mockup self.recorder = betamax.Betamax(self.get_requests_session()) self.get_requests_session().headers['Accept-Encoding'] = 'identity' # have git commands logged Git.GIT_PYTHON_TRACE = True FORMAT = '> %(message)s' formatter = logging.Formatter(fmt=FORMAT) handler = logging.StreamHandler() handler.setFormatter(formatter) logging.getLogger('git.cmd').removeHandler(logging.NullHandler()) logging.getLogger('git.cmd').addHandler(handler) logging.getLogger('git.cmd').propagate = True # have HTTP requests logged import http.client http.client.HTTPConnection.debuglevel = 1 logging.getLogger('requests.packages.urllib3').setLevel(logging.DEBUG) logging.getLogger('requests.packages.urllib3').propagate = True def teardown_method(self, method): self.log.info('GitRepoTestCase.teardown_method({})'.format(method)) self.tempdir.cleanup() '''popen helper''' def set_mock_popen_commands(self, cmd_list): for cmd, out, err, rc in cmd_list: self.Popen.set_command(cmd, out, err, returncode=rc) def mockup_git(self, namespace, repository): # disable refspec check from git import remote remote.Remote._assert_refspec = lambda self: None # write FETCH_HEAD ref with open(os.path.join(self.repository.git_dir, 'FETCH_HEAD'), 'w') as f: f.write("749656b8b3b282d11a4221bb84e48291ca23ecc6" \ " branch 'master' of git@{}/{}/{}".format(self.service.fqdn, namespace, repository)) return Replace('git.cmd.Popen', self.Popen) '''assertion helpers''' def assert_repository_exists(self, namespace, repository): try: self.service.get_repository(namespace, repository) except Exception as err: raise AssertionError("Repository {}/{} not found on {}: {}".format(namespace, repository, self.service.name, err)) from err def assert_repository_not_exists(self, namespace, repository): try: self.service.get_repository(namespace, repository) except Exception as err: return #raise AssertionError("Repository {}/{} exists on {}".format(namespace, # repository, # self.service.name, # )) def assert_added_remote(self, remote): try: self.repository.remote(remote) except ValueError as err: raise AssertionError("Remote {} not in repository".format(remote)) from err def assert_added_remote_defaults(self): self.assert_added_remote(self.service.name) self.assert_added_remote('all') def assert_tracking_remote(self, remote_name=None, branch_name='master'): if not remote_name: remote_name = self.service.name for branch in self.repository.branches: if branch == branch_name: assert remote_name in self.repository.branches[0].tracking_branch().name, \ 'Could not set "{}" as tracking branch master'.format(self.service.name) '''test cases templates''' def action_fork(self, cassette_name, local_namespace, remote_namespace, repository): # hijack subprocess call with self.mockup_git(local_namespace, repository): # prepare output for git commands remote_slug = self.service.format_path(namespace=remote_namespace, repository=repository, rw=True) local_slug = self.service.format_path(namespace=local_namespace, repository=repository, rw=True) self.set_mock_popen_commands([ ('git remote add upstream {}'.format(remote_slug), b'', b'', 0), ('git remote add all {}'.format(local_slug), b'', b'', 0), ('git remote add {} {}'.format(self.service.name, local_slug), b'', b'', 0), ('git version', b'git version 2.8.0', b'', 0), ('git pull --progress -v {} master'.format(self.service.name), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, local_namespace, repository), ' * branch master -> FETCH_HEAD', ' * [new branch] master -> {}/master'.format(self.service.name)]).encode('utf-8'), 0) ]) with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() self.service.fork(remote_namespace, repository) # emulate the outcome of the git actions self.service.repository.create_remote('upstream', url=remote_slug) self.service.repository.create_remote('all', url=local_slug) self.service.repository.create_remote(self.service.name, url=local_slug) def action_fork__no_clone(self, cassette_name, local_namespace, remote_namespace, repository): # hijack subprocess call with self.mockup_git(local_namespace, repository): # prepare output for git commands remote_slug = self.service.format_path(namespace=remote_namespace, repository=repository, rw=True) local_slug = self.service.format_path(namespace=local_namespace, repository=repository, rw=True) self.set_mock_popen_commands([ ('git remote add upstream {}'.format(remote_slug), b'', b'', 0), ('git remote add all {}'.format(local_slug), b'', b'', 0), ('git remote add {} {}'.format(self.service.name, local_slug), b'', b'', 0), ('git version', b'git version 2.8.0', b'', 0), ('git pull --progress -v {} master'.format(self.service.name), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, local_namespace, repository), ' * branch master -> FETCH_HEAD', ' * [new branch] master -> {}/master'.format(self.service.name)]).encode('utf-8'), 0) ]) with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() self.service.fork(remote_namespace, repository) # emulate the outcome of the git actions self.service.repository.create_remote('upstream', url=remote_slug) self.service.repository.create_remote('all', url=local_slug) self.service.repository.create_remote(self.service.name, url=local_slug) def action_clone(self, cassette_name, namespace, repository): # hijack subprocess call with self.mockup_git(namespace, repository): local_slug = self.service.format_path(namespace=namespace, repository=repository, rw=True) self.set_mock_popen_commands([ ('git remote add all {}'.format(local_slug), b'', b'', 0), ('git remote add {} {}'.format(self.service.name, local_slug), b'', b'', 0), ('git version', b'git version 2.8.0', b'', 0), ('git pull --progress -v {} master'.format(self.service.name), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, namespace, repository), ' * branch master -> FETCH_HEAD', ' * [new branch] master -> {}/master'.format(self.service.name)]).encode('utf-8'), 0) ]) with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() self.service.clone(namespace, repository) self.service.repository.create_remote('all', url=local_slug) self.service.repository.create_remote(self.service.name, url=local_slug) def action_create(self, cassette_name, namespace, repository): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() self.service.create(namespace, repository, add=True) # self.assert_repository_exists(namespace, repository) self.assert_added_remote_defaults() def action_create__no_add(self, cassette_name, namespace, repository): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() self.service.create(namespace, repository, add=False) # self.assert_repository_exists(namespace, repository) self.assert_added_remote_defaults() def action_delete(self, cassette_name, repository, namespace=None): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() if namespace: self.service.delete(user=namespace, repo=repository) else: self.service.delete(repo=repository) # if not namespace: namespace = self.service.user self.assert_repository_not_exists(namespace, repository) def action_add(self, cassette_name, namespace, repository, alone=False, name=None, tracking='master'): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): # init git in the repository's destination self.repository.init() self.service.connect() self.service.add(user=namespace, repo=repository, alone=alone, name=name, tracking=tracking) # if not tracking: if not alone and not name: self.assert_added_remote_defaults() elif not alone and name: self.assert_added_remote(name) self.assert_added_remote('all') elif alone and not name: self.assert_added_remote(self.service.name) elif alone and name: self.assert_added_remote(name) else: if not alone and not name: self.assert_added_remote_defaults() self.assert_tracking_remote() elif not alone and name: self.assert_added_remote(name) self.assert_added_remote('all') self.assert_tracking_remote(name) elif alone and not name: self.assert_added_remote(self.service.name) self.assert_tracking_remote(branch_name=tracking) elif alone and name: self.assert_added_remote(name) self.assert_tracking_remote(name, tracking) def action_request_list(self, cassette_name, namespace, repository, rq_list_data=[]): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() requests = list(self.service.request_list(user=namespace, repo=repository)) for i, rq in enumerate(rq_list_data): assert requests[i] == rq def action_request_fetch(self, cassette_name, namespace, repository, request, pull=False, fail=False): local_slug = self.service.format_path(namespace=namespace, repository=repository, rw=False) with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): with self.mockup_git(namespace, repository): self.set_mock_popen_commands([ ('git remote add all {}'.format(local_slug), b'', b'', 0), ('git remote add {} {}'.format(self.service.name, local_slug), b'', b'', 0), ('git version', b'git version 2.8.0', b'', 0), ('git pull --progress -v {} master'.format(self.service.name), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, namespace, repository), ' * branch master -> FETCH_HEAD', ' * [new branch] master -> {}/master'.format(self.service.name)]).encode('utf-8'), 0), ('git version', b'git version 2.8.0', b'', 0), ('git fetch --progress -v {0} pull/{1}/head:request/{1}'.format(self.service.name, request), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, namespace, repository), ' * [new branch] master -> request/{}'.format(request)]).encode('utf-8'), 0) ]) self.service.connect() self.service.clone(namespace, repository, rw=False) if not fail: self.service.repository.create_remote('all', url=local_slug) self.service.repository.create_remote(self.service.name, url=local_slug) with self.mockup_git(namespace, repository): self.set_mock_popen_commands([ ('git version', b'git version 2.8.0', b'', 0), ('git fetch --progress -v {0} pull/{1}/head:request/{1}'.format(self.service.name, request), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, namespace, repository), ' * [new branch] master -> request/{}'.format(request)]).encode('utf-8'), 0) ]) self.service.request_fetch(repository, namespace, request) def action_request_create(self, cassette_name, namespace, repository, branch, title, description, create_repository='test_create_requests', create_branch='pr-test'): ''' Here we are testing the subcommand 'request create'. this test needs sensibly more preparation than other tests, because to create a pull request, you need: * a repository with commits on both the service and your workspace * a new branch with new commits, that has been pushed on the service So that's what we're doing below: * create a test project on the service, * populate the temporary git repository with it * create a commit and push it to the service as master * create a branch in the workspace * create a commit and push it to the service as pr-test Then we test the feature: * using the branch create a pull request and check the pull request is there Finally clean the remote repository So all the contextual work is only done ''' cassette_name = '_'.join(['test', self.service.name, cassette_name]) will_record = 'never' != self.recorder.config.default_cassette_options['record_mode'] \ and not os.path.exists(os.path.join(self.recorder.config.cassette_library_dir, cassette_name+'.json')) @contextmanager def prepare_project_for_test(): if will_record: self.service.connect() # let's create a project and add it to current repository self.service.create(namespace, create_repository, add=True) # make a modification, commit and push it with open(os.path.join(self.repository.working_dir, 'first_file'), 'w') as test: test.write('he who makes a beast of himself gets rid of the pain of being a man. Dr Johnson') self.repository.git.add('first_file') self.repository.git.commit(message='First commit') self.repository.git.push(self.service.name, 'master') # create a new branch new_branch = self.repository.create_head(create_branch, 'HEAD') self.repository.head.reference = new_branch self.repository.head.reset(index=True, working_tree=True) # make a modification, commit and push it to that branch with open(os.path.join(self.repository.working_dir, 'second_file'), 'w') as test: test.write('La meilleure façon de ne pas avancer est de suivre une idée fixe. J.Prévert') self.repository.git.add('second_file') self.repository.git.commit(message='Second commit') self.repository.git.push('github', create_branch) yield if will_record: self.service.delete(create_repository) #self.service.repository = self.repository with prepare_project_for_test(): with self.recorder.use_cassette(cassette_name): self.service.connect() request = self.service.request_create( namespace, repository, branch, title, description ) return request def action_gist_list(self, cassette_name, gist=None, gist_list_data=[]): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() if gist is None: gists = list(self.service.gist_list()) for i, g in enumerate(gist_list_data): assert gists[i] == g else: gist_files = list(self.service.gist_list()) for i, gf in enumerate(gist_list_data): assert gist_files[i] == gf def action_gist_clone(self, cassette_name, gist): with self.mockup_git(None, None): self.set_mock_popen_commands([ ('git version', b'git version 2.8.0', b'', 0), ('git remote add gist {}.git'.format(gist), b'', b'', 0), ('git pull --progress -v gist master', b'', b'\n'.join([ b'POST git-upload-pack (140 bytes)', b'remote: Counting objects: 8318, done.', b'remote: Compressing objects: 100% (3/3), done.', b'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', b'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', b'Resolving deltas: 100% (5126/5126), done.', bytes('From {}'.format(gist), 'utf-8'), b' * branch master -> FETCH_HEAD']), 0), ]) with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() self.service.gist_clone(gist) def action_gist_fetch(self, cassette_name, gist, gist_file=None): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() content = self.service.gist_fetch(gist, gist_file) return content def action_gist_create(self, cassette_name, description, gist_files, secret): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() content = self.service.gist_create(gist_files, description, secret) def action_gist_delete(self, cassette_name, gist): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() content = self.service.gist_delete(gist) def action_open(self, cassette_name, namespace, repository): self.set_mock_popen_commands([ ('xdg-open {}'.format(self.service.format_path(namespace=namespace, repository=repository)), b'', b'', 0), ('open {}'.format(self.service.format_path(namespace=namespace, repository=repository)), b'', b'', 0), ]) with Replace('subprocess.Popen', self.Popen): self.service.open(user=namespace, repo=repository)
class GitRepoTestCase(TestCase): def setUp(self): self.log.info('GitRepoTestCase') # build temporary directory self.tempdir = TemporaryDirectory() self.addCleanup(self.tempdir.cleanup) # repository mockup (in a temporary place) self.repository = Repo.init(self.tempdir.name) # setup git command mockup self.Popen = MockPopen() self.Popen.mock.Popen_instance.stdin = None self.Popen.mock.Popen_instance.wait = lambda *a, **k: self.Popen.wait() self.Popen.mock.Popen_instance.__enter__ = lambda self: self self.Popen.mock.Popen_instance.__exit__ = lambda self, *a, **k: None # when initiating service with no repository, the connection is not triggered self.service = self.get_service() self.service.repository = self.repository # setup http api mockup self.recorder = betamax.Betamax(self.get_requests_session()) # have git commands logged Git.GIT_PYTHON_TRACE = True FORMAT = '> %(message)s' formatter = logging.Formatter(fmt=FORMAT) handler = logging.StreamHandler() handler.setFormatter(formatter) logging.getLogger('git.cmd').removeHandler(logging.NullHandler()) logging.getLogger('git.cmd').addHandler(handler) logging.getLogger('git.cmd').propagate = True # have HTTP requests logged import http.client http.client.HTTPConnection.debuglevel = 1 logging.getLogger('requests.packages.urllib3').setLevel(logging.DEBUG) logging.getLogger('requests.packages.urllib3').propagate = True '''popen helper''' def set_mock_popen_commands(self, cmd_list): for cmd, out, err, rc in cmd_list: self.Popen.set_command(cmd, out, err, returncode=rc) def mockup_git(self, namespace, repository): # disable refspec check from git import remote remote.Remote._assert_refspec = lambda self: None # write FETCH_HEAD ref with open(os.path.join(self.repository.git_dir, 'FETCH_HEAD'), 'w') as f: f.write("749656b8b3b282d11a4221bb84e48291ca23ecc6" \ " branch 'master' of git@{}/{}/{}".format(self.service.fqdn, namespace, repository)) return Replace('git.cmd.Popen', self.Popen) '''assertion helpers''' def assert_repository_exists(self, namespace, repository): try: self.service.get_repository(namespace, repository) except Exception as err: raise AssertionError("Repository {}/{} not found on {}: {}".format(namespace, repository, self.service.name, err)) from err def assert_repository_not_exists(self, namespace, repository): try: self.service.get_repository(namespace, repository) except Exception as err: return #raise AssertionError("Repository {}/{} exists on {}".format(namespace, # repository, # self.service.name, # )) def assert_added_remote(self, remote): try: self.repository.remote(remote) except ValueError as err: raise AssertionError("Remote {} not in repository".format(remote)) from err def assert_added_remote_defaults(self): self.assert_added_remote(self.service.name) self.assert_added_remote('all') def assert_tracking_remote(self, remote_name=None, branch_name='master'): if not remote_name: remote_name = self.service.name for branch in self.repository.branches: if branch == branch_name: assert remote_name in self.repository.branches[0].tracking_branch().name, \ 'Could not set "{}" as tracking branch master'.format(self.service.name) '''test cases templates''' def action_fork(self, cassette_name, local_namespace, remote_namespace, repository): # hijack subprocess call with self.mockup_git(local_namespace, repository): # prepare output for git commands remote_slug = self.service.format_path(namespace=remote_namespace, repository=repository, rw=True) local_slug = self.service.format_path(namespace=local_namespace, repository=repository, rw=True) self.set_mock_popen_commands([ ('git remote add upstream {}'.format(remote_slug), b'', b'', 0), ('git remote add all {}'.format(local_slug), b'', b'', 0), ('git remote add {} {}'.format(self.service.name, local_slug), b'', b'', 0), ('git pull -v {} master'.format(self.service.name), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, local_namespace, repository), ' * branch master -> FETCH_HEAD', ' * [new branch] master -> {}/master'.format(self.service.name)]).encode('utf-8'), 0) ]) with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() self.service.fork(remote_namespace, repository, clone=True) def action_fork__no_clone(self, cassette_name, local_namespace, remote_namespace, repository): # hijack subprocess call with self.mockup_git(local_namespace, repository): # prepare output for git commands remote_slug = self.service.format_path(namespace=remote_namespace, repository=repository, rw=True) local_slug = self.service.format_path(namespace=local_namespace, repository=repository, rw=True) self.set_mock_popen_commands([ ('git remote add upstream {}'.format(remote_slug), b'', b'', 0), ('git remote add all {}'.format(local_slug), b'', b'', 0), ('git remote add {} {}'.format(self.service.name, local_slug), b'', b'', 0), ('git pull -v {} master'.format(self.service.name), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, local_namespace, repository), ' * branch master -> FETCH_HEAD', ' * [new branch] master -> {}/master'.format(self.service.name)]).encode('utf-8'), 0) ]) with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() self.service.fork(remote_namespace, repository, clone=False) def action_clone(self, cassette_name, namespace, repository): # hijack subprocess call with self.mockup_git(namespace, repository): local_slug = self.service.format_path(namespace=namespace, repository=repository, rw=True) self.set_mock_popen_commands([ ('git remote add all {}'.format(local_slug), b'', b'', 0), ('git remote add {} {}'.format(self.service.name, local_slug), b'', b'', 0), ('git pull -v {} master'.format(self.service.name), b'', '\n'.join([ 'POST git-upload-pack (140 bytes)', 'remote: Counting objects: 8318, done.', 'remote: Compressing objects: 100% (3/3), done.', 'remote: Total 8318 (delta 0), reused 0 (delta 0), pack-reused 8315', 'Receiving objects: 100% (8318/8318), 3.59 MiB | 974.00 KiB/s, done.', 'Resolving deltas: 100% (5126/5126), done.', 'From {}:{}/{}'.format(self.service.fqdn, namespace, repository), ' * branch master -> FETCH_HEAD', ' * [new branch] master -> {}/master'.format(self.service.name)]).encode('utf-8'), 0) ]) with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() self.service.clone(namespace, repository) def action_create(self, cassette_name, namespace, repository): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() self.service.create(namespace, repository, add=True) # self.assert_repository_exists(namespace, repository) self.assert_added_remote_defaults() def action_create__no_add(self, cassette_name, namespace, repository): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() self.service.create(namespace, repository, add=False) # self.assert_repository_exists(namespace, repository) self.assert_added_remote_defaults() def action_delete(self, cassette_name, repository, namespace=None): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): self.service.connect() if namespace: self.service.delete(user=namespace, repo=repository) else: self.service.delete(repo=repository) # if not namespace: namespace = self.service.user self.assert_repository_not_exists(namespace, repository) def action_add(self, cassette_name, namespace, repository, alone=False, name=None, tracking='master'): with self.recorder.use_cassette('_'.join(['test', self.service.name, cassette_name])): # init git in the repository's destination self.repository.init() self.service.connect() self.service.add(user=namespace, repo=repository, alone=alone, name=name, tracking=tracking) # if not tracking: if not alone and not name: self.assert_added_remote_defaults() elif not alone and name: self.assert_added_remote(name) self.assert_added_remote('all') elif alone and not name: self.assert_added_remote(self.service.name) elif alone and name: self.assert_added_remote(name) else: if not alone and not name: self.assert_added_remote_defaults() self.assert_tracking_remote() elif not alone and name: self.assert_added_remote(name) self.assert_added_remote('all') self.assert_tracking_remote(name) elif alone and not name: self.assert_added_remote(self.service.name) self.assert_tracking_remote(branch_name=tracking) elif alone and name: self.assert_added_remote(name) self.assert_tracking_remote(name, tracking) def action_open(self, cassette_name, namespace, repository): self.set_mock_popen_commands([ ('xdg-open {}'.format(self.service.format_path(namespace=namespace, repository=repository)), b'', b'', 0), ('open {}'.format(self.service.format_path(namespace=namespace, repository=repository)), b'', b'', 0), ]) with Replace('subprocess.Popen', self.Popen): self.service.open(user=namespace, repo=repository)