<commit_before>import collections

from nose.tools import assert_is_none, eq_
import mock

from pylcp import api


class TestHeaderFormatting(object):
    def test_header_dict_formatted_as_string(self):
        headers = collections.OrderedDict()
        headers['header1'] = 'value1'
        headers['header2'] = 'value2'
        formatted_headers = 'header1: value1\nheader2: value2'
        eq_(formatted_headers, api.format_headers(headers))

    def test_empty_header_dict_formatted_as_empty_string(self):
        eq_('', api.format_headers({}))


class TestJsonPrettifying(object):
    def setup(self):
        self.indent_amount = 2

    def test_prettified_json_is_indented(self):
        eq_(
            '{{\n{0}"answer": 42\n}}'.format(' ' * self.indent_amount),
            api.prettify_alleged_json('{"answer": 42}')
        )

    def test_prettified_json_is_sorted(self):
        eq_(
            '{{\n{0}"x": 2, \n{0}"y": 1\n}}'.format(' ' * self.indent_amount),
            api.prettify_alleged_json('{"y": 1, "x": 2}')
        )

    def test_non_json_is_not_prettified(self):
        non_json = 'This is not JSON.'
        eq_(non_json, api.prettify_alleged_json(non_json))


class TestCreditCardDataMasking(object):
    def test_none_is_not_masked(self):
        assert_is_none(api.mask_credit_card_number(None))

    def test_all_but_last_four_digits_are_masked(self):
        card_number = '1234567890'
        masked_card_number = api.mask_credit_card_number(card_number)
        eq_(card_number[-4], masked_card_number[-4])
        eq_(len(card_number), len(masked_card_number))
        eq_('X' * (len(card_number) - 4), masked_card_number[:-4])


class TestSensitiveDataMasking(object):
    def setup(self):
        self.unmasked = {
            'billingInfo': {
                'cardNumber': '1234567890123456',
                'securityCode': '123',
            },
            'password': '<PASSWORD>',
            'language': 'Python',
        }
        self.masked = api.mask_sensitive_data(self.unmasked)

    def test_non_data_is_not_masked(self):
        assert_is_none(api.mask_sensitive_data(None))
        assert_is_none(api.mask_sensitive_data(''))
        assert_is_none(api.mask_sensitive_data([]))
        assert_is_none(api.mask_sensitive_data({}))

    def test_non_json_data_is_not_masked(self):
        data = 'This is not JSON. cardNumber: 1234567890123456'
        eq_(data, api.mask_sensitive_data(data))

    def test_card_number_is_masked(self):
        eq_('XXXXXXXXXXXX3456', self.masked['billingInfo']['cardNumber'])

    def test_security_code_is_masked(self):
        eq_('XXX', self.masked['billingInfo']['securityCode'])

    def test_password_is_masked(self):
        eq_('XXX', self.masked['password'])

    def test_non_sensitive_data_is_not_masked(self):
        eq_(self.unmasked['language'], self.masked['language'])

    def test_json_in_a_string_is_masked(self):
        unmasked_string = '{"billingInfo": {}, "password": "<PASSWORD>"}'
        masked_string = '{"password": "<PASSWORD>", "billingInfo": {}}'
        eq_(masked_string, api.mask_sensitive_data(unmasked_string))


class TestApiClient(object):
    def setup(self):
        self.client = api.Client('BASE_URL')
        self.request_log_format_string = (
            '------------------------------------------------------------\n'
            '%(method)s %(url)s HTTP/1.1\n%'
            '(headers)s\n\n%(body)s')
        self.response_log_format_string = (
            '------------------------------------------------------------\n'
            'HTTP/1.1 %(status_code)d %(reason)s\n'
            '%(headers)s\n\n'
            '%(body)s')

    def test_request_does_not_alter_absolute_urls(self):
        for absolute_url in [
                'http://www.points.com',
                'https://www.points.com']:
            yield self._assert_calls_requests_with_url, absolute_url, absolute_url

    def test_client_remembers_credentials(self):
        client = api.Client(mock.sentinel.BASE_URL, mock.sentinel.KEY_ID, mock.sentinel.SHARED_SECRET)
        eq_(mock.sentinel.BASE_URL, client.base_url)
        eq_(mock.sentinel.KEY_ID, client.key_id)
        eq_(mock.sentinel.SHARED_SECRET, client.shared_secret)

    def test_request_adds_base_url_to_relative_urls(self):
        self._assert_calls_requests_with_url('some/relative/path', 'BASE_URL/some/relative/path')
        self._assert_calls_requests_with_url('/some/absolute/path', 'BASE_URL/some/absolute/path')

    def test_request_adds_base_url_with_trailing_slash_to_relative_urls(self):
        self.client = api.Client('BASE_URL/')
        self._assert_calls_requests_with_url('some/relative/path', 'BASE_URL/some/relative/path')
        self._assert_calls_requests_with_url('/some/absolute/path', 'BASE_URL/some/absolute/path')

    @mock.patch('requests.request')
    def _assert_calls_requests_with_url(self, original_url, expected_url, request_mock):
        self.client.request('METHOD', original_url)
        eq_(request_mock.call_args_list, [
            mock.call('METHOD', expected_url, headers={})])

    @mock.patch('requests.request')
    def test_delete_issues_a_DELETE_request_with_none_headers(self, request_mock):
        self.client.delete('/url')
        eq_(request_mock.call_args_list, [
            mock.call('DELETE', 'BASE_URL/url', headers={})])

    @mock.patch('requests.request')
    def test_delete_issues_a_DELETE_request_with_headers(self, request_mock):
        self.client.delete('/url', headers={'Header-Name': 'some header value'})
        eq_(request_mock.call_args_list, [
            mock.call('DELETE', 'BASE_URL/url', headers={'Header-Name': 'some header value'})])

    @mock.patch('requests.request')
    def test_options_issues_an_OPTIONS_request_with_none_headers(self, request_mock):
        self.client.options('/url')
        eq_(request_mock.call_args_list, [
            mock.call('OPTIONS', 'BASE_URL/url', headers={})])

    @mock.patch('requests.request')
    def test_options_issues_an_OPTIONS_request_with_headers(self, request_mock):
        self.client.options('/url', headers={'Header-Name': 'some header value'})
        eq_(request_mock.call_args_list, [
            mock.call('OPTIONS', 'BASE_URL/url', headers={'Header-Name': 'some header value'})])

    @mock.patch('requests.request')
    def test_put_issues_a_PUT_request_with_json_content_type(self, request_mock):
        self.client.put('/url')
        eq_(request_mock.call_args_list, [
            mock.call('PUT', 'BASE_URL/url', headers={'Content-Type': 'application/json'})])

    @mock.patch('requests.request')
    def test_post_issues_a_POST_request_with_json_content_type(self, request_mock):
        self.client.post('/url')
        eq_(request_mock.call_args_list, [
            mock.call('POST', 'BASE_URL/url', headers={'Content-Type': 'application/json'})])

    @mock.patch('pylcp.api.generate_authorization_header_value', return_value='auth_value')
    @mock.patch('requests.request')
    def test_get_issues_a_GET_request_with_none_headers_and_none_params(self, request_mock, auth_header_mock):
        self.client.key_id = 'foobar'<commit_msg>Adds missing test for PATCH.<commit_after>import collections

from nose.tools import assert_is_none, eq_
import mock

from pylcp import api


class TestHeaderFormatting(object):
    def test_header_dict_formatted_as_string(self):
        headers = collections.OrderedDict()
        headers['header1'] = 'value1'
        headers['header2'] = 'value2'
        formatted_headers = 'header1: value1\nheader2: value2'
        eq_(formatted_headers, api.format_headers(headers))

    def test_empty_header_dict_formatted_as_empty_string(self):
        eq_('', api.format_headers({}))


class TestJsonPrettifying(object):
    def setup(self):
        self.indent_amount = 2

    def test_prettified_json_is_indented(self):
        eq_(
            '{{\n{0}"answer": 42\n}}'.format(' ' * self.indent_amount),
            api.prettify_alleged_json('{"answer": 42}')
        )

    def test_prettified_json_is_sorted(self):
        eq_(
            '{{\n{0}"x": 2, \n{0}"y": 1\n}}'.format(' ' * self.indent_amount),
            api.prettify_alleged_json('{"y": 1, "x": 2}')
        )

    def test_non_json_is_not_prettified(self):
        non_json = 'This is not JSON.'
        eq_(non_json, api.prettify_alleged_json(non_json))


class TestCreditCardDataMasking(object):
    def test_none_is_not_masked(self):
        assert_is_none(api.mask_credit_card_number(None))

    def test_all_but_last_four_digits_are_masked(self):
        card_number = '1234567890'
        masked_card_number = api.mask_credit_card_number(card_number)
        eq_(card_number[-4], masked_card_number[-4])
        eq_(len(card_number), len(masked_card_number))
        eq_('X' * (len(card_number) - 4), masked_card_number[:-4])


class TestSensitiveDataMasking(object):
    def setup(self):
        self.unmasked = {
            'billingInfo': {
                'cardNumber': '1234567890123456',
                'securityCode': '123',
            },
            'password': '<PASSWORD>',
            'language': 'Python',
        }
        self.masked = api.mask_sensitive_data(self.unmasked)

    def test_non_data_is_not_masked(self):
        assert_is_none(api.mask_sensitive_data(None))
        assert_is_none(api.mask_sensitive_data(''))
        assert_is_none(api.mask_sensitive_data([]))
        assert_is_none(api.mask_sensitive_data({}))

    def test_non_json_data_is_not_masked(self):
        data = 'This is not JSON. cardNumber: 1234567890123456'
        eq_(data, api.mask_sensitive_data(data))

    def test_card_number_is_masked(self):
        eq_('XXXXXXXXXXXX3456', self.masked['billingInfo']['cardNumber'])

    def test_security_code_is_masked(self):
        eq_('XXX', self.masked['billingInfo']['securityCode'])

    def test_password_is_masked(self):
        eq_('XXX', self.masked['password'])

    def test_non_sensitive_data_is_not_masked(self):
        eq_(self.unmasked['language'], self.masked['language'])

    def test_json_in_a_string_is_masked(self):
        unmasked_string = '{"billingInfo": {}, "password": "<PASSWORD>"}'
        masked_string = '{"password": "XXX", "billingInfo": {}}'
        eq_(masked_string, api.mask_sensitive_data(unmasked_string))


class TestApiClient(object):
    def setup(self):
        self.client = api.Client('BASE_URL')
        self.request_log_format_string = (
            '------------------------------------------------------------\n'
            '%(method)s %(url)s HTTP/1.1\n%'
            '(headers)s\n\n%(body)s')
        self.response_log_format_string = (
            '------------------------------------------------------------\n'
            'HTTP/1.1 %(status_code)d %(reason)s\n'
            '%(headers)s\n\n'
            '%(body)s')

    def test_request_does_not_alter_absolute_urls(self):
        for absolute_url in [
                'http://www.points.com',
                'https://www.points.com']:
            yield self._assert_calls_requests_with_url, absolute_url, absolute_url

    def test_client_remembers_credentials(self):
        client = api.Client(mock.sentinel.BASE_URL, mock.sentinel.KEY_ID, mock.sentinel.SHARED_SECRET)
        eq_(mock.sentinel.BASE_URL, client.base_url)
        eq_(mock.sentinel.KEY_ID, client.key_id)
        eq_(mock.sentinel.SHARED_SECRET, client.shared_secret)

    def test_request_adds_base_url_to_relative_urls(self):
        self._assert_calls_requests_with_url('some/relative/path', 'BASE_URL/some/relative/path')
        self._assert_calls_requests_with_url('/some/absolute/path', 'BASE_URL/some/absolute/path')

    def test_request_adds_base_url_with_trailing_slash_to_relative_urls(self):
        self.client = api.Client('BASE_URL/')
        self._assert_calls_requests_with_url('some/relative/path', 'BASE_URL/some/relative/path')
        self._assert_calls_requests_with_url('/some/absolute/path', 'BASE_URL/some/absolute/path')

    @mock.patch('requests.request')
    def _assert_calls_requests_with_url(self, original_url, expected_url, request_mock):
        self.client.request('METHOD', original_url)
        eq_(request_mock.call_args_list, [
            mock.call('METHOD', expected_url, headers={})])

    @mock.patch('requests.request')
    def test_delete_issues_a_DELETE_request_with_none_headers(self, request_mock):
        self.client.delete('/url')
        eq_(request_mock.call_args_list, [
            mock.call('DELETE', 'BASE_URL/url', headers={})])

    @mock.patch('requests.request')
    def test_delete_issues_a_DELETE_request_with_headers(self, request_mock):
        self.client.delete('/url', headers={'Header-Name': 'some header value'})
        eq_(request_mock.call_args_list, [
            mock.call('DELETE', 'BASE_URL/url', headers={'Header-Name': 'some header value'})])

    @mock.patch('requests.request')
    def test_options_issues_an_OPTIONS_request_with_none_headers(self, request_mock):
        self.client.options('/url')
        eq_(request_mock.call_args_list, [
            mock.call('OPTIONS', 'BASE_URL/url', headers={})])

    @mock.patch('requests.request')
    def test_options_issues_an_OPTIONS_request_with_headers(self, request_mock):
        self.client.options('/url', headers={'Header-Name': 'some header value'})
        eq_(request_mock.call_args_list, [
            mock.call('OPTIONS', 'BASE_URL/url', headers={'Header-Name': 'some header value'})])

    @mock.patch('requests.request')
    def test_patch_issues_a_PATCH_request_with_json_content_type(self, request_mock):
        self.client.patch('/url')
        eq_(request_mock.call_args_list, [
            mock.call('PATCH', 'BASE_URL/url', headers={'Content-Type': 'application/json'})])

    @mock.patch('requests.request')
    def test_put_issues_a_PUT_request_with_json_content_type(self, request_mock):
        self.client.put('/url')
        eq_(request_mock.call_args_list, [
            mock.call('PUT', 'BASE_URL/url', headers={'Content-Type': 'application/json'})])

    @mock.patch('requests.request')
    def test_post_issues_a_POST_request_with_json_content_type(self, request_mock):
        self.client.post('/url')
        eq_(request_mock.call_args_list, [
            mock.call('POST', 'BASE_URL/url', headers={'Content-Type': 'application/json'})])

    @mock.patch('pylcp.api.generate_authorization_header_value', return_value='auth_value')
    @mock.patch('requests.request')
    def test_get_issues_a_GET_request_with_none_headers_and_none_params(self, request_mock, auth_header_mock):
        self.client.key_id = 'foobar'
{'fraction_non_alphanumeric': 0.14746260501331876, 'fraction_numerical': 0.010518407212622089, 'mean_word_length': 3.3039388594944152, 'pattern_counts': {'":': 20, '<': 8, '<?xml version=': 0, '>': 8, 'https://': 2, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1954083', 'n_tokens_mistral': 4925, 'n_tokens_neox': 4741, 'n_words': 603}