def test_failed_review_status__job_config(self): config = build_review_config(review_ini, app_config) self.assertEqual('success', config.failed_review_status()) ini = "[review]\nfail_on_comments = true" config = build_review_config(ini, app_config) self.assertEqual('failure', config.failed_review_status())
def test_publish_pull_review_remove_ok_label(self): problems = Problems() filename_1 = 'Console/Command/Task/AssetBuildTask.php' errors = ( Comment(filename_1, 117, 117, 'Something bad'), Comment(filename_1, 119, 119, 'Something bad'), ) problems.add_many(errors) tst_config = build_review_config(fixer_ini, {'OK_LABEL': 'No lint'}) review = Review(self.repo, self.pr, tst_config) sha = 'abc123' review.publish_pull_review(problems, sha) assert self.pr.remove_label.called, 'Label should be removed' assert self.pr.create_review.called, 'Review should be added' self.assertEqual(1, self.pr.create_review.call_count) self.pr.remove_label.assert_called_with(tst_config['OK_LABEL']) assert_review( self, self.pr.create_review.call_args, errors, sha)
def test_run_tools__no_changes(self): pull = self.get_pull_request() repo = Mock() config = build_review_config('', app_config) subject = Processor(repo, pull, './tests', config) subject.run_tools()
def test_run_tools__execute_fixers(self, fixer_stub, tool_stub): pull = self.get_pull_request() repo = Mock() tool_stub.factory.return_value = sentinel.tools fixer_stub.create_context.return_value = sentinel.context fixer_stub.run_fixers.return_value = sentinel.diff config = build_review_config(fixer_ini, app_config) subject = Processor(repo, pull, './tests', config) subject.load_changes() subject.run_tools() file_path = 'View/Helper/AssetCompressHelper.php' fixer_stub.create_context.assert_called_with( config, './tests', repo, pull) fixer_stub.run_fixers.assert_called_with( sentinel.tools, './tests', [file_path]) fixer_stub.apply_fixer_diff.assert_called_with( subject._changes, sentinel.diff, sentinel.context) assert tool_stub.run.called, 'Should have ran'
def test_publish_checkrun(self): self.repo.create_checkrun = Mock() config = build_review_config(fixer_ini, {'PULLREQUEST_STATUS': True}) problems = Problems() filename_1 = 'Console/Command/Task/AssetBuildTask.php' errors = ( Comment(filename_1, 117, 8, 'Something bad'), Comment(filename_1, 119, 9, 'Something worse'), ) problems.add_many(errors) run_id = 42 review = Review(self.repo, self.pr, config) review.publish_checkrun(problems, run_id) assert self.repo.update_checkrun.called eq_(1, self.repo.update_checkrun.call_count) assert_checkrun( self.repo.update_checkrun.call_args, errors, run_id) assert self.repo.create_status.called is False, 'no status required'
def test_run(): config = build_review_config(simple_ini) problems = Problems() files = ['./tests/fixtures/pep8/has_errors.py'] tool_list = tools.factory(config, problems, root_dir) tools.run(tool_list, files, []) eq_(7, len(problems))
def test_publish_review_no_count_change(self): fixture = load_fixture('comments_current.json') self.pr.review_comments.return_value = [ GhIssueComment(f) for f in json.loads(fixture) ] problems = Problems() # Match the line/positions in comments_current.json filename_1 = 'Console/Command/Task/AssetBuildTask.php' errors = ( Comment(filename_1, 40, 40, '2. Something bad'), Comment(filename_1, 87, 87, '1. Something bad'), Comment(filename_1, 89, 89, '2. Something bad'), ) problems.add_many(errors) problems.set_changes([1]) sha = 'abc123' config = build_review_config(fixer_ini, {'SUMMARY_THRESHOLD': 1}) review = Review(self.repo, self.pr, config) review.publish_summary = Mock() review.publish_status = Mock() review.publish_review(problems, sha) # Ensure publish_status(True) means the status=failed review.publish_status.assert_called_with(True)
def test_run_tools__execute_fixers(self): pull = self.get_pull_request() repo = Mock() self.tool_stub.factory.return_value = sentinel.tools self.fixer_stub.create_context.return_value = sentinel.context self.fixer_stub.run_fixers.return_value = sentinel.diff config = build_review_config(fixer_ini, app_config) subject = Processor(repo, pull, './tests', config) subject.load_changes() subject.run_tools() file_path = 'View/Helper/AssetCompressHelper.php' self.fixer_stub.create_context.assert_called_with( config, './tests', repo, pull ) self.fixer_stub.run_fixers.assert_called_with( sentinel.tools, './tests', [file_path] ) self.fixer_stub.apply_fixer_diff.assert_called_with( subject._changes, sentinel.diff, sentinel.context ) self.tool_stub.run.assert_called()
def test_factory_generates_tools(): gh = Mock(spec=github3.GitHub) config = build_review_config(sample_ini) linters = tools.factory(Review(gh, None), config, '') eq_(2, len(linters)) assert isinstance(linters[0], tools.pep8.Pep8) assert isinstance(linters[1], tools.jshint.Jshint)
def _test_run_tools_fixer_error_scenario(self, error): pull = self.get_pull_request() repo = Mock() self.tool_stub.factory.return_value = sentinel.tools self.fixer_stub.create_context.return_value = sentinel.context self.fixer_stub.apply_fixer_diff.side_effect = error config = build_review_config(fixer_ini, app_config) subject = Processor(repo, pull, './tests', config) subject.load_changes() subject.run_tools() self.fixer_stub.create_context.assert_called() self.fixer_stub.run_fixers.assert_called() self.tool_stub.run.assert_called() self.fixer_stub.rollback_changes.assert_called_with( './tests', pull.head) assert 1 == len(subject.problems), 'strategy error adds pull comment' assert 0 == subject.problems.error_count( ), 'fixer failure should be info level' assert 'Unable to apply fixers. ' + str(error) == subject.problems.all( )[0].body assert 1 == len(subject.problems), 'strategy error adds pull comment'
def test_create_context__missing_key_raises(): config = build_review_config(fixer_ini) with assert_raises(KeyError): empty = {} fixers.create_context( config, empty, clone_path, sentinel.repo, sentinel.pull_request)
def test_factory_generates_tools(self): gh = Mock(spec=github3.GitHub) config = build_review_config(sample_ini) linters = tools.factory(config, Review(gh, None, config), '') self.assertEqual(2, len(linters)) self.assertIsInstance(linters[0], pep8.Pep8) self.assertIsInstance(linters[1], jshint.Jshint)
def test_publish_pull_review_remove_ok_label(self): problems = Problems() filename_1 = 'Console/Command/Task/AssetBuildTask.php' errors = ( Comment(filename_1, 117, 117, 'Something bad'), Comment(filename_1, 119, 119, 'Something bad'), ) problems.add_many(errors) sha = 'abc123' config = build_review_config(fixer_ini, {'OK_LABEL': 'No lint'}) review = Review(self.repo, self.pr, config) sha = 'abc123' review.publish_pull_review(problems, sha) assert self.pr.remove_label.called, 'Label should be removed' assert self.pr.create_review.called, 'Review should be added' eq_(1, self.pr.create_review.call_count) self.pr.remove_label.assert_called_with(config['OK_LABEL']) assert_review( self.pr.create_review.call_args, errors, sha)
def test_run(self): config = build_review_config(simple_ini) problems = Problems() files = ['./tests/fixtures/pep8/has_errors.py'] tool_list = tools.factory(config, problems, root_dir) tools.run(tool_list, files, []) self.assertEqual(7, len(problems))
def test_publish_checkrun(self): self.repo.create_checkrun = Mock() tst_config = build_review_config(fixer_ini, {'PULLREQUEST_STATUS': True}) problems = Problems() filename_1 = 'Console/Command/Task/AssetBuildTask.php' errors = ( Comment(filename_1, 117, 8, 'Something bad'), Comment(filename_1, 119, 9, 'Something worse'), ) problems.add_many(errors) run_id = 42 review = Review(self.repo, self.pr, tst_config) review.publish_checkrun(problems, run_id) assert self.repo.update_checkrun.called self.assertEqual(1, self.repo.update_checkrun.call_count) assert_checkrun( self, self.repo.update_checkrun.call_args, errors, run_id) assert self.repo.create_status.called is False, 'no status required'
def process_pull_request(self, user, repo_name, number, lintrc): """ Starts processing a pull request and running the various lint tools against it. """ log.info('Starting to process lint for %s/%s/%s', user, repo_name, number) log.debug("lintrc contents '%s'", lintrc) review_config = build_review_config(lintrc, deepcopy(config)) if len(review_config.linters()) == 0: log.info('No configured linters, skipping processing.') return try: log.info( 'Loading pull request data from github. user=%s ' 'repo=%s number=%s', user, repo_name, number) repo = GithubRepository(config, user, repo_name) pull_request = repo.pull_request(number) clone_url = pull_request.clone_url pr_head = pull_request.head target_branch = pull_request.target_branch if target_branch in review_config.ignore_branches(): log.info('Pull request into ignored branch %s, skipping review.', target_branch) return repo.create_status(pr_head, 'pending', 'Lintreview processing') # Clone/Update repository target_path = git.get_repo_path(user, repo_name, number, config) git.clone_or_update(config, clone_url, target_path, pr_head) processor = Processor(repo, pull_request, target_path, review_config) processor.load_changes() processor.run_tools() processor.publish() log.info('Completed lint processing for %s/%s/%s' % (user, repo_name, number)) except Exception as e: log.exception(e) except TimeoutError as e: log.exception(e) raise self.retry( countdown=5, # Pause for 5 seconds to clear things out max_retries=2, # only give it one more shot ) finally: try: git.destroy(target_path) log.info('Cleaned up pull request %s/%s/%s', user, repo_name, number) except Exception as e: log.exception(e)
def test_run_tools__no_changes(self): pull = self.get_pull_request() repo = Mock() config = build_review_config('', app_config) subject = Processor(repo, pull, './tests', config) self.assertRaises(RuntimeError, subject.run_tools)
def test_create_context__missing_key_raises(self): config = build_review_config(fixer_ini) self.assertRaises(KeyError, fixers.create_context, config, clone_path, sentinel.repo, sentinel.pull_request)
def process_pull_request(self, user, repo_name, number, lintrc): """ Starts processing a pull request and running the various lint tools against it. """ log.info('Starting to process lint for %s/%s/%s', user, repo_name, number) log.debug("lintrc contents '%s'", lintrc) review_config = build_review_config(lintrc, deepcopy(config)) if len(review_config.linters()) == 0: log.info('No configured linters, skipping processing.') return try: log.info('Loading pull request data from github. user=%s ' 'repo=%s number=%s', user, repo_name, number) repo = GithubRepository(config, user, repo_name) pull_request = repo.pull_request(number) clone_url = pull_request.clone_url pr_head = pull_request.head target_branch = pull_request.target_branch if target_branch in review_config.ignore_branches(): log.info('Pull request into ignored branch %s, skipping review.', target_branch) return repo.create_status(pr_head, 'pending', 'Lintreview processing') # Clone/Update repository target_path = git.get_repo_path(user, repo_name, number, config) git.clone_or_update(config, clone_url, target_path, pr_head) processor = Processor(repo, pull_request, target_path, review_config) processor.load_changes() processor.run_tools() processor.publish() log.info('Completed lint processing for %s/%s/%s' % ( user, repo_name, number)) except Exception as e: log.exception(e) except TimeoutError as e: log.exception(e) raise self.retry( countdown=5, # Pause for 5 seconds to clear things out max_retries=2, # only give it one more shot ) finally: try: git.destroy(target_path) log.info('Cleaned up pull request %s/%s/%s', user, repo_name, number) except Exception as e: log.exception(e)
def test_run__filter_files(): config = build_review_config(simple_ini) problems = Problems() files = [ './tests/fixtures/pep8/has_errors.py', './tests/fixtures/phpcs/has_errors.php' ] tools.run(config, problems, files, [], '') eq_(6, len(problems))
def test_run__filter_files(self): config = build_review_config(simple_ini) problems = Problems() files = [ './tests/fixtures/pep8/has_errors.py', './tests/fixtures/phpcs/has_errors.php' ] tool_list = tools.factory(config, problems, root_dir) tools.run(tool_list, files, []) self.assertEqual(7, len(problems))
def test_load_changes(self): pull = self.get_pull_request() repo = Mock() config = build_review_config('', app_config) subject = Processor(repo, pull, './tests', config) subject.load_changes() eq_(1, len(subject._changes), 'File count is wrong') assert isinstance(subject._changes, DiffCollection)
def setUp(self): repo = Mock(spec=GithubRepository) pr = Mock(spec=GithubPullRequest, head='abc123', display_name='markstory/lint-review#1', number=2) repo.pull_request.return_value = pr self.repo, self.pr = repo, pr self.config = build_review_config(fixer_ini, config)
def test_load_changes(self): pull = self.get_pull_request() repo = Mock() config = build_review_config('', app_config) subject = Processor(repo, pull, './tests', config) subject.load_changes() self.assertEqual(1, len(subject._changes), 'File count is wrong') assert isinstance(subject._changes, DiffCollection)
def test_create_context(): config = build_review_config(fixer_ini, app_config) context = fixers.create_context(config, clone_path, sentinel.repo, sentinel.pull_request) eq_('commit', context['strategy']) eq_(config['GITHUB_AUTHOR_EMAIL'], context['author_email']) eq_(config['GITHUB_AUTHOR_NAME'], context['author_name']) eq_(clone_path, context['repo_path']) eq_(sentinel.repo, context['repository']) eq_(sentinel.pull_request, context['pull_request'])
def test_run_tools__ignore_patterns(self, fixer_stub, tool_stub): pull = self.get_pull_request() repo = Mock() config = build_review_config(fixer_ini, app_config) config.ignore_patterns = lambda: ['View/Helper/*'] subject = Processor(repo, pull, './tests', config) subject.load_changes() subject.run_tools() tool_stub.run.assert_called_with(ANY, [], ANY)
def test_linter_config(self): config = build_review_config(sample_ini) res = config.linter_config('phpcs') expected = { 'standard': 'test/CodeStandards', 'config': 'test/phpcs.xml' } eq_(res, expected) res = config.linter_config('not there') eq_(res, {})
def setUp(self): repo = Mock(spec=GithubRepository) pr = Mock(spec=GithubPullRequest, head='abc123', display_name='markstory/lint-review#1', number=2) repo.pull_request.return_value = pr self.repo, self.pr = repo, pr self.config = build_review_config(fixer_ini, config) self.session = GitHubSession()
def test_run_timeout_error(self, mock_docker): mock_docker.side_effect = TimeoutError("Read timed out. (read timeout=300)") config = build_review_config(simple_ini) problems = Problems() files = ['./tests/fixtures/pep8/has_errors.py'] tool_list = tools.factory(config, problems, root_dir) tools.run(tool_list, files, []) errors = problems.all() assert 1 == len(errors) assert 'timed out during' in errors[0].body assert 'run pep8 linter' in errors[0].body
def test_run_timeout_error(self, mock_docker): mock_docker.side_effect = TimeoutError( "Read timed out. (read timeout=300)") config = build_review_config(simple_ini) problems = Problems() files = ['./tests/fixtures/pep8/has_errors.py'] tool_list = tools.factory(config, problems, root_dir) tools.run(tool_list, files, []) errors = problems.all() assert 1 == len(errors) assert 'timed out during' in errors[0].body assert 'run pep8 linter' in errors[0].body
def test_create_context(self): config = build_review_config(fixer_ini, app_config) context = fixers.create_context( config, clone_path, sentinel.repo, sentinel.pull_request) self.assertEqual('commit', context['strategy']) self.assertEqual(config['GITHUB_AUTHOR_EMAIL'], context['author_email']) self.assertEqual(config['GITHUB_AUTHOR_NAME'], context['author_name']) self.assertEqual(clone_path, context['repo_path']) self.assertEqual(sentinel.repo, context['repository']) self.assertEqual(sentinel.pull_request, context['pull_request'])
def test_publish_checks_api__no_problems(self): self.repo.create_checkrun = Mock() config = build_review_config(checks_ini, {'PULLREQUEST_STATUS': True}) problems = Problems() sha = 'abc123' review = Review(self.repo, self.pr, config) review.publish_checkrun(problems, False, sha) assert self.repo.create_checkrun.called eq_(1, self.repo.create_checkrun.call_count) assert_checkrun(self.repo.create_checkrun.call_args, [], sha)
def test_publish_status__ok_no_comment_or_label(self): app_config = { 'OK_COMMENT': None, 'OK_LABEL': None, 'PULLREQUEST_STATUS': False, } tst_config = build_review_config(fixer_ini, app_config) review = Review(self.repo, self.pr, tst_config) review.publish_status(False) assert self.repo.create_status.called, 'Create status called' assert not self.pr.create_comment.called, 'Comment not created' assert not self.pr.add_label.called, 'Label added created'
def test_publish_status__ok_no_comment_or_label(self): app_config = { 'OK_COMMENT': None, 'OK_LABEL': None, 'PULLREQUEST_STATUS': False, } config = build_review_config(fixer_ini, app_config) review = Review(self.repo, self.pr, config) review.publish_status(False) assert self.repo.create_status.called, 'Create status called' assert not self.pr.create_comment.called, 'Comment not created' assert not self.pr.add_label.called, 'Label added created'
def test_publish_checkrun__no_problems(self): tst_config = build_review_config(fixer_ini, {'PULLREQUEST_STATUS': True}) problems = Problems() run_id = 42 review = Review(self.repo, self.pr, tst_config) review.publish_checkrun(problems, run_id) assert self.repo.update_checkrun.called self.assertEqual(1, self.repo.update_checkrun.call_count) assert_checkrun(self.repo.update_checkrun.call_args, [], run_id) assert self.repo.create_status.called is False, 'no status required'
def process_pull_request(user, repo_name, number, lintrc): """ Starts processing a pull request and running the various lint tools against it. """ log.info('Starting to process lint for %s/%s/%s', user, repo_name, number) log.debug("lintrc contents '%s'", lintrc) review_config = build_review_config(lintrc, config) if len(review_config.linters()) == 0: log.info('No configured linters, skipping processing.') return try: log.info('Loading pull request data from github. user=%s ' 'repo=%s number=%s', user, repo_name, number) repo = GithubRepository(config, user, repo_name) pull_request = repo.pull_request(number) clone_url = pull_request.clone_url pr_head = pull_request.head target_branch = pull_request.target_branch if target_branch in review_config.ignore_branches(): log.info('Pull request into ignored branch %s, skipping review.', target_branch) return status = config.get('PULLREQUEST_STATUS', True) if status: repo.create_status(pr_head, 'pending', 'Lintreview processing...') # Clone/Update repository target_path = git.get_repo_path(user, repo_name, number, config) git.clone_or_update(config, clone_url, target_path, pr_head) processor = Processor(repo, pull_request, target_path, config) processor.load_changes() processor.run_tools(review_config) processor.publish() log.info('Completed lint processing for %s/%s/%s' % ( user, repo, number)) git.destroy(target_path) log.info('Cleaned up pull request %s/%s/%s', user, repo, number) except BaseException as e: log.exception(e)
def process_pull_request(user, repo_name, number, lintrc): """ Starts processing a pull request and running the various lint tools against it. """ log.info('Starting to process lint for %s/%s/%s', user, repo_name, number) log.debug("lintrc contents '%s'", lintrc) review_config = build_review_config(lintrc, config) if len(review_config.linters()) == 0: log.info('No configured linters, skipping processing.') return try: log.info('Loading pull request data from github. user=%s ' 'repo=%s number=%s', user, repo_name, number) repo = GithubRepository(config, user, repo_name) pull_request = repo.pull_request(number) head_repo = pull_request.clone_url private_repo = pull_request.is_private pr_head = pull_request.head target_branch = pull_request.target_branch if target_branch in review_config.ignore_branches(): log.info('Pull request into ignored branch %s, skipping processing.' % target_branch) return status = config.get('PULLREQUEST_STATUS', True) if status: repo.create_status(pr_head, 'pending', 'Lintreview processing...') # Clone/Update repository target_path = git.get_repo_path(user, repo_name, number, config) git.clone_or_update(config, head_repo, target_path, pr_head, private_repo) processor = Processor(repo, pull_request, target_path, config) processor.load_changes() processor.run_tools(review_config) processor.publish() log.info('Completed lint processing for %s/%s/%s' % ( user, repo, number)) except BaseException, e: log.exception(e)
def test_publish_empty_comment_add_ok_label(self): problems = Problems(changes=[]) config = build_review_config(fixer_ini, {'OK_LABEL': 'No lint'}) review = Review(self.repo, self.pr, config) sha = 'abc123' review.publish(problems, sha) assert self.pr.create_comment.called, 'ok comment should be added.' assert self.pr.remove_label.called, 'label should be removed.' self.pr.remove_label.assert_called_with(config['OK_LABEL']) msg = ('Could not review pull request. ' 'It may be too large, or contain no reviewable changes.') self.pr.create_comment.assert_called_with(msg)
def test_publish_review_empty_comment_add_ok_label(self): problems = Problems(changes=DiffCollection([])) tst_config = build_review_config(fixer_ini, {'OK_LABEL': 'No lint'}) review = Review(self.repo, self.pr, tst_config) sha = 'abc123' review.publish_review(problems, sha) assert self.pr.create_comment.called, 'ok comment should be added.' assert self.pr.remove_label.called, 'label should be removed.' self.pr.remove_label.assert_called_with(tst_config['OK_LABEL']) msg = ('Could not review pull request. ' 'It may be too large, or contain no reviewable changes.') self.pr.create_comment.assert_called_with(msg)
def test_publish_checkrun(self): pull = self.get_pull_request() repo = Mock() config = build_review_config(fixer_ini, app_config) subject = Processor(repo, pull, './tests', config) subject.problems = Mock() subject._review = Mock() subject.publish(check_run_id=9) eq_(True, subject.problems.limit_to_changes.called, 'Problems should be filtered.') eq_(True, subject._review.publish_checkrun.called, 'Review should be published.') subject._review.publish_checkrun.assert_called_with( subject.problems, 9)
def process_pull_request(user, repo, number, lintrc): """ Starts processing a pull request and running the various lint tools against it. """ log.info('Starting to process lint for %s/%s/%s', user, repo, number) log.debug("lintrc contents '%s'", lintrc) review_config = build_review_config(lintrc, config) if len(review_config.linters()) == 0: log.info('No configured linters, skipping processing.') return try: log.info( 'Loading pull request data from github. user=%s ' 'repo=%s number=%s', user, repo, number) gh = github.get_repository(config, user, repo) pull_request = gh.pull_request(number) pr_dict = pull_request.as_dict() head_repo = pr_dict['head']['repo']['clone_url'] private_repo = pr_dict['head']['repo']['private'] pr_head = pr_dict['head']['sha'] target_branch = pr_dict['base']['ref'] if target_branch in review_config.ignore_branches(): log.info( 'Pull request into ignored branch %s, skipping processing.' % target_branch) return # Clone/Update repository target_path = git.get_repo_path(user, repo, number, config) git.clone_or_update(config, head_repo, target_path, pr_head, private_repo) processor = Processor(gh, number, pr_head, target_path, config) processor.load_changes() processor.run_tools(review_config) processor.publish() log.info('Completed lint processing for %s/%s/%s' % (user, repo, number)) except BaseException as e: log.exception(e)
def test_publish_status__has_errors(self): app_config = { 'OK_COMMENT': 'Great job!', 'OK_LABEL': 'No lint errors', 'APP_NAME': 'custom-name' } config = build_review_config(fixer_ini, app_config) review = Review(self.repo, self.pr, config) review.publish_status(True) assert self.repo.create_status.called, 'Create status not called' self.repo.create_status.assert_called_with( self.pr.head, 'failure', 'Lint errors found, see pull request comments.') assert not self.pr.create_comment.called, 'Comment not created' assert not self.pr.add_label.called, 'Label added created'
def test_publish(self): pull = self.get_pull_request() repo = Mock() config = build_review_config(fixer_ini, app_config) subject = Processor(repo, pull, './tests', config) subject.problems = Mock() subject._review = Mock() subject.publish() self.assertTrue(subject.problems.limit_to_changes.called, 'Problems should be filtered.') self.assertTrue(subject._review.publish_review.called, 'Review should be published.') subject._review.publish_review.assert_called_with( subject.problems, pull.head)
def test_run_tools__ignore_patterns(self): pull = self.get_pull_request() repo = Mock() config = build_review_config(fixer_ini, app_config) config.ignore_patterns = lambda: [ 'View/Helper/*'] subject = Processor(repo, pull, './tests', config) subject.load_changes() subject.run_tools() self.tool_stub.run.assert_called_with( ANY, [], ANY )
def test_publish_checkrun__multiple_chunks(self): self.repo.create_checkrun = Mock() tst_config = build_review_config(fixer_ini, {'PULLREQUEST_STATUS': True}) problems = Problems() filename_1 = 'Console/Command/Task/AssetBuildTask.php' errors = [ Comment(filename_1, i, i, 'Something worse') for i in range(0, 70) ] problems.add_many(errors) problems.add(IssueComment('In the body')) run_id = 42 review = Review(self.repo, self.pr, tst_config) review.publish_checkrun(problems, run_id) assert self.repo.update_checkrun.call_count == 2 assert self.repo.create_status.called is False, 'no status required' first_call = self.repo.update_checkrun.call_args_list[0] assert run_id == first_call[0][0] first_payload = first_call[0][1] assert 'failure' == first_payload['conclusion'] assert 'completed_at' in first_payload assert 'title' in first_payload['output'] assert 'summary' in first_payload['output'] assert 'annotations' in first_payload['output'] assert 'In the body' == first_payload['output']['summary'] assert 50 == len(first_payload['output']['annotations']) second_call = self.repo.update_checkrun.call_args_list[1] assert run_id == second_call[0][0] # The second payload should only contain additional annotations. second_payload = second_call[0][1] assert 'completed_at' not in second_payload assert 'title' in second_payload['output'] assert 'summary' in second_payload['output'] assert 'annotations' in second_payload['output'] assert 'In the body' == second_payload['output']['summary'] assert 20 == len(second_payload['output']['annotations'])
def process_pull_request(user, repo, number, lintrc): """ Starts processing a pull request and running the various lint tools against it. """ log.info('Starting to process lint for %s/%s/%s', user, repo, number) log.debug("lintrc contents '%s'", lintrc) review_config = build_review_config(lintrc, config) if len(review_config.linters()) == 0: log.info('No configured linters, skipping processing.') return try: log.info('Loading pull request data from github. user=%s ' 'repo=%s number=%s', user, repo, number) gh = github.get_repository(config, user, repo) pull_request = gh.pull_request(number) pr_dict = pull_request.as_dict() head_repo = pr_dict['head']['repo']['clone_url'] private_repo = pr_dict['head']['repo']['private'] pr_head = pr_dict['head']['sha'] target_branch = pr_dict['base']['ref'] if target_branch in review_config.ignore_branches(): log.info('Pull request into ignored branch %s, skipping processing.' % target_branch) return # Clone/Update repository target_path = git.get_repo_path(user, repo, number, config) git.clone_or_update(config, head_repo, target_path, pr_head, private_repo) processor = Processor(gh, number, pr_head, target_path, config) processor.load_changes() processor.run_tools(review_config) processor.publish() log.info('Completed lint processing for %s/%s/%s' % ( user, repo, number)) except BaseException, e: log.exception(e)
def test_publish_empty_comment_with_comment_status(self): config = build_review_config(fixer_ini, {'PULLREQUEST_STATUS': True}) problems = Problems(changes=[]) review = Review(self.repo, self.pr, config) sha = 'abc123' review.publish(problems, sha) assert self.pr.create_comment.called, 'Should create a comment' msg = ('Could not review pull request. ' 'It may be too large, or contain no reviewable changes.') self.repo.create_status.assert_called_with(self.pr.head, 'success', msg) self.pr.create_comment.assert_called_with(msg)
def test_publish_status__has_errors(self): app_config = { 'OK_COMMENT': 'Great job!', 'OK_LABEL': 'No lint errors', 'APP_NAME': 'custom-name' } tst_config = build_review_config(fixer_ini, app_config) review = Review(self.repo, self.pr, tst_config) review.publish_status(True) assert self.repo.create_status.called, 'Create status not called' self.repo.create_status.assert_called_with( self.pr.head, 'failure', 'Lint errors found, see pull request comments.') assert not self.pr.create_comment.called, 'Comment not created' assert not self.pr.add_label.called, 'Label added created'
def test_run_tools__execute_fixers_fail(self): pull = self.get_pull_request() repo = Mock() self.tool_stub.factory.return_value = sentinel.tools self.fixer_stub.create_context.return_value = sentinel.context self.fixer_stub.run_fixers.side_effect = RuntimeError config = build_review_config(fixer_ini, app_config) subject = Processor(repo, pull, './tests', config) subject.load_changes() subject.run_tools() self.fixer_stub.create_context.assert_called() self.fixer_stub.run_fixers.assert_called() self.fixer_stub.apply_fixer_diff.assert_not_called() self.fixer_stub.rollback_changes.assert_called() self.tool_stub.run_assert_called()
def test_publish_checkrun__no_problems(self): self.repo.create_checkrun = Mock() tst_config = build_review_config(fixer_ini, {'PULLREQUEST_STATUS': True}) problems = Problems() run_id = 42 review = Review(self.repo, self.pr, tst_config) review.publish_checkrun(problems, run_id) assert self.repo.update_checkrun.called self.assertEqual(1, self.repo.update_checkrun.call_count) assert_checkrun( self, self.repo.update_checkrun.call_args, [], run_id) assert self.repo.create_status.called is False, 'no status required'
def test_publish_review_empty_comment_with_comment_status(self): tst_config = build_review_config(fixer_ini, {'PULLREQUEST_STATUS': True}) problems = Problems(changes=DiffCollection([])) review = Review(self.repo, self.pr, tst_config) sha = 'abc123' review.publish_review(problems, sha) assert self.pr.create_comment.called, 'Should create a comment' msg = ('Could not review pull request. ' 'It may be too large, or contain no reviewable changes.') self.repo.create_status.assert_called_with( self.pr.head, 'success', msg) self.pr.create_comment.assert_called_with(msg)
def test_publish_status__has_errors__success_status(self): app_config = { 'PULLREQUEST_STATUS': False, 'OK_COMMENT': 'Great job!', 'OK_LABEL': 'No lint errors', 'APP_NAME': 'custom-name' } tst_config = build_review_config(fixer_ini, app_config) self.assertEqual('success', tst_config.failed_review_status(), 'config object changed') review = Review(self.repo, self.pr, tst_config) review.publish_status(True) assert self.repo.create_status.called, 'Create status not called' self.repo.create_status.assert_called_with( self.pr.head, 'success', 'Lint errors found, see pull request comments.') assert not self.pr.create_comment.called, 'Comment not created' assert not self.pr.add_label.called, 'Label added created'
def test_publish_status__ok_with_comment_label(self): app_config = { 'OK_COMMENT': 'Great job!', 'OK_LABEL': 'No lint errors', 'PULLREQUEST_STATUS': True, } tst_config = build_review_config(fixer_ini, app_config) Review(self.repo, self.pr, tst_config) review = Review(self.repo, self.pr, tst_config) review.publish_status(False) assert self.repo.create_status.called, 'Create status not called' self.repo.create_status.assert_called_with( self.pr.head, 'success', 'No lint errors found.') assert self.pr.create_comment.called, 'Issue comment created' self.pr.create_comment.assert_called_with('Great job!') assert self.pr.add_label.called, 'Label added created' self.pr.add_label.assert_called_with('No lint errors')
def _test_run_tools_fixer_error_scenario(self, error): pull = self.get_pull_request() repo = Mock() self.tool_stub.factory.return_value = sentinel.tools self.fixer_stub.create_context.return_value = sentinel.context self.fixer_stub.apply_fixer_diff.side_effect = error config = build_review_config(fixer_ini, app_config) subject = Processor(repo, pull, './tests', config) subject.load_changes() subject.run_tools() self.fixer_stub.create_context.assert_called() self.fixer_stub.run_fixers.assert_called() self.tool_stub.run.assert_called() self.fixer_stub.rollback_changes.assert_not_called() assert 1 == len(subject.problems), 'strategy error adds pull comment' assert 0 == subject.problems.error_count(), 'fixer failure should be info level' assert 'Unable to apply fixers. ' + str(error) == subject.problems.all()[0].body assert 1 == len(subject.problems), 'strategy error adds pull comment'
def test_publish_review_no_count_change(self, pub_status_mock, _): fixture = load_fixture('comments_current.json') self.pr.review_comments.return_value = [ GhIssueComment(f, self.session) for f in json.loads(fixture)] problems = Problems() # Match the line/positions in comments_current.json filename_1 = 'Console/Command/Task/AssetBuildTask.php' errors = ( Comment(filename_1, 40, 40, '2. Something bad'), Comment(filename_1, 87, 87, '1. Something bad'), Comment(filename_1, 89, 89, '2. Something bad'), ) problems.add_many(errors) problems.set_changes([1]) sha = 'abc123' tst_config = build_review_config(fixer_ini, {'SUMMARY_THRESHOLD': 1}) review = Review(self.repo, self.pr, tst_config) review.publish_review(problems, sha) # Ensure publish_status(True) means the status=failed pub_status_mock.assert_called_with(True)