Esempio n. 1
0
def get_framework_contract_title(frameworks_path: str,
                                 framework_slug: str) -> str:
    """The contract title is different for G-Cloud and DOS. Look up the correct name with the content loader"""
    content_loader = ContentLoader(frameworks_path)
    content_loader.load_messages(framework_slug, ["e-signature"])
    return str(
        content_loader.get_message(framework_slug, "e-signature",
                                   "framework_contract_title"))
def generate_schema(framework_slug, question_set, manifest_name):
    loader = ContentLoader("./")
    loader.load_manifest(
        framework_slug,
        question_set,
        manifest_name,
    )
    manifest = loader.get_manifest(
        framework_slug,
        manifest_name,
    )

    assessed_questions = tuple(question for question in chain.from_iterable(
        section.questions for section in manifest.sections)
                               if "passIfIn" in question.get("assessment", {}))

    discretionary_properties = {
        question.id: _enum_for_question(question)
        for question in assessed_questions
        if question["assessment"].get("discretionary")
    }
    baseline_properties = {
        question.id: _enum_for_question(question)
        for question in assessed_questions
        if not question["assessment"].get("discretionary")
    }

    return {
        "$schema":
        "http://json-schema.org/draft-07/schema#",  # hardcoded to draft 7 because jsonschema > 3 supports it
        "title":
        "{} Declaration Assessment Schema (Definite Pass Schema)".format(
            framework_slug),
        "type":
        "object",
        "allOf": [
            {
                "$ref": "#/definitions/baseline"
            },
            {
                "properties": discretionary_properties
            },
        ],
        "definitions": {
            "baseline": {
                "$schema":
                "http://json-schema.org/draft-04/schema#",
                "title":
                "{} Declaration Assessment Schema (Baseline Schema)".format(
                    framework_slug),
                "type":
                "object",
                "properties":
                baseline_properties,
            },
        },
    }
def _get_questions_by_type(framework_slug, doc_type, question_types):
    manifest_name = '{}_search_filters'.format(doc_type)
    loader = ContentLoader(_base_dir)
    loader.load_manifest(
        framework_slug,
        doc_type,
        manifest_name,
    )

    manifest = loader.get_manifest(framework_slug, manifest_name)
    return (q for q in sum((s.questions for s in manifest.sections), []) if q.type in question_types)
Esempio n. 4
0
def load_questions(schema_type, framework_slug, lot_slug):
    loader = ContentLoader('./')
    loader.load_manifest(framework_slug,
                         MANIFESTS[schema_type]['question_set'],
                         MANIFESTS[schema_type]['manifest'])

    manifest = loader.get_manifest(framework_slug,
                                   MANIFESTS[schema_type]['manifest']).filter(
                                       {'lot': lot_slug}, dynamic=False)
    return {
        q['id']: q
        for q in sum((s.questions for s in manifest.sections), [])
    }
def load_questions(schema_type, framework_slug, lot_slug):
    loader = ContentLoader('./')
    loader.load_manifest(
        framework_slug,
        MANIFESTS[schema_type]['question_set'],
        MANIFESTS[schema_type]['manifest']
    )

    manifest = loader.get_manifest(framework_slug, MANIFESTS[schema_type]['manifest']).filter(
        {'lot': lot_slug},
        dynamic=False
    )
    return {q['id']: q for q in sum((s.questions for s in manifest.sections), [])}
def generate_schema(framework_slug, question_set, manifest_name):
    loader = ContentLoader("./")
    loader.load_manifest(
        framework_slug,
        question_set,
        manifest_name,
    )
    manifest = loader.get_manifest(
        framework_slug,
        manifest_name,
    )

    assessed_questions = tuple(
        question
        for question in chain.from_iterable(section.questions for section in manifest.sections)
        if "passIfIn" in question.get("assessment", {})
    )

    discretionary_properties = {
        question.id: _enum_for_question(question)
        for question in assessed_questions if question["assessment"].get("discretionary")
    }
    baseline_properties = {
        question.id: _enum_for_question(question)
        for question in assessed_questions if not question["assessment"].get("discretionary")
    }

    return {
        "$schema": "http://json-schema.org/draft-07/schema#",  # hardcoded to draft 7 because jsonschema > 3 supports it
        "title": "{} Declaration Assessment Schema (Definite Pass Schema)".format(framework_slug),
        "type": "object",
        "allOf": [
            {"$ref": "#/definitions/baseline"},
            {"properties": discretionary_properties},
        ],
        "definitions": {
            "baseline": {
                "$schema": "http://json-schema.org/draft-04/schema#",
                "title": "{} Declaration Assessment Schema (Baseline Schema)".format(framework_slug),
                "type": "object",
                "properties": baseline_properties,
            },
        },
    }
Esempio n. 7
0
import glob

import mock
import pytest

from dmcontent import ContentLoader
from dmcontent.utils import TemplateField

content = ContentLoader('./')

MANIFEST_QUESTION_SET = {
    "display_brief": "briefs",
    "edit_brief": "briefs",
    "display_brief_response": "brief-responses",
    "legacy_display_brief_response": "brief-responses",
    "edit_brief_response": "brief-responses",
    "legacy_edit_brief_response": "brief-responses",
    "output_brief_response": "brief-responses",
    "legacy_output_brief_response": "brief-responses",
    "clarification_question": "clarification_question",
    "declaration": "declaration",
    "display_service": "services",
    "edit_service": "services",
    "edit_service_as_admin": "services",
    "edit_submission": "services",
    "search_filters": "services",
}

# `item` property wouldn't usually be part of a brief response but is included here as the dynamic list child question
# `evidence` relies on an `item` in its context to render
QUESTION_SET_CONTEXT = {
Esempio n. 8
0
def generate_schema(framework_slug, question_set, manifest_name):
    loader = ContentLoader(_base_dir)
    loader.load_manifest(
        framework_slug,
        question_set,
        manifest_name,
    )
    manifest = loader.get_manifest(
        framework_slug,
        manifest_name,
    )

    try:
        with open(
                os.path.join(
                    _base_dir,
                    "frameworks",
                    framework_slug,
                    "assessment",
                    manifest_name,
                    "extra.schema.json",
                ), "r") as f:
            extra_schema = json.load(f)
    except FileNotFoundError:
        extra_schema = {}

    assessed_questions = []
    for question in chain.from_iterable(section.questions
                                        for section in manifest.sections):
        if question.type == 'multiquestion':
            for nested_question in question.questions:
                if "passIfIn" in nested_question.get("assessment", {}):
                    assessed_questions.append(nested_question)
        elif "passIfIn" in question.get("assessment", {}):
            assessed_questions.append(question)

    discretionary_questions = tuple(q for q in assessed_questions
                                    if q["assessment"].get("discretionary"))
    baseline_questions = tuple(q for q in assessed_questions
                               if not q["assessment"].get("discretionary"))

    # we add all assessed_questions as "required" by default, not because it's the assessment schema's job to
    # enforce that requirement, but it's a stronger guarantee we keep the content, the tests for this function
    # and the custom validation in the supplier frontend in agreement with each other.

    generated_schema = {
        "$schema":
        "http://json-schema.org/draft-07/schema#",  # hardcoded to draft 7 because jsonschema > 3 supports it
        "title":
        "{} Declaration Assessment Schema (Definite Pass Schema)".format(
            framework_slug),
        "type":
        "object",
        "allOf": [
            {
                "$ref": "#/definitions/baseline"
            },
            {
                "properties":
                {q.id: _enum_for_question(q)
                 for q in discretionary_questions}
            },
        ],
        "required":
        sorted(q.id for q in discretionary_questions
               if q["assessment"].get("required", True)),
        "definitions": {
            "baseline": {
                "$schema":
                "http://json-schema.org/draft-04/schema#",
                "title":
                "{} Declaration Assessment Schema (Baseline Schema)".format(
                    framework_slug),
                "type":
                "object",
                "allOf": [
                    {
                        "properties": {
                            q.id: _enum_for_question(q)
                            for q in baseline_questions
                        }
                    },
                ],
                "required":
                sorted(q.id for q in baseline_questions
                       if q["assessment"].get("required", True)),
            },
        },
    }

    return always_merger.merge(generated_schema, extra_schema)
class BaseApplicationTest(object):
    injected_content_loader = ContentLoader('app/content')

    def setup_method(self, method):

        # We need to mock the API client in create_app, however we can't use patch the constructor,
        # as the DataAPIClient instance has already been created; nor can we temporarily replace app.data_api_client
        # with a mock, because then the shared instance won't have been configured (done in create_app). Instead,
        # just mock the one function that would make an API call in this case.
        data_api_client.find_frameworks = mock.Mock()
        # the value set here has an effect on the content that gets initially loaded by the content loader by default
        data_api_client.find_frameworks.return_value = self._get_frameworks_list_fixture_data()

        # if we don't make this tweak, the content loader will get re-built for every test, which is incredibly slow.
        # instead we replace the `_make_content_loader_factory` with a variant which injects `injected_content_loader`
        # as the `initial_instance` argument, which we keep as a class attribute. `_make_content_loader_factory` still
        # executes inside `create_app`, but all the content it asks to be loaded should already be present in the
        # content_loader it is operating on, so it effectively does nothing.
        # a test that needed a "clean" content loader for some reason would be able to override a test instance's
        # injected_content_loader early in the setup_method process (e.g. with None)
        self.make_content_loader_factory_mock = mock.patch("app._make_content_loader_factory")
        self.make_content_loader_factory_mock.start().side_effect = partial(
            _make_content_loader_factory,
            initial_instance=self.injected_content_loader,
        )

        self.app_env_var_mock = mock.patch.dict('gds_metrics.os.environ', {'PROMETHEUS_METRICS_PATH': '/_metrics'})
        self.app_env_var_mock.start()

        self.session_mock = mock.patch('dmutils.session.init_app')
        self.session_mock.start()
        self.app = create_app('test')
        self.client = self.app.test_client()

        self._s3_patch = mock.patch('dmutils.s3.S3')

        self.s3 = self._s3_patch.start()
        self.s3.return_value = mock.Mock()
        self.s3.return_value.list.return_value = []

        self._default_suffix_patch = mock.patch(
            'dmutils.documents.default_file_suffix',
            return_value='2015-01-01-1200'
        )
        self._default_suffix_patch.start()

    def teardown_method(self, method):
        self._s3_patch.stop()
        self._default_suffix_patch.stop()
        self.app_env_var_mock.stop()
        self.session_mock.stop()
        self.make_content_loader_factory_mock.stop()

    def load_example_listing(self, name):
        file_path = os.path.join("example_responses", "{}.json".format(name))
        with open(file_path) as f:
            return json.load(f)

    @staticmethod
    def strip_all_whitespace(content):
        pattern = re.compile(r'\s+')
        return re.sub(pattern, '', content)

    def assert_flashes(self, expected_message, expected_category='message'):
        with self.client.session_transaction() as session:
            if '_flashes' not in session:
                raise AssertionError('nothing flashed')
            messages = MultiDict(session['_flashes'])
            assert expected_message in messages.getlist(expected_category), \
                "Didn't find '{}' in '{}'".format(expected_message, messages.getlist(expected_category))

    def assert_no_flashes(self):
        with self.client.session_transaction() as session:
            assert '_flashes' not in session

    @staticmethod
    def _get_fixture_data(fixture_filename):
        test_root = os.path.abspath(
            os.path.join(os.path.dirname(__file__), ".")
        )
        fixture_path = os.path.join(
            test_root, 'fixtures', fixture_filename
        )
        with open(fixture_path) as fixture_file:
            return json.load(fixture_file)

    @staticmethod
    def _get_frameworks_list_fixture_data():
        return BaseApplicationTest._get_fixture_data('frameworks.json')
class BaseApplicationTest(object):
    injected_content_loader = ContentLoader('app/content')

    def setup_method(self, method):
        """
        A data_api_client instance is required for `create_app`, so we need some careful patching to initialise
        the Flask test client:
         - patch the .find_frameworks() method of `app.data_api_client` with the fixture
         - initialise the app with `create_app('test')`
         - in the tests, use a subclass of `BaseAPIClientMixin` above, with the path to the imported data_api_client
         - the .find_frameworks() return value will need to be provided separately in those tests, as the import
           path will (hopefully!) be different there.
        """
        self.app_env_var_mock = mock.patch.dict(
            'gds_metrics.os.environ', {'PROMETHEUS_METRICS_PATH': '/_metrics'})
        self.app_env_var_mock.start()

        self.session_mock = mock.patch('dmutils.session.init_app')
        self.session_mock.start()

        data_api_client.find_frameworks = mock.Mock()
        data_api_client.find_frameworks.return_value = self._get_frameworks_list_fixture_data(
        )

        # if we don't make this tweak, the content loader will get re-built for every test, which is incredibly slow.
        # instead we replace the `_make_content_loader_factory` with a variant which injects `injected_content_loader`
        # as the `initial_instance` argument, which we keep as a class attribute. `_make_content_loader_factory` still
        # executes inside `create_app`, but all the content it asks to be loaded should already be present in the
        # content_loader it is operating on, so it effectively does nothing.
        # a test that needed a "clean" content loader for some reason would be able to override a test instance's
        # injected_content_loader early in the setup_method process (e.g. with None)
        self.make_content_loader_factory_mock = mock.patch(
            "app._make_content_loader_factory")
        self.make_content_loader_factory_mock.start().side_effect = partial(
            _make_content_loader_factory,
            initial_instance=self.injected_content_loader,
        )

        self.app = create_app('test')
        self.app.register_blueprint(login_for_tests)
        self.client = self.app.test_client()
        self.get_user_patch = None

    def teardown_method(self, method):
        self.teardown_login()
        self.make_content_loader_factory_mock.stop()
        self.app_env_var_mock.stop()
        self.session_mock.stop()

    @staticmethod
    def user(id,
             email_address,
             supplier_id,
             supplier_name,
             name,
             is_token_valid=True,
             locked=False,
             active=True,
             role='buyer'):

        hours_offset = -1 if is_token_valid else 1
        date = datetime.utcnow() + timedelta(hours=hours_offset)
        password_changed_at = date.strftime(DATETIME_FORMAT)

        user = {
            "id": id,
            "emailAddress": email_address,
            "name": name,
            "role": role,
            "locked": locked,
            'active': active,
            'passwordChangedAt': password_changed_at
        }

        if supplier_id:
            supplier = {
                "supplierId": supplier_id,
                "name": supplier_name,
            }
            user['role'] = 'supplier'
            user['supplier'] = supplier
        return {"users": user}

    @staticmethod
    def _get_fixture_data(fixture_filename):
        test_root = os.path.abspath(
            os.path.join(os.path.dirname(__file__), "."))
        fixture_path = os.path.join(test_root, 'fixtures', fixture_filename)
        with open(fixture_path) as fixture_file:
            return json.load(fixture_file)

    @staticmethod
    def _get_search_results_fixture_data():
        return BaseApplicationTest._get_fixture_data(
            'search_results_fixture.json')

    @staticmethod
    def _get_g9_search_results_fixture_data():
        return BaseApplicationTest._get_fixture_data(
            'g9_search_results_fixture.json')

    @staticmethod
    def _get_search_results_multiple_page_fixture_data():
        return BaseApplicationTest._get_fixture_data(
            'search_results_multiple_pages_fixture.json')

    @staticmethod
    def _get_frameworks_list_fixture_data():
        return get_frameworks_list_fixture_data()

    @staticmethod
    def _get_g4_service_fixture_data():
        return BaseApplicationTest._get_fixture_data('g4_service_fixture.json')

    @staticmethod
    def _get_g5_service_fixture_data():
        return BaseApplicationTest._get_fixture_data('g5_service_fixture.json')

    @staticmethod
    def _get_g6_service_fixture_data():
        return BaseApplicationTest._get_fixture_data('g6_service_fixture.json')

    @staticmethod
    def _get_framework_fixture_data(framework_slug):
        return {
            'frameworks':
            next(f for f in BaseApplicationTest.
                 _get_frameworks_list_fixture_data()['frameworks']
                 if f['slug'] == framework_slug)
        }

    @staticmethod
    def _get_dos_brief_fixture_data(multi=False):
        if multi:
            return BaseApplicationTest._get_fixture_data(
                'dos_multiple_briefs_fixture.json')
        else:
            return BaseApplicationTest._get_fixture_data(
                'dos_brief_fixture.json')

    @staticmethod
    def _get_dos_brief_search_api_response_fixture_data():
        return BaseApplicationTest._get_fixture_data(
            'dos_brief_search_api_response.json')

    @staticmethod
    def _get_dos_brief_search_api_aggregations_response_outcomes_fixture_data(
    ):
        return BaseApplicationTest._get_fixture_data(
            'dos_brief_search_api_aggregations_response_outcomes.json')

    @staticmethod
    def _get_dos_brief_search_api_aggregations_response_specialists_fixture_data(
    ):
        return BaseApplicationTest._get_fixture_data(
            'dos_brief_search_api_aggregations_response_specialists.json')

    @staticmethod
    def _get_dos_brief_search_api_aggregations_response_user_research_fixture_data(
    ):
        return BaseApplicationTest._get_fixture_data(
            'dos_brief_search_api_aggregations_response_user_research.json')

    @staticmethod
    def _get_dos_brief_responses_fixture_data():
        return BaseApplicationTest._get_fixture_data(
            'dos_brief_responses_fixture.json')

    @staticmethod
    def _get_supplier_fixture_data():
        return BaseApplicationTest._get_fixture_data('supplier_fixture.json')

    @staticmethod
    def _get_supplier_with_minimum_fixture_data():
        return BaseApplicationTest._get_fixture_data(
            'supplier_fixture_with_minium_data.json')

    @staticmethod
    def _get_suppliers_by_prefix_fixture_data():
        return BaseApplicationTest._get_fixture_data(
            'suppliers_by_prefix_fixture.json')

    @staticmethod
    def _get_suppliers_by_prefix_fixture_data_page_2():
        return BaseApplicationTest._get_fixture_data(
            'suppliers_by_prefix_fixture_page_2.json')

    @staticmethod
    def _get_suppliers_by_prefix_fixture_with_next_and_prev():
        return BaseApplicationTest._get_fixture_data(
            'suppliers_by_prefix_fixture_page_with_next_and_prev.json')

    @staticmethod
    def _get_direct_award_project_list_fixture(**kwargs):
        return BaseApplicationTest._get_fixture_data(
            'direct_award_project_list_fixture.json')

    @staticmethod
    def _get_direct_award_project_list_xss_fixture(**kwargs):
        return BaseApplicationTest._get_fixture_data(
            'direct_award_project_list_xss_fixture.json')

    @staticmethod
    def _get_direct_award_project_fixture(**kwargs):
        project = BaseApplicationTest._get_fixture_data(
            'direct_award_project_fixture.json')

        for key, value in kwargs.items():
            if key in project['project']:
                project['project'][key] = value
            else:
                raise ValueError(
                    'Key "{}" does not exist in the Direct Award project fixture.'
                    .format(key))

        return project

    @staticmethod
    def _get_direct_award_lock_project_fixture():
        return BaseApplicationTest._get_direct_award_project_fixture(
            lockedAt="2017-09-08T00:00:00.000000Z")

    @staticmethod
    def _get_direct_award_not_lock_project_fixture():
        return BaseApplicationTest._get_direct_award_project_fixture(
            lockedAt=False)

    @staticmethod
    def _get_direct_award_project_outcome_awarded_fixture():
        return BaseApplicationTest._get_fixture_data(
            'direct_award_project_outcome_awarded_fixture.json')

    @staticmethod
    def _get_direct_award_project_completed_outcome_awarded_fixture():
        outcome = BaseApplicationTest._get_direct_award_project_outcome_awarded_fixture(
        )
        outcome['outcome']['completed'] = True
        return outcome

    @staticmethod
    def _get_direct_award_project_with_outcome_awarded_fixture():
        project = BaseApplicationTest._get_direct_award_lock_project_fixture()
        project['project']['outcome'] = \
            BaseApplicationTest._get_direct_award_project_outcome_awarded_fixture()['outcome']
        return project

    @staticmethod
    def _get_direct_award_project_with_completed_outcome_awarded_fixture():
        project = BaseApplicationTest._get_direct_award_lock_project_fixture()
        project['project']['outcome'] = \
            BaseApplicationTest._get_direct_award_project_completed_outcome_awarded_fixture()['outcome']
        return project

    @staticmethod
    def _get_direct_award_project_searches_fixture(only_active=False):
        searches = BaseApplicationTest._get_fixture_data(
            'direct_award_project_searches_fixture.json')

        if only_active:
            searches = {
                'searches': [
                    search for search in searches['searches']
                    if search['active']
                ]
            }

        return searches

    @staticmethod
    def _get_direct_award_project_services_fixture():
        return BaseApplicationTest._get_fixture_data(
            'direct_award_project_services_fixture.json')

    @staticmethod
    def _get_direct_award_project_services_zero_state_fixture():
        return BaseApplicationTest._get_fixture_data(
            'direct_award_project_services_zero_state_fixture.json')

    @staticmethod
    def _strip_whitespace(whitespace_in_this):
        return re.sub(r"\s+", "", whitespace_in_this, flags=re.UNICODE)

    @staticmethod
    def _normalize_whitespace(whitespace_in_this):
        # NOTE proper xml-standard way of doing this is a little more complex afaik
        return re.sub(r"\s+", " ", whitespace_in_this,
                      flags=re.UNICODE).strip()

    @classmethod
    def _squashed_element_text(cls, element):
        return element.text + "".join(
            cls._squashed_element_text(child_element) + child_element.tail
            for child_element in element)

    def teardown_login(self):
        if self.get_user_patch is not None:
            self.get_user_patch.stop()

    def login_as_supplier(self):
        with mock.patch('app.data_api_client') as login_api_client:
            login_api_client.authenticate_user.return_value = self.user(
                123, "*****@*****.**", 1234, u'Supplier NĀme', u'Năme')

            self.get_user_patch = mock.patch.object(
                data_api_client,
                'get_user',
                return_value=self.user(123, "*****@*****.**", 1234,
                                       u'Supplier NĀme', u'Năme'))
            self.get_user_patch.start()

            response = self.client.post("/auto-supplier-login")
            assert response.status_code == 200

    def login_as_buyer(self, user_id=123):
        with mock.patch('app.data_api_client') as login_api_client:
            login_api_client.authenticate_user.return_value = self.user(
                user_id,
                "*****@*****.**",
                None,
                None,
                'Ā Buyer',
                role='buyer')

            self.get_user_patch = mock.patch.object(data_api_client,
                                                    'get_user',
                                                    return_value=self.user(
                                                        user_id,
                                                        "*****@*****.**",
                                                        None,
                                                        None,
                                                        'Buyer',
                                                        role='buyer'))
            self.get_user_patch.start()

            response = self.client.post("/auto-buyer-login")
            assert response.status_code == 200

    def login_as_admin(self):
        with mock.patch(
                'app.main.views.login.data_api_client') as login_api_client:
            login_api_client.authenticate_user.return_value = self.user(
                123, "*****@*****.**", None, None, 'Name', role='admin')

            self.get_user_patch = mock.patch.object(data_api_client,
                                                    'get_user',
                                                    return_value=self.user(
                                                        123,
                                                        "*****@*****.**",
                                                        None,
                                                        None,
                                                        'Some Admin',
                                                        role='admin'))
            self.get_user_patch.start()

            self.client.post("/login",
                             data={
                                 'email_address': '*****@*****.**',
                                 'password': '******'
                             })

            login_api_client.authenticate_user.assert_called_once_with(
                "*****@*****.**", "1234567890")

    @staticmethod
    def get_cookie_by_name(response, name):
        cookies = response.headers.getlist('Set-Cookie')
        for cookie in cookies:
            if name in parse_cookie(cookie):
                return parse_cookie(cookie)
        return None

    @staticmethod
    def strip_all_whitespace(content):
        pattern = re.compile(r'\s+')
        return re.sub(pattern, '', content)

    @staticmethod
    def find_search_summary(res_data):
        return re.findall(
            r'<span class="app-search-summary__count">.+</span>[^\n]+',
            res_data)

    # Method to test flashes taken from http://blog.paulopoiati.com/2013/02/22/testing-flash-messages-in-flask/
    def assert_flashes(self,
                       expected_message_markup,
                       expected_category='message'):
        with self.client.session_transaction() as session:
            try:
                category, message = session['_flashes'][0]
            except KeyError:
                raise AssertionError('nothing flashed')
            # The code under test put `message` into the template, and jinja will call `escape` on it.
            # We need to ensure we didn't wrap something unsafe in a `Markup` object, so should be checking the
            # output markup, not the message that went in.
            assert expected_message_markup in escape(message)
            assert expected_category == category