def test_mail(mock_config, mock_issues, mock_phabricator):
    '''
    Test mail sending through Taskcluster
    '''
    from static_analysis_bot.report.mail import MailReporter
    from static_analysis_bot.revisions import PhabricatorRevision, ImprovementPatch

    def _check_email(request):
        payload = json.loads(request.body)

        assert payload['subject'] in (
            '[test] New Static Analysis Phabricator #42 - PHID-DIFF-test', )
        assert payload['address'] == '*****@*****.**'
        assert payload['template'] == 'fullscreen'
        assert payload['content'] == MAIL_CONTENT.format(
            results=mock_config.taskcluster.results_dir)

        return (200, {}, '')  # ack

    # Add mock taskcluster email to check output
    responses.add_callback(
        responses.POST,
        'https://notify.taskcluster.net/v1/email',
        callback=_check_email,
    )

    # Publish email
    conf = {
        'emails': [
            '*****@*****.**',
        ],
    }
    r = MailReporter(conf, 'test_tc', 'token_tc')

    with mock_phabricator as api:
        prev = PhabricatorRevision(api, 'PHID-DIFF-test')
        prev.improvement_patches = [
            ImprovementPatch('clang-tidy', repr(prev), 'Some code fixes'),
            ImprovementPatch('clang-format', repr(prev), 'Some lint fixes'),
        ]
        list(map(lambda p: p.write(),
                 prev.improvement_patches))  # trigger local write
        r.publish(mock_issues, prev)

    # Check stats
    mock_cls = mock_issues[0].__class__
    assert r.calc_stats(mock_issues) == {
        mock_cls: {
            'total': 5,
            'publishable': 3,
            'publishable_paths': ['/path/to/file']
        }
    }
    def _test_reporter(api, analyzers):
        # Always use the same setup, only varies the analyzers
        revision = Revision(api, mock_try_task)
        revision.lines = {
            'test.cpp': [0, 41, 42, 43],
            'dom/test.cpp': [
                42,
            ],
        }
        reporter = PhabricatorReporter({'analyzers': analyzers}, api=api)

        issues = [
            ClangFormatIssue('dom/test.cpp', 42, 1, revision),
            ClangTidyIssue(revision, 'test.cpp', '42', '51',
                           'modernize-use-nullptr', 'dummy message', 'error'),
            InferIssue(
                {
                    'file': 'test.cpp',
                    'line': 42,
                    'column': 1,
                    'bug_type': 'dummy',
                    'kind': 'whatever',
                    'qualifier': 'dummy message.',
                }, revision),
            MozLintIssue('test.cpp', 1, 'danger', 42, 'flake8', 'Python error',
                         'EXXX', revision),
            CoverageIssue('test.cpp', 0, 'This file is uncovered', revision),
        ]

        assert all(i.is_publishable() for i in issues)

        revision.improvement_patches = [
            ImprovementPatch('dummy', repr(revision), 'Whatever'),
            ImprovementPatch('clang-tidy', repr(revision), 'Some C fixes'),
            ImprovementPatch('clang-format', repr(revision),
                             'Some lint fixes'),
            ImprovementPatch('infer', repr(revision), 'Some java fixes'),
            ImprovementPatch('mozlint', repr(revision), 'Some js fixes'),
        ]
        list(map(lambda p: p.write(),
                 revision.improvement_patches))  # trigger local write

        return reporter.publish(issues, revision)
def test_phabricator_clang_format(mock_config, mock_phabricator):
    '''
    Test Phabricator reporter publication on a mock clang-format issue
    '''
    from static_analysis_bot.report.phabricator import PhabricatorReporter
    from static_analysis_bot.revisions import PhabricatorRevision, ImprovementPatch
    from static_analysis_bot.tasks.clang_format import ClangFormatIssue

    def _check_comment(request):
        # Check the Phabricator main comment is well formed
        payload = urllib.parse.parse_qs(request.body)
        assert payload['output'] == ['json']
        assert len(payload['params']) == 1
        details = json.loads(payload['params'][0])
        assert details['message'] == VALID_CLANG_FORMAT_MESSAGE.format(results=mock_config.taskcluster.results_dir)

        # Outputs dummy empty response
        resp = {
            'error_code': None,
            'result': None,
        }
        return 201, {'Content-Type': 'application/json', 'unittest': 'clang-format'}, json.dumps(resp)

    responses.add_callback(
        responses.POST,
        'http://phabricator.test/api/differential.createcomment',
        callback=_check_comment,
    )

    with mock_phabricator as api:
        revision = PhabricatorRevision(api, 'PHID-DIFF-abcdef')
        revision.lines = {
            # Add dummy lines diff
            'test.cpp': [41, 42, 43],
            'dom/test.cpp': [42, ],
        }
        reporter = PhabricatorReporter({'analyzers': ['clang-format']}, api=api)

    issue = ClangFormatIssue('dom/test.cpp', 42, 1, revision)
    assert issue.is_publishable()

    revision.improvement_patches = [
        ImprovementPatch('clang-format', repr(revision), 'Some lint fixes'),
    ]
    list(map(lambda p: p.write(), revision.improvement_patches))  # trigger local write

    issues, patches = reporter.publish([issue, ], revision)
    assert len(issues) == 1
    assert len(patches) == 1

    # Check the callback has been used
    assert len(responses.calls) > 0
    call = responses.calls[-1]
    assert call.request.url == 'http://phabricator.test/api/differential.createcomment'
    assert call.response.headers.get('unittest') == 'clang-format'