<commit_before>from datetime import datetime

from flask import json
from freezegun import freeze_time
import pytest

from app import db
from app.models import Supplier, ContactInformation, AuditEvent, \
    SupplierFramework, Framework, FrameworkAgreement, DraftService, Service
from tests.bases import BaseApplicationTest, JSONTestMixin, JSONUpdateTestMixin
from tests.helpers import fixture_params, FixtureMixin, load_example_listing


class TestGetSupplier(BaseApplicationTest, FixtureMixin):
    supplier = supplier_id = None

    def setup(self):
        super(TestGetSupplier, self).setup()

        payload = load_example_listing("supplier_creation")
        self.supplier = payload

        response = self.client.post(
            '/suppliers',
            data=json.dumps({
                'suppliers': self.supplier
            }),
            content_type='application/json')
        assert response.status_code == 201
        self.supplier_id = json.loads(response.get_data())['suppliers']['id']

    def test_get_non_existent_supplier(self):
        response = self.client.get('/suppliers/100')
        assert response.status_code == 404

    def test_invalid_supplier_id(self):
        response = self.client.get('/suppliers/abc123')
        assert response.status_code == 404

    def test_get_supplier(self):
        response = self.client.get('/suppliers/{}'.format(self.supplier_id))

        data = json.loads(response.get_data())
        assert response.status_code == 200
        assert self.supplier_id == data['suppliers']['id']
        assert self.supplier['name'] == data['suppliers']['name']

    def test_get_supplier_returns_service_counts(self):
        self.setup_dummy_services(
            5, supplier_id=self.supplier_id, framework_id=1
        )
        self.setup_dummy_services(
            10, start_id=5, supplier_id=self.supplier_id, framework_id=2
        )
        self.setup_dummy_services(
            15, start_id=15, supplier_id=self.supplier_id, framework_id=3
        )

        response = self.client.get('/suppliers/{}'.format(self.supplier_id))

        data = json.loads(response.get_data())
        assert data['suppliers']['service_counts'] == {
            u'G-Cloud 5': 15,
            u'G-Cloud 6': 5
        }


class TestListSuppliers(BaseApplicationTest, FixtureMixin):
    def setup(self):
        super(TestListSuppliers, self).setup()

        # Supplier names look like "Supplier {n}"
        self.setup_dummy_suppliers(7)

    def test_query_string_missing(self):
        response = self.client.get('/suppliers')
        assert response.status_code == 200

    def test_query_string_prefix_empty(self):
        response = self.client.get('/suppliers?prefix=')
        assert response.status_code == 200

    def test_query_string_prefix_returns_none(self):
        response = self.client.get('/suppliers?prefix=canada')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 0

    def test_other_prefix_returns_non_alphanumeric_suppliers(self):
        db.session.add(
            Supplier(supplier_id=999, name=u"<NAME>")
        )
        self.setup_dummy_service(service_id='1230000000', supplier_id=999)

        response = self.client.get('/suppliers?prefix=other')

        data = json.loads(response.get_data())
        assert response.status_code == 200
        assert len(data['suppliers']) == 1
        assert data['suppliers'][0]['id'] == 999
        assert u"999 Supplier" == data['suppliers'][0]['name']

    def test_query_string_prefix_returns_paginated_page_one(self):
        response = self.client.get('/suppliers?prefix=s')
        data = json.loads(response.get_data())

        assert response.status_code == 200
        assert len(data['suppliers']) == 5
        next_link = data['links']['next']
        assert 'page=2' in next_link

    def test_query_string_prefix_returns_paginated_page_two(self):
        response = self.client.get('/suppliers?prefix=s&page=2')
        data = json.loads(response.get_data())

        assert response.status_code == 200
        assert len(data['suppliers']) == 2
        prev_link = data['links']['prev']
        assert 'page=1' in prev_link

    def test_query_string_prefix_returns_no_pagination_for_single_page(self):
        self.setup_additional_dummy_suppliers(5, 'T')
        response = self.client.get('/suppliers?prefix=t')
        data = json.loads(response.get_data())

        assert response.status_code == 200
        assert len(data['suppliers']) == 5
        assert ['self'] == list(data['links'].keys())

    def test_query_string_prefix_page_out_of_range(self):
        response = self.client.get('/suppliers?prefix=s&page=10')

        assert response.status_code == 404

    def test_query_string_prefix_invalid_page_argument(self):
        response = self.client.get('/suppliers?prefix=s&page=a')

        assert response.status_code == 400

    def test_below_one_page_number_is_404(self):
        response = self.client.get('/suppliers?page=0')

        assert response.status_code == 404


class TestListSuppliersOnFramework(BaseApplicationTest, FixtureMixin):

    def setup(self):
        super(TestListSuppliersOnFramework, self).setup()

        db.session.add(
            Supplier(supplier_id=1, name=u"Active")
        )
        db.session.add(
            Supplier(supplier_id=2, name=u"Inactive Framework")
        )
        db.session.add(
            Supplier(supplier_id=3, name=u"Unpublished Service")
        )
        self.setup_dummy_service(
            service_id='1000000001', supplier_id=1
        )
        self.setup_dummy_service(
            service_id='1000000004', supplier_id=1, status='enabled'
        )
        self.setup_dummy_service(
            service_id='1000000002', supplier_id=2, framework_id=2
        )
        self.setup_dummy_service(
            service_id='1000000003', supplier_id=3, status='enabled'
        )

    def test_invalid_framework_returns_400(self):
        response = self.client.get('/suppliers?framework=invalid!')
        assert response.status_code == 400

    def test_should_return_suppliers_on_framework_backwards_compatibility(self):
        # TODO: REMOVE WHEN BUYER APP IS UPDATED
        response = self.client.get('/suppliers?framework=gcloud')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 1
        assert data['suppliers'][0]['name'] == 'Active'

    def test_should_return_suppliers_on_framework(self):
        response = self.client.get('/suppliers?framework=g-cloud')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 1
        assert data['suppliers'][0]['name'] == 'Active'

    def test_should_return_no_suppliers_no_framework(self):
        response = self.client.get('/suppliers?framework=bad')
        data = json.loads(response.get_data())

        assert response.status_code == 200
        assert len(data['suppliers']) == 0

    def test_should_return_all_suppliers_if_no_framework(self):
        response = self.client.get('/suppliers')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 3


class TestListSuppliersByDunsNumber(BaseApplicationTest):

    def setup(self):
        super(TestListSuppliersByDunsNumber, self).setup()

        db.session.add(
            Supplier(supplier_id=1, name=u"Duns 123", duns_number="123")
        )
        db.session.add(
            Supplier(supplier_id=2, name=u"<NAME>", duns_number="xyz")
        )
        db.session.commit()

    def test_invalid_duns_number_returns_400(self):
        response = self.client.get('/suppliers?duns_number=invalid!')
        assert response.status_code == 400

    def test_should_return_suppliers_by_duns_number(self):
        response = self.client.get('/suppliers?duns_number=123')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 1
        assert data['suppliers'][0]['name'] == 'Duns 123'

    def test_should_return_no_suppliers_if_nonexisting_duns(self):
        response = self.client.get('/suppliers?duns_number=not-existing')
        data = json.loads(response.get_data())
        assert response.status_code == 200
        assert len(data['suppliers']) == 0

    def test_should_return_all_suppliers_if_no_duns_number(self):
        response = self.client.get('/suppliers')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 2


class TestPutSupplier(BaseApplicationTest, JSONTestMixin):
    method = "put"
    endpoint = "/suppliers/{self.supplier_id}"
    supplier = supplier_id = None

    def setup(self):
        super(TestPutSupplier, self).setup()
        self.supplier = load_example_listing("Supplier")
        self.supplier_id = self.supplier['id']

    def put_import_supplier(self, supplier, route_parameter=None):
        if route_parameter is None:
            route_parameter = '/{}'.format(supplier.get('id', 1))

        return self.client.put(
            '/suppliers' + route_parameter,
            data=json.dumps({
                'suppliers': supplier
            }),
            content_type='application/json')

    def test_add_a_new_supplier(self):
        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 201
        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()
        assert supplier.name == self.supplier['name']

    def test_reinserting_the_same_supplier(self):
        example_listing_contact_information = self.supplier['contactInformation']

        # Exact loop number is arbitrary
        for i in range(3):
            response = self.put_import_supplier(self.supplier)

            assert response.status_code == 201

            supplier = Supplier.query.filter(
                Supplier.supplier_id == self.supplier_id
            ).first()
            assert supplier.name == self.supplier['name']

            contact_informations = ContactInformation.query.filter(
                ContactInformation.supplier_id == supplier.supplier_id
            ).all()

            assert len(example_listing_contact_information) == len(contact_informations)

            # Contact Information without a supplier_id should not exist
            contact_informations_no_supplier_id = \
                ContactInformation.query.filter(
                    ContactInformation.supplier_id == None  # noqa
                ).all()

            assert len(contact_informations_no_supplier_id) == 0

    def test_cannot_put_to_root_suppliers_url(self):
        response = self.put_import_supplier(self.supplier, "")
        assert response.status_code == 405

    def test_supplier_json_id_does_not_match_route_id_parameter(self):
        response = self.put_import_supplier(self.supplier, '/1234567890')
        assert response.status_code == 400
        assert 'id parameter must match id in data' in json.loads(response.get_data())['error']

    def test_when_supplier_has_missing_contact_information(self):
        self.supplier.pop('contactInformation')

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['Invalid JSON must have', '\'contactInformation\'']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_has_malformed_contact_information(self):
        self.supplier['contactInformation'] = {'wobble': 'woo'}

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['JSON was not a valid format', 'is not of type', 'array']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_has_a_missing_key(self):
        self.supplier.pop('id')

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['JSON was not a valid format', '\'id\'', 'is a required property']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_has_missing_keys(self):
        # only one key is returned in the error message
        self.supplier.pop('id')
        self.supplier.pop('name')

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['JSON was not a valid format', '\'id\'', 'is a required property']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_contact_information_has_a_missing_key(self):
        self.supplier['contactInformation'][0].pop('email')

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['JSON was not a valid format', '\'email\'', 'is a required property']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_contact_information_has_missing_keys(self):
        # only one key is returned in the error message
        self.supplier['contactInformation'][0].pop('email')
        self.supplier['contactInformation'][0].pop('contactName')

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['JSON was not a valid format', '\'contactName\'', 'is a required property']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_has_extra_keys(self):
        self.supplier.update({'newKey': 1})

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        assert 'Additional properties are not allowed' in json.loads(response.get_data())['error']

    def test_when_supplier_contact_information_has_extra_keys(self):
        self.supplier['contactInformation'][0].update({'newKey': 1})

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        assert 'Additional properties are not allowed' in json.loads(response.get_data())['error']

    def test_supplier_duns_number_invalid(self):
        self.supplier.update({'dunsNumber': "only-digits-permitted"})

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['only-digits-permitted', 'does not match']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_contact_information_email_invalid(self):
        self.supplier['contactInformation'][0].update({'email': "bad-email-99"})

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['bad-email-99', 'is not a']:
            assert item in json.loads(response.get_data())['error']


class TestUpdateSupplier(BaseApplicationTest, JSONUpdateTestMixin):
    method = "post"
    endpoint = "/suppliers/{self.supplier_id}"
    supplier = supplier_id = None

    def setup(self):
        super(TestUpdateSupplier, self).setup()

        payload = load_example_listing("supplier_creation")
        self.supplier = payload

        response = self.client.post(
            '/suppliers',
            data=json.dumps({'suppliers': self.supplier}),
            content_type='application/json')
        assert response.status_code == 201
        self.supplier_id = json.loads(response.get_data())['suppliers']['id']

    def update_request(self, data=None, user=None, full_data=None):
        return self.client.post(
            '/suppliers/{}'.format(self.supplier_id),
            data=json.dumps({
                'suppliers': data,
                'updated_by': user or '<EMAIL>',
            } if full_data is None else full_data),
            content_type='application/json',
        )

    def test_empty_update_supplier(self):
        response = self.update_request({})
        assert response.status_code == 200

    def test_name_update(self):
        response = self.update_request({'name': "New Name"})
        assert response.status_code == 200

        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()

        assert supplier.name == "New Name"

    def test_supplier_update_creates_audit_event(self):
        self.update_request({'name': "Name"})

        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()

        audit = AuditEvent.query.filter(
            AuditEvent.object == supplier,
            AuditEvent.type == "supplier_update"
        ).first()

        assert audit.user == "<EMAIL>"
        assert audit.data == {
            'update': {'name': "Name"},
            'supplierId': supplier.supplier_id,
        }

    def test_update_response_matches_payload_plus_defaults(self):
        payload = load_example_listing("supplier_creation")
        response = self.update_request({'name': "New Name"})
        assert response.status_code == 200

        payload.update({'name': 'New Name'})
        supplier = json.loads(response.get_data())['suppliers']

        payload.pop('contactInformation')
        supplier.pop('contactInformation')
        supplier.pop('links')
        supplier.pop('id')

        payload_with_defaults = {**payload, 'companyDetailsConfirmed': False}

        assert supplier == payload_with_defaults

    def test_update_all_fields(self):
        response = self.update_request({
            "name": "<NAME>",
            "description": "New Description",
            "companiesHouseNumber": "AA123456",
            "dunsNumber": "010101",
            "otherCompanyRegistrationNumber": "A11",
            "registeredName": "<NAME>.",
            "registrationCountry": "country:GT",
            "vatNumber": "12312312",
            "organisationSize": "micro",
            "tradingStatus": "sole trader",
            "companyDetailsConfirmed": True,
        })

        assert response.status_code == 200

        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()

        assert supplier.name == '<NAME>'
        assert supplier.description == "New Description"
        assert supplier.duns_number == "010101"
        assert supplier.companies_house_number == "AA123456"
        assert supplier.other_company_registration_number == "A11"
        assert supplier.registered_name == "<NAME>."
        assert supplier.registration_country == "country:GT"
        assert supplier.vat_number == "12312312"
        assert supplier.organisation_size == "micro"
        assert supplier.trading_status == "sole trader"
        assert supplier.company_details_confirmed is True

    def test_supplier_json_id_does_not_match_original_id(self):
        response = self.update_request({
            'id': self.supplier_id + 1,
            'name': "<NAME>"
        })

        assert response.status_code == 400

    def test_update_missing_supplier(self):
        response = self.client.post(
            '/suppliers/234567',
            data=json.dumps({'suppliers': {}}),
            content_type='application/json',
        )

        assert response.status_code == 404

    def test_links_and_contact_information_are_ignored(self):
        response = self.update_request(full_data={'suppliers': {
            'name': "<NAME>",
            'contactInformation': [],
            'links': [],
        }, 'links': [], 'updated_by': '<EMAIL>'})

        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()

        assert response.status_code == 200
        assert len(supplier.contact_information) == 2

    def test_update_with_unexpected_keys(self):
        response = self.update_request({
            'new_key': "value",
            'name': "<NAME>"
        })

        assert response.status_code == 400

    def test_update_without_updated_by(self):
        response = self.update_request(full_data={
            'suppliers': {'name': "<NAME>"},
        })

        assert response.status_code == 400

    def test_update_chid_with_none(self):
        response = self.update_request({"companiesHouseNumber": None})
        assert response.status_code == 200

        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()

        assert supplier.companies_house_number is None

    def test_update_with_bad_company_number(self):
        response = self.update_request({"companiesHouseNumber": "ABCDEFGH"})
        assert response.status_code == 400
        assert "Invalid companies house number" in response.get_data(as_text=True)

    def test_update_with_bad_company_size(self):
        response = self.update_request({"organisationSize": "tiny"})
        assert response.status_code == 400
        assert "Invalid organisation size" in response.get_data(as_text=True)

    def test_update_with_bad_trading_status(self):
        response = self.update_request({"tradingStatus": "invalid"})
        assert response.status_code == 400
        assert "Invalid trading status" in response.get_data(as_text=True)

    def test_update_succeeds_with_null_other_company_registration_number(self):
        response = self.update_request({"otherCompanyRegistrationNumber": None})
        assert response.status_code == 200

    @pytest.mark.parametrize('trading_status', filter(lambda x: x, Supplier.TRADING_STATUSES))
    def test_update_succeeds_with_valid_trading_status(self, trading_status):
        response = self.update_request({"tradingStatus": trading_status})
        assert response.status_code == 200

    def test_update_with_bad_registration_country(self):
        # Country picker data is in the format "country:gb"
        response = self.update_request({"registrationCountry": "Wales"})
        assert response.status_code == 400
        assert "Invalid registration country" in response.get_data(as_text=True)

    @pytest.mark.parametrize('country_code, expected_response',
                             (("country:GB", 200),
                              ("country:gb", 400),
                              ("country:GBA", 400),
                              ("country:AB-12", 400),
                              ("territory:AX", 200),
                              ("territory:ax", 400),
                              ("territory:GBA", 200),
                              ("territory:gba", 400),
                              ("territory:AB-12", 200),
                              ("territory:ab-12", 400),
                              ("territory:AB-CD", 200),
                              ("territory:ab-cd", 400),
                              ("territory:ABC-12", 200),
                              ("territory:AB-123", 400),
                              ("Wales", 400),
                              ))
    def test_update_with_registration_countries(self, country_code, expected_response):
        response = self.update_request({"registrationCountry": country_code})
        error_message_is_displayed = "Invalid registration country" in response.get_data(as_text=True)
        assert response.status_code == expected_response
        if expected_response == 200:
            assert not error_message_is_displayed
        else:
            assert error_message_is_displayed


class TestUpdateContactInformation(BaseApplicationTest, JSONUpdateTestMixin):
    method = "post"
    endpoint = "/suppliers/{self.supplier_id}/contact-information/{self.contact_id}"
    supplier = supplier_id = contact_id = None

    def setup(self):
        super(TestUpdateContactInformation, self).setup()

        payload = load_example_listing("supplier_creation")
        self.supplier = payload

        response = self.client.post(
            '/suppliers',
            data=json.dumps({'suppliers': self.supplier}),
            content_type='application/json')
        assert response.status_code == 201
        supplier = json.loads(response.get_data())['suppliers']
        self.supplier_id = supplier['id']
        self.contact_id = supplier['contactInformation'][0]['id']

    def update_request(self, data=None, user=None, full_data=None):
        return self.client.post(
            '/suppliers/{}/contact-information/{}'.format(self.supplier_id, self.contact_id),
            data=json.dumps({
                'contactInformation': data,
                'updated_by': user or '<EMAIL>',
            } if full_data is None else full_data),
            content_type='application/json',
        )

    def test_empty_update(self):
        response = self.update_request({})
        assert response.status_code == 200

    def test_simple_field_update(self):
        response = self.update_request({
            'city': "New City"
        })
        assert response.status_code == 200

        contact = ContactInformation.query.filter(
            ContactInformation.id == self.contact_id
        ).first()

        assert contact.city == "New City"

    def test_simple_field_update_for_supplier_with_no_companies_house_number(self):
        supplier_db = Supplier.query.first()  # Only one supplier is set up for these tests
        supplier_db.companies_house_number = None

        response = self.update_request({
            'city': "New City"
        })
        assert response.status_code == 200

        contact = ContactInformation.query.filter(
            ContactInformation.id == self.contact_id
        ).first()

        assert contact.city == "New City"

    def test_update_creates_audit_event(self):
        self.update_request({
            'city': "New City"
        })

        contact = ContactInformation.query.filter(
            ContactInformation.id == self.contact_id
        ).first()

        audit = AuditEvent.query.filter(
            AuditEvent.object == contact.supplier,
            AuditEvent.type == "contact_update"
        ).first()

        assert audit.user == "<EMAIL>"
        assert audit.data == {
            'update': {'city': "New City"},
            'supplierId': contact.supplier.supplier_id,
        }

    def test_update_response_matches_payload(self):
        payload = load_example_listing("Supplier")
        response = self.update_request({
            'city': "New City"
        })
        assert response.status_code == 200

        payload = payload['contactInformation'][0]
        payload.update({'city': 'New City'})
        payload.pop('links')
        contact = json.loads(response.get_data())['contactInformation']
        contact.pop('id')
        contact.pop('links')

        assert contact == payload

    def test_update_all_fields(self):
        response = self.update_request({
            "contactName": "<NAME>",
            "phoneNumber": "New phone",
            "email": "<EMAIL>",
            "address1": "New address1",
            "city": "New city",
            "postcode": "New postcode",
        })

        assert response.status_code == 200

        contact = ContactInformation.query.filter(
            ContactInformation.id == self.contact_id
        ).first()

        assert contact.contact_name == "<NAME>"
        assert contact.phone_number == "New phone"
        assert contact.email == "<EMAIL>"
        assert contact.address1 == "New address1"
        assert contact.city == "New city"
        assert contact.postcode == "New postcode"

    def test_supplier_json_id_does_not_match_oiginal_id(self):
        response = self.update_request({
            'supplierId': 234567,
            'city': "New City"
        })

        assert response.status_code == 400

    def test_json_id_does_not_match_oiginal_id(self):
        response = self.update_request({
            'id': 2,
            'city': "New City"
        })

        assert response.status_code == 400

    def test_update_missing_supplier(self):
        response = self.client.post(
            '/suppliers/234567/contact-information/%s' % self.contact_id,
            data=json.dumps({}),
            content_type='application/json',
        )

        assert response.status_code == 404

    def test_update_missing_contact_information(self):
        response = self.client.post(
            '/suppliers/123456/contact-information/100000',
            data=json.dumps({'contactInformation': {}}),
            content_type='application/json',
        )

        assert response.status_code == 404

    def test_update_with_unexpected_keys(self):
        response = self.update_request({
            'new_key': "value",
            'city': "New City"
        })

        assert response.status_code == 400

    def test_update_ignores_links(self):
        response = self.update_request({
            'links': "value",
            'city': "New City"
        })

        assert response.status_code == 200

    def test_update_without_updated_by(self):
        response = self.update_request(full_data={
            'contactInformation': {'city': "New City"},
        })

        assert response.status_code == 400


class TestSetSupplierDeclarations(BaseApplicationTest, FixtureMixin, JSONUpdateTestMixin):
    method = 'put'
    endpoint = '/suppliers/0/frameworks/g-cloud-4/declaration'

    def setup(self):
        super(TestSetSupplierDeclarations, self).setup()
        # This is automatically removed in BaseApplicationTest.teardown
        framework = Framework(
            id=100,
            slug='test-open',
            name='Test open',
            framework='g-cloud',
            status='open',
            has_direct_award=True,
            has_further_competition=False)
        db.session.add(framework)
        db.session.commit()
        self.setup_dummy_suppliers(1)

    def test_add_new_declaration(self):<commit_msg>Test for /suppliers/{}/contact-information/{}/remove-personal-data view<commit_after>from datetime import datetime

from flask import json
from freezegun import freeze_time
import pytest

from app import db
from app.models import Supplier, ContactInformation, AuditEvent, \
    SupplierFramework, Framework, FrameworkAgreement, DraftService, Service
from mock import mock
from sqlalchemy.exc import DataError, IntegrityError
from tests.bases import BaseApplicationTest, JSONTestMixin, JSONUpdateTestMixin
from tests.helpers import fixture_params, FixtureMixin, load_example_listing


class TestGetSupplier(BaseApplicationTest, FixtureMixin):
    supplier = supplier_id = None

    def setup(self):
        super(TestGetSupplier, self).setup()

        payload = load_example_listing("supplier_creation")
        self.supplier = payload

        response = self.client.post(
            '/suppliers',
            data=json.dumps({
                'suppliers': self.supplier
            }),
            content_type='application/json')
        assert response.status_code == 201
        self.supplier_id = json.loads(response.get_data())['suppliers']['id']

    def test_get_non_existent_supplier(self):
        response = self.client.get('/suppliers/100')
        assert response.status_code == 404

    def test_invalid_supplier_id(self):
        response = self.client.get('/suppliers/abc123')
        assert response.status_code == 404

    def test_get_supplier(self):
        response = self.client.get('/suppliers/{}'.format(self.supplier_id))

        data = json.loads(response.get_data())
        assert response.status_code == 200
        assert self.supplier_id == data['suppliers']['id']
        assert self.supplier['name'] == data['suppliers']['name']

    def test_get_supplier_returns_service_counts(self):
        self.setup_dummy_services(
            5, supplier_id=self.supplier_id, framework_id=1
        )
        self.setup_dummy_services(
            10, start_id=5, supplier_id=self.supplier_id, framework_id=2
        )
        self.setup_dummy_services(
            15, start_id=15, supplier_id=self.supplier_id, framework_id=3
        )

        response = self.client.get('/suppliers/{}'.format(self.supplier_id))

        data = json.loads(response.get_data())
        assert data['suppliers']['service_counts'] == {
            u'G-Cloud 5': 15,
            u'G-Cloud 6': 5
        }


class TestListSuppliers(BaseApplicationTest, FixtureMixin):
    def setup(self):
        super(TestListSuppliers, self).setup()

        # Supplier names look like "Supplier {n}"
        self.setup_dummy_suppliers(7)

    def test_query_string_missing(self):
        response = self.client.get('/suppliers')
        assert response.status_code == 200

    def test_query_string_prefix_empty(self):
        response = self.client.get('/suppliers?prefix=')
        assert response.status_code == 200

    def test_query_string_prefix_returns_none(self):
        response = self.client.get('/suppliers?prefix=canada')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 0

    def test_other_prefix_returns_non_alphanumeric_suppliers(self):
        db.session.add(
            Supplier(supplier_id=999, name=u"999 Supplier")
        )
        self.setup_dummy_service(service_id='1230000000', supplier_id=999)

        response = self.client.get('/suppliers?prefix=other')

        data = json.loads(response.get_data())
        assert response.status_code == 200
        assert len(data['suppliers']) == 1
        assert data['suppliers'][0]['id'] == 999
        assert u"999 Supplier" == data['suppliers'][0]['name']

    def test_query_string_prefix_returns_paginated_page_one(self):
        response = self.client.get('/suppliers?prefix=s')
        data = json.loads(response.get_data())

        assert response.status_code == 200
        assert len(data['suppliers']) == 5
        next_link = data['links']['next']
        assert 'page=2' in next_link

    def test_query_string_prefix_returns_paginated_page_two(self):
        response = self.client.get('/suppliers?prefix=s&page=2')
        data = json.loads(response.get_data())

        assert response.status_code == 200
        assert len(data['suppliers']) == 2
        prev_link = data['links']['prev']
        assert 'page=1' in prev_link

    def test_query_string_prefix_returns_no_pagination_for_single_page(self):
        self.setup_additional_dummy_suppliers(5, 'T')
        response = self.client.get('/suppliers?prefix=t')
        data = json.loads(response.get_data())

        assert response.status_code == 200
        assert len(data['suppliers']) == 5
        assert ['self'] == list(data['links'].keys())

    def test_query_string_prefix_page_out_of_range(self):
        response = self.client.get('/suppliers?prefix=s&page=10')

        assert response.status_code == 404

    def test_query_string_prefix_invalid_page_argument(self):
        response = self.client.get('/suppliers?prefix=s&page=a')

        assert response.status_code == 400

    def test_below_one_page_number_is_404(self):
        response = self.client.get('/suppliers?page=0')

        assert response.status_code == 404


class TestListSuppliersOnFramework(BaseApplicationTest, FixtureMixin):

    def setup(self):
        super(TestListSuppliersOnFramework, self).setup()

        db.session.add(
            Supplier(supplier_id=1, name=u"Active")
        )
        db.session.add(
            Supplier(supplier_id=2, name=u"Inactive Framework")
        )
        db.session.add(
            Supplier(supplier_id=3, name=u"Unpublished Service")
        )
        self.setup_dummy_service(
            service_id='1000000001', supplier_id=1
        )
        self.setup_dummy_service(
            service_id='1000000004', supplier_id=1, status='enabled'
        )
        self.setup_dummy_service(
            service_id='1000000002', supplier_id=2, framework_id=2
        )
        self.setup_dummy_service(
            service_id='1000000003', supplier_id=3, status='enabled'
        )

    def test_invalid_framework_returns_400(self):
        response = self.client.get('/suppliers?framework=invalid!')
        assert response.status_code == 400

    def test_should_return_suppliers_on_framework_backwards_compatibility(self):
        # TODO: REMOVE WHEN BUYER APP IS UPDATED
        response = self.client.get('/suppliers?framework=gcloud')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 1
        assert data['suppliers'][0]['name'] == 'Active'

    def test_should_return_suppliers_on_framework(self):
        response = self.client.get('/suppliers?framework=g-cloud')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 1
        assert data['suppliers'][0]['name'] == 'Active'

    def test_should_return_no_suppliers_no_framework(self):
        response = self.client.get('/suppliers?framework=bad')
        data = json.loads(response.get_data())

        assert response.status_code == 200
        assert len(data['suppliers']) == 0

    def test_should_return_all_suppliers_if_no_framework(self):
        response = self.client.get('/suppliers')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 3


class TestListSuppliersByDunsNumber(BaseApplicationTest):

    def setup(self):
        super(TestListSuppliersByDunsNumber, self).setup()

        db.session.add(
            Supplier(supplier_id=1, name=u"Duns 123", duns_number="123")
        )
        db.session.add(
            Supplier(supplier_id=2, name=u"<NAME>", duns_number="xyz")
        )
        db.session.commit()

    def test_invalid_duns_number_returns_400(self):
        response = self.client.get('/suppliers?duns_number=invalid!')
        assert response.status_code == 400

    def test_should_return_suppliers_by_duns_number(self):
        response = self.client.get('/suppliers?duns_number=123')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 1
        assert data['suppliers'][0]['name'] == 'Duns 123'

    def test_should_return_no_suppliers_if_nonexisting_duns(self):
        response = self.client.get('/suppliers?duns_number=not-existing')
        data = json.loads(response.get_data())
        assert response.status_code == 200
        assert len(data['suppliers']) == 0

    def test_should_return_all_suppliers_if_no_duns_number(self):
        response = self.client.get('/suppliers')
        assert response.status_code == 200
        data = json.loads(response.get_data())
        assert len(data['suppliers']) == 2


class TestPutSupplier(BaseApplicationTest, JSONTestMixin):
    method = "put"
    endpoint = "/suppliers/{self.supplier_id}"
    supplier = supplier_id = None

    def setup(self):
        super(TestPutSupplier, self).setup()
        self.supplier = load_example_listing("Supplier")
        self.supplier_id = self.supplier['id']

    def put_import_supplier(self, supplier, route_parameter=None):
        if route_parameter is None:
            route_parameter = '/{}'.format(supplier.get('id', 1))

        return self.client.put(
            '/suppliers' + route_parameter,
            data=json.dumps({
                'suppliers': supplier
            }),
            content_type='application/json')

    def test_add_a_new_supplier(self):
        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 201
        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()
        assert supplier.name == self.supplier['name']

    def test_reinserting_the_same_supplier(self):
        example_listing_contact_information = self.supplier['contactInformation']

        # Exact loop number is arbitrary
        for i in range(3):
            response = self.put_import_supplier(self.supplier)

            assert response.status_code == 201

            supplier = Supplier.query.filter(
                Supplier.supplier_id == self.supplier_id
            ).first()
            assert supplier.name == self.supplier['name']

            contact_informations = ContactInformation.query.filter(
                ContactInformation.supplier_id == supplier.supplier_id
            ).all()

            assert len(example_listing_contact_information) == len(contact_informations)

            # Contact Information without a supplier_id should not exist
            contact_informations_no_supplier_id = \
                ContactInformation.query.filter(
                    ContactInformation.supplier_id == None  # noqa
                ).all()

            assert len(contact_informations_no_supplier_id) == 0

    def test_cannot_put_to_root_suppliers_url(self):
        response = self.put_import_supplier(self.supplier, "")
        assert response.status_code == 405

    def test_supplier_json_id_does_not_match_route_id_parameter(self):
        response = self.put_import_supplier(self.supplier, '/1234567890')
        assert response.status_code == 400
        assert 'id parameter must match id in data' in json.loads(response.get_data())['error']

    def test_when_supplier_has_missing_contact_information(self):
        self.supplier.pop('contactInformation')

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['Invalid JSON must have', '\'contactInformation\'']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_has_malformed_contact_information(self):
        self.supplier['contactInformation'] = {'wobble': 'woo'}

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['JSON was not a valid format', 'is not of type', 'array']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_has_a_missing_key(self):
        self.supplier.pop('id')

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['JSON was not a valid format', '\'id\'', 'is a required property']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_has_missing_keys(self):
        # only one key is returned in the error message
        self.supplier.pop('id')
        self.supplier.pop('name')

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['JSON was not a valid format', '\'id\'', 'is a required property']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_contact_information_has_a_missing_key(self):
        self.supplier['contactInformation'][0].pop('email')

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['JSON was not a valid format', '\'email\'', 'is a required property']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_contact_information_has_missing_keys(self):
        # only one key is returned in the error message
        self.supplier['contactInformation'][0].pop('email')
        self.supplier['contactInformation'][0].pop('contactName')

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['JSON was not a valid format', '\'contactName\'', 'is a required property']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_has_extra_keys(self):
        self.supplier.update({'newKey': 1})

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        assert 'Additional properties are not allowed' in json.loads(response.get_data())['error']

    def test_when_supplier_contact_information_has_extra_keys(self):
        self.supplier['contactInformation'][0].update({'newKey': 1})

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        assert 'Additional properties are not allowed' in json.loads(response.get_data())['error']

    def test_supplier_duns_number_invalid(self):
        self.supplier.update({'dunsNumber': "only-digits-permitted"})

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['only-digits-permitted', 'does not match']:
            assert item in json.loads(response.get_data())['error']

    def test_when_supplier_contact_information_email_invalid(self):
        self.supplier['contactInformation'][0].update({'email': "bad-email-99"})

        response = self.put_import_supplier(self.supplier)
        assert response.status_code == 400
        for item in ['bad-email-99', 'is not a']:
            assert item in json.loads(response.get_data())['error']


class TestUpdateSupplier(BaseApplicationTest, JSONUpdateTestMixin):
    method = "post"
    endpoint = "/suppliers/{self.supplier_id}"
    supplier = supplier_id = None

    def setup(self):
        super(TestUpdateSupplier, self).setup()

        payload = load_example_listing("supplier_creation")
        self.supplier = payload

        response = self.client.post(
            '/suppliers',
            data=json.dumps({'suppliers': self.supplier}),
            content_type='application/json')
        assert response.status_code == 201
        self.supplier_id = json.loads(response.get_data())['suppliers']['id']

    def update_request(self, data=None, user=None, full_data=None):
        return self.client.post(
            '/suppliers/{}'.format(self.supplier_id),
            data=json.dumps({
                'suppliers': data,
                'updated_by': user or '<EMAIL>',
            } if full_data is None else full_data),
            content_type='application/json',
        )

    def test_empty_update_supplier(self):
        response = self.update_request({})
        assert response.status_code == 200

    def test_name_update(self):
        response = self.update_request({'name': "New Name"})
        assert response.status_code == 200

        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()

        assert supplier.name == "New Name"

    def test_supplier_update_creates_audit_event(self):
        self.update_request({'name': "Name"})

        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()

        audit = AuditEvent.query.filter(
            AuditEvent.object == supplier,
            AuditEvent.type == "supplier_update"
        ).first()

        assert audit.user == "<EMAIL>"
        assert audit.data == {
            'update': {'name': "Name"},
            'supplierId': supplier.supplier_id,
        }

    def test_update_response_matches_payload_plus_defaults(self):
        payload = load_example_listing("supplier_creation")
        response = self.update_request({'name': "<NAME>"})
        assert response.status_code == 200

        payload.update({'name': '<NAME>'})
        supplier = json.loads(response.get_data())['suppliers']

        payload.pop('contactInformation')
        supplier.pop('contactInformation')
        supplier.pop('links')
        supplier.pop('id')

        payload_with_defaults = {**payload, 'companyDetailsConfirmed': False}

        assert supplier == payload_with_defaults

    def test_update_all_fields(self):
        response = self.update_request({
            "name": "<NAME>",
            "description": "New Description",
            "companiesHouseNumber": "AA123456",
            "dunsNumber": "010101",
            "otherCompanyRegistrationNumber": "A11",
            "registeredName": "<NAME>.",
            "registrationCountry": "country:GT",
            "vatNumber": "12312312",
            "organisationSize": "micro",
            "tradingStatus": "sole trader",
            "companyDetailsConfirmed": True,
        })

        assert response.status_code == 200

        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()

        assert supplier.name == '<NAME>'
        assert supplier.description == "New Description"
        assert supplier.duns_number == "010101"
        assert supplier.companies_house_number == "AA123456"
        assert supplier.other_company_registration_number == "A11"
        assert supplier.registered_name == "<NAME>."
        assert supplier.registration_country == "country:GT"
        assert supplier.vat_number == "12312312"
        assert supplier.organisation_size == "micro"
        assert supplier.trading_status == "sole trader"
        assert supplier.company_details_confirmed is True

    def test_supplier_json_id_does_not_match_original_id(self):
        response = self.update_request({
            'id': self.supplier_id + 1,
            'name': "<NAME>"
        })

        assert response.status_code == 400

    def test_update_missing_supplier(self):
        response = self.client.post(
            '/suppliers/234567',
            data=json.dumps({'suppliers': {}}),
            content_type='application/json',
        )

        assert response.status_code == 404

    def test_links_and_contact_information_are_ignored(self):
        response = self.update_request(full_data={'suppliers': {
            'name': "<NAME>",
            'contactInformation': [],
            'links': [],
        }, 'links': [], 'updated_by': '<EMAIL>'})

        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()

        assert response.status_code == 200
        assert len(supplier.contact_information) == 2

    def test_update_with_unexpected_keys(self):
        response = self.update_request({
            'new_key': "value",
            'name': "<NAME>"
        })

        assert response.status_code == 400

    def test_update_without_updated_by(self):
        response = self.update_request(full_data={
            'suppliers': {'name': "<NAME>"},
        })

        assert response.status_code == 400

    def test_update_chid_with_none(self):
        response = self.update_request({"companiesHouseNumber": None})
        assert response.status_code == 200

        supplier = Supplier.query.filter(
            Supplier.supplier_id == self.supplier_id
        ).first()

        assert supplier.companies_house_number is None

    def test_update_with_bad_company_number(self):
        response = self.update_request({"companiesHouseNumber": "ABCDEFGH"})
        assert response.status_code == 400
        assert "Invalid companies house number" in response.get_data(as_text=True)

    def test_update_with_bad_company_size(self):
        response = self.update_request({"organisationSize": "tiny"})
        assert response.status_code == 400
        assert "Invalid organisation size" in response.get_data(as_text=True)

    def test_update_with_bad_trading_status(self):
        response = self.update_request({"tradingStatus": "invalid"})
        assert response.status_code == 400
        assert "Invalid trading status" in response.get_data(as_text=True)

    def test_update_succeeds_with_null_other_company_registration_number(self):
        response = self.update_request({"otherCompanyRegistrationNumber": None})
        assert response.status_code == 200

    @pytest.mark.parametrize('trading_status', filter(lambda x: x, Supplier.TRADING_STATUSES))
    def test_update_succeeds_with_valid_trading_status(self, trading_status):
        response = self.update_request({"tradingStatus": trading_status})
        assert response.status_code == 200

    def test_update_with_bad_registration_country(self):
        # Country picker data is in the format "country:gb"
        response = self.update_request({"registrationCountry": "Wales"})
        assert response.status_code == 400
        assert "Invalid registration country" in response.get_data(as_text=True)

    @pytest.mark.parametrize('country_code, expected_response',
                             (("country:GB", 200),
                              ("country:gb", 400),
                              ("country:GBA", 400),
                              ("country:AB-12", 400),
                              ("territory:AX", 200),
                              ("territory:ax", 400),
                              ("territory:GBA", 200),
                              ("territory:gba", 400),
                              ("territory:AB-12", 200),
                              ("territory:ab-12", 400),
                              ("territory:AB-CD", 200),
                              ("territory:ab-cd", 400),
                              ("territory:ABC-12", 200),
                              ("territory:AB-123", 400),
                              ("Wales", 400),
                              ))
    def test_update_with_registration_countries(self, country_code, expected_response):
        response = self.update_request({"registrationCountry": country_code})
        error_message_is_displayed = "Invalid registration country" in response.get_data(as_text=True)
        assert response.status_code == expected_response
        if expected_response == 200:
            assert not error_message_is_displayed
        else:
            assert error_message_is_displayed


class TestUpdateContactInformation(BaseApplicationTest, JSONUpdateTestMixin):
    method = "post"
    endpoint = "/suppliers/{self.supplier_id}/contact-information/{self.contact_id}"
    supplier = supplier_id = contact_id = None

    def setup(self):
        super(TestUpdateContactInformation, self).setup()

        payload = load_example_listing("supplier_creation")
        self.supplier = payload

        response = self.client.post(
            '/suppliers',
            data=json.dumps({'suppliers': self.supplier}),
            content_type='application/json')
        assert response.status_code == 201
        supplier = json.loads(response.get_data())['suppliers']
        self.supplier_id = supplier['id']
        self.contact_id = supplier['contactInformation'][0]['id']

    def update_request(self, data=None, user=None, full_data=None):
        return self.client.post(
            '/suppliers/{}/contact-information/{}'.format(self.supplier_id, self.contact_id),
            data=json.dumps({
                'contactInformation': data,
                'updated_by': user or '<EMAIL>',
            } if full_data is None else full_data),
            content_type='application/json',
        )

    def test_empty_update(self):
        response = self.update_request({})
        assert response.status_code == 200

    def test_simple_field_update(self):
        response = self.update_request({
            'city': "New City"
        })
        assert response.status_code == 200

        contact = ContactInformation.query.filter(
            ContactInformation.id == self.contact_id
        ).first()

        assert contact.city == "New City"

    def test_simple_field_update_for_supplier_with_no_companies_house_number(self):
        supplier_db = Supplier.query.first()  # Only one supplier is set up for these tests
        supplier_db.companies_house_number = None

        response = self.update_request({
            'city': "New City"
        })
        assert response.status_code == 200

        contact = ContactInformation.query.filter(
            ContactInformation.id == self.contact_id
        ).first()

        assert contact.city == "New City"

    def test_update_creates_audit_event(self):
        self.update_request({
            'city': "New City"
        })

        contact = ContactInformation.query.filter(
            ContactInformation.id == self.contact_id
        ).first()

        audit = AuditEvent.query.filter(
            AuditEvent.object == contact.supplier,
            AuditEvent.type == "contact_update"
        ).first()

        assert audit.user == "<EMAIL>"
        assert audit.data == {
            'update': {'city': "New City"},
            'supplierId': contact.supplier.supplier_id,
        }

    def test_update_response_matches_payload(self):
        payload = load_example_listing("Supplier")
        response = self.update_request({
            'city': "New City"
        })
        assert response.status_code == 200

        payload = payload['contactInformation'][0]
        payload.update({'city': 'New City'})
        payload.pop('links')
        contact = json.loads(response.get_data())['contactInformation']
        contact.pop('id')
        contact.pop('links')

        assert contact == payload

    def test_update_all_fields(self):
        response = self.update_request({
            "contactName": "New contact",
            "phoneNumber": "New phone",
            "email": "new-<EMAIL>",
            "address1": "New address1",
            "city": "New city",
            "postcode": "New postcode",
        })

        assert response.status_code == 200

        contact = ContactInformation.query.filter(
            ContactInformation.id == self.contact_id
        ).first()

        assert contact.contact_name == "New contact"
        assert contact.phone_number == "New phone"
        assert contact.email == "<EMAIL>"
        assert contact.address1 == "New address1"
        assert contact.city == "New city"
        assert contact.postcode == "New postcode"

    def test_supplier_json_id_does_not_match_oiginal_id(self):
        response = self.update_request({
            'supplierId': 234567,
            'city': "New City"
        })

        assert response.status_code == 400

    def test_json_id_does_not_match_oiginal_id(self):
        response = self.update_request({
            'id': 2,
            'city': "New City"
        })

        assert response.status_code == 400

    def test_update_missing_supplier(self):
        response = self.client.post(
            '/suppliers/234567/contact-information/%s' % self.contact_id,
            data=json.dumps({}),
            content_type='application/json',
        )

        assert response.status_code == 404

    def test_update_missing_contact_information(self):
        response = self.client.post(
            '/suppliers/123456/contact-information/100000',
            data=json.dumps({'contactInformation': {}}),
            content_type='application/json',
        )

        assert response.status_code == 404

    def test_update_with_unexpected_keys(self):
        response = self.update_request({
            'new_key': "value",
            'city': "New City"
        })

        assert response.status_code == 400

    def test_update_ignores_links(self):
        response = self.update_request({
            'links': "value",
            'city': "New City"
        })

        assert response.status_code == 200

    def test_update_without_updated_by(self):
        response = self.update_request(full_data={
            'contactInformation': {'city': "New City"},
        })

        assert response.status_code == 400


class TestRemoveContactInformationPersonalData(BaseApplicationTest):

    def setup(self):
        super(TestRemoveContactInformationPersonalData, self).setup()
        self.supplier = Supplier(name="<NAME>", organisation_size="micro", supplier_id=11111)
        self.contact_information = ContactInformation(
            supplier_id=11111,
            contact_name='<NAME>',
            phone_number='Test Number',
            email='<EMAIL>',
            address1='Test address line 1',
            city='Test city',
            postcode='Test Postcode'
        )
        db.session.add_all([self.supplier, self.contact_information])
        db.session.commit()

    def test_remove_contact_information_personal_data(self):
        url = '/suppliers/{}/contact-information/{}/remove-personal-data'.format(
            self.supplier.supplier_id,
            self.contact_information.id
        )
        response = self.client.post(
            url,
            data=json.dumps({'updated_by': '<EMAIL>'}),
            content_type='application/json'
        )
        assert response.status_code == 200

        data = json.loads(response.get_data())

        assert data['contactInformation']['address1'] == '<removed>'
        assert data['contactInformation']['city'] == '<removed>'
        assert data['contactInformation']['contactName'] == '<removed>'
        assert data['contactInformation']['email'] == '<removed>'
        assert data['contactInformation']['phoneNumber'] == '<removed>'
        assert data['contactInformation']['postcode'] == '<removed>'

    def test_updated_by_required(self):
        url = '/suppliers/{}/contact-information/{}/remove-personal-data'.format(
            self.supplier.supplier_id,
            self.contact_information.id
        )
        response = self.client.post(
            url,
            data=json.dumps({}),
            content_type='application/json'
        )
        assert response.status_code == 400

        data = json.loads(response.get_data())
        assert data['error'] == "JSON validation error: 'updated_by' is a required property"

    def test_remove_contact_information_personal_data_audit_event(self):
        url = '/suppliers/{}/contact-information/{}/remove-personal-data'.format(
            self.supplier.supplier_id,
            self.contact_information.id
        )
        response = self.client.post(
            url,
            data=json.dumps({'updated_by': '<EMAIL>'}),
            content_type='application/json'
        )

        assert response.status_code == 200
        assert AuditEvent.query.filter(
            AuditEvent.type == 'contact_update',
            AuditEvent.object_type == 'Supplier',
            AuditEvent.object_id == self.supplier.id
        ).count() == 1

    @pytest.mark.parametrize('error_class', (DataError, IntegrityError))
    def test_errors_on_commit(self, error_class):
        url = '/suppliers/{}/contact-information/{}/remove-personal-data'.format(
            self.supplier.supplier_id,
            self.contact_information.id
        )
        expected_error_message = (
            "Could not remove personal data from contact information: supplier_id {}, id {}"
        ).format(
            self.supplier.supplier_id,
            self.contact_information.id
        )

        with mock.patch('app.db.session.commit', side_effect=error_class("Unable to commit", orig=None, params={})):
            response = self.client.post(
                url,
                data=json.dumps({'updated_by': '<EMAIL>'}),
                content_type='application/json'
            )
        assert response.status_code == 400

        data = json.loads(response.get_data())

        assert data['error'] == expected_error_message


class TestSetSupplierDeclarations(BaseApplicationTest, FixtureMixin, JSONUpdateTestMixin):
    method = 'put'
    endpoint = '/suppliers/0/frameworks/g-cloud-4/declaration'

    def setup(self):
        super(TestSetSupplierDeclarations, self).setup()
        # This is automatically removed in BaseApplicationTest.teardown
        framework = Framework(
            id=100,
            slug='test-open',
            name='Test open',
            framework='g-cloud',
            status='open',
            has_direct_award=True,
            has_further_competition=False)
        db.session.add(framework)
        db.session.commit()
        self.setup_dummy_suppliers(1)

    def test_add_new_declaration(self):
{'fraction_non_alphanumeric': 0.11370280622215274, 'fraction_numerical': 0.016493394825295084, 'mean_word_length': 2.636911530589038, 'pattern_counts': {'":': 50, '<': 52, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 10, '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': '109314', 'n_tokens_mistral': 18987, 'n_tokens_neox': 17742, 'n_words': 3289}