示例#1
0
def test_dataset_shows_code_snippets_to_tool_user():
    ds = factories.DataSetFactory.create(type=DataSetType.MASTER.value,
                                         published=True)
    user = User.objects.create(is_superuser=False)
    factories.DataSetUserPermissionFactory.create(user=user, dataset=ds)
    factories.SourceTableFactory.create(dataset=ds,
                                        schema="public",
                                        table="MY_LOVELY_TABLE")

    client = Client(**get_http_sso_data(user))
    response = client.get(ds.get_absolute_url())

    assert response.status_code == 200
    assert (
        """SELECT * FROM "public"."MY_LOVELY_TABLE" LIMIT 50"""
        not in response.content.decode(response.charset))

    user.is_superuser = True
    user.save()

    client = Client(**get_http_sso_data(user))
    response = client.get(ds.get_absolute_url())

    assert response.status_code == 200
    assert (
        """SELECT * FROM "public"."MY_LOVELY_TABLE" LIMIT 50"""
        in response.content.decode(response.charset))
示例#2
0
    def test_metabase_link(self, mocker):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            user_access_type='REQUIRES_AUTHENTICATION'
        )
        link = VisualisationLinkFactory.create(
            visualisation_type='METABASE',
            identifier='123456789',
            visualisation_catalogue_item=vis,
        )

        jwt_encode = mocker.patch('dataworkspace.apps.applications.views.jwt.encode')
        jwt_encode.return_value = b'my-token'

        client = Client(**get_http_sso_data(user))
        response = client.get(link.get_absolute_url())

        assert response.status_code == 200
        assert (
            '//metabase.dataworkspace.test:8000/embed/dashboard/my-token#bordered=false&titled=false'
            in response.content.decode(response.charset)
        )
        assert (
            'frame-src metabase.dataworkspace.test'
            in response['content-security-policy']
        )
示例#3
0
    def test_not_developer(self, mock_get_gitlab_project):
        mock_get_gitlab_project.return_value = {
            "id": 1,
            "default_branch": "master",
            "name": "test-gitlab-project",
        }
        develop_visualisations_permission = Permission.objects.get(
            codename="develop_visualisations",
            content_type=ContentType.objects.get_for_model(ApplicationInstance),
        )
        user = factories.UserFactory.create(
            username="******",
            is_staff=False,
            is_superuser=False,
        )
        user.user_permissions.add(develop_visualisations_permission)

        client = Client(**get_http_sso_data(user))
        client.post(reverse("admin:index"), follow=True)

        with mock.patch(
            "dataworkspace.apps.applications.views.gitlab_has_developer_access"
        ) as access_mock:
            access_mock.return_value = False
            response = client.get(reverse("visualisations:logs", args=(1, "xxx")))
        assert response.status_code == 403
示例#4
0
    def test_approve_visualisation_successfully(self):
        develop_visualisations_permission = Permission.objects.get(
            codename='develop_visualisations',
            content_type=ContentType.objects.get_for_model(ApplicationInstance),
        )
        user = factories.UserFactory.create(
            username='******',
            is_staff=False,
            is_superuser=False,
        )
        user.user_permissions.add(develop_visualisations_permission)
        visualisation = factories.VisualisationCatalogueItemFactory.create(
            published=False, visualisation_template__gitlab_project_id=1
        )

        # Login to admin site
        client = Client(**get_http_sso_data(user))
        client.post(reverse('admin:index'), follow=True)

        with _visualisation_ui_gitlab_mocks():
            response = client.post(
                reverse(
                    'visualisations:approvals',
                    args=(visualisation.visualisation_template.gitlab_project_id,),
                ),
                {
                    "approved": "on",
                    "approver": user.id,
                    "visualisation": str(visualisation.visualisation_template.id),
                },
                follow=True,
            )

        assert response.status_code == 200
        assert len(VisualisationApproval.objects.all()) == 1
示例#5
0
    def test_user_needs_access_via_catalogue_item(self, mocker):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            user_access_type='REQUIRES_AUTHORIZATION')
        link = VisualisationLinkFactory.create(
            visualisation_type='QUICKSIGHT',
            identifier=str(uuid4()),
            visualisation_catalogue_item=vis,
        )
        quicksight = mocker.patch(
            'dataworkspace.apps.applications.views.get_quicksight_dashboard_name_url'
        )
        quicksight.return_value = (
            'my-dashboard',
            'https://my.dashboard.quicksight.amazonaws.com',
        )

        client = Client(**get_http_sso_data(user))
        response = client.get(link.get_absolute_url())
        assert response.status_code == 403

        VisualisationUserPermissionFactory.create(visualisation=vis, user=user)

        response = client.get(link.get_absolute_url())
        assert response.status_code == 200
示例#6
0
 def test_commit_does_not_exist(self, mocker):
     application_template = factories.ApplicationTemplateFactory()
     factories.ApplicationInstanceFactory(
         application_template=application_template,
         commit_id='',
         spawner_application_template_options=json.dumps(
             {'CONTAINER_NAME': 'user-defined-container'}),
         spawner_application_instance_id=json.dumps(
             {'task_arn': 'arn:test:vis/task-id/999'}),
     )
     mock_get_application_template = mocker.patch(
         'dataworkspace.apps.applications.views._application_template')
     mock_get_application_template.return_value = application_template
     develop_visualisations_permission = Permission.objects.get(
         codename='develop_visualisations',
         content_type=ContentType.objects.get_for_model(
             ApplicationInstance),
     )
     user = factories.UserFactory.create(
         username='******',
         is_staff=False,
         is_superuser=False,
     )
     user.user_permissions.add(develop_visualisations_permission)
     client = Client(**get_http_sso_data(user))
     client.post(reverse('admin:index'), follow=True)
     with _visualisation_ui_gitlab_mocks():
         response = client.get(
             reverse('visualisations:logs', args=(1, 'xxx')))
         assert response.status_code == 200
         assert response.content == b'No logs were found for this visualisation.'
示例#7
0
    def test_quicksight_link(self, mocker):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            user_access_type='REQUIRES_AUTHENTICATION'
        )
        link = VisualisationLinkFactory.create(
            visualisation_type='QUICKSIGHT',
            identifier='5d75e131-20f4-48f8-b0eb-f4ebf36434f4',
            visualisation_catalogue_item=vis,
        )

        quicksight = mocker.patch(
            'dataworkspace.apps.applications.views.get_quicksight_dashboard_name_url'
        )
        quicksight.return_value = (
            'my-dashboard',
            'https://my.dashboard.quicksight.amazonaws.com',
        )

        client = Client(**get_http_sso_data(user))
        response = client.get(link.get_absolute_url())

        assert response.status_code == 200
        assert (
            'https://my.dashboard.quicksight.amazonaws.com'
            in response.content.decode(response.charset)
        )
        assert (
            'frame-src https://eu-west-2.quicksight.aws.amazon.com'
            in response['content-security-policy']
        )
示例#8
0
def test_other_users_query_results(user):
    client = Client(raise_request_exception=False, **get_http_sso_data(user))
    ql = QueryLogFactory.create(run_by_user=factories.UserFactory.create())
    response = client.get(reverse("explorer:running_query", args=(ql.id, )))
    assert response.status_code == 403
    assert "You can collaborate on Data Explorer queries" in response.content.decode(
        response.charset)
示例#9
0
 def test_no_events(self, mocker):
     application_template = factories.ApplicationTemplateFactory()
     factories.ApplicationInstanceFactory(
         application_template=application_template,
         commit_id="xxx",
         spawner_application_template_options=json.dumps(
             {"CONTAINER_NAME": "user-defined-container"}
         ),
         spawner_application_instance_id=json.dumps({"task_arn": "arn:test:vis/task-id/999"}),
     )
     mock_get_application_template = mocker.patch(
         "dataworkspace.apps.applications.views._application_template"
     )
     mock_get_application_template.return_value = application_template
     mock_boto = mocker.patch("dataworkspace.apps.core.boto3_client.boto3.client")
     mock_boto.return_value.get_log_events.side_effect = botocore.exceptions.ClientError(
         error_response={"Error": {"Code": "ResourceNotFoundException"}},
         operation_name="get_log_events",
     )
     develop_visualisations_permission = Permission.objects.get(
         codename="develop_visualisations",
         content_type=ContentType.objects.get_for_model(ApplicationInstance),
     )
     user = factories.UserFactory.create(
         username="******",
         is_staff=False,
         is_superuser=False,
     )
     user.user_permissions.add(develop_visualisations_permission)
     client = Client(**get_http_sso_data(user))
     client.post(reverse("admin:index"), follow=True)
     with _visualisation_ui_gitlab_mocks():
         response = client.get(reverse("visualisations:logs", args=(1, "xxx")))
         assert response.status_code == 200
         assert response.content == b"No logs were found for this visualisation."
示例#10
0
def test_find_datasets_includes_unpublished_results_based_on_permissions(
        permissions, result_dataset_names):
    user = User.objects.create(is_staff=True)
    perms = Permission.objects.filter(codename__in=permissions).all()
    user.user_permissions.add(*perms)
    user.save()

    client = Client(**get_http_sso_data(user))

    factories.DataSetFactory.create(published=False,
                                    type=DataSetType.MASTER.value,
                                    name='Master dataset')
    factories.DataSetFactory.create(published=False,
                                    type=DataSetType.DATACUT.value,
                                    name='Datacut dataset')
    factories.ReferenceDatasetFactory.create(published=False,
                                             name='Reference dataset')

    factories.VisualisationCatalogueItemFactory.create(published=False,
                                                       name='Visualisation')

    response = client.get(reverse('datasets:find_datasets'))

    assert response.status_code == 200
    assert {dataset['name']
            for dataset in response.context["datasets"]
            } == result_dataset_names
示例#11
0
    def test_user_with_size_config_shows_correct_config(self):
        group_name = "Visualisation Tools"
        template = factories.ApplicationTemplateFactory()
        template.group_name = group_name
        template.save()

        user = get_user_model().objects.create()
        UserToolConfiguration.objects.create(
            user=user,
            tool_template=template,
            size=UserToolConfiguration.SIZE_EXTRA_LARGE,
        )

        client = Client(**get_http_sso_data(user))
        response = client.get(reverse("applications:tools"), follow=True)

        assert len(response.context["tools"][group_name]["tools"]) == 3

        tool = None
        for item in response.context["tools"][group_name]["tools"]:
            if item.name == template.nice_name:
                tool = item
                break

        assert tool is not None
        assert tool.tool_configuration.size_config.name == "Extra Large"
        assert tool.tool_configuration.size_config.cpu == 4096
        assert tool.tool_configuration.size_config.memory == 30720
示例#12
0
    def test_invalid_link_404s(self):
        user = UserFactory.create()

        client = Client(**get_http_sso_data(user))
        response = client.get(
            reverse(
                'visualisations:link',
                kwargs={"link_id": "2af5890a-bbcc-4e7d-8b2d-2a63139b3e4f"},
            ))
        assert response.status_code == 404
示例#13
0
    def test_view_redirects_to_quicksight_sso_url(self, mock_boto_client):
        user = get_user_model().objects.create(is_staff=True, is_superuser=True)

        # Login to admin site
        client = Client(**get_http_sso_data(user))
        client.post(reverse("admin:index"), follow=True)

        with mock.patch("dataworkspace.apps.applications.views.sync_quicksight_permissions"):
            resp = client.get(reverse("applications:quicksight_redirect"), follow=False)

        assert resp["Location"] == "https://sso.quicksight"
示例#14
0
    def test_with_events(self, mocker):
        application_template = factories.ApplicationTemplateFactory()
        factories.ApplicationInstanceFactory(
            application_template=application_template,
            commit_id='xxx',
            spawner_application_template_options=json.dumps(
                {'CONTAINER_NAME': 'user-defined-container'}),
            spawner_application_instance_id=json.dumps(
                {'task_arn': 'arn:test:vis/task-id/999'}),
        )
        mock_get_application_template = mocker.patch(
            'dataworkspace.apps.applications.views._application_template')
        mock_get_application_template.return_value = application_template
        mock_boto = mocker.patch(
            'dataworkspace.apps.applications.utils.boto3.client')
        mock_boto.return_value.get_log_events.side_effect = [
            {
                'nextForwardToken':
                '12345',
                'events': [{
                    'timestamp': 1605891793796,
                    'message': 'log message 1'
                }],
            },
            {
                'events': [{
                    'timestamp': 1605891793797,
                    'message': 'log message 2'
                }]
            },
        ]
        develop_visualisations_permission = Permission.objects.get(
            codename='develop_visualisations',
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )
        user = factories.UserFactory.create(
            username='******',
            is_staff=False,
            is_superuser=False,
        )
        user.user_permissions.add(develop_visualisations_permission)

        client = Client(**get_http_sso_data(user))
        client.post(reverse('admin:index'), follow=True)

        with _visualisation_ui_gitlab_mocks():
            response = client.get(
                reverse('visualisations:logs', args=(1, 'xxx')))
            assert response.status_code == 200
            assert response.content == (
                b'2020-11-20 17:03:13.796000 - log message 1\n'
                b'2020-11-20 17:03:13.797000 - log message 2\n')
示例#15
0
def test_dataset_preview_permission_denied(url_name, source_factory):
    user = factories.UserFactory.create(is_superuser=False)
    client = Client(raise_request_exception=False, **get_http_sso_data(user))
    source = source_factory.create(
        dataset=factories.DataSetFactory.create(
            user_access_type=UserAccessType.REQUIRES_AUTHORIZATION,
            published=True),
        data_grid_enabled=True,
    )
    response = client.get(
        reverse(url_name, args=(source.dataset.id, source.id)))
    assert response.status_code == 403
    assert "You do not have permission to access this dataset" in response.content.decode(
        response.charset)
示例#16
0
    def test_user_with_no_size_config_shows_default_config(self):
        factories.ApplicationTemplateFactory()
        user = get_user_model().objects.create()

        client = Client(**get_http_sso_data(user))
        response = client.get(reverse('applications:tools'), follow=True)

        assert len(response.context['applications']) == 1
        assert (response.context['applications'][0]
                ['tool_configuration'].size_config.name == 'Medium')
        assert (response.context['applications'][0]
                ['tool_configuration'].size_config.cpu == 1024)
        assert (response.context['applications'][0]
                ['tool_configuration'].size_config.memory == 8192)
示例#17
0
def test_dataset_shows_external_link_warning(source_urls, show_warning):
    ds = factories.DataSetFactory.create(published=True)
    user = User.objects.create()
    factories.DataSetUserPermissionFactory.create(user=user, dataset=ds)

    for source_url in source_urls:
        factories.SourceLinkFactory.create(dataset=ds, url=source_url)

    client = Client(**get_http_sso_data(user))
    response = client.get(ds.get_absolute_url())

    assert response.status_code == 200
    assert ("This data set is hosted by an external source."
            in response.content.decode(response.charset)) is show_warning
示例#18
0
def test_appstream_link_only_shown_to_user_with_permission(
        has_appstream_update, expected_href, expected_text):
    user = UserFactory.create(is_staff=False, is_superuser=False)
    if has_appstream_update:
        perm = Permission.objects.get(codename='access_appstream')
        user.user_permissions.add(perm)
    user.save()
    client = Client(**get_http_sso_data(user))

    response = client.get(reverse("applications:tools"))

    soup = BeautifulSoup(response.content.decode(response.charset))
    quicksight_link = soup.find('a', href=True, text=expected_text)
    assert quicksight_link.get('href') == expected_href
示例#19
0
def test_dataset_unpublished(url_name, dataset_factories):
    user = factories.UserFactory.create(is_superuser=False)
    client = Client(raise_request_exception=False, **get_http_sso_data(user))
    for factory in dataset_factories:
        ds = factory.create(published=True)
        dataset_id = getattr(ds, "uuid", ds.id)
        response = client.get(reverse(url_name, args=(dataset_id, )))
        assert response.status_code in [200, 302]
        ds.published = False
        ds.save()
        response = client.get(reverse(url_name, args=(dataset_id, )))
        assert response.status_code == 403
        assert (f"This {ds.get_type_display().lower()} has not been published"
                in response.content.decode(response.charset))
示例#20
0
    def test_get_shows_all_size_choices(self):
        tool = factories.ApplicationTemplateFactory()
        user = get_user_model().objects.create()

        client = Client(**get_http_sso_data(user))
        response = client.get(
            reverse(
                'applications:configure_tool_size',
                kwargs={'tool_host_basename': tool.host_basename},
            ), )
        assert response.status_code == 200
        assert b'Small' in response.content
        assert b'Medium (default)' in response.content
        assert b'Large' in response.content
        assert b'Extra Large' in response.content
示例#21
0
    def test_datastudio_link(self):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            user_access_type='REQUIRES_AUTHENTICATION')
        link = VisualisationLinkFactory.create(
            visualisation_type='DATASTUDIO',
            identifier='https://www.data.studio',
            visualisation_catalogue_item=vis,
        )

        client = Client(**get_http_sso_data(user))
        response = client.get(link.get_absolute_url())

        assert response.status_code == 302
        assert response['location'] == 'https://www.data.studio'
示例#22
0
def test_dataset_preview_disabled(url_name, source_factory):
    user = factories.UserFactory.create(is_superuser=False)
    client = Client(raise_request_exception=False, **get_http_sso_data(user))
    source = source_factory.create(
        dataset=factories.DataSetFactory.create(
            user_access_type=UserAccessType.REQUIRES_AUTHORIZATION,
            published=True),
        data_grid_enabled=False,
    )
    response = client.get(
        reverse(url_name, args=(source.dataset.id, source.id)))
    assert response.status_code == 403
    assert (
        f"Data preview is not enabled for this {source.dataset.get_type_display().lower()}."
        in response.content.decode(response.charset))
示例#23
0
    def test_unauthorised_visualisation(self, has_access):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            visualisation_template__user_access_type='REQUIRES_AUTHORIZATION')

        if has_access:
            ApplicationTemplateUserPermissionFactory.create(
                application_template=vis.visualisation_template, user=user)

        client = Client(**get_http_sso_data(user))
        response = client.get(vis.get_absolute_url())

        assert response.status_code == 200
        assert vis.name in response.content.decode(response.charset)
        assert ("You do not have permission to access this data visualisation."
                in response.content.decode(response.charset)) is not has_access
示例#24
0
    def test_view_starts_celery_polling_job(self, mock_boto_client):
        user = get_user_model().objects.create(is_staff=True, is_superuser=True)

        # Login to admin site
        client = Client(**get_http_sso_data(user))
        client.post(reverse("admin:index"), follow=True)

        with mock.patch(
            "dataworkspace.apps.applications.views.sync_quicksight_permissions"
        ) as sync_mock:
            client.get(reverse("applications:quicksight_redirect"), follow=False)

        assert sync_mock.delay.call_args_list == [
            mock.call(
                user_sso_ids_to_update=(user.profile.sso_id,),
            )
        ]
示例#25
0
def test_find_datasets_filters_by_access_and_use_only_returns_the_dataset_once(
):
    """Meant to prevent a regression where the combination of these two filters would return datasets multiple times
    based on the number of users with permissions to see that dataset, but the dataset didn't actually require any
    permission to use."""
    user = factories.UserFactory.create(is_superuser=False)
    user2 = factories.UserFactory.create(is_superuser=False)
    client = Client(**get_http_sso_data(user))

    access_granted_master = factories.DataSetFactory.create(
        published=True,
        type=DataSetType.MASTER.value,
        name='Master - access redundantly granted',
        user_access_type='REQUIRES_AUTHENTICATION',
    )
    factories.DataSetUserPermissionFactory.create(
        user=user, dataset=access_granted_master)
    factories.DataSetUserPermissionFactory.create(
        user=user2, dataset=access_granted_master)

    response = client.get(
        reverse('datasets:find_datasets'),
        {
            "access": "yes",
            "use": str(DataSetType.MASTER.value)
        },
    )

    assert response.status_code == 200
    assert list(response.context["datasets"]) == [{
        'id':
        access_granted_master.id,
        'name':
        access_granted_master.name,
        'slug':
        access_granted_master.slug,
        'search_rank':
        mock.ANY,
        'short_description':
        access_granted_master.short_description,
        'source_tag_ids':
        mock.ANY,
        'purpose':
        access_granted_master.type,
    }]
示例#26
0
    def test_user_needs_access_via_catalogue_item(self):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            user_access_type='REQUIRES_AUTHORIZATION')
        link = VisualisationLinkFactory.create(
            visualisation_type='METABASE',
            identifier='123',
            visualisation_catalogue_item=vis,
        )

        client = Client(**get_http_sso_data(user))
        response = client.get(link.get_absolute_url())
        assert response.status_code == 403

        VisualisationUserPermissionFactory.create(visualisation=vis, user=user)

        response = client.get(link.get_absolute_url())
        assert response.status_code == 200
示例#27
0
    def test_user_with_size_config_shows_correct_config(self):
        tool = factories.ApplicationTemplateFactory()
        user = get_user_model().objects.create()
        UserToolConfiguration.objects.create(
            user=user,
            tool_template=tool,
            size=UserToolConfiguration.SIZE_EXTRA_LARGE)

        client = Client(**get_http_sso_data(user))
        response = client.get(reverse('applications:tools'), follow=True)

        assert len(response.context['applications']) == 1
        assert (response.context['applications'][0]
                ['tool_configuration'].size_config.name == 'Extra Large')
        assert (response.context['applications'][0]
                ['tool_configuration'].size_config.cpu == 4096)
        assert (response.context['applications'][0]
                ['tool_configuration'].size_config.memory == 30720)
示例#28
0
    def test_with_events(self, mocker):
        application_template = factories.ApplicationTemplateFactory()
        factories.ApplicationInstanceFactory(
            application_template=application_template,
            commit_id="xxx",
            spawner_application_template_options=json.dumps(
                {"CONTAINER_NAME": "user-defined-container"}
            ),
            spawner_application_instance_id=json.dumps({"task_arn": "arn:test:vis/task-id/999"}),
        )
        mock_get_application_template = mocker.patch(
            "dataworkspace.apps.applications.views._application_template"
        )
        mock_get_application_template.return_value = application_template
        mock_boto = mocker.patch("dataworkspace.apps.core.boto3_client.boto3.client")
        mock_boto.return_value.get_log_events.side_effect = [
            {
                "nextForwardToken": "12345",
                "events": [{"timestamp": 1605891793796, "message": "log message 1"}],
            },
            {"events": [{"timestamp": 1605891793797, "message": "log message 2"}]},
        ]
        develop_visualisations_permission = Permission.objects.get(
            codename="develop_visualisations",
            content_type=ContentType.objects.get_for_model(ApplicationInstance),
        )
        user = factories.UserFactory.create(
            username="******",
            is_staff=False,
            is_superuser=False,
        )
        user.user_permissions.add(develop_visualisations_permission)

        client = Client(**get_http_sso_data(user))
        client.post(reverse("admin:index"), follow=True)

        with _visualisation_ui_gitlab_mocks():
            response = client.get(reverse("visualisations:logs", args=(1, "xxx")))
            assert response.status_code == 200
            assert response.content == (
                b"2020-11-20 17:03:13.796000 - log message 1\n"
                b"2020-11-20 17:03:13.797000 - log message 2\n"
            )
示例#29
0
    def test_post_creates_new_tool_configuration(self):
        tool = factories.ApplicationTemplateFactory(nice_name='RStudio')
        user = get_user_model().objects.create()

        assert not tool.user_tool_configuration.filter(user=user).first()

        client = Client(**get_http_sso_data(user))
        response = client.post(
            reverse(
                'applications:configure_tool_size',
                kwargs={'tool_host_basename': tool.host_basename},
            ),
            {'size': UserToolConfiguration.SIZE_EXTRA_LARGE},
            follow=True,
        )
        assert response.status_code == 200
        assert str(list(
            response.context['messages'])[0]) == 'Saved RStudio size'
        assert (tool.user_tool_configuration.filter(
            user=user).first().size == UserToolConfiguration.SIZE_EXTRA_LARGE)
示例#30
0
    def test_bad_post_data_approved_box_not_checked(self):
        develop_visualisations_permission = Permission.objects.get(
            codename='develop_visualisations',
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )
        user = factories.UserFactory.create(
            username='******',
            is_staff=False,
            is_superuser=False,
        )
        user.user_permissions.add(develop_visualisations_permission)
        visualisation = factories.VisualisationCatalogueItemFactory.create(
            published=False, visualisation_template__gitlab_project_id=1)

        # Login to admin site
        client = Client(**get_http_sso_data(user))
        client.post(reverse('admin:index'), follow=True)

        with _visualisation_ui_gitlab_mocks():
            response = client.post(
                reverse(
                    'visualisations:approvals',
                    args=(visualisation.visualisation_template.
                          gitlab_project_id, ),
                ),
                {
                    "action": "approve",
                    "approver": user.id,
                    "visualisation": str(
                        visualisation.visualisation_template.id),
                },
                follow=True,
            )

        visualisation.refresh_from_db()
        assert response.status_code == 400
        assert ("You must confirm that you have reviewed this visualisation"
                in response.content.decode(response.charset))
        assert len(VisualisationApproval.objects.all()) == 0