/sharing/", include(workspace_sharing_patterns)),
+ ],
+ "workspaces",
+)
+
+urlpatterns = [
+ path("billing_projects/", include(billing_project_patterns)),
+ path("accounts/", include(account_patterns)),
+ path("managed_groups/", include(managed_group_patterns)),
+ path("workspaces/", include(workspace_patterns)),
+]
diff --git a/anvil_consortium_manager/auditor/viewmixins.py b/anvil_consortium_manager/auditor/viewmixins.py
new file mode 100644
index 00000000..72cc9342
--- /dev/null
+++ b/anvil_consortium_manager/auditor/viewmixins.py
@@ -0,0 +1,36 @@
+from django.core.exceptions import ImproperlyConfigured
+from django.utils import timezone
+
+
+class AnVILAuditMixin:
+ """Mixin to display AnVIL audit results."""
+
+ audit_class = None
+
+ def get_audit_instance(self):
+ if not self.audit_class:
+ raise ImproperlyConfigured(
+ "%(cls)s is missing an audit class. Define %(cls)s.audit_class or override "
+ "%(cls)s.get_audit_instance()." % {"cls": self.__class__.__name__}
+ )
+ else:
+ return self.audit_class()
+
+ def run_audit(self):
+ self.audit_results = self.get_audit_instance()
+ self.audit_results.run_audit()
+
+ def get(self, request, *args, **kwargs):
+ self.run_audit()
+ return super().get(request, *args, **kwargs)
+
+ def get_context_data(self, *args, **kwargs):
+ """Add audit results to the context data."""
+ context = super().get_context_data(*args, **kwargs)
+ context["audit_timestamp"] = timezone.now()
+ context["audit_ok"] = self.audit_results.ok()
+ context["verified_table"] = self.audit_results.get_verified_table()
+ context["error_table"] = self.audit_results.get_error_table()
+ context["not_in_app_table"] = self.audit_results.get_not_in_app_table()
+ context["ignored_table"] = self.audit_results.get_ignored_table()
+ return context
diff --git a/anvil_consortium_manager/auditor/views.py b/anvil_consortium_manager/auditor/views.py
new file mode 100644
index 00000000..4b914311
--- /dev/null
+++ b/anvil_consortium_manager/auditor/views.py
@@ -0,0 +1,280 @@
+from django.contrib import messages
+from django.contrib.messages.views import SuccessMessageMixin
+from django.forms import HiddenInput
+from django.http import Http404, HttpResponseRedirect
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import CreateView, DeleteView, DetailView, TemplateView, UpdateView
+from django.views.generic.detail import SingleObjectMixin
+from django_filters.views import FilterView
+from django_tables2.views import SingleTableMixin
+
+from anvil_consortium_manager import auth
+from anvil_consortium_manager.models import ManagedGroup, Workspace
+
+from . import filters, forms, models, tables, viewmixins
+from .audit import accounts as account_audit
+from .audit import billing_projects as billing_project_audit
+from .audit import managed_groups as managed_group_audit
+from .audit import workspaces as workspace_audit
+
+
+class BillingProjectAudit(auth.AnVILConsortiumManagerStaffViewRequired, viewmixins.AnVILAuditMixin, TemplateView):
+ """View to run an audit on Workspaces and display the results."""
+
+ template_name = "anvil_consortium_manager/billing_project_audit.html"
+ audit_class = billing_project_audit.BillingProjectAudit
+
+
+class AccountAudit(auth.AnVILConsortiumManagerStaffViewRequired, viewmixins.AnVILAuditMixin, TemplateView):
+ """View to run an audit on Accounts and display the results."""
+
+ template_name = "anvil_consortium_manager/account_audit.html"
+ audit_class = account_audit.AccountAudit
+
+
+class ManagedGroupAudit(auth.AnVILConsortiumManagerStaffViewRequired, viewmixins.AnVILAuditMixin, TemplateView):
+ """View to run an audit on ManagedGroups and display the results."""
+
+ template_name = "anvil_consortium_manager/managedgroup_audit.html"
+ audit_class = managed_group_audit.ManagedGroupAudit
+
+
+class ManagedGroupMembershipAudit(
+ auth.AnVILConsortiumManagerStaffViewRequired,
+ SingleObjectMixin,
+ viewmixins.AnVILAuditMixin,
+ TemplateView,
+):
+ """View to run an audit on ManagedGroups and display the results."""
+
+ model = ManagedGroup
+ slug_field = "name"
+ template_name = "anvil_consortium_manager/managedgroup_membership_audit.html"
+ message_not_managed_by_app = "Cannot audit membership because group is not managed by this app."
+
+ def get(self, request, *args, **kwargs):
+ self.object = self.get_object()
+ # Check if managed by the app.
+ if not self.object.is_managed_by_app:
+ messages.add_message(
+ self.request,
+ messages.ERROR,
+ self.message_not_managed_by_app,
+ )
+ # Redirect to the object detail page.
+ return HttpResponseRedirect(self.object.get_absolute_url())
+ # Otherwise, return the response.
+ return super().get(request, *args, **kwargs)
+
+ def get_audit_instance(self):
+ return managed_group_audit.ManagedGroupMembershipAudit(self.object)
+
+
+class IgnoredManagedGroupMembershipDetail(auth.AnVILConsortiumManagerStaffViewRequired, DetailView):
+ """View to display the details of an models.IgnoredManagedGroupMembership."""
+
+ model = models.IgnoredManagedGroupMembership
+
+ def get_object(self, queryset=None):
+ """Return the object the view is displaying."""
+ if queryset is None:
+ queryset = self.get_queryset()
+ # Filter the queryset based on kwargs.
+ group_slug = self.kwargs.get("slug", None)
+ email = self.kwargs.get("email", None)
+ queryset = queryset.filter(group__name=group_slug, ignored_email=email)
+ try:
+ # Get the single item from the filtered queryset
+ obj = queryset.get()
+ except queryset.model.DoesNotExist:
+ raise Http404(
+ _("No %(verbose_name)s found matching the query") % {"verbose_name": queryset.model._meta.verbose_name}
+ )
+ return obj
+
+
+class IgnoredManagedGroupMembershipCreate(
+ auth.AnVILConsortiumManagerStaffEditRequired, SuccessMessageMixin, CreateView
+):
+ """View to create a new models.IgnoredManagedGroupMembership."""
+
+ model = models.IgnoredManagedGroupMembership
+ form_class = forms.IgnoredManagedGroupMembershipForm
+ message_already_exists = "Record already exists for this group and email."
+ success_message = "Successfully ignored managed group membership."
+
+ def get_group(self):
+ try:
+ name = self.kwargs["slug"]
+ group = ManagedGroup.objects.get(name=name)
+ except ManagedGroup.DoesNotExist:
+ raise Http404("ManagedGroup not found.")
+ return group
+
+ def get_email(self):
+ return self.kwargs["email"]
+
+ def get(self, request, *args, **kwargs):
+ self.group = self.get_group()
+ self.email = self.get_email()
+ try:
+ obj = models.IgnoredManagedGroupMembership.objects.get(group=self.group, ignored_email=self.email)
+ messages.error(self.request, self.message_already_exists)
+ return HttpResponseRedirect(obj.get_absolute_url())
+ except models.IgnoredManagedGroupMembership.DoesNotExist:
+ return super().get(request, *args, **kwargs)
+
+ def post(self, request, *args, **kwargs):
+ self.group = self.get_group()
+ self.email = self.get_email()
+ try:
+ obj = models.IgnoredManagedGroupMembership.objects.get(group=self.group, ignored_email=self.email)
+ messages.error(self.request, self.message_already_exists)
+ return HttpResponseRedirect(obj.get_absolute_url())
+ except models.IgnoredManagedGroupMembership.DoesNotExist:
+ return super().post(request, *args, **kwargs)
+
+ def get_initial(self):
+ initial = super().get_initial()
+ initial["group"] = self.group
+ initial["ignored_email"] = self.email
+ return initial
+
+ def get_form(self, **kwargs):
+ """Get the form and set the inputs to use a hidden widget."""
+ form = super().get_form(**kwargs)
+ form.fields["group"].widget = HiddenInput()
+ form.fields["ignored_email"].widget = HiddenInput()
+ return form
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context["group"] = self.group
+ context["email"] = self.email
+ return context
+
+ def form_valid(self, form):
+ """If the form is valid, save the associated model."""
+ form.instance.added_by = self.request.user
+ return super().form_valid(form)
+
+
+class IgnoredManagedGroupMembershipList(auth.AnVILConsortiumManagerStaffViewRequired, SingleTableMixin, FilterView):
+ """View to display a list of models.IgnoredManagedGroupMembership."""
+
+ model = models.IgnoredManagedGroupMembership
+ table_class = tables.IgnoredManagedGroupMembershipTable
+ template_name = "auditor/ignoredmanagedgroupmembership_list.html"
+ filterset_class = filters.IgnoredManagedGroupMembershipFilter
+
+
+class IgnoredManagedGroupMembershipUpdate(
+ auth.AnVILConsortiumManagerStaffEditRequired, SuccessMessageMixin, UpdateView
+):
+ """View to update an existing models.IgnoredManagedGroupMembership."""
+
+ model = models.IgnoredManagedGroupMembership
+ fields = ("note",)
+ success_message = "Successfully updated ignored record."
+
+ def get_object(self, queryset=None):
+ if queryset is None:
+ queryset = self.get_queryset()
+ # Filter the queryset based on kwargs.
+ group_slug = self.kwargs.get("slug", None)
+ email = self.kwargs.get("email", None)
+ queryset = queryset.filter(group__name=group_slug, ignored_email=email)
+ try:
+ # Get the single item from the filtered queryset
+ obj = queryset.get()
+ except queryset.model.DoesNotExist:
+ raise Http404(
+ _("No %(verbose_name)s found matching the query") % {"verbose_name": queryset.model._meta.verbose_name}
+ )
+ return obj
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context["group"] = self.object.group
+ context["email"] = self.object.ignored_email
+ return context
+
+
+class IgnoredManagedGroupMembershipDelete(
+ auth.AnVILConsortiumManagerStaffEditRequired, SuccessMessageMixin, DeleteView
+):
+ model = models.IgnoredManagedGroupMembership
+ success_message = "Successfully stopped ignoring managed group membership record."
+
+ def get_object(self, queryset=None):
+ """Return the object the view is displaying."""
+
+ # Use a custom queryset if provided; this is required for subclasses
+ # like DateDetailView
+ if queryset is None:
+ queryset = self.get_queryset()
+ # Filter the queryset based on kwargs.
+ group_slug = self.kwargs.get("slug", None)
+ email = self.kwargs.get("email", None)
+ queryset = queryset.filter(
+ group__name=group_slug,
+ ignored_email=email,
+ )
+ try:
+ # Get the single item from the filtered queryset
+ obj = queryset.get()
+ except queryset.model.DoesNotExist:
+ raise Http404(
+ _("No %(verbose_name)s found matching the query") % {"verbose_name": queryset.model._meta.verbose_name}
+ )
+ return obj
+
+ def get_success_url(self):
+ return self.object.group.get_absolute_url()
+
+
+class WorkspaceAudit(auth.AnVILConsortiumManagerStaffViewRequired, viewmixins.AnVILAuditMixin, TemplateView):
+ """View to run an audit on Workspaces and display the results."""
+
+ template_name = "anvil_consortium_manager/workspace_audit.html"
+ audit_class = workspace_audit.WorkspaceAudit
+
+
+class WorkspaceSharingAudit(
+ auth.AnVILConsortiumManagerStaffViewRequired,
+ SingleObjectMixin,
+ viewmixins.AnVILAuditMixin,
+ TemplateView,
+):
+ """View to run an audit on access to a specific Workspace and display the results."""
+
+ model = Workspace
+ template_name = "anvil_consortium_manager/workspace_sharing_audit.html"
+
+ def get_object(self, queryset=None):
+ """Return the object the view is displaying."""
+
+ # Use a custom queryset if provided; this is required for subclasses
+ # like DateDetailView
+ if queryset is None:
+ queryset = self.get_queryset()
+ # Filter the queryset based on kwargs.
+ billing_project_slug = self.kwargs.get("billing_project_slug", None)
+ workspace_slug = self.kwargs.get("workspace_slug", None)
+ queryset = queryset.filter(billing_project__name=billing_project_slug, name=workspace_slug)
+ try:
+ # Get the single item from the filtered queryset
+ obj = queryset.get()
+ except queryset.model.DoesNotExist:
+ raise Http404(
+ _("No %(verbose_name)s found matching the query") % {"verbose_name": queryset.model._meta.verbose_name}
+ )
+ return obj
+
+ def get(self, request, *args, **kwargs):
+ self.object = self.get_object()
+ # Otherwise, return the response.
+ return super().get(request, *args, **kwargs)
+
+ def get_audit_instance(self):
+ return workspace_audit.WorkspaceSharingAudit(self.object)
diff --git a/anvil_consortium_manager/templates/anvil_consortium_manager/audit.html b/anvil_consortium_manager/templates/anvil_consortium_manager/audit.html
index 7491206b..0a3b1647 100644
--- a/anvil_consortium_manager/templates/anvil_consortium_manager/audit.html
+++ b/anvil_consortium_manager/templates/anvil_consortium_manager/audit.html
@@ -103,5 +103,30 @@
- Audit billing projects
-
-
@@ -72,10 +68,6 @@
{% endif %}
-
- Audit accounts
-
-
@@ -112,10 +104,6 @@
- Audit groups
-
-
@@ -146,12 +134,48 @@
- Audit membership
+ Audit membership
{% if show_edit_links %}
diff --git a/anvil_consortium_manager/templates/anvil_consortium_manager/navbar.html b/anvil_consortium_manager/templates/anvil_consortium_manager/navbar.html
index 57687f0d..25bf9906 100644
--- a/anvil_consortium_manager/templates/anvil_consortium_manager/navbar.html
+++ b/anvil_consortium_manager/templates/anvil_consortium_manager/navbar.html
@@ -34,10 +34,6 @@
{% endif %}
-
- Audit billing projects
-
-
@@ -57,9 +53,6 @@
{% endif %}
-
- Audit accounts
-
@@ -85,9 +78,7 @@
Add a group to a group
{% endif %}
-
- Audit groups
-
+
@@ -110,13 +101,11 @@
{% endif %}
-
- Audit workspaces
-
-
+ {% include 'auditor/nav_items.html' %}
+
{% endblock anvil_consortium_manager_nav_items %}
diff --git a/anvil_consortium_manager/templates/anvil_consortium_manager/snippets/audit_managedgroupmembership_notinapp_ignore_button.html b/anvil_consortium_manager/templates/anvil_consortium_manager/snippets/audit_managedgroupmembership_notinapp_ignore_button.html
new file mode 100644
index 00000000..62df9e72
--- /dev/null
+++ b/anvil_consortium_manager/templates/anvil_consortium_manager/snippets/audit_managedgroupmembership_notinapp_ignore_button.html
@@ -0,0 +1,8 @@
+
+
+ Add ignored result
+
diff --git a/anvil_consortium_manager/templates/anvil_consortium_manager/workspace_detail.html b/anvil_consortium_manager/templates/anvil_consortium_manager/workspace_detail.html
index 10b5754d..4914e9e4 100644
--- a/anvil_consortium_manager/templates/anvil_consortium_manager/workspace_detail.html
+++ b/anvil_consortium_manager/templates/anvil_consortium_manager/workspace_detail.html
@@ -112,7 +112,7 @@
{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_view %}
- Audit sharing
+ Audit sharing
{% endif %}
{% if show_edit_links %}
diff --git a/anvil_consortium_manager/templates/anvil_consortium_manager/email_audit_report.html b/anvil_consortium_manager/templates/auditor/email_audit_report.html
similarity index 82%
rename from anvil_consortium_manager/templates/anvil_consortium_manager/email_audit_report.html
rename to anvil_consortium_manager/templates/auditor/email_audit_report.html
index 28434835..90ea23f6 100644
--- a/anvil_consortium_manager/templates/anvil_consortium_manager/email_audit_report.html
+++ b/anvil_consortium_manager/templates/auditor/email_audit_report.html
@@ -17,7 +17,12 @@
Audit report - {{ model_name }}
Verified
-
{{ verified_results|length }} instance(s) verified.
+
{{ audit_results.get_verified_results|length }} instance(s) verified.
+
+
+ Ignored
+
+
Ignoring {{ n_ignored }} record(s)
Errors
diff --git a/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_confirm_delete.html b/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_confirm_delete.html
new file mode 100644
index 00000000..704d8b5e
--- /dev/null
+++ b/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_confirm_delete.html
@@ -0,0 +1,29 @@
+{% extends "anvil_consortium_manager/base.html" %}
+{% load static %}
+
+{% block title %}Stop ignoring Managed Group Membership Audit result{% endblock %}
+
+{% block content %}
+
+
+
+
+
+
+
Stop ignoring Managed Group Membership Audit result
+
+
Are you sure you want to remove {{ object }} on AnVIL?
+
+
+
+
+
+
+
+
+{% endblock content %}
diff --git a/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_detail.html b/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_detail.html
new file mode 100644
index 00000000..4429e986
--- /dev/null
+++ b/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_detail.html
@@ -0,0 +1,35 @@
+{% extends "anvil_consortium_manager/__object_detail.html" %}
+{% load static %}
+
+
+{% block title %}{{ object }}{% endblock %}
+
+
+{% block panel %}
+
+ - Managed Group
-
+ {{ object.group }}
+
+ - Ignored email
- {{ object.ignored_email }}
+ - Added by
-
+ {% if object.added_by.get_absolute_url %}
+ {{ object.added_by }}
+ {% else %}
+ {{ object.added_by }}
+ {% endif %}
+
+ - Date created
- {{ object.created }}
+ - Date modified
- {{ object.modified }}
+
+{% endblock panel %}
+
+
+{% block action_buttons %}
+
+{% if perms.anvil_consortium_manager.anvil_consortium_manager_staff_edit %}
+
+ Update
+ Stop ignoring
+
+{% endif %}
+{% endblock action_buttons %}
diff --git a/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_form.html b/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_form.html
new file mode 100644
index 00000000..7429345d
--- /dev/null
+++ b/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_form.html
@@ -0,0 +1,26 @@
+{% extends "anvil_consortium_manager/__object_detail.html" %}
+{% load static %}
+{% load crispy_forms_tags %}
+
+{% block title %}Ignore Managed Group Membership audit result{% endblock title %}
+
+{% block panel %}
+
+ - Managed group
-
+ {{ group }}
+
+ - Ignored email
- {{ email }}
+{% endblock panel %}
+
+{% block after_panel %}
+
+
+{% endblock after_panel %}
+
+{% block inline_javascript %}
+ {{ form.media }}
+{% endblock inline_javascript %}
diff --git a/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_list.html b/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_list.html
new file mode 100644
index 00000000..e4dcdf0d
--- /dev/null
+++ b/anvil_consortium_manager/templates/auditor/ignoredmanagedgroupmembership_list.html
@@ -0,0 +1,32 @@
+{% extends "anvil_consortium_manager/base.html" %}
+{% load static %}
+
+{% load render_table from django_tables2 %}
+{% load crispy_forms_tags %}
+
+{% block title %}Ignored Managed Group membership audit records{% endblock %}
+
+{% block content %}
+
+
+
+
+
+
Ignored Managed Group membership audit records
+
+
+ The following records will be ignored when running AnVIL audits for group membership.
+
+
+
+ {% crispy filter.form %}
+
+
+ {% render_table table %}
+
+
+
+
+
+
+{% endblock content %}
diff --git a/anvil_consortium_manager/templates/auditor/nav_items.html b/anvil_consortium_manager/templates/auditor/nav_items.html
new file mode 100644
index 00000000..2eeec563
--- /dev/null
+++ b/anvil_consortium_manager/templates/auditor/nav_items.html
@@ -0,0 +1,30 @@
+
+
+
+
diff --git a/anvil_consortium_manager/tests/settings/test.py b/anvil_consortium_manager/tests/settings/test.py
index 7e9fd426..58dc45db 100644
--- a/anvil_consortium_manager/tests/settings/test.py
+++ b/anvil_consortium_manager/tests/settings/test.py
@@ -73,6 +73,7 @@
"fontawesomefree", # icons
# Your stuff: custom apps go here
"anvil_consortium_manager",
+ "anvil_consortium_manager.auditor",
# Test app
"anvil_consortium_manager.tests.test_app",
]
diff --git a/anvil_consortium_manager/tests/test_audit.py b/anvil_consortium_manager/tests/test_audit.py
deleted file mode 100644
index d00a0fc4..00000000
--- a/anvil_consortium_manager/tests/test_audit.py
+++ /dev/null
@@ -1,4649 +0,0 @@
-import responses
-from django.test import TestCase
-from faker import Faker
-
-from .. import exceptions, models
-from ..audit import audit
-from . import api_factories, factories
-from .utils import AnVILAPIMockTestMixin
-
-fake = Faker()
-
-
-class ModelInstanceResultTest(TestCase):
- def test_init(self):
- """Constructor works as expected."""
- obj = factories.AccountFactory.create()
- result = audit.ModelInstanceResult(obj)
- self.assertEqual(result.model_instance, obj)
- self.assertEqual(result.errors, set())
-
- def test_str(self):
- """__str__ method works as expected."""
- obj = factories.AccountFactory.create()
- result = audit.ModelInstanceResult(obj)
- self.assertEqual(str(result), (str(obj)))
-
- def test_eq_no_errors(self):
- """__eq__ method works as expected when there are no errors."""
- obj = factories.AccountFactory.create()
- result_1 = audit.ModelInstanceResult(obj)
- result_2 = audit.ModelInstanceResult(obj)
- self.assertEqual(result_1, result_2)
-
- def test_eq_errors(self):
- """__eq__ method works as expected when there are errors."""
- obj = factories.AccountFactory.create()
- result_1 = audit.ModelInstanceResult(obj)
- result_1.add_error("foo")
- result_2 = audit.ModelInstanceResult(obj)
- self.assertNotEqual(result_1, result_2)
- result_2.add_error("foo")
- self.assertEqual(result_1, result_2)
-
- def test_add_error(self):
- """add_error method works as expected."""
- obj = factories.AccountFactory.create()
- result = audit.ModelInstanceResult(obj)
- result.add_error("foo")
- self.assertEqual(result.errors, set(["foo"]))
- result.add_error("bar")
- self.assertEqual(result.errors, set(["foo", "bar"]))
-
- def test_add_error_duplicate(self):
- """can add a second, duplicate error without error."""
- obj = factories.AccountFactory.create()
- result = audit.ModelInstanceResult(obj)
- result.add_error("foo")
- self.assertEqual(result.errors, set(["foo"]))
- result.add_error("foo")
- self.assertEqual(result.errors, set(["foo"]))
-
- def test_ok_no_errors(self):
- """ok method returns True when there are no errors."""
- obj = factories.AccountFactory.create()
- result = audit.ModelInstanceResult(obj)
- self.assertTrue(result.ok())
-
- def test_ok_errors(self):
- """ok method returns False when there are errors."""
- obj = factories.AccountFactory.create()
- result = audit.ModelInstanceResult(obj)
- result.add_error("foo")
- self.assertFalse(result.ok())
-
-
-class NotInAppResultTest(TestCase):
- def test_init(self):
- """Constructor works as expected."""
- result = audit.NotInAppResult("foo bar")
- self.assertEqual(result.record, "foo bar")
-
- def test_str(self):
- """__str__ method works as expected."""
- result = audit.NotInAppResult("foo bar")
- self.assertEqual(str(result), "foo bar")
-
- def test_eq(self):
- """__eq__ method works as expected."""
- result = audit.NotInAppResult("foo")
- self.assertEqual(audit.NotInAppResult("foo"), result)
- self.assertNotEqual(audit.NotInAppResult("bar"), result)
-
-
-class VerifiedTableTest(TestCase):
- def test_zero_rows(self):
- results = []
- table = audit.VerifiedTable(results)
- self.assertEqual(len(table.rows), 0)
-
- def test_one_row(self):
- results = [audit.ModelInstanceResult(factories.AccountFactory())]
- table = audit.VerifiedTable(results)
- self.assertEqual(len(table.rows), 1)
-
- def test_two_rows(self):
- results = [
- audit.ModelInstanceResult(factories.AccountFactory()),
- audit.ModelInstanceResult(factories.AccountFactory()),
- ]
- table = audit.VerifiedTable(results)
- self.assertEqual(len(table.rows), 2)
-
-
-class ErrorTableTest(TestCase):
- def test_zero_rows(self):
- results = []
- table = audit.ErrorTable(results)
- self.assertEqual(len(table.rows), 0)
-
- def test_one_row(self):
- results = [audit.ModelInstanceResult(factories.AccountFactory())]
- table = audit.ErrorTable(results)
- self.assertEqual(len(table.rows), 1)
-
- def test_two_rows(self):
- result_1 = audit.ModelInstanceResult(factories.AccountFactory())
- result_1.add_error("foo")
- result_2 = audit.ModelInstanceResult(factories.AccountFactory())
- result_2.add_error("bar")
- results = [result_1, result_2]
- table = audit.ErrorTable(results)
- self.assertEqual(len(table.rows), 2)
-
-
-class AnVILAuditTest(TestCase):
- """Tests for the AnVILAudit abstract base class."""
-
- def setUp(self):
- super().setUp()
-
- class GenericAudit(audit.AnVILAudit):
- TEST_ERROR_1 = "Test error 1"
- TEST_ERROR_2 = "Test error 2"
-
- self.audit_results = GenericAudit()
- # It doesn't matter what model we use at this point, so just pick Account.
- self.model_factory = factories.AccountFactory
-
- def test_init(self):
- """Init method works as expected."""
- self.assertEqual(len(self.audit_results._model_instance_results), 0)
- self.assertEqual(len(self.audit_results._not_in_app_results), 0)
-
- def test_ok_no_results(self):
- """ok() returns True when there are no results."""
- self.assertTrue(self.audit_results.ok())
-
- def test_ok_one_result_ok(self):
- """ok() returns True when there is one ok result."""
- model_instance_result = audit.ModelInstanceResult(self.model_factory())
- self.audit_results.add_result(model_instance_result)
- self.assertTrue(self.audit_results.ok())
-
- def test_ok_two_results_ok(self):
- """ok() returns True when there is one ok result."""
- model_instance_result_1 = audit.ModelInstanceResult(self.model_factory())
- self.audit_results.add_result(model_instance_result_1)
- model_instance_result_2 = audit.ModelInstanceResult(self.model_factory())
- self.audit_results.add_result(model_instance_result_2)
- self.assertTrue(self.audit_results.ok())
-
- def test_ok_one_result_with_errors(self):
- """ok() returns True when there is one ok result."""
- model_instance_result = audit.ModelInstanceResult(self.model_factory())
- model_instance_result.add_error("foo")
- self.audit_results.add_result(model_instance_result)
- self.assertFalse(self.audit_results.ok())
-
- def test_ok_one_not_in_app(self):
- """ok() returns True when there are no results."""
- self.audit_results.add_result(audit.NotInAppResult("foo"))
- self.assertFalse(self.audit_results.ok())
-
- def test_run_audit_not_implemented(self):
- with self.assertRaises(NotImplementedError):
- self.audit_results.run_audit()
-
- def test_add_result_not_in_app(self):
- """Can add a NotInAppResult."""
- not_in_app_result = audit.NotInAppResult("foo")
- self.audit_results.add_result(not_in_app_result)
- self.assertEqual(len(self.audit_results._not_in_app_results), 1)
-
- def test_add_result_wrong_class(self):
- """Can add a NotInAppResult."""
- with self.assertRaises(ValueError):
- self.audit_results.add_result("foo")
-
- def test_add_result_duplicate_not_in_app(self):
- """Cannot add a duplicate NotInAppResult."""
- not_in_app_result = audit.NotInAppResult("foo")
- self.audit_results.add_result(not_in_app_result)
- # import ipdb; ipdb.set_trace()
- with self.assertRaises(ValueError):
- self.audit_results.add_result(not_in_app_result)
- self.assertEqual(len(self.audit_results._not_in_app_results), 1)
-
- def test_add_result_not_in_app_same_record(self):
- """Cannot add a duplicate NotInAppResult."""
- not_in_app_result = audit.NotInAppResult("foo")
- self.audit_results.add_result(not_in_app_result)
- # import ipdb; ipdb.set_trace()
- with self.assertRaises(ValueError):
- self.audit_results.add_result(audit.NotInAppResult("foo"))
- self.assertEqual(len(self.audit_results._not_in_app_results), 1)
-
- def test_add_result_model_instance(self):
- """Can add a model instance result."""
- model_instance_result = audit.ModelInstanceResult(self.model_factory())
- self.audit_results.add_result(model_instance_result)
- self.assertEqual(len(self.audit_results._model_instance_results), 1)
-
- def test_add_result_duplicate_model_instance_result(self):
- """Cannot add a duplicate model instance result."""
- model_instance_result = audit.ModelInstanceResult(self.model_factory())
- self.audit_results.add_result(model_instance_result)
- # import ipdb; ipdb.set_trace()
- with self.assertRaises(ValueError):
- self.audit_results.add_result(model_instance_result)
- self.assertEqual(len(self.audit_results._model_instance_results), 1)
-
- def test_add_result_second_result_for_same_model_instance(self):
- obj = self.model_factory()
- model_instance_result_1 = audit.ModelInstanceResult(obj)
- self.audit_results.add_result(model_instance_result_1)
- model_instance_result_2 = audit.ModelInstanceResult(obj)
- # import ipdb; ipdb.set_trace()
- with self.assertRaises(ValueError):
- self.audit_results.add_result(model_instance_result_2)
- self.assertEqual(len(self.audit_results._model_instance_results), 1)
- self.assertEqual(self.audit_results._model_instance_results, [model_instance_result_1])
-
- def test_add_result_second_result_for_same_model_instance_with_error(self):
- obj = self.model_factory()
- model_instance_result_1 = audit.ModelInstanceResult(obj)
- self.audit_results.add_result(model_instance_result_1)
- model_instance_result_2 = audit.ModelInstanceResult(obj)
- model_instance_result_2.add_error("Foo")
- with self.assertRaises(ValueError):
- self.audit_results.add_result(model_instance_result_2)
- self.assertEqual(len(self.audit_results._model_instance_results), 1)
- self.assertEqual(self.audit_results._model_instance_results, [model_instance_result_1])
-
- def test_get_result_for_model_instance_no_matches(self):
- obj = self.model_factory()
- audit.ModelInstanceResult(obj)
- with self.assertRaises(ValueError):
- self.audit_results.get_result_for_model_instance(obj)
-
- def test_get_result_for_model_instance_one_match(self):
- obj = self.model_factory()
- model_instance_result = audit.ModelInstanceResult(obj)
- self.audit_results.add_result(model_instance_result)
- result = self.audit_results.get_result_for_model_instance(obj)
- self.assertIs(result, model_instance_result)
-
- def test_get_verified_results_no_results(self):
- """get_verified_results returns an empty list when there are no results."""
- self.assertEqual(len(self.audit_results.get_verified_results()), 0)
-
- def test_get_verified_results_one_verified_result(self):
- """get_verified_results returns a list when there is one result."""
- model_instance_result = audit.ModelInstanceResult(self.model_factory())
- self.audit_results.add_result(model_instance_result)
- self.assertEqual(len(self.audit_results.get_verified_results()), 1)
- self.assertIn(model_instance_result, self.audit_results.get_verified_results())
-
- def test_get_error_results_two_verified_result(self):
- """get_verified_results returns a list of lenght two when there are two verified results."""
- model_instance_result_1 = audit.ModelInstanceResult(self.model_factory())
- self.audit_results.add_result(model_instance_result_1)
- model_instance_result_2 = audit.ModelInstanceResult(self.model_factory())
- self.audit_results.add_result(model_instance_result_2)
- self.assertEqual(len(self.audit_results.get_verified_results()), 2)
- self.assertIn(model_instance_result_1, self.audit_results.get_verified_results())
- self.assertIn(model_instance_result_2, self.audit_results.get_verified_results())
-
- def test_get_verified_results_one_error_result(self):
- """get_verified_results returns a list of lenght zero when there is one error result."""
- model_instance_result = audit.ModelInstanceResult(self.model_factory())
- model_instance_result.add_error("foo")
- self.audit_results.add_result(model_instance_result)
- self.assertEqual(len(self.audit_results.get_verified_results()), 0)
-
- def test_get_verified_results_one_not_in_app_result(self):
- """get_verified_results returns a list of lenght zero when there is one not_in_app result."""
- self.audit_results.add_result(audit.NotInAppResult("foo"))
- self.assertEqual(len(self.audit_results.get_verified_results()), 0)
-
- def test_get_error_results_no_results(self):
- """get_error_results returns an empty list when there are no results."""
- self.assertEqual(len(self.audit_results.get_error_results()), 0)
-
- def test_get_error_results_one_verified_result(self):
- """get_error_results returns a list of length zero when there is one verified result."""
- model_instance_result = audit.ModelInstanceResult(self.model_factory())
- self.audit_results.add_result(model_instance_result)
- self.assertEqual(len(self.audit_results.get_error_results()), 0)
-
- def test_get_error_results_one_error_result(self):
- """get_error_results returns a list of lenght one when there is one error result."""
- model_instance_result = audit.ModelInstanceResult(self.model_factory())
- model_instance_result.add_error("foo")
- self.audit_results.add_result(model_instance_result)
- self.assertEqual(len(self.audit_results.get_error_results()), 1)
- self.assertIn(model_instance_result, self.audit_results.get_error_results())
-
- def test_get_error_results_two_error_result(self):
- """get_error_results returns a list of lenght two when there is one result."""
- model_instance_result_1 = audit.ModelInstanceResult(self.model_factory())
- model_instance_result_1.add_error("foo")
- self.audit_results.add_result(model_instance_result_1)
- model_instance_result_2 = audit.ModelInstanceResult(self.model_factory())
- model_instance_result_2.add_error("foo")
- self.audit_results.add_result(model_instance_result_2)
- self.assertEqual(len(self.audit_results.get_error_results()), 2)
- self.assertIn(model_instance_result_1, self.audit_results.get_error_results())
- self.assertIn(model_instance_result_2, self.audit_results.get_error_results())
-
- def test_get_error_results_one_not_in_app_result(self):
- """get_error_results returns a list of length zero when there is one not_in_app result."""
- self.audit_results.add_result(audit.NotInAppResult("foo"))
- self.assertEqual(len(self.audit_results.get_error_results()), 0)
-
- def test_get_not_in_app_results_no_results(self):
- """get_not_in_app_results returns an empty list when there are no results."""
- self.assertEqual(len(self.audit_results.get_not_in_app_results()), 0)
-
- def test_get_not_in_app_results_one_verified_result(self):
- """get_not_in_app_results returns a list of length zero when there is one verified result."""
- model_instance_result = audit.ModelInstanceResult(self.model_factory())
- self.audit_results.add_result(model_instance_result)
- self.assertEqual(len(self.audit_results.get_not_in_app_results()), 0)
-
- def test_get_not_in_app_results_one_error_result(self):
- """get_not_in_app_results returns a list of lenght one when there is one error result."""
- model_instance_result = audit.ModelInstanceResult(self.model_factory())
- model_instance_result.add_error("foo")
- self.audit_results.add_result(model_instance_result)
- self.assertEqual(len(self.audit_results.get_not_in_app_results()), 0)
-
- def test_get_not_in_app_results_one_not_in_app_result(self):
- """get_not_in_app_results returns a list of length zero when there is one not_in_app result."""
- result = audit.NotInAppResult("foo")
- self.audit_results.add_result(result)
- self.assertEqual(len(self.audit_results.get_not_in_app_results()), 1)
- self.assertIn(result, self.audit_results.get_not_in_app_results())
-
- def test_get_not_in_app_results_two_not_in_app_results(self):
- """get_not_in_app_results returns a list of lenght two when there is one result."""
- result_1 = audit.NotInAppResult("foo")
- self.audit_results.add_result(result_1)
- result_2 = audit.NotInAppResult("bar")
- self.audit_results.add_result(result_2)
- self.assertEqual(len(self.audit_results.get_not_in_app_results()), 2)
- self.assertIn(result_1, self.audit_results.get_not_in_app_results())
- self.assertIn(result_2, self.audit_results.get_not_in_app_results())
-
- def test_export(self):
- # One Verified result.
- verified_result = audit.ModelInstanceResult(self.model_factory())
- self.audit_results.add_result(verified_result)
- # One error result.
- error_result = audit.ModelInstanceResult(self.model_factory())
- error_result.add_error("foo")
- self.audit_results.add_result(error_result)
- # Not in app result.
- not_in_app_result = audit.NotInAppResult("bar")
- self.audit_results.add_result(not_in_app_result)
- # Check export.
- exported_data = self.audit_results.export()
- self.assertIn("verified", exported_data)
- self.assertEqual(
- exported_data["verified"],
- [
- {
- "id": verified_result.model_instance.pk,
- "instance": verified_result.model_instance,
- }
- ],
- )
- self.assertIn("errors", exported_data)
- self.assertEqual(
- exported_data["errors"],
- [
- {
- "id": error_result.model_instance.pk,
- "instance": error_result.model_instance,
- "errors": ["foo"],
- }
- ],
- )
- self.assertIn("not_in_app", exported_data)
- self.assertEqual(exported_data["not_in_app"], ["bar"])
-
- def test_export_include_verified_false(self):
- exported_data = self.audit_results.export(include_verified=False)
- self.assertNotIn("verified", exported_data)
- self.assertIn("errors", exported_data)
- self.assertIn("not_in_app", exported_data)
-
- def test_export_include_errors_false(self):
- exported_data = self.audit_results.export(include_errors=False)
- self.assertIn("verified", exported_data)
- self.assertNotIn("errors", exported_data)
- self.assertIn("not_in_app", exported_data)
-
- def test_export_include_not_in_app_false(self):
- exported_data = self.audit_results.export(include_not_in_app=False)
- self.assertIn("verified", exported_data)
- self.assertIn("errors", exported_data)
- self.assertNotIn("not_in_app", exported_data)
-
- def test_export_not_in_app_sorted(self):
- """export sorts the not_in_app results."""
- self.audit_results.add_result(audit.NotInAppResult("foo"))
- self.audit_results.add_result(audit.NotInAppResult("bar"))
- exported_data = self.audit_results.export()
- self.assertEqual(exported_data["not_in_app"], ["bar", "foo"])
-
-
-class BillingProjectAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests for the BillingProject.anvil_audit method."""
-
- def get_api_url(self, billing_project_name):
- return self.api_client.rawls_entry_point + "/api/billing/v2/" + billing_project_name
-
- def get_api_json_response(self):
- return {
- "roles": ["User"],
- }
-
- def test_anvil_audit_no_billing_projects(self):
- """anvil_audit works correct if there are no billing projects in the app."""
- audit_results = audit.BillingProjectAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_anvil_audit_one_billing_project_no_errors(self):
- """anvil_audit works correct if one billing project exists in the app and in AnVIL."""
- billing_project = factories.BillingProjectFactory.create(has_app_as_user=True)
- api_url = self.get_api_url(billing_project.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=self.get_api_json_response(),
- )
- audit_results = audit.BillingProjectAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(billing_project)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_one_billing_project_not_on_anvil(self):
- """anvil_audit raises exception with one billing project exists in the app but not on AnVIL."""
- billing_project = factories.BillingProjectFactory.create(has_app_as_user=True)
- api_url = self.get_api_url(billing_project.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=404,
- json={"message": "other error"},
- )
- audit_results = audit.BillingProjectAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(billing_project)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
-
- def test_anvil_audit_two_billing_projects_no_errors(self):
- """anvil_audit returns None if there are two billing projects and both exist on AnVIL."""
- billing_project_1 = factories.BillingProjectFactory.create(has_app_as_user=True)
- api_url_1 = self.get_api_url(billing_project_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_1,
- status=200,
- json=self.get_api_json_response(),
- )
- billing_project_2 = factories.BillingProjectFactory.create(has_app_as_user=True)
- api_url_2 = self.get_api_url(billing_project_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_2,
- status=200,
- json=self.get_api_json_response(),
- )
- audit_results = audit.BillingProjectAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(billing_project_1)
- self.assertTrue(record_result.ok())
- record_result = audit_results.get_result_for_model_instance(billing_project_2)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_two_billing_projects_first_not_on_anvil(self):
- """anvil_audit raises exception if two billing projects exist in the app but the first is not on AnVIL."""
- billing_project_1 = factories.BillingProjectFactory.create(has_app_as_user=True)
- api_url_1 = self.get_api_url(billing_project_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_1,
- status=404,
- json={"message": "other error"},
- )
- billing_project_2 = factories.BillingProjectFactory.create(has_app_as_user=True)
- api_url_2 = self.get_api_url(billing_project_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_2,
- status=200,
- json=self.get_api_json_response(),
- )
- audit_results = audit.BillingProjectAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(billing_project_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(billing_project_2)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_two_billing_projects_both_missing(self):
- """anvil_audit raises exception if there are two billing projects that exist in the app but not in AnVIL."""
- billing_project_1 = factories.BillingProjectFactory.create(has_app_as_user=True)
- api_url_1 = self.get_api_url(billing_project_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_1,
- status=404,
- json={"message": "other error"},
- )
- billing_project_2 = factories.BillingProjectFactory.create(has_app_as_user=True)
- api_url_2 = self.get_api_url(billing_project_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_2,
- status=404,
- json={"message": "other error"},
- )
- audit_results = audit.BillingProjectAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(billing_project_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(billing_project_2)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
-
- def test_anvil_audit_ignore_not_has_app_has_user(self):
- """anvil_audit does not check AnVIL about billing projects that do not have the app as a user."""
- factories.BillingProjectFactory.create(has_app_as_user=False)
- # No API calls made.
- audit_results = audit.BillingProjectAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
-
-class AccountAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests for the Account.anvil_audit method."""
-
- def get_api_url(self, email):
- return self.api_client.sam_entry_point + "/api/users/v1/" + email
-
- def get_api_json_response(self, email):
- id = fake.bothify(text="#" * 21)
- return {
- "googleSubjectId": id,
- "userEmail": email,
- "userSubjectId": id,
- }
-
- def test_anvil_audit_no_accounts(self):
- """anvil_audit works correct if there are no Accounts in the app."""
- audit_results = audit.AccountAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_anvil_audit_one_account_no_errors(self):
- """anvil_audit works correct if there is one account in the app and it exists on AnVIL."""
- account = factories.AccountFactory.create()
- api_url = self.get_api_url(account.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=self.get_api_json_response(account.email),
- )
- audit_results = audit.AccountAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(account)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_one_account_not_on_anvil(self):
- """anvil_audit raises exception if one billing project exists in the app but not on AnVIL."""
- account = factories.AccountFactory.create()
- api_url = self.get_api_url(account.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=404,
- json={"message": "other error"},
- )
- audit_results = audit.AccountAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(account)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
-
- def test_anvil_audit_two_accounts_no_errors(self):
- """anvil_audit returns None if if two accounts exist in both the app and AnVIL."""
- account_1 = factories.AccountFactory.create()
- api_url_1 = self.get_api_url(account_1.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_1,
- status=200,
- json=self.get_api_json_response(account_1.email),
- )
- account_2 = factories.AccountFactory.create()
- api_url_2 = self.get_api_url(account_2.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_2,
- status=200,
- json=self.get_api_json_response(account_2.email),
- )
- audit_results = audit.AccountAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(account_1)
- self.assertTrue(record_result.ok())
- record_result = audit_results.get_result_for_model_instance(account_2)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_two_accounts_first_not_on_anvil(self):
- """anvil_audit raises exception if two accounts exist in the app but the first is not not on AnVIL."""
- account_1 = factories.AccountFactory.create()
- api_url_1 = self.get_api_url(account_1.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_1,
- status=404,
- json={"message": "other error"},
- )
- account_2 = factories.AccountFactory.create()
- api_url_2 = self.get_api_url(account_2.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_2,
- status=200,
- json=self.get_api_json_response(account_2.email),
- )
- audit_results = audit.AccountAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(account_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(account_2)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_two_accounts_both_missing(self):
- """anvil_audit raises exception if there are two accounts that exist in the app but not in AnVIL."""
- account_1 = factories.AccountFactory.create()
- api_url_1 = self.get_api_url(account_1.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_1,
- status=404,
- json={"message": "other error"},
- )
- account_2 = factories.AccountFactory.create()
- api_url_2 = self.get_api_url(account_2.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_2,
- status=404,
- json={"message": "other error"},
- )
- audit_results = audit.AccountAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(account_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(account_2)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
-
- def test_anvil_audit_deactivated_account(self):
- """anvil_audit does not check AnVIL about accounts that are deactivated."""
- account = factories.AccountFactory.create()
- account.deactivate()
- # No API calls made.
- audit_results = audit.AccountAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
-
-class ManagedGroupAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests forthe ManagedGroup.anvil_audit method."""
-
- def get_api_groups_url(self):
- """Return the API url being called by the method."""
- return self.api_client.sam_entry_point + "/api/groups/v1"
-
- def get_api_url_members(self, group_name):
- """Return the API url being called by the method."""
- return self.api_client.sam_entry_point + "/api/groups/v1/" + group_name + "/member"
-
- def get_api_url_admins(self, group_name):
- """Return the API url being called by the method."""
- return self.api_client.sam_entry_point + "/api/groups/v1/" + group_name + "/admin"
-
- def test_anvil_audit_no_groups(self):
- """anvil_audit works correct if there are no ManagedGroups in the app."""
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory().response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_anvil_audit_one_group_managed_by_app_no_errors(self):
- """anvil_audit works correct if there is one group in the app and it exists on AnVIL."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=True)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[api_factories.GroupDetailsAdminFactory(groupName=group.name)]
- ).response,
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_one_group_managed_by_app_lowercase_role(self):
- """anvil_audit works correct if there is one account in the app and it exists on AnVIL."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=True)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[api_factories.GroupDetailsAdminFactory(groupName=group.name)]
- ).response,
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_one_group_not_managed_by_app_no_errors(self):
- """anvil_audit works correct if there is one account in the app and it exists on AnVIL."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=False)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[api_factories.GroupDetailsMemberFactory(groupName=group.name)]
- ).response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_one_group_not_managed_by_app_no_errors_uppercase_role(self):
- """anvil_audit works correct if there is one account in the app and it exists on AnVIL."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=False)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[api_factories.GroupDetailsFactory(groupName=group.name, role="Member")]
- ).response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_one_group_not_on_anvil(self):
- """anvil_audit raises exception if one group exists in the app but not on AnVIL."""
- group = factories.ManagedGroupFactory.create()
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(n_groups=0).response,
- )
- self.anvil_response_mock.add(
- responses.GET,
- "https://sam.dsde-prod.broadinstitute.org/api/groups/v1/" + group.name,
- status=404,
- json=api_factories.ErrorResponseFactory().response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
-
- def test_anvil_audit_one_group_on_anvil_but_app_not_in_group_not_managed_by_app(
- self,
- ):
- """anvil_audit is correct if the group is not managed by the app."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=False)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(n_groups=0).response,
- )
- # Add the response.
- self.anvil_response_mock.add(
- responses.GET,
- "https://sam.dsde-prod.broadinstitute.org/api/groups/v1/" + group.name,
- status=200,
- json="FOO@BAR.COM",
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_one_group_managed_by_app_on_anvil_but_app_not_in_group(self):
- """anvil_audit raises exception if one group exists in the app but not on AnVIL."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=True)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(n_groups=0).response,
- )
- # Add the response.
- self.anvil_response_mock.add(
- responses.GET,
- "https://sam.dsde-prod.broadinstitute.org/api/groups/v1/" + group.name,
- status=200,
- json="FOO@BAR.COM",
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_ROLE]))
-
- def test_anvil_audit_one_group_admin_in_app_member_on_anvil(self):
- """anvil_audit raises exception if one group exists in the app as an admin but the role on AnVIL is member."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=True)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[api_factories.GroupDetailsMemberFactory(groupName=group.name)]
- ).response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_ROLE]))
-
- def test_anvil_audit_one_group_member_in_app_admin_on_anvil(self):
- """anvil_audit raises exception if one group exists in the app as an member but the role on AnVIL is admin."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=False)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[api_factories.GroupDetailsAdminFactory(groupName=group.name)]
- ).response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_ROLE]))
-
- def test_anvil_audit_two_groups_no_errors(self):
- """anvil_audit works correctly if if two groups exist in both the app and AnVIL."""
- group_1 = factories.ManagedGroupFactory.create()
- group_2 = factories.ManagedGroupFactory.create(is_managed_by_app=False)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[
- api_factories.GroupDetailsAdminFactory(groupName=group_1.name),
- api_factories.GroupDetailsMemberFactory(groupName=group_2.name),
- ]
- ).response,
- )
- api_url_members = self.get_api_url_members(group_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group_1)
- self.assertTrue(record_result.ok())
- record_result = audit_results.get_result_for_model_instance(group_2)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_two_groups_json_response_order_does_not_matter(self):
- """Order of groups in the json response does not matter."""
- group_1 = factories.ManagedGroupFactory.create()
- group_2 = factories.ManagedGroupFactory.create(is_managed_by_app=False)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[
- api_factories.GroupDetailsMemberFactory(groupName=group_2.name),
- api_factories.GroupDetailsAdminFactory(groupName=group_1.name),
- ]
- ).response,
- )
- api_url_members = self.get_api_url_members(group_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group_1)
- self.assertTrue(record_result.ok())
- record_result = audit_results.get_result_for_model_instance(group_2)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_two_groups_first_not_on_anvil(self):
- """anvil_audit raises exception if two groups exist in the app but the first is not not on AnVIL."""
- group_1 = factories.ManagedGroupFactory.create()
- group_2 = factories.ManagedGroupFactory.create()
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[
- api_factories.GroupDetailsAdminFactory(groupName=group_2.name),
- ]
- ).response,
- )
- # Add response for the group that is not in the app.
- self.anvil_response_mock.add(
- responses.GET,
- "https://sam.dsde-prod.broadinstitute.org/api/groups/v1/" + group_1.name,
- status=404,
- json=api_factories.ErrorResponseFactory().response,
- )
- # Add responses for the group that is in the app.
- api_url_members = self.get_api_url_members(group_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(group_2)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_two_groups_both_missing(self):
- """anvil_audit raises exception if there are two groups that exist in the app but not in AnVIL."""
- group_1 = factories.ManagedGroupFactory.create()
- group_2 = factories.ManagedGroupFactory.create()
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory().response,
- )
- # Add response for the group that is not in the app.
- self.anvil_response_mock.add(
- responses.GET,
- "https://sam.dsde-prod.broadinstitute.org/api/groups/v1/" + group_1.name,
- status=404,
- json=api_factories.ErrorResponseFactory().response,
- )
- # Add response for the group that is not in the app.
- self.anvil_response_mock.add(
- responses.GET,
- "https://sam.dsde-prod.broadinstitute.org/api/groups/v1/" + group_2.name,
- status=404,
- json=api_factories.ErrorResponseFactory().response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(group_2)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
-
- def test_anvil_audit_one_group_member_missing_in_app(self):
- """Groups that the app is a member of are not reported in the app."""
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[api_factories.GroupDetailsMemberFactory(groupName="test-group")]
- ).response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_anvil_audit_one_group_admin_missing_in_app(self):
- """anvil_audit works correctly if the service account is an admin of a group not in the app."""
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[api_factories.GroupDetailsAdminFactory(groupName="test-group")]
- ).response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "test-group")
-
- def test_anvil_audit_two_groups_admin_missing_in_app(self):
- """anvil_audit works correctly if there are two groups in AnVIL that aren't in the app."""
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[
- api_factories.GroupDetailsAdminFactory(groupName="test-group-1"),
- api_factories.GroupDetailsAdminFactory(groupName="test-group-2"),
- ]
- ).response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 2)
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "test-group-1")
- record_result = audit_results.get_not_in_app_results()[1]
- self.assertEqual(record_result.record, "test-group-2")
-
- def test_fails_membership_audit(self):
- """Error is reported when a group fails the membership audit."""
- group = factories.ManagedGroupFactory.create()
- factories.GroupAccountMembershipFactory.create(group=group)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[api_factories.GroupDetailsAdminFactory(groupName=group.name)]
- ).response,
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_GROUP_MEMBERSHIP]))
-
- def test_admin_in_app_both_member_and_admin_on_anvil(self):
- """anvil_audit works correctly when the app is an admin and AnVIL returns both a member and admin record."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=True)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[
- api_factories.GroupDetailsAdminFactory(groupName=group.name),
- api_factories.GroupDetailsMemberFactory(groupName=group.name),
- ]
- ).response,
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertTrue(record_result.ok())
-
- def test_admin_in_app_both_member_and_admin_different_order_on_anvil(self):
- """anvil_audit works correctly when the app is an admin and AnVIL returns both a member and admin record."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=True)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[
- api_factories.GroupDetailsMemberFactory(groupName=group.name),
- api_factories.GroupDetailsAdminFactory(groupName=group.name),
- ]
- ).response,
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertTrue(record_result.ok())
-
- def test_member_in_app_both_member_and_admin_on_anvil(self):
- """anvil_audit works correctly when the app is a member and AnVIL returns both a member and admin record."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=False)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[
- api_factories.GroupDetailsMemberFactory(groupName=group.name),
- api_factories.GroupDetailsAdminFactory(groupName=group.name),
- ]
- ).response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_ROLE]))
-
- def test_member_in_app_both_member_and_admin_different_order_on_anvil(self):
- """anvil_audit works correctly when the app is a member and AnVIL returns both a member and admin record."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=False)
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=api_factories.GetGroupsResponseFactory(
- response=[
- api_factories.GroupDetailsAdminFactory(groupName=group.name),
- api_factories.GroupDetailsMemberFactory(groupName=group.name),
- ]
- ).response,
- )
- audit_results = audit.ManagedGroupAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(group)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_ROLE]))
-
-
-class ManagedGroupMembershipAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests forthe ManagedGroupMembershipAudit class."""
-
- def get_api_url_members(self, group_name):
- """Return the API url being called by the method."""
- return self.api_client.sam_entry_point + "/api/groups/v1/" + group_name + "/member"
-
- def get_api_url_admins(self, group_name):
- """Return the API url being called by the method."""
- return self.api_client.sam_entry_point + "/api/groups/v1/" + group_name + "/admin"
-
- def test_group_not_managed_by_app(self):
- group = factories.ManagedGroupFactory.create(is_managed_by_app=False)
- with self.assertRaises(exceptions.AnVILNotGroupAdminError):
- audit.ManagedGroupMembershipAudit(group)
-
- def test_no_members(self):
- """audit works correctly if this group has no members."""
- group = factories.ManagedGroupFactory.create()
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_one_account_members(self):
- """audit works correctly if this group has one account member."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupAccountMembershipFactory.create(group=group)
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(response=[membership.account.email]).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- model_result = audit_results.get_result_for_model_instance(membership)
- self.assertIsInstance(model_result, audit.ModelInstanceResult)
- self.assertTrue(model_result.ok())
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_two_account_members(self):
- """audit works correctly if this group has two account members."""
- group = factories.ManagedGroupFactory.create()
- membership_1 = factories.GroupAccountMembershipFactory.create(group=group)
- membership_2 = factories.GroupAccountMembershipFactory.create(group=group)
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(
- response=[membership_1.account.email, membership_2.account.email]
- ).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- model_result = audit_results.get_result_for_model_instance(membership_1)
- self.assertIsInstance(model_result, audit.ModelInstanceResult)
- self.assertTrue(model_result.ok())
- model_result = audit_results.get_result_for_model_instance(membership_2)
- self.assertIsInstance(model_result, audit.ModelInstanceResult)
- self.assertTrue(model_result.ok())
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_one_account_members_not_in_anvil(self):
- """audit works correctly if this group has one account member not in anvil."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupAccountMembershipFactory.create(group=group)
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(membership)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_ACCOUNT_MEMBER_NOT_IN_ANVIL]))
-
- def test_two_account_members_not_in_anvil(self):
- """anvil_audit works correctly if this group has two account member not in anvil."""
- group = factories.ManagedGroupFactory.create()
- membership_1 = factories.GroupAccountMembershipFactory.create(group=group)
- membership_2 = factories.GroupAccountMembershipFactory.create(group=group)
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- model_result = audit_results.get_result_for_model_instance(membership_1)
- self.assertIsInstance(model_result, audit.ModelInstanceResult)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_ACCOUNT_MEMBER_NOT_IN_ANVIL]))
- model_result = audit_results.get_result_for_model_instance(membership_2)
- self.assertIsInstance(model_result, audit.ModelInstanceResult)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_ACCOUNT_MEMBER_NOT_IN_ANVIL]))
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_one_account_members_not_in_app(self):
- """anvil_audit works correctly if this group has one account member not in the app."""
- group = factories.ManagedGroupFactory.create()
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(response=["test-member@example.com"]).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- # Check individual records.
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertIsInstance(record_result, audit.NotInAppResult)
- self.assertEqual(record_result.record, "MEMBER: test-member@example.com")
-
- def test_two_account_members_not_in_app(self):
- """anvil_audit works correctly if this group has two account member not in the app."""
- group = factories.ManagedGroupFactory.create()
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(
- response=["test-member-1@example.com", "test-member-2@example.com"]
- ).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 2)
- # Check individual records.
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertIsInstance(record_result, audit.NotInAppResult)
- self.assertEqual(record_result.record, "MEMBER: test-member-1@example.com")
- record_result = audit_results.get_not_in_app_results()[1]
- self.assertIsInstance(record_result, audit.NotInAppResult)
- self.assertEqual(record_result.record, "MEMBER: test-member-2@example.com")
-
- def test_one_account_members_case_insensitive(self):
- """anvil_audit works correctly if this group has one account member not in the app."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupAccountMembershipFactory.create(
- group=group, account__email="tEsT-mEmBeR@example.com"
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(response=["Test-Member@example.com"]).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertTrue(record_result.ok())
-
- def test_one_account_admin(self):
- """anvil_audit works correctly if this group has one account admin."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupAccountMembershipFactory.create(
- group=group, role=models.GroupAccountMembership.ADMIN
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(response=[membership.account.email]).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertTrue(record_result.ok())
-
- def test_two_account_admin(self):
- """anvil_audit works correctly if this group has two account members."""
- group = factories.ManagedGroupFactory.create()
- membership_1 = factories.GroupAccountMembershipFactory.create(
- group=group, role=models.GroupAccountMembership.ADMIN
- )
- membership_2 = factories.GroupAccountMembershipFactory.create(
- group=group, role=models.GroupAccountMembership.ADMIN
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(
- response=[membership_1.account.email, membership_2.account.email]
- ).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership_1)
- self.assertTrue(record_result.ok())
- record_result = audit_results.get_result_for_model_instance(membership_2)
- self.assertTrue(record_result.ok())
-
- def test_one_account_admin_not_in_anvil(self):
- """anvil_audit works correctly if this group has one account member not in anvil."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupAccountMembershipFactory.create(
- group=group, role=models.GroupAccountMembership.ADMIN
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_ACCOUNT_ADMIN_NOT_IN_ANVIL]))
-
- def test_two_account_admins_not_in_anvil(self):
- """anvil_audit works correctly if this group has two account member not in anvil."""
- group = factories.ManagedGroupFactory.create()
- membership_1 = factories.GroupAccountMembershipFactory.create(
- group=group, role=models.GroupAccountMembership.ADMIN
- )
- membership_2 = factories.GroupAccountMembershipFactory.create(
- group=group, role=models.GroupAccountMembership.ADMIN
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_ACCOUNT_ADMIN_NOT_IN_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(membership_2)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_ACCOUNT_ADMIN_NOT_IN_ANVIL]))
-
- def test_one_account_admin_not_in_app(self):
- """anvil_audit works correctly if this group has one account member not in the app."""
- group = factories.ManagedGroupFactory.create()
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(response=["test-admin@example.com"]).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "ADMIN: test-admin@example.com")
-
- def test_two_account_admin_not_in_app(self):
- """anvil_audit works correctly if this group has two account admin not in the app."""
- group = factories.ManagedGroupFactory.create()
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(
- response=["test-admin-1@example.com", "test-admin-2@example.com"]
- ).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 2)
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "ADMIN: test-admin-1@example.com")
- record_result = audit_results.get_not_in_app_results()[1]
- self.assertEqual(record_result.record, "ADMIN: test-admin-2@example.com")
-
- def test_one_account_admin_case_insensitive(self):
- """anvil_audit works correctly if this group has one account member not in the app."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupAccountMembershipFactory.create(
- group=group,
- account__email="tEsT-aDmIn@example.com",
- role=models.GroupAccountMembership.ADMIN,
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(response=["Test-Admin@example.com"]).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertTrue(record_result.ok())
-
- def test_account_different_role_member_in_app_admin_in_anvil(self):
- """anvil_audit works correctly if an account has a different role in AnVIL."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupAccountMembershipFactory.create(
- group=group, role=models.GroupAccountMembership.MEMBER
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(response=[membership.account.email]).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_ACCOUNT_MEMBER_NOT_IN_ANVIL]))
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "ADMIN: " + membership.account.email)
-
- def test_one_group_members(self):
- """anvil_audit works correctly if this group has one group member."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupGroupMembershipFactory.create(parent_group=group)
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(response=[membership.child_group.email]).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertTrue(record_result.ok())
-
- def test_two_group_members(self):
- """anvil_audit works correctly if this group has two account members."""
- group = factories.ManagedGroupFactory.create()
- membership_1 = factories.GroupGroupMembershipFactory.create(parent_group=group)
- membership_2 = factories.GroupGroupMembershipFactory.create(parent_group=group)
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(
- response=[
- membership_1.child_group.email,
- membership_2.child_group.email,
- ]
- ).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership_1)
- self.assertTrue(record_result.ok())
- record_result = audit_results.get_result_for_model_instance(membership_2)
- self.assertTrue(record_result.ok())
-
- def test_one_group_members_not_in_anvil(self):
- """anvil_audit works correctly if this group has one group member not in anvil."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupGroupMembershipFactory.create(parent_group=group)
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_GROUP_MEMBER_NOT_IN_ANVIL]))
-
- def test_two_group_members_not_in_anvil(self):
- """anvil_audit works correctly if this group has two group member not in anvil."""
- group = factories.ManagedGroupFactory.create()
- membership_1 = factories.GroupGroupMembershipFactory.create(parent_group=group)
- membership_2 = factories.GroupGroupMembershipFactory.create(parent_group=group)
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_GROUP_MEMBER_NOT_IN_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(membership_2)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_GROUP_MEMBER_NOT_IN_ANVIL]))
-
- def test_one_group_members_not_in_app(self):
- """anvil_audit works correctly if this group has one group member not in the app."""
- group = factories.ManagedGroupFactory.create()
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(response=["test-member@firecloud.org"]).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "MEMBER: test-member@firecloud.org")
-
- def test_two_group_members_not_in_app(self):
- """anvil_audit works correctly if this group has two group member not in the app."""
- group = factories.ManagedGroupFactory.create()
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(
- response=["test-member-1@firecloud.org", "test-member-2@firecloud.org"]
- ).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 2)
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "MEMBER: test-member-1@firecloud.org")
- record_result = audit_results.get_not_in_app_results()[1]
- self.assertEqual(record_result.record, "MEMBER: test-member-2@firecloud.org")
-
- def test_one_group_members_case_insensitive(self):
- """anvil_audit works correctly if this group has one group member not in the app."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupGroupMembershipFactory.create(parent_group=group, child_group__name="tEsT-mEmBeR")
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(response=["Test-Member@firecloud.org"]).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertTrue(record_result.ok())
-
- def test_one_group_admin(self):
- """anvil_audit works correctly if this group has one group admin."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupGroupMembershipFactory.create(
- parent_group=group, role=models.GroupGroupMembership.ADMIN
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(response=[membership.child_group.email]).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertTrue(record_result.ok())
-
- def test_two_group_admin(self):
- """anvil_audit works correctly if this group has two group admin."""
- group = factories.ManagedGroupFactory.create()
- membership_1 = factories.GroupGroupMembershipFactory.create(
- parent_group=group, role=models.GroupGroupMembership.ADMIN
- )
- membership_2 = factories.GroupGroupMembershipFactory.create(
- parent_group=group, role=models.GroupGroupMembership.ADMIN
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(
- response=[
- membership_1.child_group.email,
- membership_2.child_group.email,
- ]
- ).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership_1)
- self.assertTrue(record_result.ok())
- record_result = audit_results.get_result_for_model_instance(membership_2)
- self.assertTrue(record_result.ok())
-
- def test_one_group_admin_not_in_anvil(self):
- """anvil_audit works correctly if this group has one group member not in anvil."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupGroupMembershipFactory.create(
- parent_group=group, role=models.GroupGroupMembership.ADMIN
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_GROUP_ADMIN_NOT_IN_ANVIL]))
-
- def test_two_group_admins_not_in_anvil(self):
- """anvil_audit works correctly if this group has two group member not in anvil."""
- group = factories.ManagedGroupFactory.create()
- membership_1 = factories.GroupGroupMembershipFactory.create(
- parent_group=group, role=models.GroupGroupMembership.ADMIN
- )
- membership_2 = factories.GroupGroupMembershipFactory.create(
- parent_group=group, role=models.GroupGroupMembership.ADMIN
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_GROUP_ADMIN_NOT_IN_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(membership_2)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_GROUP_ADMIN_NOT_IN_ANVIL]))
-
- def test_one_group_admin_not_in_app(self):
- """anvil_audit works correctly if this group has one group member not in the app."""
- group = factories.ManagedGroupFactory.create()
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(response=["test-admin@firecloud.org"]).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "ADMIN: test-admin@firecloud.org")
-
- def test_two_group_admin_not_in_app(self):
- """anvil_audit works correctly if this group has two group admin not in the app."""
- group = factories.ManagedGroupFactory.create()
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(
- response=["test-admin-1@firecloud.org", "test-admin-2@firecloud.org"]
- ).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 2)
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "ADMIN: test-admin-1@firecloud.org")
- record_result = audit_results.get_not_in_app_results()[1]
- self.assertEqual(record_result.record, "ADMIN: test-admin-2@firecloud.org")
-
- def test_one_group_admin_case_insensitive(self):
- """anvil_audit works correctly if this group has one group member not in the app."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupGroupMembershipFactory.create(
- parent_group=group,
- child_group__name="tEsT-aDmIn",
- role=models.GroupGroupMembership.ADMIN,
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(response=["Test-Admin@firecloud.org"]).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertTrue(record_result.ok())
-
- def test_group_different_role_member_in_app_admin_in_anvil(self):
- """anvil_audit works correctly if an group has a different role in AnVIL."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupGroupMembershipFactory.create(
- parent_group=group, role=models.GroupGroupMembership.MEMBER
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(response=[membership.child_group.email]).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_GROUP_MEMBER_NOT_IN_ANVIL]))
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "ADMIN: " + membership.child_group.email)
-
- def test_service_account_is_both_admin_and_member(self):
- """No errors are reported when the service account is both a member and an admin of a group."""
- group = factories.ManagedGroupFactory.create()
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(response=[self.service_account_email]).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_different_group_member_email(self):
- """anvil_audit works correctly if this group has one group member with a different email."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupGroupMembershipFactory.create(parent_group=group, child_group__email="foo@bar.com")
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(response=[membership.child_group.email]).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertTrue(record_result.ok())
-
- def test_different_group_member_email_case_insensitive(self):
- """anvil_audit works correctly if this group has one group member with a different email, case insensitive."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupGroupMembershipFactory.create(parent_group=group, child_group__email="foo@bar.com")
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(response=["Foo@Bar.com"]).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertTrue(record_result.ok())
-
- def test_service_account_is_not_directly_admin(self):
- """Audit works when the service account is not directly an admin of a group (but is via a group admin)."""
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupGroupMembershipFactory.create(
- parent_group=group,
- child_group__email="foo@bar.com",
- role=models.GroupGroupMembership.ADMIN,
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- # Use the Membership factory because it doesn't add the service account as a direct admin.
- json=api_factories.GetGroupMembershipResponseFactory(response=["foo@bar.com"]).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertTrue(record_result.ok())
-
- def test_group_is_both_admin_and_member(self):
- group = factories.ManagedGroupFactory.create()
- membership = factories.GroupGroupMembershipFactory.create(
- parent_group=group, role=models.GroupGroupMembership.ADMIN
- )
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(response=[membership.child_group.email]).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(response=[membership.child_group.email]).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertTrue(record_result.ok())
-
- def test_deactivated_account_not_member_in_anvil(self):
- """Audit fails if a deactivated account is not in the group on AnVIL."""
- group = factories.ManagedGroupFactory.create()
- # Create an inactive account that is a member of this group.
- membership = factories.GroupAccountMembershipFactory.create(
- group=group, account__status=models.Account.INACTIVE_STATUS
- )
- # The Account is not a member in AnVIL
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(membership)
- self.assertFalse(model_result.ok())
- self.assertEqual(
- model_result.errors,
- set([audit_results.ERROR_ACCOUNT_MEMBER_NOT_IN_ANVIL, audit_results.ERROR_DEACTIVATED_ACCOUNT]),
- )
-
- def test_deactivated_account_member_in_anvil(self):
- """Audit is not ok if a deactivated account is in the group on AnVIL."""
- group = factories.ManagedGroupFactory.create()
- # Create an inactive account that is a member of this group.
- membership = factories.GroupAccountMembershipFactory.create(
- group=group, account__status=models.Account.INACTIVE_STATUS
- )
- # The Account is not a member in AnVIL
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory(response=[membership.account.email]).response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertEqual(
- record_result.errors,
- set([audit_results.ERROR_DEACTIVATED_ACCOUNT]),
- )
-
- def test_deactivated_account_not_admin_in_anvil(self):
- """Audit is not ok if a deactivated account is not in the group on AnVIL."""
- group = factories.ManagedGroupFactory.create()
- # Create an inactive account that is a member of this group.
- membership = factories.GroupAccountMembershipFactory.create(
- group=group,
- account__status=models.Account.INACTIVE_STATUS,
- role=models.GroupAccountMembership.ADMIN,
- )
- # The Account is not a member in AnVIL
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory().response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(membership)
- self.assertFalse(model_result.ok())
- self.assertEqual(
- model_result.errors,
- set([audit_results.ERROR_ACCOUNT_ADMIN_NOT_IN_ANVIL, audit_results.ERROR_DEACTIVATED_ACCOUNT]),
- )
-
- def test_deactivated_account_admin_in_anvil(self):
- """Audit is not ok if a deactivated account is in the group on AnVIL."""
- group = factories.ManagedGroupFactory.create()
- # Create an inactive account that is a member of this group.
- membership = factories.GroupAccountMembershipFactory.create(
- group=group,
- account__status=models.Account.INACTIVE_STATUS,
- role=models.GroupAccountMembership.ADMIN,
- )
- # The Account is not a member in AnVIL
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=api_factories.GetGroupMembershipResponseFactory().response,
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=api_factories.GetGroupMembershipAdminResponseFactory(response=[membership.account.email]).response,
- )
- audit_results = audit.ManagedGroupMembershipAudit(group)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(membership)
- self.assertEqual(
- record_result.errors,
- set([audit_results.ERROR_DEACTIVATED_ACCOUNT]),
- )
-
-
-class WorkspaceAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests for the Workspace.anvil_audit method."""
-
- def get_api_url(self):
- return self.api_client.rawls_entry_point + "/api/workspaces"
-
- def get_api_workspace_json(
- self,
- billing_project_name,
- workspace_name,
- access,
- auth_domains=[],
- is_locked=False,
- ):
- """Return the json dictionary for a single workspace on AnVIL."""
- return {
- "accessLevel": access,
- "workspace": {
- "name": workspace_name,
- "namespace": billing_project_name,
- "authorizationDomain": [{"membersGroupName": x} for x in auth_domains],
- "isLocked": is_locked,
- },
- }
-
- def get_api_workspace_acl_url(self, billing_project_name, workspace_name):
- return (
- self.api_client.rawls_entry_point
- + "/api/workspaces/"
- + billing_project_name
- + "/"
- + workspace_name
- + "/acl"
- )
-
- def get_api_workspace_acl_response(self):
- """Return a json for the workspace/acl method where no one else can access."""
- return {
- "acl": {
- self.service_account_email: {
- "accessLevel": "OWNER",
- "canCompute": True,
- "canShare": True,
- "pending": False,
- }
- }
- }
-
- def get_api_bucket_options_url(self, billing_project_name, workspace_name):
- return self.api_client.rawls_entry_point + "/api/workspaces/" + billing_project_name + "/" + workspace_name
-
- def get_api_bucket_options_response(self):
- """Return a json for the workspace/acl method that is not requester pays."""
- return {"bucketOptions": {"requesterPays": False}}
-
- def test_anvil_audit_no_workspaces(self):
- """anvil_audit works correct if there are no Workspaces in the app."""
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_anvil_audit_one_workspace_no_errors(self):
- """anvil_audit works correct if there is one workspace in the app and it exists on AnVIL."""
- workspace = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_workspace_json(workspace.billing_project.name, workspace.name, "OWNER")],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_one_workspace_not_on_anvil(self):
- """anvil_audit raises exception if one group exists in the app but not on AnVIL."""
- workspace = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
-
- def test_anvil_audit_one_workspace_owner_in_app_reader_on_anvil(self):
- """anvil_audit raises exception if one workspace exists in the app but the access on AnVIL is READER."""
- workspace = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_workspace_json(workspace.billing_project.name, workspace.name, "READER")],
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_OWNER_ON_ANVIL]))
-
- def test_anvil_audit_one_workspace_owner_in_app_writer_on_anvil(self):
- """anvil_audit raises exception if one workspace exists in the app but the access on AnVIL is WRITER."""
- workspace = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_workspace_json(workspace.billing_project.name, workspace.name, "WRITER")],
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_OWNER_ON_ANVIL]))
-
- def test_anvil_audit_one_workspace_is_locked_in_app_not_on_anvil(self):
- """anvil_audit raises exception if workspace is locked in the app but not on AnVIL."""
- workspace = factories.WorkspaceFactory.create(is_locked=True)
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace.billing_project.name,
- workspace.name,
- "OWNER",
- is_locked=False,
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_LOCK]))
-
- def test_anvil_audit_one_workspace_is_not_locked_in_app_but_is_on_anvil(self):
- """anvil_audit raises exception if workspace is locked in the app but not on AnVIL."""
- workspace = factories.WorkspaceFactory.create(is_locked=False)
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace.billing_project.name,
- workspace.name,
- "OWNER",
- is_locked=True,
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_LOCK]))
-
- def test_anvil_audit_one_workspace_is_requester_pays_in_app_not_on_anvil(self):
- """anvil_audit raises exception if workspace is requester_pays in the app but not on AnVIL."""
- workspace = factories.WorkspaceFactory.create(is_requester_pays=True)
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace.billing_project.name,
- workspace.name,
- "OWNER",
- is_locked=False,
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_REQUESTER_PAYS]))
-
- def test_anvil_audit_one_workspace_is_not_requester_pays_in_app_but_is_on_anvil(self):
- """anvil_audit raises exception if workspace is requester_pays in the app but not on AnVIL."""
- workspace = factories.WorkspaceFactory.create(is_requester_pays=False)
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace.billing_project.name,
- workspace.name,
- "OWNER",
- is_locked=False,
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- response = self.get_api_bucket_options_response()
- response["bucketOptions"]["requesterPays"] = True
- self.anvil_response_mock.add(responses.GET, workspace_acl_url, status=200, json=response)
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_REQUESTER_PAYS]))
-
- def test_anvil_audit_two_workspaces_no_errors(self):
- """anvil_audit returns None if if two workspaces exist in both the app and AnVIL."""
- workspace_1 = factories.WorkspaceFactory.create()
- workspace_2 = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(workspace_1.billing_project.name, workspace_1.name, "OWNER"),
- self.get_api_workspace_json(workspace_2.billing_project.name, workspace_2.name, "OWNER"),
- ],
- )
- # Response to check workspace access.
- workspace_acl_url_1 = self.get_api_workspace_acl_url(workspace_1.billing_project.name, workspace_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url_1,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace access.
- workspace_acl_url_2 = self.get_api_workspace_acl_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url_2,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace_1.billing_project.name, workspace_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace_1)
- self.assertTrue(record_result.ok())
- record_result = audit_results.get_result_for_model_instance(workspace_2)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_two_groups_json_response_order_does_not_matter(self):
- """Order of groups in the json response does not matter."""
- workspace_1 = factories.WorkspaceFactory.create()
- workspace_2 = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(workspace_2.billing_project.name, workspace_2.name, "OWNER"),
- self.get_api_workspace_json(workspace_1.billing_project.name, workspace_1.name, "OWNER"),
- ],
- )
- # Response to check workspace access.
- workspace_acl_url_1 = self.get_api_workspace_acl_url(workspace_1.billing_project.name, workspace_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url_1,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace access.
- workspace_acl_url_2 = self.get_api_workspace_acl_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url_2,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace_1.billing_project.name, workspace_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace_1)
- self.assertTrue(record_result.ok())
- record_result = audit_results.get_result_for_model_instance(workspace_2)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_two_workspaces_first_not_on_anvil(self):
- """anvil_audit raises exception if two workspaces exist in the app but the first is not not on AnVIL."""
- workspace_1 = factories.WorkspaceFactory.create()
- workspace_2 = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(workspace_2.billing_project.name, workspace_2.name, "OWNER"),
- ],
- )
- # Response to check workspace access.
- workspace_acl_url_2 = self.get_api_workspace_acl_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url_2,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(workspace_2)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_two_workspaces_first_different_access(self):
- """anvil_audit when if two workspaces exist in the app but access to the first is different on AnVIL."""
- workspace_1 = factories.WorkspaceFactory.create()
- workspace_2 = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(workspace_1.billing_project.name, workspace_1.name, "READER"),
- self.get_api_workspace_json(workspace_2.billing_project.name, workspace_2.name, "OWNER"),
- ],
- )
- # Response to check workspace access.
- workspace_acl_url_2 = self.get_api_workspace_acl_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url_2,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace_1.billing_project.name, workspace_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_OWNER_ON_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(workspace_2)
- self.assertTrue(record_result.ok())
-
- def test_anvil_audit_two_workspaces_both_missing_in_anvil(self):
- """anvil_audit when there are two workspaces that exist in the app but not in AnVIL."""
- workspace_1 = factories.WorkspaceFactory.create()
- workspace_2 = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
- record_result = audit_results.get_result_for_model_instance(workspace_2)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
-
- def test_anvil_audit_one_workspace_missing_in_app(self):
- """anvil_audit returns not_in_app info if a workspace exists on AnVIL but not in the app."""
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_workspace_json("test-bp", "test-ws", "OWNER")],
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "test-bp/test-ws")
-
- def test_anvil_audit_two_workspaces_missing_in_app(self):
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json("test-bp-1", "test-ws-1", "OWNER"),
- self.get_api_workspace_json("test-bp-2", "test-ws-2", "OWNER"),
- ],
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 2)
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "test-bp-1/test-ws-1")
- record_result = audit_results.get_not_in_app_results()[1]
- self.assertEqual(record_result.record, "test-bp-2/test-ws-2")
-
- def test_different_billing_project(self):
- """A workspace is reported as missing if it has the same name but a different billing project in app."""
- workspace = factories.WorkspaceFactory.create(billing_project__name="test-bp-app", name="test-ws")
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_workspace_json("test-bp-anvil", "test-ws", "OWNER")],
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_NOT_IN_ANVIL]))
- record_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(record_result.record, "test-bp-anvil/test-ws")
-
- def test_ignores_workspaces_where_app_is_reader_on_anvil(self):
- """Audit ignores workspaces on AnVIL where app is a READER on AnVIL."""
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_workspace_json("test-bp", "test-ws", "READER")],
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_ignores_workspaces_where_app_is_writer_on_anvil(self):
- """Audit ignores workspaces on AnVIL where app is a WRITER on AnVIL."""
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_workspace_json("test-bp", "test-ws", "WRITER")],
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_one_workspace_one_auth_domain(self):
- """anvil_audit works properly when there is one workspace with one auth domain."""
- auth_domain = factories.WorkspaceAuthorizationDomainFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- auth_domain.workspace.billing_project.name,
- auth_domain.workspace.name,
- "OWNER",
- auth_domains=[auth_domain.group.name],
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(
- auth_domain.workspace.billing_project.name, auth_domain.workspace.name
- )
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(
- auth_domain.workspace.billing_project.name, auth_domain.workspace.name
- )
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(auth_domain.workspace)
- self.assertTrue(record_result.ok())
-
- def test_one_workspace_two_auth_domains(self):
- """anvil_audit works properly when there is one workspace with two auth domains."""
- workspace = factories.WorkspaceFactory.create()
- auth_domain_1 = factories.WorkspaceAuthorizationDomainFactory.create(workspace=workspace)
- auth_domain_2 = factories.WorkspaceAuthorizationDomainFactory.create(workspace=workspace)
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace.billing_project.name,
- workspace.name,
- "OWNER",
- auth_domains=[auth_domain_1.group.name, auth_domain_2.group.name],
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertTrue(record_result.ok())
-
- def test_one_workspace_two_auth_domains_order_does_not_matter(self):
- """anvil_audit works properly when there is one workspace with two auth domains."""
- workspace = factories.WorkspaceFactory.create()
- auth_domain_1 = factories.WorkspaceAuthorizationDomainFactory.create(workspace=workspace, group__name="aa")
- auth_domain_2 = factories.WorkspaceAuthorizationDomainFactory.create(workspace=workspace, group__name="zz")
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace.billing_project.name,
- workspace.name,
- "OWNER",
- auth_domains=[auth_domain_2.group.name, auth_domain_1.group.name],
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertTrue(record_result.ok())
-
- def test_one_workspace_no_auth_domain_in_app_one_auth_domain_on_anvil(self):
- """anvil_audit works properly when there is one workspace with no auth domain in the app but one on AnVIL."""
- workspace = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace.billing_project.name,
- workspace.name,
- "OWNER",
- auth_domains=["auth-anvil"],
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_AUTH_DOMAINS]))
-
- def test_one_workspace_one_auth_domain_in_app_no_auth_domain_on_anvil(self):
- """anvil_audit works properly when there is one workspace with one auth domain in the app but none on AnVIL."""
- auth_domain = factories.WorkspaceAuthorizationDomainFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- auth_domain.workspace.billing_project.name,
- auth_domain.workspace.name,
- "OWNER",
- auth_domains=[],
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(
- auth_domain.workspace.billing_project.name, auth_domain.workspace.name
- )
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(
- auth_domain.workspace.billing_project.name, auth_domain.workspace.name
- )
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(auth_domain.workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_AUTH_DOMAINS]))
-
- def test_one_workspace_no_auth_domain_in_app_two_auth_domains_on_anvil(self):
- """anvil_audit works properly when there is one workspace with no auth domain in the app but two on AnVIL."""
- workspace = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace.billing_project.name,
- workspace.name,
- "OWNER",
- auth_domains=["auth-domain"],
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_AUTH_DOMAINS]))
-
- def test_one_workspace_two_auth_domains_in_app_no_auth_domain_on_anvil(self):
- """anvil_audit works properly when there is one workspace with two auth domains in the app but none on AnVIL."""
- workspace = factories.WorkspaceFactory.create()
- factories.WorkspaceAuthorizationDomainFactory.create(workspace=workspace)
- factories.WorkspaceAuthorizationDomainFactory.create(workspace=workspace)
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace.billing_project.name,
- workspace.name,
- "OWNER",
- auth_domains=[],
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_AUTH_DOMAINS]))
-
- def test_one_workspace_two_auth_domains_in_app_one_auth_domain_on_anvil(self):
- """anvil_audit works properly when there is one workspace with two auth domains in the app but one on AnVIL."""
- workspace = factories.WorkspaceFactory.create()
- auth_domain_1 = factories.WorkspaceAuthorizationDomainFactory.create(workspace=workspace)
- factories.WorkspaceAuthorizationDomainFactory.create(workspace=workspace)
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace.billing_project.name,
- workspace.name,
- "OWNER",
- auth_domains=[auth_domain_1.group.name],
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_AUTH_DOMAINS]))
-
- def test_one_workspace_different_auth_domains(self):
- """anvil_audit works properly when the app and AnVIL have different auth domains for the same workspace."""
- auth_domain = factories.WorkspaceAuthorizationDomainFactory.create(group__name="app")
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- auth_domain.workspace.billing_project.name,
- auth_domain.workspace.name,
- "OWNER",
- auth_domains=["anvil"],
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(
- auth_domain.workspace.billing_project.name, auth_domain.workspace.name
- )
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(
- auth_domain.workspace.billing_project.name, auth_domain.workspace.name
- )
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(auth_domain.workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_AUTH_DOMAINS]))
-
- def test_two_workspaces_first_auth_domains_do_not_match(self):
- """anvil_audit works properly when there are two workspaces in the app and the first has auth domain issues."""
- workspace_1 = factories.WorkspaceFactory.create()
- workspace_2 = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace_1.billing_project.name,
- workspace_1.name,
- "OWNER",
- auth_domains=["anvil"],
- ),
- self.get_api_workspace_json(
- workspace_2.billing_project.name,
- workspace_2.name,
- "OWNER",
- auth_domains=[],
- ),
- ],
- )
- # Response to check workspace access.
- workspace_acl_url_1 = self.get_api_workspace_acl_url(workspace_1.billing_project.name, workspace_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url_1,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace access.
- workspace_acl_url_2 = self.get_api_workspace_acl_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url_2,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace_1.billing_project.name, workspace_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_AUTH_DOMAINS]))
- record_result = audit_results.get_result_for_model_instance(workspace_2)
- self.assertTrue(record_result.ok())
-
- def test_two_workspaces_auth_domains_do_not_match_for_both(self):
- """anvil_audit works properly when there are two workspaces in the app and both have auth domain issues."""
- workspace_1 = factories.WorkspaceFactory.create()
- workspace_2 = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace_1.billing_project.name,
- workspace_1.name,
- "OWNER",
- auth_domains=["anvil-1"],
- ),
- self.get_api_workspace_json(
- workspace_2.billing_project.name,
- workspace_2.name,
- "OWNER",
- auth_domains=["anvil-2"],
- ),
- ],
- )
- # Response to check workspace access.
- workspace_acl_url_1 = self.get_api_workspace_acl_url(workspace_1.billing_project.name, workspace_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url_1,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace access.
- workspace_acl_url_2 = self.get_api_workspace_acl_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url_2,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace_1.billing_project.name, workspace_1.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace_2.billing_project.name, workspace_2.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace_1)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_AUTH_DOMAINS]))
- record_result = audit_results.get_result_for_model_instance(workspace_2)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_DIFFERENT_AUTH_DOMAINS]))
-
- def test_one_workspace_with_two_errors(self):
- """One workspace has two errors: different auth domains and not owner."""
- workspace = factories.WorkspaceFactory.create()
- factories.WorkspaceAuthorizationDomainFactory.create(workspace=workspace)
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_workspace_json(workspace.billing_project.name, workspace.name, "READER")],
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(
- record_result.errors,
- set(
- [
- audit_results.ERROR_NOT_OWNER_ON_ANVIL,
- audit_results.ERROR_DIFFERENT_AUTH_DOMAINS,
- ]
- ),
- )
-
- def test_fails_sharing_audit(self):
- """anvil_audit works properly when one workspace fails its sharing audit."""
- workspace = factories.WorkspaceFactory.create()
- factories.WorkspaceGroupSharingFactory.create(workspace=workspace)
- # Response for the main call about workspaces.
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[
- self.get_api_workspace_json(
- workspace.billing_project.name,
- workspace.name,
- "OWNER",
- )
- ],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- audit_results = audit.WorkspaceAudit()
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- record_result = audit_results.get_result_for_model_instance(workspace)
- self.assertFalse(record_result.ok())
- self.assertEqual(record_result.errors, set([audit_results.ERROR_WORKSPACE_SHARING]))
-
-
-class WorkspaceSharingAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests for the WorkspaceSharingAudit class."""
-
- def setUp(self):
- super().setUp()
- # Set this variable here because it will include the service account.
- # Tests can update it with the update_api_response method.
- self.api_response = {"acl": {}}
- # Create a workspace for use in tests.
- self.workspace = factories.WorkspaceFactory.create()
- self.api_url = (
- self.api_client.rawls_entry_point
- + "/api/workspaces/"
- + self.workspace.billing_project.name
- + "/"
- + self.workspace.name
- + "/acl"
- )
-
- def update_api_response(self, email, access, can_compute=False, can_share=False):
- """Return a paired down json for a single ACL, including the service account."""
- self.api_response["acl"].update(
- {
- email: {
- "accessLevel": access,
- "canCompute": can_compute,
- "canShare": can_share,
- "pending": False,
- }
- }
- )
-
- def test_no_access(self):
- """anvil_audit works correctly if this workspace is not shared with any groups."""
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_one_group_reader(self):
- """anvil_audit works correctly if this group has one group member."""
- access = factories.WorkspaceGroupSharingFactory.create(workspace=self.workspace)
- self.update_api_response(access.group.email, access.access)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertTrue(model_result.ok())
-
- def test_two_group_readers(self):
- """anvil_audit works correctly if this workspace has two group readers."""
- access_1 = factories.WorkspaceGroupSharingFactory.create(workspace=self.workspace)
- access_2 = factories.WorkspaceGroupSharingFactory.create(workspace=self.workspace)
- self.update_api_response(access_1.group.email, "READER")
- self.update_api_response(access_2.group.email, "READER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access_1)
- self.assertTrue(model_result.ok())
- model_result = audit_results.get_result_for_model_instance(access_2)
- self.assertTrue(model_result.ok())
-
- def test_one_group_reader_not_in_anvil(self):
- """anvil_audit works correctly if this workspace has one group reader not in anvil."""
- access = factories.WorkspaceGroupSharingFactory.create(workspace=self.workspace)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_NOT_SHARED_IN_ANVIL]))
-
- def test_two_group_readers_not_in_anvil(self):
- """anvil_audit works correctly if this workspace has two group readers not in anvil."""
- access_1 = factories.WorkspaceGroupSharingFactory.create(workspace=self.workspace)
- access_2 = factories.WorkspaceGroupSharingFactory.create(workspace=self.workspace)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access_1)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_NOT_SHARED_IN_ANVIL]))
- model_result = audit_results.get_result_for_model_instance(access_2)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_NOT_SHARED_IN_ANVIL]))
-
- def test_one_group_readers_not_in_app(self):
- """anvil_audit works correctly if this workspace has one group reader not in the app."""
- self.update_api_response("test-member@firecloud.org", "READER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- model_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(model_result.record, "READER: test-member@firecloud.org")
-
- def test_two_group_readers_not_in_app(self):
- """anvil_audit works correctly if this workspace has two group readers not in the app."""
- self.update_api_response("test-member-1@firecloud.org", "READER")
- self.update_api_response("test-member-2@firecloud.org", "READER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 2)
- model_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(model_result.record, "READER: test-member-1@firecloud.org")
- model_result = audit_results.get_not_in_app_results()[1]
- self.assertEqual(model_result.record, "READER: test-member-2@firecloud.org")
-
- def test_one_group_members_case_insensitive(self):
- """anvil_audit ignores case."""
- access = factories.WorkspaceGroupSharingFactory.create(workspace=self.workspace, group__name="tEsT-mEmBeR")
- self.update_api_response("Test-Member@firecloud.org", "READER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertTrue(model_result.ok())
-
- def test_one_group_writer(self):
- """anvil_audit works correctly if this workspace has one group writer."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.WRITER
- )
- self.update_api_response(access.group.email, "WRITER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertTrue(model_result.ok())
-
- def test_two_group_writers(self):
- """anvil_audit works correctly if this workspace has two group writers."""
- access_1 = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.WRITER
- )
- access_2 = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.WRITER
- )
- self.update_api_response(access_1.group.email, "WRITER")
- self.update_api_response(access_2.group.email, "WRITER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access_1)
- self.assertTrue(model_result.ok())
- model_result = audit_results.get_result_for_model_instance(access_2)
- self.assertTrue(model_result.ok())
-
- def test_one_group_writer_not_in_anvil(self):
- """anvil_audit works correctly if this workspace has one group writer not in anvil."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.WRITER
- )
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_NOT_SHARED_IN_ANVIL]))
-
- def test_two_group_writers_not_in_anvil(self):
- """anvil_audit works correctly if this workspace has two group writers not in anvil."""
- access_1 = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.WRITER
- )
- access_2 = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.WRITER
- )
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access_1)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_NOT_SHARED_IN_ANVIL]))
- model_result = audit_results.get_result_for_model_instance(access_2)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_NOT_SHARED_IN_ANVIL]))
-
- def test_one_group_writer_not_in_app(self):
- """anvil_audit works correctly if this workspace has one group writer not in the app."""
- self.update_api_response("test-writer@firecloud.org", "WRITER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- model_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(model_result.record, "WRITER: test-writer@firecloud.org")
-
- def test_two_group_writers_not_in_app(self):
- """anvil_audit works correctly if this workspace has two group writers not in the app."""
- self.update_api_response("test-writer-1@firecloud.org", "WRITER")
- self.update_api_response("test-writer-2@firecloud.org", "WRITER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 2)
- model_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(model_result.record, "WRITER: test-writer-1@firecloud.org")
- model_result = audit_results.get_not_in_app_results()[1]
- self.assertEqual(model_result.record, "WRITER: test-writer-2@firecloud.org")
-
- def test_one_group_admin_case_insensitive(self):
- """anvil_audit works correctly if this workspace has one group member not in the app."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace,
- group__name="tEsT-wRiTeR",
- access=models.WorkspaceGroupSharing.WRITER,
- )
- self.update_api_response("Test-Writer@firecloud.org", "WRITER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertTrue(model_result.ok())
-
- def test_one_group_owner(self):
- """anvil_audit works correctly if this workspace has one group owner."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.OWNER
- )
- self.update_api_response(access.group.email, "OWNER", can_share=True)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertTrue(model_result.ok())
-
- def test_two_group_owners(self):
- """anvil_audit works correctly if this workspace has two group owners."""
- access_1 = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.OWNER
- )
- access_2 = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.OWNER
- )
- self.update_api_response(access_1.group.email, "OWNER", can_share=True)
- self.update_api_response(access_2.group.email, "OWNER", can_share=True)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 2)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access_1)
- self.assertTrue(model_result.ok())
- model_result = audit_results.get_result_for_model_instance(access_2)
- self.assertTrue(model_result.ok())
-
- def test_one_group_owner_not_in_anvil(self):
- """anvil_audit works correctly if this workspace has one group owners not in anvil."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.OWNER
- )
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_NOT_SHARED_IN_ANVIL]))
-
- def test_two_group_owners_not_in_anvil(self):
- """anvil_audit works correctly if this workspace has two group owners not in anvil."""
- access_1 = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.OWNER
- )
- access_2 = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.OWNER
- )
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 2)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access_1)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_NOT_SHARED_IN_ANVIL]))
- model_result = audit_results.get_result_for_model_instance(access_2)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_NOT_SHARED_IN_ANVIL]))
-
- def test_one_group_owner_not_in_app(self):
- """anvil_audit works correctly if this workspace has one group owner not in the app."""
- self.update_api_response("test-writer@firecloud.org", "OWNER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 1)
- model_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(model_result.record, "OWNER: test-writer@firecloud.org")
-
- def test_two_group_owners_not_in_app(self):
- """anvil_audit works correctly if this workspace has two group owners not in the app."""
- self.update_api_response("test-writer-1@firecloud.org", "OWNER")
- self.update_api_response("test-writer-2@firecloud.org", "OWNER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 2)
- model_result = audit_results.get_not_in_app_results()[0]
- self.assertEqual(model_result.record, "OWNER: test-writer-1@firecloud.org")
- model_result = audit_results.get_not_in_app_results()[1]
- self.assertEqual(model_result.record, "OWNER: test-writer-2@firecloud.org")
-
- def test_one_group_owner_case_insensitive(self):
- """anvil_audit works correctly with different cases for owner emails."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace,
- group__name="tEsT-oWnEr",
- access=models.WorkspaceGroupSharing.OWNER,
- )
- self.update_api_response("Test-Owner@firecloud.org", "OWNER", can_share=True)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertTrue(model_result.ok())
-
- def test_group_different_access_reader_in_app_writer_in_anvil(self):
- """anvil_audit works correctly if a group has different access to a workspace in AnVIL."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.READER
- )
- self.update_api_response(access.group.email, "WRITER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_DIFFERENT_ACCESS]))
-
- def test_group_different_access_reader_in_app_owner_in_anvil(self):
- """anvil_audit works correctly if a group has different access to a workspace in AnVIL."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.READER
- )
- self.update_api_response(access.group.email, "OWNER", can_compute=True, can_share=True)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertFalse(model_result.ok())
- self.assertEqual(
- model_result.errors,
- set(
- [
- audit_results.ERROR_DIFFERENT_ACCESS,
- audit_results.ERROR_DIFFERENT_CAN_COMPUTE,
- audit_results.ERROR_DIFFERENT_CAN_SHARE,
- ]
- ),
- )
-
- def test_group_different_can_compute(self):
- """anvil_audit works correctly if can_compute is different between the app and AnVIL."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace,
- access=models.WorkspaceGroupSharing.WRITER,
- can_compute=True,
- )
- self.update_api_response(access.group.email, "WRITER", can_compute=False)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_DIFFERENT_CAN_COMPUTE]))
-
- def test_group_different_can_share(self):
- """anvil_audit works correctly if can_share is True in AnVIL."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace, access=models.WorkspaceGroupSharing.WRITER
- )
- self.update_api_response(access.group.email, "WRITER", can_share=True)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_DIFFERENT_CAN_SHARE]))
-
- def test_removes_service_account(self):
- """Removes the service account from acl if it exists."""
- self.update_api_response(self.service_account_email, "OWNER", can_compute=True, can_share=True)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_group_owner_can_share_true(self):
- """Owners must have can_share=True."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace,
- access=models.WorkspaceGroupSharing.OWNER,
- can_compute=True,
- )
- self.update_api_response(access.group.email, "OWNER", can_compute=True, can_share=True)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertTrue(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 1)
- self.assertEqual(len(audit_results.get_error_results()), 0)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
-
- def test_group_writer_can_share_false(self):
- """Writers must have can_share=False."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace,
- access=models.WorkspaceGroupSharing.WRITER,
- can_compute=True,
- )
- self.update_api_response(access.group.email, "WRITER", can_compute=True, can_share=True)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_DIFFERENT_CAN_SHARE]))
-
- def test_group_reader_can_share_false(self):
- """Readers must have can_share=False."""
- access = factories.WorkspaceGroupSharingFactory.create(
- workspace=self.workspace,
- access=models.WorkspaceGroupSharing.READER,
- can_compute=False,
- )
- self.update_api_response(access.group.email, "READER", can_compute=False, can_share=True)
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- audit_results = audit.WorkspaceSharingAudit(self.workspace)
- audit_results.run_audit()
- self.assertFalse(audit_results.ok())
- self.assertEqual(len(audit_results.get_verified_results()), 0)
- self.assertEqual(len(audit_results.get_error_results()), 1)
- self.assertEqual(len(audit_results.get_not_in_app_results()), 0)
- model_result = audit_results.get_result_for_model_instance(access)
- self.assertFalse(model_result.ok())
- self.assertEqual(model_result.errors, set([audit_results.ERROR_DIFFERENT_CAN_SHARE]))
diff --git a/anvil_consortium_manager/tests/test_commands.py b/anvil_consortium_manager/tests/test_commands.py
index 8ce9a3ca..e2e9887d 100644
--- a/anvil_consortium_manager/tests/test_commands.py
+++ b/anvil_consortium_manager/tests/test_commands.py
@@ -1,305 +1,11 @@
"""Tests for management commands in `anvil_consortium_manager`."""
-import pprint
from io import StringIO
-from unittest import skip, skipUnless
+from unittest import skipUnless
-import responses
from django.conf import settings
-from django.contrib.sites.models import Site
-from django.core import mail
-from django.core.management import CommandError, call_command
-from django.test import TestCase, TransactionTestCase
-
-from ..audit import audit
-from ..management.commands.run_anvil_audit import ErrorTableWithLink
-from . import factories
-from .utils import AnVILAPIMockTestMixin
-
-
-class RunAnvilAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests for the run_anvil_audit command"""
-
- def get_api_url_billing_project(self, billing_project_name):
- return self.api_client.rawls_entry_point + "/api/billing/v2/" + billing_project_name
-
- def test_command_output_invalid_model(self):
- """Appropriate error is returned when an invalid model is specified."""
- out = StringIO()
- with self.assertRaises(CommandError) as e:
- # Call with the "--models=foo" so it goes through the argparse validation.
- # Calling with models=["foo"] does not throw an exception.
- call_command("run_anvil_audit", "--models=foo", stdout=out)
- self.assertIn("invalid choice", str(e.exception))
-
- def test_command_output_no_model_specified(self):
- """Runs on all models if no models are specified."""
- # Add API call responses.
- self.anvil_response_mock.add(
- responses.GET,
- self.api_client.sam_entry_point + "/api/groups/v1",
- status=200,
- json=[],
- )
- self.anvil_response_mock.add(
- responses.GET,
- self.api_client.rawls_entry_point + "/api/workspaces",
- status=200,
- json=[],
- )
- out = StringIO()
- call_command("run_anvil_audit", "--no-color", stdout=out)
- self.assertIn("BillingProjectAudit... ok!", out.getvalue())
- self.assertIn("AccountAudit... ok!", out.getvalue())
- self.assertIn("ManagedGroupAudit... ok!", out.getvalue())
- self.assertIn("WorkspaceAudit... ok!", out.getvalue())
-
- def test_command_output_multiple_models(self):
- """Can audit multiple models at the same time."""
- out = StringIO()
- call_command(
- "run_anvil_audit",
- "--no-color",
- models=["BillingProject", "Account"],
- stdout=out,
- )
- self.assertIn("BillingProjectAudit... ok!", out.getvalue())
- self.assertIn("AccountAudit... ok!", out.getvalue())
-
- def test_command_output_billing_project_no_instances(self):
- """Test command output."""
- out = StringIO()
- call_command("run_anvil_audit", "--no-color", models=["BillingProject"], stdout=out)
- self.assertIn("BillingProjectAudit... ok!", out.getvalue())
-
- def test_command_output_account_no_instances(self):
- """Test command output."""
- out = StringIO()
- call_command("run_anvil_audit", "--no-color", models=["Account"], stdout=out)
- self.assertIn("AccountAudit... ok!", out.getvalue())
-
- def test_command_output_managed_group_no_instances(self):
- """Test command output."""
- self.anvil_response_mock.add(
- responses.GET,
- self.api_client.sam_entry_point + "/api/groups/v1",
- status=200,
- json=[],
- )
- out = StringIO()
- call_command("run_anvil_audit", "--no-color", models=["ManagedGroup"], stdout=out)
- self.assertIn("ManagedGroupAudit... ok!", out.getvalue())
-
- def test_command_output_workspace_no_instances(self):
- """Test command output."""
- self.anvil_response_mock.add(
- responses.GET,
- self.api_client.rawls_entry_point + "/api/workspaces",
- status=200,
- json=[],
- )
- out = StringIO()
- call_command("run_anvil_audit", "--no-color", models=["Workspace"], stdout=out)
- self.assertIn("WorkspaceAudit... ok!", out.getvalue())
-
- def test_command_run_audit_one_instance_ok(self):
- """Test command output."""
- billing_project = factories.BillingProjectFactory.create()
- # Add a response.
- api_url = self.get_api_url_billing_project(billing_project.name)
- self.anvil_response_mock.add(responses.GET, api_url, status=200)
- out = StringIO()
- call_command("run_anvil_audit", "--no-color", models=["BillingProject"], stdout=out)
- self.assertIn("BillingProjectAudit... ok!", out.getvalue())
- self.assertNotIn("errors", out.getvalue())
- self.assertNotIn("not_in_app", out.getvalue())
-
- def test_command_run_audit_ok_email(self):
- """Test command output."""
- billing_project = factories.BillingProjectFactory.create()
- # Add a response.
- api_url = self.get_api_url_billing_project(billing_project.name)
- self.anvil_response_mock.add(responses.GET, api_url, status=200)
- out = StringIO()
- call_command(
- "run_anvil_audit",
- "--no-color",
- models=["BillingProject"],
- email="test@example.com",
- stdout=out,
- )
- self.assertIn("BillingProjectAudit... ok!", out.getvalue())
- # One message has been sent by default.
- self.assertEqual(len(mail.outbox), 1)
- email = mail.outbox[0]
- self.assertEqual(email.to, ["test@example.com"])
- self.assertIn("ok", email.subject)
- # Text body.
- audit_results = audit.BillingProjectAudit()
- audit_results.run_audit()
- self.assertEqual(pprint.pformat(audit_results.export()), email.body)
- # HTML body.
- self.assertEqual(len(email.alternatives), 1)
- # Check that the number of "ok" instances is correct in email body.
- self.assertIn("1 instance(s) verified", email.alternatives[0][0])
-
- def test_command_run_audit_ok_email_errors_only(self):
- """Test command output when email and errors_only is set."""
- billing_project = factories.BillingProjectFactory.create()
- # Add a response.
- api_url = self.get_api_url_billing_project(billing_project.name)
- self.anvil_response_mock.add(responses.GET, api_url, status=200)
- out = StringIO()
- call_command(
- "run_anvil_audit",
- "--no-color",
- models=["BillingProject"],
- email="test@example.com",
- errors_only=True,
- stdout=out,
- )
- self.assertIn("BillingProjectAudit... ok!", out.getvalue())
- # No message has been sent by default.
- self.assertEqual(len(mail.outbox), 0)
-
- def test_command_run_audit_not_ok(self):
- """Test command output when BillingProject audit is not ok."""
- billing_project = factories.BillingProjectFactory.create()
- # Add a response.
- api_url = self.get_api_url_billing_project(billing_project.name)
- self.anvil_response_mock.add(responses.GET, api_url, status=404, json={"message": "error"})
- out = StringIO()
- call_command("run_anvil_audit", "--no-color", models=["BillingProject"], stdout=out)
- self.assertIn("BillingProjectAudit... problems found.", out.getvalue())
- self.assertIn("""'errors':""", out.getvalue())
- self.assertIn(audit.BillingProjectAudit.ERROR_NOT_IN_ANVIL, out.getvalue())
-
- def test_command_run_audit_not_ok_email(self):
- """Test command output when BillingProject audit is not ok with email specified."""
- billing_project = factories.BillingProjectFactory.create()
- # Add a response.
- api_url = self.get_api_url_billing_project(billing_project.name)
- self.anvil_response_mock.add(responses.GET, api_url, status=404, json={"message": "error"})
- out = StringIO()
- call_command(
- "run_anvil_audit",
- "--no-color",
- models=["BillingProject"],
- email="test@example.com",
- stdout=out,
- )
- self.assertIn("BillingProjectAudit... problems found.", out.getvalue())
- # Not printed to stdout.
- self.assertIn("""'errors':""", out.getvalue())
- # One message has been sent.
- self.assertEqual(len(mail.outbox), 1)
- email = mail.outbox[0]
- self.assertEqual(email.to, ["test@example.com"])
- self.assertIn("errors", email.subject)
- # Text body.
- audit_results = audit.BillingProjectAudit()
- audit_results.run_audit()
- # HTML body.
- self.assertEqual(len(email.alternatives), 1)
-
- def test_command_run_audit_not_ok_email_has_html_link(self):
- """Test command output when BillingProject audit is not ok with email specified."""
- billing_project = factories.BillingProjectFactory.create()
- # Add a response.
- api_url = self.get_api_url_billing_project(billing_project.name)
- self.anvil_response_mock.add(responses.GET, api_url, status=404, json={"message": "error"})
- out = StringIO()
- call_command(
- "run_anvil_audit",
- "--no-color",
- models=["BillingProject"],
- email="test@example.com",
- stdout=out,
- )
- email = mail.outbox[0]
- self.assertEqual(len(email.alternatives), 1)
- html_fragment = """{obj}""".format(
- obj=str(billing_project), url=billing_project.get_absolute_url()
- )
- self.assertInHTML(html_fragment, email.alternatives[0][0])
-
- def test_command_run_audit_not_ok_email_has_html_link_different_domain(self):
- """Test command output when BillingProject audit is not ok with email specified."""
- site = Site.objects.create(domain="foobar.com", name="test")
- site.save()
- with self.settings(SITE_ID=site.id):
- billing_project = factories.BillingProjectFactory.create()
- # Add a response.
- api_url = self.get_api_url_billing_project(billing_project.name)
- self.anvil_response_mock.add(responses.GET, api_url, status=404, json={"message": "error"})
- out = StringIO()
- call_command(
- "run_anvil_audit",
- "--no-color",
- models=["BillingProject"],
- email="test@example.com",
- stdout=out,
- )
- email = mail.outbox[0]
- self.assertEqual(len(email.alternatives), 1)
- html_fragment = """{obj}""".format(
- obj=str(billing_project), url=billing_project.get_absolute_url()
- )
- self.assertInHTML(html_fragment, email.alternatives[0][0])
-
- def test_command_run_audit_api_error(self):
- """Test command output when BillingProject audit is not ok."""
- billing_project = factories.BillingProjectFactory.create()
- # Add a response.
- api_url = self.get_api_url_billing_project(billing_project.name)
- self.anvil_response_mock.add(responses.GET, api_url, status=500, json={"message": "error"})
- out = StringIO()
- with self.assertRaises(CommandError):
- call_command("run_anvil_audit", "--no-color", models=["BillingProject"], stdout=out)
-
- # This test is complicated so skipping for now.
- # When trying to change the settings, the test attempts to repopulate the
- # workspace registry. This then causes an error. Skip it until we figure out
- # how best to handle this situation.
- @skip
- def test_command_without_sites(self):
- """Test command behavior without the Sites framework enabled."""
- out = StringIO()
- with self.modify_settings(INSTALLED_APPS={"remove": ["django.contrib.sites"]}):
- # with self.assertRaises(ImproperlyConfigured):
- call_command(
- "run_anvil_audit",
- "--no-color",
- models=["BillingProject"],
- email="test@example.com",
- stdout=out,
- )
-
-
-class RunAnVILAuditTablesTest(TestCase):
- def setUp(self):
- super().setUp()
-
- class GenericAuditResults(audit.AnVILAudit):
- TEST_ERROR_1 = "Test error 1"
- TEST_ERROR_2 = "Test error 2"
-
- self.audit_results = GenericAuditResults()
- # It doesn't matter what model we use at this point, so just pick Account.
- self.model_factory = factories.AccountFactory
-
- def test_errors_table(self):
- """Errors table is correct."""
- obj_verified = self.model_factory.create()
- self.audit_results.add_result(audit.ModelInstanceResult(obj_verified))
- obj_error = self.model_factory.create()
- error_result = audit.ModelInstanceResult(obj_error)
- error_result.add_error(self.audit_results.TEST_ERROR_1)
- error_result.add_error(self.audit_results.TEST_ERROR_2)
- self.audit_results.add_result(error_result)
- self.audit_results.add_result(audit.NotInAppResult("foo"))
- table = ErrorTableWithLink(self.audit_results.get_error_results())
- self.assertEqual(table.rows[0].get_cell("errors"), "Test error 1, Test error 2")
+from django.core.management import call_command
+from django.test import TransactionTestCase
class ConvertMariaDbUUIDFieldsTest(TransactionTestCase):
diff --git a/anvil_consortium_manager/tests/test_viewmixins.py b/anvil_consortium_manager/tests/test_viewmixins.py
index 558d1ff6..7405d190 100644
--- a/anvil_consortium_manager/tests/test_viewmixins.py
+++ b/anvil_consortium_manager/tests/test_viewmixins.py
@@ -1,4 +1,3 @@
-from django.core.exceptions import ImproperlyConfigured
from django.test import RequestFactory, TestCase
from .. import viewmixins
@@ -7,14 +6,6 @@
from .test_app.adapters import TestWorkspaceAdapter
-class AnVILAuditMixinTest(TestCase):
- """ManagedGroupGraphMixin tests that aren't covered elsewhere."""
-
- def test_run_audit_not_implemented(self):
- with self.assertRaises(ImproperlyConfigured):
- viewmixins.AnVILAuditMixin().run_audit()
-
-
class ManagedGroupGraphMixinTest(TestCase):
"""ManagedGroupGraphMixin tests that aren't covered elsewhere."""
diff --git a/anvil_consortium_manager/tests/test_views.py b/anvil_consortium_manager/tests/test_views.py
index a95791ec..25761d47 100644
--- a/anvil_consortium_manager/tests/test_views.py
+++ b/anvil_consortium_manager/tests/test_views.py
@@ -20,10 +20,9 @@
from faker import Faker
from freezegun import freeze_time
-from .. import __version__, anvil_api, filters, forms, models, tables, views
+from .. import __version__, filters, forms, models, tables, views
from ..adapters.default import DefaultWorkspaceAdapter
from ..adapters.workspace import workspace_adapter_registry
-from ..audit import audit
from ..tokens import account_verification_token
from . import factories
from .test_app import forms as app_forms
@@ -109,15 +108,15 @@ class ViewEditUrlTest(TestCase):
reverse("anvil_consortium_manager:index"),
reverse("anvil_consortium_manager:status"),
reverse("anvil_consortium_manager:accounts:list"),
- reverse("anvil_consortium_manager:accounts:audit"),
+ reverse("anvil_consortium_manager:auditor:accounts:all"),
reverse("anvil_consortium_manager:billing_projects:list"),
- reverse("anvil_consortium_manager:billing_projects:audit"),
+ reverse("anvil_consortium_manager:auditor:billing_projects:all"),
reverse("anvil_consortium_manager:managed_groups:list"),
reverse("anvil_consortium_manager:managed_groups:visualization"),
- reverse("anvil_consortium_manager:managed_groups:audit"),
+ reverse("anvil_consortium_manager:auditor:managed_groups:all"),
reverse("anvil_consortium_manager:workspaces:landing_page"),
reverse("anvil_consortium_manager:workspaces:list_all"),
- reverse("anvil_consortium_manager:workspaces:audit"),
+ reverse("anvil_consortium_manager:auditor:workspaces:all"),
)
# other_urls = (
@@ -132,8 +131,8 @@ class ViewEditUrlTest(TestCase):
# reverse("anvil_consortium_manager:managed_groups:member_groups:delete"),
# reverse("anvil_consortium_manager:workspaces:sharing:update"),
# reverse("anvil_consortium_manager:workspaces:delete"),
- # reverse("anvil_consortium_manager:managed_groups:audit_membership"),
- # reverse("anvil_consortium_manager:workspaces:audit_access"),
+ # reverse("anvil_consortium_manager:auditor:managed_groups:membership:by_group:all"),
+ # reverse("anvil_consortium_manager:auditor:workspaces:all_access"),
# )
edit_urls = (
@@ -1042,156 +1041,6 @@ def test_does_not_return_billing_projects_where_app_is_not_user(self):
self.assertEqual(json.loads(response.content.decode("utf-8"))["results"], [])
-class BillingProjectAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests for the BillingProjectAudit view."""
-
- def setUp(self):
- """Set up test class."""
- super().setUp()
- self.factory = RequestFactory()
- # Create a user with only view permission.
- self.user = User.objects.create_user(username="test", password="test")
- self.user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
- )
-
- def get_url(self, *args):
- """Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:billing_projects:audit", args=args)
-
- def get_api_url(self, billing_project_name):
- return self.api_client.rawls_entry_point + "/api/billing/v2/" + billing_project_name
-
- def get_api_json_response(self):
- return {
- "roles": ["User"],
- }
-
- def get_view(self):
- """Return the view being tested."""
- return views.BillingProjectAudit.as_view()
-
- def test_view_redirect_not_logged_in(self):
- "View redirects to login view when user is not logged in."
- # Need a client for redirects.
- response = self.client.get(self.get_url())
- self.assertRedirects(response, resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url())
-
- def test_status_code_with_user_permission(self):
- """Returns successful response code."""
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertEqual(response.status_code, 200)
-
- def test_access_without_user_permission(self):
- """Raises permission denied if user has no permissions."""
- user_no_perms = User.objects.create_user(username="test-none", password="test-none")
- request = self.factory.get(self.get_url())
- request.user = user_no_perms
- with self.assertRaises(PermissionDenied):
- self.get_view()(request)
-
- def test_access_with_limited_view_permission(self):
- """Raises permission denied if user has limited view permission."""
- user = User.objects.create_user(username="test-limited", password="test-limited")
- user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
- )
- request = self.factory.get(self.get_url())
- request.user = user
- with self.assertRaises(PermissionDenied):
- self.get_view()(request)
-
- def test_template(self):
- """Template loads successfully."""
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertEqual(response.status_code, 200)
-
- def test_audit_verified(self):
- """audit_verified is in the context data."""
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("verified_table", response.context_data)
- self.assertIsInstance(response.context_data["verified_table"], audit.VerifiedTable)
- self.assertEqual(len(response.context_data["verified_table"].rows), 0)
-
- def test_audit_verified_one_record(self):
- """audit_verified with one verified record."""
- billing_project = factories.BillingProjectFactory.create()
- api_url = self.get_api_url(billing_project.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=self.get_api_json_response(),
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("verified_table", response.context_data)
- self.assertEqual(len(response.context_data["verified_table"].rows), 1)
-
- def test_audit_errors(self):
- """audit_errors is in the context data."""
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("error_table", response.context_data)
- self.assertIsInstance(response.context_data["error_table"], audit.ErrorTable)
- self.assertEqual(len(response.context_data["error_table"].rows), 0)
-
- def test_audit_errors_one_record(self):
- """audit_errors with one verified record."""
- billing_project = factories.BillingProjectFactory.create()
- api_url = self.get_api_url(billing_project.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=404,
- json={"message": "other error"},
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("error_table", response.context_data)
- self.assertEqual(len(response.context_data["error_table"].rows), 1)
-
- def test_audit_not_in_app(self):
- """audit_errors is in the context data."""
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("not_in_app_table", response.context_data)
- self.assertIsInstance(response.context_data["not_in_app_table"], audit.NotInAppTable)
- self.assertEqual(len(response.context_data["not_in_app_table"].rows), 0)
-
- def test_audit_ok_is_ok(self):
- """audit_ok when audit_results.ok() is True."""
- billing_project = factories.BillingProjectFactory.create()
- api_url = self.get_api_url(billing_project.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], True)
-
- def test_audit_ok_is_not_ok(self):
- """audit_ok when audit_results.ok() is True."""
- billing_project = factories.BillingProjectFactory.create()
- api_url = self.get_api_url(billing_project.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=404,
- json={"message": "other error"},
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], False)
-
-
class AccountDetailTest(TestCase):
def setUp(self):
"""Set up test class."""
@@ -4591,159 +4440,6 @@ def test_adapter_labels(self):
self.assertEqual(view.get_selected_result_label(account), "TEST test@bar.com")
-class AccountAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests for the AccountAudit view."""
-
- def setUp(self):
- """Set up test class."""
- super().setUp()
- self.factory = RequestFactory()
- # Create a user with only view permission.
- self.user = User.objects.create_user(username="test", password="test")
- self.user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
- )
-
- def get_url(self, *args):
- """Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:accounts:audit", args=args)
-
- def get_api_url(self, email):
- return self.api_client.sam_entry_point + "/api/users/v1/" + email
-
- def get_api_json_response(self, email):
- id = fake.bothify(text="#" * 21)
- return {
- "googleSubjectId": id,
- "userEmail": email,
- "userSubjectId": id,
- }
-
- def get_view(self):
- """Return the view being tested."""
- return views.AccountAudit.as_view()
-
- def test_view_redirect_not_logged_in(self):
- "View redirects to login view when user is not logged in."
- # Need a client for redirects.
- response = self.client.get(self.get_url())
- self.assertRedirects(response, resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url())
-
- def test_status_code_with_user_permission(self):
- """Returns successful response code."""
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertEqual(response.status_code, 200)
-
- def test_access_with_limited_view_permission(self):
- """Raises permission denied if user has limited view permission."""
- user = User.objects.create_user(username="test-limited", password="test-limited")
- user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
- )
- request = self.factory.get(self.get_url())
- request.user = user
- with self.assertRaises(PermissionDenied):
- self.get_view()(request)
-
- def test_access_without_user_permission(self):
- """Raises permission denied if user has no permissions."""
- user_no_perms = User.objects.create_user(username="test-none", password="test-none")
- request = self.factory.get(self.get_url())
- request.user = user_no_perms
- with self.assertRaises(PermissionDenied):
- self.get_view()(request)
-
- def test_template(self):
- """Template loads successfully."""
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertEqual(response.status_code, 200)
-
- def test_audit_verified(self):
- """audit_verified is in the context data."""
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("verified_table", response.context_data)
- self.assertIsInstance(response.context_data["verified_table"], audit.VerifiedTable)
- self.assertEqual(len(response.context_data["verified_table"].rows), 0)
-
- def test_audit_verified_one_verified(self):
- """audit_verified with one verified record."""
- account = factories.AccountFactory.create()
- api_url = self.get_api_url(account.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=self.get_api_json_response(account.email),
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("verified_table", response.context_data)
- self.assertEqual(len(response.context_data["verified_table"].rows), 1)
-
- def test_audit_errors(self):
- """audit_errors is in the context data."""
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("error_table", response.context_data)
- self.assertIsInstance(response.context_data["error_table"], audit.ErrorTable)
- self.assertEqual(len(response.context_data["error_table"].rows), 0)
-
- def test_audit_errors_one_verified(self):
- """audit_errors with one verified record."""
- account = factories.AccountFactory.create()
- api_url = self.get_api_url(account.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=404,
- json={"message": "other error"},
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("error_table", response.context_data)
- self.assertEqual(len(response.context_data["error_table"].rows), 1)
-
- def test_audit_not_in_app(self):
- """audit_errors is in the context data."""
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("not_in_app_table", response.context_data)
- self.assertEqual(len(response.context_data["error_table"].rows), 0)
-
- def test_audit_ok_is_ok(self):
- """audit_ok when audit_results.ok() is True."""
- account = factories.AccountFactory.create()
- api_url = self.get_api_url(account.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=self.get_api_json_response(account.email),
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], True)
-
- def test_audit_ok_is_not_ok(self):
- """audit_ok when audit_results.ok() is True."""
- account = factories.AccountFactory.create()
- api_url = self.get_api_url(account.email)
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=404,
- json={"message": "other error"},
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], False)
-
-
class ManagedGroupDetailTest(TestCase):
def setUp(self):
"""Set up test class."""
@@ -6117,14 +5813,11 @@ def test_does_not_return_groups_not_managed_by_app_when_specified(self):
self.assertEqual(json.loads(response.content.decode("utf-8"))["results"], [])
-class ManagedGroupAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests for the ManagedGroupAudit view."""
-
+class ManagedGroupVisualizationTest(TestCase):
def setUp(self):
"""Set up test class."""
- super().setUp()
self.factory = RequestFactory()
- # Create a user with only view permission.
+ # Create a user with both view and edit permission.
self.user = User.objects.create_user(username="test", password="test")
self.user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
@@ -6132,40 +5825,7 @@ def setUp(self):
def get_url(self, *args):
"""Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:managed_groups:audit", args=args)
-
- def get_api_groups_url(self):
- """Return the API url being called by the method."""
- return self.api_client.sam_entry_point + "/api/groups/v1"
-
- def get_api_group_json(self, group_name, role):
- """Return json data about groups in the API format."""
- json_data = {
- "groupEmail": group_name + "@firecloud.org",
- "groupName": group_name,
- "role": role,
- }
- return json_data
-
- def get_api_url_members(self, group_name):
- """Return the API url being called by the method."""
- return self.api_client.sam_entry_point + "/api/groups/v1/" + group_name + "/member"
-
- def get_api_url_admins(self, group_name):
- """Return the API url being called by the method."""
- return self.api_client.sam_entry_point + "/api/groups/v1/" + group_name + "/admin"
-
- def get_api_json_response_admins(self, emails=[]):
- """Return json data about groups in the API format."""
- return [anvil_api.AnVILAPIClient().auth_session.credentials.service_account_email] + emails
-
- def get_api_json_response_members(self, emails=[]):
- """Return json data about groups in the API format."""
- return emails
-
- def get_view(self):
- """Return the view being tested."""
- return views.ManagedGroupAudit.as_view()
+ return reverse("anvil_consortium_manager:managed_groups:visualization", args=args)
def test_view_redirect_not_logged_in(self):
"View redirects to login view when user is not logged in."
@@ -6175,13 +5835,6 @@ def test_view_redirect_not_logged_in(self):
def test_status_code_with_user_permission(self):
"""Returns successful response code."""
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
self.client.force_login(self.user)
response = self.client.get(self.get_url())
self.assertEqual(response.status_code, 200)
@@ -6195,949 +5848,577 @@ def test_access_with_limited_view_permission(self):
request = self.factory.get(self.get_url())
request.user = user
with self.assertRaises(PermissionDenied):
- self.get_view()(request)
+ views.ManagedGroupVisualization.as_view()(request)
- def test_access_without_user_permission(self):
- """Raises permission denied if user has no permissions."""
- user_no_perms = User.objects.create_user(username="test-none", password="test-none")
- request = self.factory.get(self.get_url())
- request.user = user_no_perms
- with self.assertRaises(PermissionDenied):
- self.get_view()(request)
+ def test_view_status_code_with_existing_object_not_managed(self):
+ """Returns a successful status code for an existing object pk."""
+ self.client.force_login(self.user)
+ response = self.client.get(self.get_url())
+ self.assertEqual(response.status_code, 200)
- def test_template(self):
- """Template loads successfully."""
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
+ def test_no_groups(self):
+ """Visualization when there are no groups."""
self.client.force_login(self.user)
response = self.client.get(self.get_url())
- self.assertEqual(response.status_code, 200)
+ self.assertIn("graph", response.context_data)
- def test_audit_verified(self):
- """audit_verified is in the context data."""
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
+ def test_one_group(self):
+ """Visualization when there is one group."""
+ factories.ManagedGroupFactory.create()
self.client.force_login(self.user)
response = self.client.get(self.get_url())
- self.assertIn("verified_table", response.context_data)
- self.assertIsInstance(response.context_data["verified_table"], audit.VerifiedTable)
- self.assertEqual(len(response.context_data["verified_table"].rows), 0)
+ self.assertIn("graph", response.context_data)
- def test_audit_verified_one_record(self):
- """audit_verified with one verified record."""
- group = factories.ManagedGroupFactory.create()
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_group_json(group.name, "Admin")],
- )
- # Group membership API call.
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=[]),
- )
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[]),
- )
+ def test_two_groups(self):
+ """Visualization when there are two groups."""
+ factories.ManagedGroupFactory.create(name="g1")
+ factories.ManagedGroupFactory.create(name="g2")
self.client.force_login(self.user)
response = self.client.get(self.get_url())
- self.assertIn("verified_table", response.context_data)
- self.assertEqual(len(response.context_data["verified_table"].rows), 1)
+ self.assertIn("graph", response.context_data)
- def test_audit_errors(self):
- """audit_errors is in the context data."""
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
+ def test_group_visualization(self):
+ factories.ManagedGroupFactory.create()
+ grandparent = factories.ManagedGroupFactory.create()
+ parent_1 = factories.ManagedGroupFactory.create()
+ parent_2 = factories.ManagedGroupFactory.create()
+ child = factories.ManagedGroupFactory.create()
+ factories.GroupGroupMembershipFactory.create(parent_group=grandparent, child_group=parent_1)
+ factories.GroupGroupMembershipFactory.create(parent_group=grandparent, child_group=parent_2)
+ factories.GroupGroupMembershipFactory.create(
+ parent_group=parent_1,
+ child_group=child,
+ role=models.GroupGroupMembership.ADMIN,
)
self.client.force_login(self.user)
response = self.client.get(self.get_url())
- self.assertIn("error_table", response.context_data)
- self.assertIsInstance(response.context_data["error_table"], audit.ErrorTable)
- self.assertEqual(len(response.context_data["error_table"].rows), 0)
+ self.assertIn("graph", response.context_data)
- def test_audit_errors_one_record(self):
- """audit_errors with one error record."""
- group = factories.ManagedGroupFactory.create()
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- # Error - we are not the admin
- json=[self.get_api_group_json(group.name, "Member")],
+
+class WorkspaceLandingPageTest(TestCase):
+ def setUp(self):
+ """Set up test class."""
+ self.factory = RequestFactory()
+ # Create a user with view permission.
+ self.view_user = User.objects.create_user(username="test_view", password="view")
+ self.view_user.user_permissions.add(
+ Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
+ )
+ # Create a user with staff view permission.
+ self.staff_view_user = User.objects.create_user(username="test_staff_view", password="view")
+ self.staff_view_user.user_permissions.add(
+ Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
+ )
+ # Create a user with edit permission.
+ self.edit_user = User.objects.create_user(username="test_edit", password="test")
+ self.edit_user.user_permissions.add(
+ Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME),
+ Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME),
)
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("error_table", response.context_data)
- self.assertEqual(len(response.context_data["error_table"].rows), 1)
- def test_audit_not_in_app(self):
- """audit_not_in_app is in the context data."""
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
+ def tearDown(self):
+ """Clean up after tests."""
+ # Unregister all adapters.
+ workspace_adapter_registry._registry = {}
+ # Register the default adapter.
+ workspace_adapter_registry.register(DefaultWorkspaceAdapter)
+ super().tearDown()
+
+ def get_url(self):
+ """Get the url for the view being tested."""
+ return reverse("anvil_consortium_manager:workspaces:landing_page")
+
+ def test_view_redirect_not_logged_in(self):
+ "View redirects to login view when user is not logged in."
+ # Need a client for redirects.
+ response = self.client.get(self.get_url())
+ self.assertRedirects(
+ response,
+ resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url(),
)
- self.client.force_login(self.user)
+
+ def test_status_code_with_staff_view_permission(self):
+ """Returns successful response code if user has staff_view permission."""
+ self.client.force_login(self.staff_view_user)
response = self.client.get(self.get_url())
- self.assertIn("not_in_app_table", response.context_data)
- self.assertIsInstance(response.context_data["not_in_app_table"], audit.NotInAppTable)
- self.assertEqual(len(response.context_data["not_in_app_table"].rows), 0)
+ self.assertEqual(response.status_code, 200)
- def test_audit_not_in_app_one_record(self):
- """audit_not_in_app with one record not in app."""
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_group_json("foo", "Admin")],
- )
- self.client.force_login(self.user)
+ def test_access_with_view_permission(self):
+ """Returns successful response code if user has view permission."""
+ self.client.force_login(self.view_user)
response = self.client.get(self.get_url())
- self.assertIn("not_in_app_table", response.context_data)
- self.assertEqual(len(response.context_data["not_in_app_table"].rows), 1)
+ self.assertEqual(response.status_code, 200)
- def test_audit_ok_is_ok(self):
- """audit_ok when audit_results.ok() is True."""
- group = factories.ManagedGroupFactory.create()
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_group_json(group.name, "Admin")],
+ def test_staff_view_permission(self):
+ """Links to edit required do not appear in the page when user only has staff_view permission."""
+ self.client.force_login(self.staff_view_user)
+ response = self.client.get(self.get_url())
+ self.assertIn("show_edit_links", response.context_data)
+ self.assertFalse(response.context_data["show_edit_links"])
+ self.assertNotContains(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:import",
+ kwargs={"workspace_type": "workspace"},
+ ),
)
- # Group membership API call.
- api_url_members = self.get_api_url_members(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=[]),
+ self.assertNotContains(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:new",
+ kwargs={"workspace_type": "workspace"},
+ ),
)
- api_url_admins = self.get_api_url_admins(group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[]),
+ self.assertContains(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:list",
+ kwargs={"workspace_type": "workspace"},
+ ),
)
- self.client.force_login(self.user)
+
+ def test_view_permission(self):
+ """Links to edit required do not appear in the page when user only has view permission."""
+ self.client.force_login(self.view_user)
response = self.client.get(self.get_url())
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], True)
+ self.assertIn("show_edit_links", response.context_data)
+ self.assertFalse(response.context_data["show_edit_links"])
+ self.assertNotContains(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:import",
+ kwargs={"workspace_type": "workspace"},
+ ),
+ )
+ self.assertNotContains(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:new",
+ kwargs={"workspace_type": "workspace"},
+ ),
+ )
+ self.assertContains(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:list",
+ kwargs={"workspace_type": "workspace"},
+ ),
+ )
- def test_audit_ok_is_not_ok(self):
- """audit_ok when audit_results.ok() is False."""
- group = factories.ManagedGroupFactory.create()
- api_url = self.get_api_groups_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- # Error - we are not admin.
- json=[self.get_api_group_json(group.name, "Member")],
+ def test_edit_permission(self):
+ """Links to edit required appear in the page when user also has edit permission."""
+ self.client.force_login(self.edit_user)
+ response = self.client.get(self.get_url())
+ self.assertIn("show_edit_links", response.context_data)
+ self.assertTrue(response.context_data["show_edit_links"])
+ self.assertContains(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:import",
+ kwargs={"workspace_type": "workspace"},
+ ),
)
- self.client.force_login(self.user)
+ self.assertContains(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:new",
+ kwargs={"workspace_type": "workspace"},
+ ),
+ )
+ self.assertContains(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:list",
+ kwargs={"workspace_type": "workspace"},
+ ),
+ )
+
+ def test_one_registered_workspace_in_context(self):
+ """One registered workspace in context when only DefaultWorkspaceAdapter is registered"""
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url())
+ self.assertIn("registered_workspace_adapters", response.context_data)
+ self.assertEqual(len(response.context_data["registered_workspace_adapters"]), 1)
+
+ def test_two_registered_workspaces_in_context(self):
+ """Two registered workspaces in context when two workspace adapters are registered"""
+ workspace_adapter_registry.register(TestWorkspaceAdapter)
+ self.client.force_login(self.view_user)
response = self.client.get(self.get_url())
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], False)
+ self.assertIn("registered_workspace_adapters", response.context_data)
+ self.assertEqual(len(response.context_data["registered_workspace_adapters"]), 2)
-class ManagedGroupMembershipAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests for the ManagedGroupAudit view."""
+class WorkspaceDetailTest(TestCase):
+ """Tests for the WorkspaceDetail view."""
def setUp(self):
"""Set up test class."""
- super().setUp()
self.factory = RequestFactory()
# Create a user with both view and edit permission.
self.user = User.objects.create_user(username="test", password="test")
self.user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
)
- self.group = factories.ManagedGroupFactory.create()
-
- def get_url(self, *args):
- """Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:managed_groups:audit_membership", args=args)
-
- def get_api_url_members(self, group_name):
- """Return the API url being called by the method."""
- return self.api_client.sam_entry_point + "/api/groups/v1/" + group_name + "/member"
-
- def get_api_url_admins(self, group_name):
- """Return the API url being called by the method."""
- return self.api_client.sam_entry_point + "/api/groups/v1/" + group_name + "/admin"
- def get_api_json_response_admins(self, emails=[]):
- """Return json data about groups in the API format."""
- return [anvil_api.AnVILAPIClient().auth_session.credentials.service_account_email] + emails
-
- def get_api_json_response_members(self, emails=[]):
- """Return json data about groups in the API format."""
- return emails
+ def tearDown(self):
+ """Clean up after tests."""
+ # Unregister all adapters.
+ workspace_adapter_registry._registry = {}
+ # Register the default adapter.
+ workspace_adapter_registry.register(DefaultWorkspaceAdapter)
+ super().tearDown()
def get_view(self):
"""Return the view being tested."""
- return views.ManagedGroupMembershipAudit.as_view()
+ return views.WorkspaceDetail.as_view()
+
+ def get_url(self, *args):
+ """Get the url for the view being tested."""
+ return reverse("anvil_consortium_manager:workspaces:detail", args=args)
def test_view_redirect_not_logged_in(self):
"View redirects to login view when user is not logged in."
# Need a client for redirects.
- response = self.client.get(self.get_url("foo"))
- self.assertRedirects(response, resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url("foo"))
-
- def test_status_code_with_user_permission(self):
+ url = reverse("anvil_consortium_manager:workspaces:detail", args=["foo1", "foo2"])
+ response = self.client.get(url)
+ self.assertRedirects(response, resolve_url(settings.LOGIN_URL) + "?next=" + url)
+
+ def test_status_code_with_staff_view_permission(self):
"""Returns successful response code."""
- api_url_members = self.get_api_url_members(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=[]),
- )
- api_url_admins = self.get_api_url_admins(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[]),
- )
+ obj = factories.DefaultWorkspaceDataFactory.create()
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.group.name))
+ response = self.client.get(obj.get_absolute_url())
self.assertEqual(response.status_code, 200)
- def test_access_with_limited_view_permission(self):
+ def test_access_with_view_permission(self):
"""Raises permission denied if user has limited view permission."""
user = User.objects.create_user(username="test-limited", password="test-limited")
user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
)
- request = self.factory.get(self.get_url("foo"))
- request.user = user
- with self.assertRaises(PermissionDenied):
- self.get_view()(request)
+ obj = factories.DefaultWorkspaceDataFactory.create()
+ self.client.force_login(user)
+ response = self.client.get(obj.get_absolute_url())
+ self.assertEqual(response.status_code, 200)
def test_access_without_user_permission(self):
"""Raises permission denied if user has no permissions."""
user_no_perms = User.objects.create_user(username="test-none", password="test-none")
- request = self.factory.get(self.get_url("foo"))
+ request = self.factory.get(self.get_url("foo", "bar"))
request.user = user_no_perms
with self.assertRaises(PermissionDenied):
- self.get_view()(request, slug="foo")
-
- def test_template(self):
- """Template loads successfully."""
- api_url_members = self.get_api_url_members(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=[]),
- )
- api_url_admins = self.get_api_url_admins(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[]),
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.group.name))
- self.assertEqual(response.status_code, 200)
-
- def test_audit_verified(self):
- """audit_verified is in the context data."""
- api_url_members = self.get_api_url_members(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=[]),
- )
- api_url_admins = self.get_api_url_admins(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[]),
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.group.name))
- self.assertIn("verified_table", response.context_data)
- self.assertIsInstance(response.context_data["verified_table"], audit.VerifiedTable)
- self.assertEqual(len(response.context_data["verified_table"].rows), 0)
-
- def test_audit_verified_one_record(self):
- """audit_verified with one verified record."""
- membership = factories.GroupAccountMembershipFactory.create(group=self.group)
- api_url_members = self.get_api_url_members(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=[membership.account.email]),
- )
- api_url_admins = self.get_api_url_admins(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[]),
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.group.name))
- self.assertIn("verified_table", response.context_data)
- self.assertEqual(len(response.context_data["verified_table"].rows), 1)
+ self.get_view()(request)
- def test_audit_errors(self):
- """audit_errors is in the context data."""
- api_url_members = self.get_api_url_members(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=[]),
- )
- api_url_admins = self.get_api_url_admins(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[]),
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.group.name))
- self.assertIn("error_table", response.context_data)
- self.assertIsInstance(response.context_data["error_table"], audit.ErrorTable)
- self.assertEqual(len(response.context_data["error_table"].rows), 0)
-
- def test_audit_errors_one_record(self):
- """audit_errors with one error record."""
- membership = factories.GroupAccountMembershipFactory.create(group=self.group)
- # Group membership API call.
- api_url_members = self.get_api_url_members(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=[]),
- )
- api_url_admins = self.get_api_url_admins(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[membership.account.email]),
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.group.name))
- self.assertIn("error_table", response.context_data)
- self.assertEqual(len(response.context_data["error_table"].rows), 1)
+ def test_view_status_code_with_invalid_pk(self):
+ """Raises a 404 error with an invalid object pk."""
+ obj = factories.DefaultWorkspaceDataFactory.create()
+ request = self.factory.get(obj.get_absolute_url())
+ request.user = self.user
+ with self.assertRaises(Http404):
+ self.get_view()(request, billing_project_slug="foo1", workspace_slug="foo2")
- def test_audit_not_in_app(self):
- """audit_not_in_app is in the context data."""
- api_url_members = self.get_api_url_members(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=[]),
- )
- api_url_admins = self.get_api_url_admins(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[]),
- )
+ def test_context_workspace_data(self):
+ """The view adds the workspace_data object to the context."""
+ obj = factories.DefaultWorkspaceDataFactory.create()
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.group.name))
- self.assertIn("not_in_app_table", response.context_data)
- self.assertIsInstance(response.context_data["not_in_app_table"], audit.NotInAppTable)
- self.assertEqual(len(response.context_data["not_in_app_table"].rows), 0)
+ response = self.client.get(obj.get_absolute_url())
+ response.context_data
+ self.assertIn("workspace_data_object", response.context_data)
+ self.assertEqual(response.context_data["workspace_data_object"], obj)
- def test_audit_not_in_app_one_record(self):
- """audit_not_in_app with one record not in app."""
- api_url_members = self.get_api_url_members(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=["foo@bar.com"]),
- )
- api_url_admins = self.get_api_url_admins(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[]),
- )
+ def test_group_sharing_table(self):
+ """The workspace group access table exists."""
+ obj = factories.DefaultWorkspaceDataFactory.create()
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.group.name))
- self.assertIn("not_in_app_table", response.context_data)
- self.assertEqual(len(response.context_data["not_in_app_table"].rows), 1)
-
- def test_audit_ok_is_ok(self):
- """audit_ok when audit_results.ok() is True."""
- api_url_members = self.get_api_url_members(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=[]),
- )
- api_url_admins = self.get_api_url_admins(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[]),
+ response = self.client.get(obj.get_absolute_url())
+ self.assertIn("group_sharing_table", response.context_data)
+ self.assertIsInstance(
+ response.context_data["group_sharing_table"],
+ tables.WorkspaceGroupSharingStaffTable,
)
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.group.name))
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], True)
- def test_audit_ok_is_not_ok(self):
- """audit_ok when audit_results.ok() is False."""
- api_url_members = self.get_api_url_members(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_members,
- status=200,
- json=self.get_api_json_response_members(emails=["foo@bar.com"]),
- )
- api_url_admins = self.get_api_url_admins(self.group.name)
- self.anvil_response_mock.add(
- responses.GET,
- api_url_admins,
- status=200,
- json=self.get_api_json_response_admins(emails=[]),
- )
+ def test_group_sharing_table_none(self):
+ """No groups are shown if the workspace has not been shared with any groups."""
+ workspace = factories.DefaultWorkspaceDataFactory.create()
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.group.name))
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], False)
+ response = self.client.get(workspace.get_absolute_url())
+ self.assertIn("group_sharing_table", response.context_data)
+ self.assertEqual(len(response.context_data["group_sharing_table"].rows), 0)
- def test_group_not_managed_by_app(self):
- """Redirects with a message when group is not managed by app."""
- group = factories.ManagedGroupFactory.create(is_managed_by_app=False)
- # Only clients load the template.
+ def test_group_sharing_table_one(self):
+ """One group is shown if the workspace has been shared with one group."""
+ workspace = factories.DefaultWorkspaceDataFactory.create()
+ factories.WorkspaceGroupSharingFactory.create(workspace=workspace.workspace)
self.client.force_login(self.user)
- response = self.client.get(self.get_url(group.name), follow=True)
- self.assertRedirects(response, group.get_absolute_url())
- messages = [m.message for m in get_messages(response.wsgi_request)]
- self.assertEqual(len(messages), 1)
- self.assertEqual(
- str(messages[0]),
- views.ManagedGroupMembershipAudit.message_not_managed_by_app,
- )
-
- def test_group_does_not_exist_in_app(self):
- """Raises a 404 error with an invalid object pk."""
- factories.ManagedGroupFactory.create()
- request = self.factory.get(self.get_url("foo"))
- request.user = self.user
- with self.assertRaises(Http404):
- self.get_view()(request, slug="foo")
-
-
-class ManagedGroupVisualizationTest(TestCase):
- def setUp(self):
- """Set up test class."""
- self.factory = RequestFactory()
- # Create a user with both view and edit permission.
- self.user = User.objects.create_user(username="test", password="test")
- self.user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
- )
-
- def get_url(self, *args):
- """Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:managed_groups:visualization", args=args)
-
- def test_view_redirect_not_logged_in(self):
- "View redirects to login view when user is not logged in."
- # Need a client for redirects.
- response = self.client.get(self.get_url())
- self.assertRedirects(response, resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url())
+ response = self.client.get(workspace.get_absolute_url())
+ self.assertIn("group_sharing_table", response.context_data)
+ self.assertEqual(len(response.context_data["group_sharing_table"].rows), 1)
- def test_status_code_with_user_permission(self):
- """Returns successful response code."""
+ def test_group_sharing_table_two(self):
+ """Two groups are shown if the workspace has been shared with two groups."""
+ workspace = factories.DefaultWorkspaceDataFactory.create()
+ factories.WorkspaceGroupSharingFactory.create(group__name="g1", workspace=workspace.workspace)
+ factories.WorkspaceGroupSharingFactory.create(group__name="g2", workspace=workspace.workspace)
self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertEqual(response.status_code, 200)
+ response = self.client.get(workspace.get_absolute_url())
+ self.assertIn("group_sharing_table", response.context_data)
+ self.assertEqual(len(response.context_data["group_sharing_table"].rows), 2)
- def test_access_with_limited_view_permission(self):
- """Raises permission denied if user has limited view permission."""
+ def test_group_sharing_table_view_permission(self):
+ """Workspace-group sharing table is not present in context when user has view permission only."""
user = User.objects.create_user(username="test-limited", password="test-limited")
user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
)
- request = self.factory.get(self.get_url())
- request.user = user
- with self.assertRaises(PermissionDenied):
- views.ManagedGroupVisualization.as_view()(request)
+ workspace = factories.DefaultWorkspaceDataFactory.create()
+ self.client.force_login(user)
+ response = self.client.get(workspace.get_absolute_url())
+ self.assertNotIn("group_sharing_table", response.context_data)
+ self.assertNotContains(response, "View groups that this workspace is shared with")
- def test_view_status_code_with_existing_object_not_managed(self):
- """Returns a successful status code for an existing object pk."""
+ def test_shows_workspace_group_sharing_for_only_that_workspace(self):
+ """Only shows groups that this workspace has been shared with."""
+ workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="workspace-1")
+ other_workspace = factories.WorkspaceFactory.create(name="workspace-2")
+ factories.WorkspaceGroupSharingFactory.create(workspace=other_workspace)
self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertEqual(response.status_code, 200)
+ response = self.client.get(workspace.get_absolute_url())
+ self.assertIn("group_sharing_table", response.context_data)
+ self.assertEqual(len(response.context_data["group_sharing_table"].rows), 0)
- def test_no_groups(self):
- """Visualization when there are no groups."""
+ def test_auth_domain_table(self):
+ """The workspace auth domain table exists."""
+ obj = factories.DefaultWorkspaceDataFactory.create()
self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("graph", response.context_data)
+ response = self.client.get(obj.get_absolute_url())
+ self.assertIn("authorization_domain_table", response.context_data)
+ self.assertIsInstance(
+ response.context_data["authorization_domain_table"],
+ tables.ManagedGroupStaffTable,
+ )
- def test_one_group(self):
- """Visualization when there is one group."""
- factories.ManagedGroupFactory.create()
+ def test_auth_domain_table_none(self):
+ """No groups are shown if the workspace has no auth domains."""
+ workspace = factories.DefaultWorkspaceDataFactory.create()
self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("graph", response.context_data)
+ response = self.client.get(workspace.get_absolute_url())
+ self.assertIn("authorization_domain_table", response.context_data)
+ self.assertEqual(len(response.context_data["authorization_domain_table"].rows), 0)
- def test_two_groups(self):
- """Visualization when there are two groups."""
- factories.ManagedGroupFactory.create(name="g1")
- factories.ManagedGroupFactory.create(name="g2")
+ def test_auth_domain_table_one(self):
+ """One group is shown if the workspace has one auth domain."""
+ workspace = factories.DefaultWorkspaceDataFactory.create()
+ group = factories.ManagedGroupFactory.create()
+ workspace.workspace.authorization_domains.add(group)
self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("graph", response.context_data)
+ response = self.client.get(workspace.get_absolute_url())
+ self.assertIn("authorization_domain_table", response.context_data)
+ table = response.context_data["authorization_domain_table"]
+ self.assertEqual(len(table.rows), 1)
+ self.assertIn(group, table.data)
- def test_group_visualization(self):
- factories.ManagedGroupFactory.create()
- grandparent = factories.ManagedGroupFactory.create()
- parent_1 = factories.ManagedGroupFactory.create()
- parent_2 = factories.ManagedGroupFactory.create()
- child = factories.ManagedGroupFactory.create()
- factories.GroupGroupMembershipFactory.create(parent_group=grandparent, child_group=parent_1)
- factories.GroupGroupMembershipFactory.create(parent_group=grandparent, child_group=parent_2)
- factories.GroupGroupMembershipFactory.create(
- parent_group=parent_1,
- child_group=child,
- role=models.GroupGroupMembership.ADMIN,
- )
+ def test_auth_domain_table_two(self):
+ """Two groups are shown if the workspace has two auth domains."""
+ workspace = factories.DefaultWorkspaceDataFactory.create()
+ group_1 = factories.ManagedGroupFactory.create(name="g1")
+ workspace.workspace.authorization_domains.add(group_1)
+ group_2 = factories.ManagedGroupFactory.create(name="g2")
+ workspace.workspace.authorization_domains.add(group_2)
self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("graph", response.context_data)
-
+ response = self.client.get(workspace.get_absolute_url())
+ self.assertIn("authorization_domain_table", response.context_data)
+ table = response.context_data["authorization_domain_table"]
+ self.assertEqual(len(table.rows), 2)
+ self.assertIn(group_1, table.data)
+ self.assertIn(group_2, table.data)
-class WorkspaceLandingPageTest(TestCase):
- def setUp(self):
- """Set up test class."""
- self.factory = RequestFactory()
- # Create a user with view permission.
- self.view_user = User.objects.create_user(username="test_view", password="view")
- self.view_user.user_permissions.add(
+ def test_auth_domain_table_view_permission(self):
+ """Auth domain table has correct class when user has view permission only."""
+ user = User.objects.create_user(username="test-limited", password="test-limited")
+ user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
)
- # Create a user with staff view permission.
- self.staff_view_user = User.objects.create_user(username="test_staff_view", password="view")
- self.staff_view_user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
- )
- # Create a user with edit permission.
- self.edit_user = User.objects.create_user(username="test_edit", password="test")
- self.edit_user.user_permissions.add(
+ workspace = factories.DefaultWorkspaceDataFactory.create()
+ factories.WorkspaceAuthorizationDomainFactory.create(workspace=workspace.workspace)
+ self.client.force_login(user)
+ response = self.client.get(workspace.get_absolute_url())
+ self.assertIn("authorization_domain_table", response.context_data)
+ self.assertIsInstance(response.context_data["authorization_domain_table"], tables.ManagedGroupUserTable)
+
+ def test_shows_auth_domains_for_only_that_workspace(self):
+ """Only shows auth domains for this workspace."""
+ workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="workspace-1")
+ other_workspace = factories.WorkspaceFactory.create(name="workspace-2")
+ group = factories.ManagedGroupFactory.create()
+ other_workspace.authorization_domains.add(group)
+ self.client.force_login(self.user)
+ response = self.client.get(workspace.get_absolute_url())
+ self.assertIn("authorization_domain_table", response.context_data)
+ self.assertEqual(len(response.context_data["authorization_domain_table"].rows), 0)
+
+ def test_staff_edit_permission(self):
+ """Links in template when user has staff edit permission."""
+ edit_user = User.objects.create_user(username="edit", password="test")
+ edit_user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME),
Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME),
)
-
- def tearDown(self):
- """Clean up after tests."""
- # Unregister all adapters.
- workspace_adapter_registry._registry = {}
- # Register the default adapter.
- workspace_adapter_registry.register(DefaultWorkspaceAdapter)
- super().tearDown()
-
- def get_url(self):
- """Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:workspaces:landing_page")
-
- def test_view_redirect_not_logged_in(self):
- "View redirects to login view when user is not logged in."
- # Need a client for redirects.
- response = self.client.get(self.get_url())
- self.assertRedirects(
+ self.client.force_login(edit_user)
+ obj = factories.DefaultWorkspaceDataFactory.create()
+ response = self.client.get(obj.get_absolute_url())
+ self.assertIn("show_edit_links", response.context_data)
+ self.assertTrue(response.context_data["show_edit_links"])
+ self.assertContains(
response,
- resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url(),
+ reverse(
+ "anvil_consortium_manager:workspaces:delete",
+ kwargs={
+ "billing_project_slug": obj.workspace.billing_project.name,
+ "workspace_slug": obj.workspace.name,
+ },
+ ),
)
-
- def test_status_code_with_staff_view_permission(self):
- """Returns successful response code if user has staff_view permission."""
- self.client.force_login(self.staff_view_user)
- response = self.client.get(self.get_url())
- self.assertEqual(response.status_code, 200)
-
- def test_access_with_view_permission(self):
- """Returns successful response code if user has view permission."""
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url())
- self.assertEqual(response.status_code, 200)
-
- def test_staff_view_permission(self):
- """Links to edit required do not appear in the page when user only has staff_view permission."""
- self.client.force_login(self.staff_view_user)
- response = self.client.get(self.get_url())
- self.assertIn("show_edit_links", response.context_data)
- self.assertFalse(response.context_data["show_edit_links"])
- self.assertNotContains(
+ # Billing project link
+ self.assertContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:import",
- kwargs={"workspace_type": "workspace"},
+ "anvil_consortium_manager:billing_projects:detail",
+ kwargs={
+ "slug": obj.workspace.billing_project.name,
+ },
),
)
- self.assertNotContains(
+ # Action buttons
+ self.assertContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:new",
- kwargs={"workspace_type": "workspace"},
+ "anvil_consortium_manager:workspaces:update",
+ kwargs={
+ "billing_project_slug": obj.workspace.billing_project.name,
+ "workspace_slug": obj.workspace.name,
+ },
),
)
self.assertContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:list",
- kwargs={"workspace_type": "workspace"},
+ "anvil_consortium_manager:workspaces:sharing:new",
+ kwargs={
+ "billing_project_slug": obj.workspace.billing_project.name,
+ "workspace_slug": obj.workspace.name,
+ },
+ ),
+ )
+ self.assertContains(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:clone",
+ kwargs={
+ "billing_project_slug": obj.workspace.billing_project.name,
+ "workspace_slug": obj.workspace.name,
+ "workspace_type": "workspace",
+ },
+ ),
+ )
+ self.assertContains(
+ response,
+ reverse(
+ "anvil_consortium_manager:auditor:workspaces:sharing:all",
+ kwargs={
+ "billing_project_slug": obj.workspace.billing_project.name,
+ "workspace_slug": obj.workspace.name,
+ },
),
)
- def test_view_permission(self):
- """Links to edit required do not appear in the page when user only has view permission."""
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url())
+ def test_staff_view_permission(self):
+ """Links in template when user has staff view permission."""
+ view_user = User.objects.create_user(username="view", password="test")
+ view_user.user_permissions.add(
+ Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME),
+ )
+ self.client.force_login(view_user)
+ obj = factories.DefaultWorkspaceDataFactory.create()
+ response = self.client.get(obj.get_absolute_url())
self.assertIn("show_edit_links", response.context_data)
self.assertFalse(response.context_data["show_edit_links"])
self.assertNotContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:import",
- kwargs={"workspace_type": "workspace"},
+ "anvil_consortium_manager:workspaces:delete",
+ kwargs={
+ "billing_project_slug": obj.workspace.billing_project.name,
+ "workspace_slug": obj.workspace.name,
+ },
),
)
- self.assertNotContains(
+ # Billing project link
+ self.assertContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:new",
- kwargs={"workspace_type": "workspace"},
+ "anvil_consortium_manager:billing_projects:detail",
+ kwargs={
+ "slug": obj.workspace.billing_project.name,
+ },
),
)
- self.assertContains(
+ # Action buttons
+ self.assertNotContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:list",
- kwargs={"workspace_type": "workspace"},
+ "anvil_consortium_manager:workspaces:update",
+ kwargs={
+ "billing_project_slug": obj.workspace.billing_project.name,
+ "workspace_slug": obj.workspace.name,
+ },
),
)
-
- def test_edit_permission(self):
- """Links to edit required appear in the page when user also has edit permission."""
- self.client.force_login(self.edit_user)
- response = self.client.get(self.get_url())
- self.assertIn("show_edit_links", response.context_data)
- self.assertTrue(response.context_data["show_edit_links"])
- self.assertContains(
+ self.assertNotContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:import",
- kwargs={"workspace_type": "workspace"},
+ "anvil_consortium_manager:workspaces:sharing:new",
+ kwargs={
+ "billing_project_slug": obj.workspace.billing_project.name,
+ "workspace_slug": obj.workspace.name,
+ },
),
)
- self.assertContains(
+ self.assertNotContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:new",
- kwargs={"workspace_type": "workspace"},
+ "anvil_consortium_manager:workspaces:clone",
+ kwargs={
+ "billing_project_slug": obj.workspace.billing_project.name,
+ "workspace_slug": obj.workspace.name,
+ "workspace_type": "workspace",
+ },
),
)
self.assertContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:list",
- kwargs={"workspace_type": "workspace"},
+ "anvil_consortium_manager:auditor:workspaces:sharing:all",
+ kwargs={
+ "billing_project_slug": obj.workspace.billing_project.name,
+ "workspace_slug": obj.workspace.name,
+ },
),
)
- def test_one_registered_workspace_in_context(self):
- """One registered workspace in context when only DefaultWorkspaceAdapter is registered"""
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url())
- self.assertIn("registered_workspace_adapters", response.context_data)
- self.assertEqual(len(response.context_data["registered_workspace_adapters"]), 1)
-
- def test_two_registered_workspaces_in_context(self):
- """Two registered workspaces in context when two workspace adapters are registered"""
- workspace_adapter_registry.register(TestWorkspaceAdapter)
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url())
- self.assertIn("registered_workspace_adapters", response.context_data)
- self.assertEqual(len(response.context_data["registered_workspace_adapters"]), 2)
-
-
-class WorkspaceDetailTest(TestCase):
- """Tests for the WorkspaceDetail view."""
-
- def setUp(self):
- """Set up test class."""
- self.factory = RequestFactory()
- # Create a user with both view and edit permission.
- self.user = User.objects.create_user(username="test", password="test")
- self.user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
- )
-
- def tearDown(self):
- """Clean up after tests."""
- # Unregister all adapters.
- workspace_adapter_registry._registry = {}
- # Register the default adapter.
- workspace_adapter_registry.register(DefaultWorkspaceAdapter)
- super().tearDown()
-
- def get_view(self):
- """Return the view being tested."""
- return views.WorkspaceDetail.as_view()
-
- def get_url(self, *args):
- """Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:workspaces:detail", args=args)
-
- def test_view_redirect_not_logged_in(self):
- "View redirects to login view when user is not logged in."
- # Need a client for redirects.
- url = reverse("anvil_consortium_manager:workspaces:detail", args=["foo1", "foo2"])
- response = self.client.get(url)
- self.assertRedirects(response, resolve_url(settings.LOGIN_URL) + "?next=" + url)
-
- def test_status_code_with_staff_view_permission(self):
- """Returns successful response code."""
- obj = factories.DefaultWorkspaceDataFactory.create()
- self.client.force_login(self.user)
- response = self.client.get(obj.get_absolute_url())
- self.assertEqual(response.status_code, 200)
-
- def test_access_with_view_permission(self):
- """Raises permission denied if user has limited view permission."""
- user = User.objects.create_user(username="test-limited", password="test-limited")
- user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
- )
- obj = factories.DefaultWorkspaceDataFactory.create()
- self.client.force_login(user)
- response = self.client.get(obj.get_absolute_url())
- self.assertEqual(response.status_code, 200)
-
- def test_access_without_user_permission(self):
- """Raises permission denied if user has no permissions."""
- user_no_perms = User.objects.create_user(username="test-none", password="test-none")
- request = self.factory.get(self.get_url("foo", "bar"))
- request.user = user_no_perms
- with self.assertRaises(PermissionDenied):
- self.get_view()(request)
-
- def test_view_status_code_with_invalid_pk(self):
- """Raises a 404 error with an invalid object pk."""
- obj = factories.DefaultWorkspaceDataFactory.create()
- request = self.factory.get(obj.get_absolute_url())
- request.user = self.user
- with self.assertRaises(Http404):
- self.get_view()(request, billing_project_slug="foo1", workspace_slug="foo2")
-
- def test_context_workspace_data(self):
- """The view adds the workspace_data object to the context."""
- obj = factories.DefaultWorkspaceDataFactory.create()
- self.client.force_login(self.user)
- response = self.client.get(obj.get_absolute_url())
- response.context_data
- self.assertIn("workspace_data_object", response.context_data)
- self.assertEqual(response.context_data["workspace_data_object"], obj)
-
- def test_group_sharing_table(self):
- """The workspace group access table exists."""
- obj = factories.DefaultWorkspaceDataFactory.create()
- self.client.force_login(self.user)
- response = self.client.get(obj.get_absolute_url())
- self.assertIn("group_sharing_table", response.context_data)
- self.assertIsInstance(
- response.context_data["group_sharing_table"],
- tables.WorkspaceGroupSharingStaffTable,
- )
-
- def test_group_sharing_table_none(self):
- """No groups are shown if the workspace has not been shared with any groups."""
- workspace = factories.DefaultWorkspaceDataFactory.create()
- self.client.force_login(self.user)
- response = self.client.get(workspace.get_absolute_url())
- self.assertIn("group_sharing_table", response.context_data)
- self.assertEqual(len(response.context_data["group_sharing_table"].rows), 0)
-
- def test_group_sharing_table_one(self):
- """One group is shown if the workspace has been shared with one group."""
- workspace = factories.DefaultWorkspaceDataFactory.create()
- factories.WorkspaceGroupSharingFactory.create(workspace=workspace.workspace)
- self.client.force_login(self.user)
- response = self.client.get(workspace.get_absolute_url())
- self.assertIn("group_sharing_table", response.context_data)
- self.assertEqual(len(response.context_data["group_sharing_table"].rows), 1)
-
- def test_group_sharing_table_two(self):
- """Two groups are shown if the workspace has been shared with two groups."""
- workspace = factories.DefaultWorkspaceDataFactory.create()
- factories.WorkspaceGroupSharingFactory.create(group__name="g1", workspace=workspace.workspace)
- factories.WorkspaceGroupSharingFactory.create(group__name="g2", workspace=workspace.workspace)
- self.client.force_login(self.user)
- response = self.client.get(workspace.get_absolute_url())
- self.assertIn("group_sharing_table", response.context_data)
- self.assertEqual(len(response.context_data["group_sharing_table"].rows), 2)
-
- def test_group_sharing_table_view_permission(self):
- """Workspace-group sharing table is not present in context when user has view permission only."""
- user = User.objects.create_user(username="test-limited", password="test-limited")
- user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
- )
- workspace = factories.DefaultWorkspaceDataFactory.create()
- self.client.force_login(user)
- response = self.client.get(workspace.get_absolute_url())
- self.assertNotIn("group_sharing_table", response.context_data)
- self.assertNotContains(response, "View groups that this workspace is shared with")
-
- def test_shows_workspace_group_sharing_for_only_that_workspace(self):
- """Only shows groups that this workspace has been shared with."""
- workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="workspace-1")
- other_workspace = factories.WorkspaceFactory.create(name="workspace-2")
- factories.WorkspaceGroupSharingFactory.create(workspace=other_workspace)
- self.client.force_login(self.user)
- response = self.client.get(workspace.get_absolute_url())
- self.assertIn("group_sharing_table", response.context_data)
- self.assertEqual(len(response.context_data["group_sharing_table"].rows), 0)
-
- def test_auth_domain_table(self):
- """The workspace auth domain table exists."""
- obj = factories.DefaultWorkspaceDataFactory.create()
- self.client.force_login(self.user)
- response = self.client.get(obj.get_absolute_url())
- self.assertIn("authorization_domain_table", response.context_data)
- self.assertIsInstance(
- response.context_data["authorization_domain_table"],
- tables.ManagedGroupStaffTable,
- )
-
- def test_auth_domain_table_none(self):
- """No groups are shown if the workspace has no auth domains."""
- workspace = factories.DefaultWorkspaceDataFactory.create()
- self.client.force_login(self.user)
- response = self.client.get(workspace.get_absolute_url())
- self.assertIn("authorization_domain_table", response.context_data)
- self.assertEqual(len(response.context_data["authorization_domain_table"].rows), 0)
-
- def test_auth_domain_table_one(self):
- """One group is shown if the workspace has one auth domain."""
- workspace = factories.DefaultWorkspaceDataFactory.create()
- group = factories.ManagedGroupFactory.create()
- workspace.workspace.authorization_domains.add(group)
- self.client.force_login(self.user)
- response = self.client.get(workspace.get_absolute_url())
- self.assertIn("authorization_domain_table", response.context_data)
- table = response.context_data["authorization_domain_table"]
- self.assertEqual(len(table.rows), 1)
- self.assertIn(group, table.data)
-
- def test_auth_domain_table_two(self):
- """Two groups are shown if the workspace has two auth domains."""
- workspace = factories.DefaultWorkspaceDataFactory.create()
- group_1 = factories.ManagedGroupFactory.create(name="g1")
- workspace.workspace.authorization_domains.add(group_1)
- group_2 = factories.ManagedGroupFactory.create(name="g2")
- workspace.workspace.authorization_domains.add(group_2)
- self.client.force_login(self.user)
- response = self.client.get(workspace.get_absolute_url())
- self.assertIn("authorization_domain_table", response.context_data)
- table = response.context_data["authorization_domain_table"]
- self.assertEqual(len(table.rows), 2)
- self.assertIn(group_1, table.data)
- self.assertIn(group_2, table.data)
-
- def test_auth_domain_table_view_permission(self):
- """Auth domain table has correct class when user has view permission only."""
- user = User.objects.create_user(username="test-limited", password="test-limited")
- user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
- )
- workspace = factories.DefaultWorkspaceDataFactory.create()
- factories.WorkspaceAuthorizationDomainFactory.create(workspace=workspace.workspace)
- self.client.force_login(user)
- response = self.client.get(workspace.get_absolute_url())
- self.assertIn("authorization_domain_table", response.context_data)
- self.assertIsInstance(response.context_data["authorization_domain_table"], tables.ManagedGroupUserTable)
-
- def test_shows_auth_domains_for_only_that_workspace(self):
- """Only shows auth domains for this workspace."""
- workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="workspace-1")
- other_workspace = factories.WorkspaceFactory.create(name="workspace-2")
- group = factories.ManagedGroupFactory.create()
- other_workspace.authorization_domains.add(group)
- self.client.force_login(self.user)
- response = self.client.get(workspace.get_absolute_url())
- self.assertIn("authorization_domain_table", response.context_data)
- self.assertEqual(len(response.context_data["authorization_domain_table"].rows), 0)
-
- def test_staff_edit_permission(self):
- """Links in template when user has staff edit permission."""
- edit_user = User.objects.create_user(username="edit", password="test")
- edit_user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME),
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME),
+ def test_view_permission(self):
+ """Links in template when user has view permission."""
+ view_user = User.objects.create_user(username="view", password="test")
+ view_user.user_permissions.add(
+ Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME),
)
- self.client.force_login(edit_user)
+ self.client.force_login(view_user)
obj = factories.DefaultWorkspaceDataFactory.create()
response = self.client.get(obj.get_absolute_url())
self.assertIn("show_edit_links", response.context_data)
- self.assertTrue(response.context_data["show_edit_links"])
- self.assertContains(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:delete",
- kwargs={
- "billing_project_slug": obj.workspace.billing_project.name,
- "workspace_slug": obj.workspace.name,
- },
- ),
- )
+ self.assertFalse(response.context_data["show_edit_links"])
# Billing project link
- self.assertContains(
+ self.assertNotContains(
response,
reverse(
"anvil_consortium_manager:billing_projects:detail",
@@ -7147,158 +6428,20 @@ def test_staff_edit_permission(self):
),
)
# Action buttons
- self.assertContains(
+ self.assertNotContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:update",
+ "anvil_consortium_manager:workspaces:delete",
kwargs={
"billing_project_slug": obj.workspace.billing_project.name,
"workspace_slug": obj.workspace.name,
},
),
)
- self.assertContains(
+ self.assertNotContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:sharing:new",
- kwargs={
- "billing_project_slug": obj.workspace.billing_project.name,
- "workspace_slug": obj.workspace.name,
- },
- ),
- )
- self.assertContains(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:clone",
- kwargs={
- "billing_project_slug": obj.workspace.billing_project.name,
- "workspace_slug": obj.workspace.name,
- "workspace_type": "workspace",
- },
- ),
- )
- self.assertContains(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:audit_sharing",
- kwargs={
- "billing_project_slug": obj.workspace.billing_project.name,
- "workspace_slug": obj.workspace.name,
- },
- ),
- )
-
- def test_staff_view_permission(self):
- """Links in template when user has staff view permission."""
- view_user = User.objects.create_user(username="view", password="test")
- view_user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME),
- )
- self.client.force_login(view_user)
- obj = factories.DefaultWorkspaceDataFactory.create()
- response = self.client.get(obj.get_absolute_url())
- self.assertIn("show_edit_links", response.context_data)
- self.assertFalse(response.context_data["show_edit_links"])
- self.assertNotContains(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:delete",
- kwargs={
- "billing_project_slug": obj.workspace.billing_project.name,
- "workspace_slug": obj.workspace.name,
- },
- ),
- )
- # Billing project link
- self.assertContains(
- response,
- reverse(
- "anvil_consortium_manager:billing_projects:detail",
- kwargs={
- "slug": obj.workspace.billing_project.name,
- },
- ),
- )
- # Action buttons
- self.assertNotContains(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:update",
- kwargs={
- "billing_project_slug": obj.workspace.billing_project.name,
- "workspace_slug": obj.workspace.name,
- },
- ),
- )
- self.assertNotContains(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:sharing:new",
- kwargs={
- "billing_project_slug": obj.workspace.billing_project.name,
- "workspace_slug": obj.workspace.name,
- },
- ),
- )
- self.assertNotContains(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:clone",
- kwargs={
- "billing_project_slug": obj.workspace.billing_project.name,
- "workspace_slug": obj.workspace.name,
- "workspace_type": "workspace",
- },
- ),
- )
- self.assertContains(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:audit_sharing",
- kwargs={
- "billing_project_slug": obj.workspace.billing_project.name,
- "workspace_slug": obj.workspace.name,
- },
- ),
- )
-
- def test_view_permission(self):
- """Links in template when user has view permission."""
- view_user = User.objects.create_user(username="view", password="test")
- view_user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME),
- )
- self.client.force_login(view_user)
- obj = factories.DefaultWorkspaceDataFactory.create()
- response = self.client.get(obj.get_absolute_url())
- self.assertIn("show_edit_links", response.context_data)
- self.assertFalse(response.context_data["show_edit_links"])
- # Billing project link
- self.assertNotContains(
- response,
- reverse(
- "anvil_consortium_manager:billing_projects:detail",
- kwargs={
- "slug": obj.workspace.billing_project.name,
- },
- ),
- )
- # Action buttons
- self.assertNotContains(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:delete",
- kwargs={
- "billing_project_slug": obj.workspace.billing_project.name,
- "workspace_slug": obj.workspace.name,
- },
- ),
- )
- self.assertNotContains(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:update",
+ "anvil_consortium_manager:workspaces:update",
kwargs={
"billing_project_slug": obj.workspace.billing_project.name,
"workspace_slug": obj.workspace.name,
@@ -7329,7 +6472,7 @@ def test_view_permission(self):
self.assertNotContains(
response,
reverse(
- "anvil_consortium_manager:workspaces:audit_sharing",
+ "anvil_consortium_manager:auditor:workspaces:sharing:all",
kwargs={
"billing_project_slug": obj.workspace.billing_project.name,
"workspace_slug": obj.workspace.name,
@@ -11787,726 +10930,321 @@ def test_view_with_filter_returns_one_object_exact(self):
response = self.client.get(self.get_url(), {"name__icontains": "workspace1"})
self.assertEqual(response.status_code, 200)
self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 1)
- self.assertIn(instance, response.context_data["table"].data)
-
- def test_view_with_filter_returns_one_object_case_insensitive(self):
- instance = factories.WorkspaceFactory.create(name="workspace1")
- factories.WorkspaceFactory.create(name="workspace2")
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(), {"name__icontains": "Workspace1"})
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 1)
- self.assertIn(instance, response.context_data["table"].data)
-
- def test_view_with_filter_returns_one_object_case_contains(self):
- instance = factories.WorkspaceFactory.create(name="workspace1")
- factories.WorkspaceFactory.create(name="workspace2")
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(), {"name__icontains": "orkspace1"})
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 1)
- self.assertIn(instance, response.context_data["table"].data)
-
- def test_view_with_filter_returns_mutiple_objects(self):
- factories.WorkspaceFactory.create(name="workspace1")
- factories.WorkspaceFactory.create(name="wOrkspace1")
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(), {"name__icontains": "Workspace"})
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 2)
-
-
-class WorkspaceListByTypeTest(TestCase):
- def setUp(self):
- """Set up test class."""
- self.factory = RequestFactory()
- # Create a user with staff view permission.
- self.staff_view_user = User.objects.create_user(username="test-staff-view", password="test")
- self.staff_view_user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
- )
- # Create a user with view permission
- self.view_user = User.objects.create_user(username="test-view", password="test")
- self.view_user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
- )
- self.workspace_type = DefaultWorkspaceAdapter().get_type()
-
- def tearDown(self):
- """Clean up after tests."""
- # Unregister all adapters.
- workspace_adapter_registry._registry = {}
- # Register the default adapter.
- workspace_adapter_registry.register(DefaultWorkspaceAdapter)
- super().tearDown()
-
- def get_url(self, *args):
- """Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:workspaces:list", args=args)
-
- def get_view(self):
- """Return the view being tested."""
- return views.WorkspaceListByType.as_view()
-
- def test_view_redirect_not_logged_in(self):
- "View redirects to login view when user is not logged in."
- # Need a client for redirects.
- response = self.client.get(self.get_url(self.workspace_type))
- self.assertRedirects(
- response,
- resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url(self.workspace_type),
- )
-
- def test_status_code_with_staff_view_permission(self):
- """Returns successful response code."""
- self.client.force_login(self.staff_view_user)
- response = self.client.get(self.get_url(self.workspace_type))
- self.assertEqual(response.status_code, 200)
-
- def test_access_with_view_permission(self):
- """Raises permission denied if user has limited view permission."""
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type))
- self.assertEqual(response.status_code, 200)
-
- def test_access_without_user_permission(self):
- """Raises permission denied if user has no permissions."""
- user_no_perms = User.objects.create_user(username="test-none", password="test-none")
- request = self.factory.get(self.get_url(self.workspace_type))
- request.user = user_no_perms
- with self.assertRaises(PermissionDenied):
- self.get_view()(request, workspace_type=self.workspace_type)
-
- def test_get_workspace_type_not_registered(self):
- """Raises 404 with get request if workspace type is not registered with adapter."""
- request = self.factory.get(self.get_url("foo"))
- request.user = self.view_user
- with self.assertRaises(Http404):
- self.get_view()(request, workspace_type="foo")
-
- def test_view_has_correct_table_class_staff_view(self):
- self.client.force_login(self.staff_view_user)
- response = self.client.get(self.get_url(self.workspace_type))
- self.assertIn("table", response.context_data)
- self.assertIsInstance(response.context_data["table"], tables.WorkspaceStaffTable)
-
- def test_view_has_correct_table_class_view(self):
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type))
- self.assertIn("table", response.context_data)
- self.assertIsInstance(response.context_data["table"], tables.WorkspaceUserTable)
-
- def test_view_with_no_objects(self):
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type))
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 0)
-
- def test_view_with_one_object(self):
- factories.WorkspaceFactory()
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type))
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 1)
-
- def test_view_with_two_objects(self):
- factories.WorkspaceFactory.create(name="w1")
- factories.WorkspaceFactory.create(name="w2")
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type))
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 2)
-
- def test_adapter_table_class_staff_view(self):
- """Displays the correct table if specified in the adapter."""
- # Overriding settings doesn't work, because appconfig.ready has already run and
- # registered the default adapter. Instead, unregister the default and register the
- # new adapter here.
- workspace_adapter_registry.unregister(DefaultWorkspaceAdapter)
- workspace_adapter_registry.register(TestWorkspaceAdapter)
- self.workspace_type = TestWorkspaceAdapter().get_type()
- self.client.force_login(self.staff_view_user)
- response = self.client.get(self.get_url(self.workspace_type))
- self.assertIn("table", response.context_data)
- self.assertIsInstance(response.context_data["table"], app_tables.TestWorkspaceDataStaffTable)
-
- def test_adapter_table_class_view(self):
- """Displays the correct table if specified in the adapter."""
- # Overriding settings doesn't work, because appconfig.ready has already run and
- # registered the default adapter. Instead, unregister the default and register the
- # new adapter here.
- workspace_adapter_registry.unregister(DefaultWorkspaceAdapter)
- workspace_adapter_registry.register(TestWorkspaceAdapter)
- self.workspace_type = TestWorkspaceAdapter().get_type()
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type))
- self.assertIn("table", response.context_data)
- self.assertIsInstance(response.context_data["table"], app_tables.TestWorkspaceDataUserTable)
-
- def test_only_shows_workspaces_with_correct_type(self):
- """Only workspaces with the same workspace_type are shown in the table."""
- workspace_adapter_registry.register(TestWorkspaceAdapter)
- factories.WorkspaceFactory(workspace_type=TestWorkspaceAdapter().get_type())
- default_type = DefaultWorkspaceAdapter().get_type()
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(default_type))
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 0)
-
- def test_view_with_filter_return_no_object(self):
- factories.WorkspaceFactory.create(name="workspace1")
- factories.WorkspaceFactory.create(name="workspace2")
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "abc"})
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 0)
-
- def test_view_with_filter_returns_one_object_exact(self):
- instance = factories.WorkspaceFactory.create(name="workspace1")
- factories.WorkspaceFactory.create(name="workspace2")
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "workspace1"})
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 1)
- self.assertIn(instance, response.context_data["table"].data)
-
- def test_view_with_filter_returns_one_object_case_insensitive(self):
- instance = factories.WorkspaceFactory.create(name="workspace1")
- factories.WorkspaceFactory.create(name="workspace2")
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "Workspace1"})
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 1)
- self.assertIn(instance, response.context_data["table"].data)
-
- def test_view_with_filter_returns_one_object_case_contains(self):
- instance = factories.WorkspaceFactory.create(name="workspace1")
- factories.WorkspaceFactory.create(name="workspace2")
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "orkspace1"})
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 1)
- self.assertIn(instance, response.context_data["table"].data)
-
- def test_view_with_filter_workspace_type(self):
- instance = factories.WorkspaceFactory.create(name="workspace1")
- factories.WorkspaceFactory.create(name="workspace2", workspace_type=TestWorkspaceAdapter().get_type())
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "workspace"})
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 1)
- self.assertIn(instance, response.context_data["table"].data)
-
- def test_view_with_filter_returns_mutiple_objects(self):
- factories.WorkspaceFactory.create(name="workspace1")
- factories.WorkspaceFactory.create(name="wOrkspace1")
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "Workspace"})
- self.assertEqual(response.status_code, 200)
- self.assertIn("table", response.context_data)
- self.assertEqual(len(response.context_data["table"].rows), 2)
-
- def test_view_with_default_adapter_use_default_workspace_list_template(self):
- default_type = DefaultWorkspaceAdapter().get_type()
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(default_type))
- self.assertTemplateUsed(response, "anvil_consortium_manager/workspace_list.html")
-
- def test_view_with_custom_adapter_use_custom_workspace_list_template(self):
- workspace_adapter_registry.unregister(DefaultWorkspaceAdapter)
- workspace_adapter_registry.register(TestWorkspaceAdapter)
- self.workspace_type = TestWorkspaceAdapter().get_type()
- self.client.force_login(self.view_user)
- response = self.client.get(self.get_url(self.workspace_type))
- self.assertTemplateUsed(response, "test_workspace_list.html")
-
-
-class WorkspaceDeleteTest(AnVILAPIMockTestMixin, TestCase):
- api_success_code = 202
-
- def setUp(self):
- """Set up test class."""
- # The superclass uses the responses package to mock API responses.
- super().setUp()
- self.factory = RequestFactory()
- # Create a user with both view and edit permissions.
- self.user = User.objects.create_user(username="test", password="test")
- self.user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
- )
- self.user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME)
- )
-
- def tearDown(self):
- """Clean up after tests."""
- # Unregister all adapters.
- workspace_adapter_registry._registry = {}
- # Register the default adapter.
- workspace_adapter_registry.register(DefaultWorkspaceAdapter)
- super().tearDown()
-
- def get_url(self, *args):
- """Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:workspaces:delete", args=args)
-
- def get_api_url(self, billing_project_name, workspace_name):
- return self.api_client.rawls_entry_point + "/api/workspaces/" + billing_project_name + "/" + workspace_name
-
- def get_view(self):
- """Return the view being tested."""
- return views.WorkspaceDelete.as_view()
-
- def test_view_redirect_not_logged_in(self):
- "View redirects to login view when user is not logged in."
- # Need a client for redirects.
- url = self.get_url("foo1", "foo2")
- response = self.client.get(url)
- self.assertRedirects(response, resolve_url(settings.LOGIN_URL) + "?next=" + url)
-
- def test_status_code_with_user_permission(self):
- """Returns successful response code."""
- obj = factories.WorkspaceFactory.create()
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(obj.billing_project.name, obj.name))
- self.assertEqual(response.status_code, 200)
-
- def test_access_with_view_permission(self):
- """Raises permission denied if user has only view permission."""
- user_with_view_perm = User.objects.create_user(username="test-other", password="test-other")
- user_with_view_perm.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
- )
- request = self.factory.get(self.get_url("foo1", "foo2"))
- request.user = user_with_view_perm
- with self.assertRaises(PermissionDenied):
- self.get_view()(request, pk=1)
-
- def test_access_with_limited_view_permission(self):
- """Raises permission denied if user has limited view permission."""
- user = User.objects.create_user(username="test-limited", password="test-limited")
- user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
- )
- request = self.factory.get(self.get_url("foo", "bar"))
- request.user = user
- with self.assertRaises(PermissionDenied):
- self.get_view()(request)
-
- def test_access_without_user_permission(self):
- """Raises permission denied if user has no permissions."""
- user_no_perms = User.objects.create_user(username="test-none", password="test-none")
- request = self.factory.get(self.get_url("foo1", "foo2"))
- request.user = user_no_perms
- with self.assertRaises(PermissionDenied):
- self.get_view()(request, pk=1)
-
- def test_view_with_invalid_pk(self):
- """Returns a 404 when the object doesn't exist."""
- request = self.factory.get(self.get_url("foo1", "foo2"))
- request.user = self.user
- with self.assertRaises(Http404):
- self.get_view()(request, pk=1)
-
- def test_view_deletes_object(self):
- """Posting submit to the form successfully deletes the object."""
- billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
- object = factories.WorkspaceFactory.create(billing_project=billing_project, name="test-workspace")
- api_url = self.get_api_url(object.billing_project.name, object.name)
- self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
- self.client.force_login(self.user)
- response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
- self.assertEqual(response.status_code, 302)
- self.assertEqual(models.Workspace.objects.count(), 0)
- # History is added.
- self.assertEqual(object.history.count(), 2)
- self.assertEqual(object.history.latest().history_type, "-")
-
- def test_success_message(self):
- """Response includes a success message if successful."""
- billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
- object = factories.WorkspaceFactory.create(billing_project=billing_project, name="test-workspace")
- api_url = self.get_api_url(object.billing_project.name, object.name)
- self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
- self.client.force_login(self.user)
- response = self.client.post(
- self.get_url(object.billing_project.name, object.name),
- {"submit": ""},
- follow=True,
- )
- messages = [m.message for m in get_messages(response.wsgi_request)]
- self.assertEqual(len(messages), 1)
- self.assertEqual(views.WorkspaceDelete.success_message, str(messages[0]))
-
- def test_only_deletes_specified_pk(self):
- """View only deletes the specified pk."""
- object = factories.WorkspaceFactory.create()
- other_object = factories.WorkspaceFactory.create()
- api_url = self.get_api_url(object.billing_project.name, object.name)
- self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
- self.client.force_login(self.user)
- response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
- self.assertEqual(response.status_code, 302)
- self.assertEqual(models.Workspace.objects.count(), 1)
- self.assertQuerySetEqual(
- models.Workspace.objects.all(),
- models.Workspace.objects.filter(pk=other_object.pk),
- )
-
- def test_can_delete_workspace_with_auth_domain(self):
- """A workspace can be deleted if it has an auth domain, and the auth domain group is not deleted."""
- billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
- object = factories.WorkspaceFactory.create(billing_project=billing_project, name="test-workspace")
- auth_domain = factories.ManagedGroupFactory.create(name="test-group")
- wad = models.WorkspaceAuthorizationDomain.objects.create(workspace=object, group=auth_domain)
- # object.authorization_domains.add(auth_domain)
- api_url = self.get_api_url(object.billing_project.name, object.name)
- self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
- self.client.force_login(self.user)
- response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
- self.assertEqual(response.status_code, 302)
- self.assertEqual(models.Workspace.objects.count(), 0)
- self.assertEqual(models.WorkspaceAuthorizationDomain.objects.count(), 0)
- # The auth domain group still exists.
- self.assertEqual(models.ManagedGroup.objects.count(), 1)
- models.ManagedGroup.objects.get(pk=auth_domain.pk)
- # History is added for workspace.
- self.assertEqual(object.history.count(), 2)
- self.assertEqual(object.history.latest().history_type, "-")
- # History is added for auth domain.
- self.assertEqual(wad.history.count(), 2)
- self.assertEqual(wad.history.latest().history_type, "-")
-
- def test_can_delete_workspace_that_has_been_shared_with_group(self):
- """A workspace can be deleted if it has been shared with a group, and the group is not deleted."""
- billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
- object = factories.WorkspaceFactory.create(billing_project=billing_project, name="test-workspace")
- group = factories.ManagedGroupFactory.create(name="test-group")
- factories.WorkspaceGroupSharingFactory.create(workspace=object, group=group)
- api_url = self.get_api_url(object.billing_project.name, object.name)
- self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
- self.client.force_login(self.user)
- response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
- self.assertEqual(response.status_code, 302)
- self.assertEqual(models.Workspace.objects.count(), 0)
- self.assertEqual(models.WorkspaceGroupSharing.objects.count(), 0)
- # The group still exists.
- self.assertEqual(models.ManagedGroup.objects.count(), 1)
- models.ManagedGroup.objects.get(pk=group.pk)
- # History is added for workspace.
- self.assertEqual(object.history.count(), 2)
- self.assertEqual(object.history.latest().history_type, "-")
- # History is added for WorkspaceGroupSharing.
- self.assertEqual(models.WorkspaceGroupSharing.history.count(), 2)
- self.assertEqual(models.WorkspaceGroupSharing.history.latest().history_type, "-")
-
- def test_success_url(self):
- """Redirects to the expected page."""
- object = factories.WorkspaceFactory.create()
- # Need to use the client instead of RequestFactory to check redirection url.
- api_url = self.get_api_url(object.billing_project.name, object.name)
- self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
- self.client.force_login(self.user)
- response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:list",
- args=[DefaultWorkspaceAdapter().get_type()],
- ),
- )
-
- def test_adapter_success_url(self):
- """Redirects to the expected page."""
- # Register a new adapter.
- workspace_adapter_registry.register(TestWorkspaceAdapter)
- object = factories.WorkspaceFactory.create(workspace_type=TestWorkspaceAdapter().get_type())
- # Need to use the client instead of RequestFactory to check redirection url.
- api_url = self.get_api_url(object.billing_project.name, object.name)
- self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
- self.client.force_login(self.user)
- response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(
- response,
- reverse(
- "anvil_consortium_manager:workspaces:list",
- args=[TestWorkspaceAdapter().get_type()],
- ),
- )
-
- def test_api_error(self):
- """Shows a message if an AnVIL API error occurs."""
- # Need a client to check messages.
- object = factories.WorkspaceFactory.create()
- api_url = self.get_api_url(object.billing_project.name, object.name)
- self.anvil_response_mock.add(
- responses.DELETE,
- api_url,
- status=500,
- json={"message": "workspace delete test error"},
- )
- self.client.force_login(self.user)
- response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
- self.assertEqual(response.status_code, 200)
- messages = [m.message for m in get_messages(response.wsgi_request)]
- self.assertEqual(len(messages), 1)
- self.assertIn("AnVIL API Error: workspace delete test error", str(messages[0]))
- # Make sure that the object still exists.
- self.assertEqual(models.Workspace.objects.count(), 1)
-
- def test_post_does_not_delete_when_protected_fk_to_another_model(self):
- """Workspace is not deleted when there is a protected foreign key reference to the workspace."""
- object = factories.DefaultWorkspaceDataFactory.create()
- app_models.ProtectedWorkspace.objects.create(workspace=object.workspace)
- self.client.force_login(self.user)
- response = self.client.post(
- self.get_url(object.workspace.billing_project.name, object.workspace.name),
- {"submit": ""},
- follow=True,
- )
- self.assertRedirects(response, object.get_absolute_url())
- # A message is added.
- messages = list(response.context["messages"])
- self.assertEqual(len(messages), 1)
- self.assertEqual(
- views.WorkspaceDelete.message_could_not_delete_workspace_from_app,
- str(messages[0]),
- )
- # Make sure the group still exists.
- self.assertEqual(models.Workspace.objects.count(), 1)
- self.assertEqual(models.DefaultWorkspaceData.objects.count(), 1)
- object.refresh_from_db()
-
- def test_post_does_not_delete_when_workspace_data_has_protected_fk_to_another_model(
- self,
- ):
- """Workspace is not deleted when there is a protected foreign key reference to the workspace data."""
- workspace_data = factories.DefaultWorkspaceDataFactory()
- object = workspace_data.workspace
- app_models.ProtectedWorkspaceData.objects.create(workspace_data=workspace_data)
- self.client.force_login(self.user)
- response = self.client.post(
- self.get_url(object.billing_project.name, object.name),
- {"submit": ""},
- follow=True,
- )
- self.assertRedirects(response, object.get_absolute_url())
- # A message is added.
- messages = list(response.context["messages"])
- self.assertEqual(len(messages), 1)
- self.assertEqual(
- views.WorkspaceDelete.message_could_not_delete_workspace_from_app,
- str(messages[0]),
- )
- # Make sure the group still exists.
- self.assertEqual(models.Workspace.objects.count(), 1)
- object.refresh_from_db()
+ self.assertEqual(len(response.context_data["table"].rows), 1)
+ self.assertIn(instance, response.context_data["table"].data)
- def test_get_is_locked(self):
- """View redirects with a get request if the workspace is locked."""
- billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
- object = factories.DefaultWorkspaceDataFactory.create(
- workspace__billing_project=billing_project,
- workspace__name="test-workspace",
- workspace__is_locked=True,
- )
- self.client.force_login(self.user)
- response = self.client.get(
- self.get_url(object.workspace.billing_project.name, object.workspace.name),
- follow=True,
- )
- # Make sure the workspace still exists.
- self.assertIn(object.workspace, models.Workspace.objects.all())
- self.assertIn(object, models.DefaultWorkspaceData.objects.all())
- # Redirects to detail page.
- self.assertRedirects(response, object.get_absolute_url())
- # With a message.
- messages = [m.message for m in get_messages(response.wsgi_request)]
- self.assertEqual(len(messages), 1)
- self.assertEqual(views.WorkspaceDelete.message_workspace_locked, str(messages[0]))
+ def test_view_with_filter_returns_one_object_case_insensitive(self):
+ instance = factories.WorkspaceFactory.create(name="workspace1")
+ factories.WorkspaceFactory.create(name="workspace2")
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(), {"name__icontains": "Workspace1"})
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 1)
+ self.assertIn(instance, response.context_data["table"].data)
- def test_post_is_locked(self):
- """View redirects with a post request if the workspace is locked."""
- billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
- object = factories.DefaultWorkspaceDataFactory.create(
- workspace__billing_project=billing_project,
- workspace__name="test-workspace",
- workspace__is_locked=True,
- )
- self.client.force_login(self.user)
- response = self.client.post(
- self.get_url(object.workspace.billing_project.name, object.workspace.name),
- {"submit": ""},
- follow=True,
- )
- # Make sure the workspace still exists.
- self.assertIn(object.workspace, models.Workspace.objects.all())
- self.assertIn(object, models.DefaultWorkspaceData.objects.all())
- # Redirects to detail page.
- self.assertRedirects(response, object.get_absolute_url())
- # With a message.
- messages = [m.message for m in get_messages(response.wsgi_request)]
- self.assertEqual(len(messages), 1)
- self.assertEqual(views.WorkspaceDelete.message_workspace_locked, str(messages[0]))
+ def test_view_with_filter_returns_one_object_case_contains(self):
+ instance = factories.WorkspaceFactory.create(name="workspace1")
+ factories.WorkspaceFactory.create(name="workspace2")
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(), {"name__icontains": "orkspace1"})
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 1)
+ self.assertIn(instance, response.context_data["table"].data)
+
+ def test_view_with_filter_returns_mutiple_objects(self):
+ factories.WorkspaceFactory.create(name="workspace1")
+ factories.WorkspaceFactory.create(name="wOrkspace1")
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(), {"name__icontains": "Workspace"})
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 2)
-class WorkspaceAutocompleteTest(TestCase):
+class WorkspaceListByTypeTest(TestCase):
def setUp(self):
"""Set up test class."""
self.factory = RequestFactory()
- # Create a user with the correct permissions.
- self.user = User.objects.create_user(username="test", password="test")
- self.user.user_permissions.add(
+ # Create a user with staff view permission.
+ self.staff_view_user = User.objects.create_user(username="test-staff-view", password="test")
+ self.staff_view_user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
)
+ # Create a user with view permission
+ self.view_user = User.objects.create_user(username="test-view", password="test")
+ self.view_user.user_permissions.add(
+ Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
+ )
+ self.workspace_type = DefaultWorkspaceAdapter().get_type()
+
+ def tearDown(self):
+ """Clean up after tests."""
+ # Unregister all adapters.
+ workspace_adapter_registry._registry = {}
+ # Register the default adapter.
+ workspace_adapter_registry.register(DefaultWorkspaceAdapter)
+ super().tearDown()
def get_url(self, *args):
"""Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:workspaces:autocomplete", args=args)
+ return reverse("anvil_consortium_manager:workspaces:list", args=args)
def get_view(self):
"""Return the view being tested."""
- return views.WorkspaceAutocomplete.as_view()
+ return views.WorkspaceListByType.as_view()
def test_view_redirect_not_logged_in(self):
"View redirects to login view when user is not logged in."
# Need a client for redirects.
- response = self.client.get(self.get_url())
- self.assertRedirects(response, resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url())
+ response = self.client.get(self.get_url(self.workspace_type))
+ self.assertRedirects(
+ response,
+ resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url(self.workspace_type),
+ )
- def test_status_code_with_user_permission(self):
+ def test_status_code_with_staff_view_permission(self):
"""Returns successful response code."""
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
+ self.client.force_login(self.staff_view_user)
+ response = self.client.get(self.get_url(self.workspace_type))
self.assertEqual(response.status_code, 200)
- def test_access_with_limited_view_permission(self):
+ def test_access_with_view_permission(self):
"""Raises permission denied if user has limited view permission."""
- user = User.objects.create_user(username="test-limited", password="test-limited")
- user.user_permissions.add(
- Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
- )
- request = self.factory.get(self.get_url())
- request.user = user
- with self.assertRaises(PermissionDenied):
- self.get_view()(request)
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type))
+ self.assertEqual(response.status_code, 200)
def test_access_without_user_permission(self):
"""Raises permission denied if user has no permissions."""
user_no_perms = User.objects.create_user(username="test-none", password="test-none")
- request = self.factory.get(self.get_url())
+ request = self.factory.get(self.get_url(self.workspace_type))
request.user = user_no_perms
with self.assertRaises(PermissionDenied):
- self.get_view()(request)
+ self.get_view()(request, workspace_type=self.workspace_type)
- def test_returns_all_objects(self):
- """Queryset returns all objects when there is no query."""
- groups = factories.WorkspaceFactory.create_batch(10)
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 10)
- self.assertEqual(sorted(returned_ids), sorted([group.pk for group in groups]))
+ def test_get_workspace_type_not_registered(self):
+ """Raises 404 with get request if workspace type is not registered with adapter."""
+ request = self.factory.get(self.get_url("foo"))
+ request.user = self.view_user
+ with self.assertRaises(Http404):
+ self.get_view()(request, workspace_type="foo")
+
+ def test_view_has_correct_table_class_staff_view(self):
+ self.client.force_login(self.staff_view_user)
+ response = self.client.get(self.get_url(self.workspace_type))
+ self.assertIn("table", response.context_data)
+ self.assertIsInstance(response.context_data["table"], tables.WorkspaceStaffTable)
+
+ def test_view_has_correct_table_class_view(self):
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type))
+ self.assertIn("table", response.context_data)
+ self.assertIsInstance(response.context_data["table"], tables.WorkspaceUserTable)
+
+ def test_view_with_no_objects(self):
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type))
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 0)
+
+ def test_view_with_one_object(self):
+ factories.WorkspaceFactory()
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type))
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 1)
+
+ def test_view_with_two_objects(self):
+ factories.WorkspaceFactory.create(name="w1")
+ factories.WorkspaceFactory.create(name="w2")
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type))
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 2)
+
+ def test_adapter_table_class_staff_view(self):
+ """Displays the correct table if specified in the adapter."""
+ # Overriding settings doesn't work, because appconfig.ready has already run and
+ # registered the default adapter. Instead, unregister the default and register the
+ # new adapter here.
+ workspace_adapter_registry.unregister(DefaultWorkspaceAdapter)
+ workspace_adapter_registry.register(TestWorkspaceAdapter)
+ self.workspace_type = TestWorkspaceAdapter().get_type()
+ self.client.force_login(self.staff_view_user)
+ response = self.client.get(self.get_url(self.workspace_type))
+ self.assertIn("table", response.context_data)
+ self.assertIsInstance(response.context_data["table"], app_tables.TestWorkspaceDataStaffTable)
+
+ def test_adapter_table_class_view(self):
+ """Displays the correct table if specified in the adapter."""
+ # Overriding settings doesn't work, because appconfig.ready has already run and
+ # registered the default adapter. Instead, unregister the default and register the
+ # new adapter here.
+ workspace_adapter_registry.unregister(DefaultWorkspaceAdapter)
+ workspace_adapter_registry.register(TestWorkspaceAdapter)
+ self.workspace_type = TestWorkspaceAdapter().get_type()
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type))
+ self.assertIn("table", response.context_data)
+ self.assertIsInstance(response.context_data["table"], app_tables.TestWorkspaceDataUserTable)
+
+ def test_only_shows_workspaces_with_correct_type(self):
+ """Only workspaces with the same workspace_type are shown in the table."""
+ workspace_adapter_registry.register(TestWorkspaceAdapter)
+ factories.WorkspaceFactory(workspace_type=TestWorkspaceAdapter().get_type())
+ default_type = DefaultWorkspaceAdapter().get_type()
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(default_type))
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 0)
+
+ def test_view_with_filter_return_no_object(self):
+ factories.WorkspaceFactory.create(name="workspace1")
+ factories.WorkspaceFactory.create(name="workspace2")
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "abc"})
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 0)
+
+ def test_view_with_filter_returns_one_object_exact(self):
+ instance = factories.WorkspaceFactory.create(name="workspace1")
+ factories.WorkspaceFactory.create(name="workspace2")
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "workspace1"})
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 1)
+ self.assertIn(instance, response.context_data["table"].data)
+
+ def test_view_with_filter_returns_one_object_case_insensitive(self):
+ instance = factories.WorkspaceFactory.create(name="workspace1")
+ factories.WorkspaceFactory.create(name="workspace2")
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "Workspace1"})
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 1)
+ self.assertIn(instance, response.context_data["table"].data)
+
+ def test_view_with_filter_returns_one_object_case_contains(self):
+ instance = factories.WorkspaceFactory.create(name="workspace1")
+ factories.WorkspaceFactory.create(name="workspace2")
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "orkspace1"})
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 1)
+ self.assertIn(instance, response.context_data["table"].data)
- def test_returns_correct_object_match(self):
- """Queryset returns the correct objects when query matches the name."""
- workspace = factories.WorkspaceFactory.create(name="test-workspace")
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(), {"q": "test-workspace"})
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], workspace.pk)
+ def test_view_with_filter_workspace_type(self):
+ instance = factories.WorkspaceFactory.create(name="workspace1")
+ factories.WorkspaceFactory.create(name="workspace2", workspace_type=TestWorkspaceAdapter().get_type())
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "workspace"})
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 1)
+ self.assertIn(instance, response.context_data["table"].data)
- def test_returns_correct_object_starting_with_query(self):
- """Queryset returns the correct objects when query matches the beginning of the name."""
- workspace = factories.WorkspaceFactory.create(name="test-workspace")
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(), {"q": "test"})
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], workspace.pk)
+ def test_view_with_filter_returns_mutiple_objects(self):
+ factories.WorkspaceFactory.create(name="workspace1")
+ factories.WorkspaceFactory.create(name="wOrkspace1")
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type), {"name__icontains": "Workspace"})
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("table", response.context_data)
+ self.assertEqual(len(response.context_data["table"].rows), 2)
- def test_returns_correct_object_containing_query(self):
- """Queryset returns the correct objects when the name contains the query."""
- workspace = factories.WorkspaceFactory.create(name="test-workspace")
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(), {"q": "work"})
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], workspace.pk)
+ def test_view_with_default_adapter_use_default_workspace_list_template(self):
+ default_type = DefaultWorkspaceAdapter().get_type()
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(default_type))
+ self.assertTemplateUsed(response, "anvil_consortium_manager/workspace_list.html")
- def test_returns_correct_object_case_insensitive(self):
- """Queryset returns the correct objects when query matches the beginning of the name."""
- workspace = factories.WorkspaceFactory.create(name="test-workspace")
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(), {"q": "TEST-WORKSPACE"})
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], workspace.pk)
+ def test_view_with_custom_adapter_use_custom_workspace_list_template(self):
+ workspace_adapter_registry.unregister(DefaultWorkspaceAdapter)
+ workspace_adapter_registry.register(TestWorkspaceAdapter)
+ self.workspace_type = TestWorkspaceAdapter().get_type()
+ self.client.force_login(self.view_user)
+ response = self.client.get(self.get_url(self.workspace_type))
+ self.assertTemplateUsed(response, "test_workspace_list.html")
-class WorkspaceAutocompleteByTypeTest(TestCase):
- """Tests for the WorkspaceAutocompleteByType view."""
+class WorkspaceDeleteTest(AnVILAPIMockTestMixin, TestCase):
+ api_success_code = 202
def setUp(self):
"""Set up test class."""
+ # The superclass uses the responses package to mock API responses.
+ super().setUp()
self.factory = RequestFactory()
- # Create a user with the correct permissions.
+ # Create a user with both view and edit permissions.
self.user = User.objects.create_user(username="test", password="test")
self.user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
)
- self.default_workspace_type = DefaultWorkspaceAdapter().get_type()
- workspace_adapter_registry.register(TestWorkspaceAdapter)
+ self.user.user_permissions.add(
+ Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME)
+ )
def tearDown(self):
- workspace_adapter_registry.unregister(TestWorkspaceAdapter)
+ """Clean up after tests."""
+ # Unregister all adapters.
+ workspace_adapter_registry._registry = {}
+ # Register the default adapter.
+ workspace_adapter_registry.register(DefaultWorkspaceAdapter)
+ super().tearDown()
def get_url(self, *args):
"""Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:workspaces:autocomplete_by_type", args=args)
+ return reverse("anvil_consortium_manager:workspaces:delete", args=args)
+
+ def get_api_url(self, billing_project_name, workspace_name):
+ return self.api_client.rawls_entry_point + "/api/workspaces/" + billing_project_name + "/" + workspace_name
def get_view(self):
"""Return the view being tested."""
- return views.WorkspaceAutocompleteByType.as_view()
+ return views.WorkspaceDelete.as_view()
def test_view_redirect_not_logged_in(self):
"View redirects to login view when user is not logged in."
# Need a client for redirects.
- response = self.client.get(self.get_url(self.default_workspace_type))
- self.assertRedirects(
- response,
- resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url(self.default_workspace_type),
- )
+ url = self.get_url("foo1", "foo2")
+ response = self.client.get(url)
+ self.assertRedirects(response, resolve_url(settings.LOGIN_URL) + "?next=" + url)
def test_status_code_with_user_permission(self):
"""Returns successful response code."""
+ obj = factories.WorkspaceFactory.create()
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.default_workspace_type))
+ response = self.client.get(self.get_url(obj.billing_project.name, obj.name))
self.assertEqual(response.status_code, 200)
+ def test_access_with_view_permission(self):
+ """Raises permission denied if user has only view permission."""
+ user_with_view_perm = User.objects.create_user(username="test-other", password="test-other")
+ user_with_view_perm.user_permissions.add(
+ Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
+ )
+ request = self.factory.get(self.get_url("foo1", "foo2"))
+ request.user = user_with_view_perm
+ with self.assertRaises(PermissionDenied):
+ self.get_view()(request, pk=1)
+
def test_access_with_limited_view_permission(self):
"""Raises permission denied if user has limited view permission."""
user = User.objects.create_user(username="test-limited", password="test-limited")
user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
)
- request = self.factory.get(self.get_url(self.default_workspace_type))
+ request = self.factory.get(self.get_url("foo", "bar"))
request.user = user
with self.assertRaises(PermissionDenied):
self.get_view()(request)
@@ -12514,111 +11252,267 @@ def test_access_with_limited_view_permission(self):
def test_access_without_user_permission(self):
"""Raises permission denied if user has no permissions."""
user_no_perms = User.objects.create_user(username="test-none", password="test-none")
- request = self.factory.get(self.get_url(self.default_workspace_type))
+ request = self.factory.get(self.get_url("foo1", "foo2"))
request.user = user_no_perms
with self.assertRaises(PermissionDenied):
- self.get_view()(request, workspace_type=self.default_workspace_type)
+ self.get_view()(request, pk=1)
- def test_404_with_unregistered_workspace_type(self):
- """Raises 404 with get request if workspace type is not registered with adapter."""
- request = self.factory.get(self.get_url("foo"))
+ def test_view_with_invalid_pk(self):
+ """Returns a 404 when the object doesn't exist."""
+ request = self.factory.get(self.get_url("foo1", "foo2"))
request.user = self.user
with self.assertRaises(Http404):
- self.get_view()(request, workspace_type="foo")
+ self.get_view()(request, pk=1)
- def test_returns_all_objects(self):
- """Queryset returns all objects when there is no query."""
- workspaces = factories.DefaultWorkspaceDataFactory.create_batch(10)
+ def test_view_deletes_object(self):
+ """Posting submit to the form successfully deletes the object."""
+ billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
+ object = factories.WorkspaceFactory.create(billing_project=billing_project, name="test-workspace")
+ api_url = self.get_api_url(object.billing_project.name, object.name)
+ self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.default_workspace_type))
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 10)
- self.assertEqual(sorted(returned_ids), sorted([workspace.pk for workspace in workspaces]))
+ response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(models.Workspace.objects.count(), 0)
+ # History is added.
+ self.assertEqual(object.history.count(), 2)
+ self.assertEqual(object.history.latest().history_type, "-")
- def test_returns_correct_object_match(self):
- """Queryset returns the correct objects when query matches the name."""
- workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="test-workspace")
+ def test_success_message(self):
+ """Response includes a success message if successful."""
+ billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
+ object = factories.WorkspaceFactory.create(billing_project=billing_project, name="test-workspace")
+ api_url = self.get_api_url(object.billing_project.name, object.name)
+ self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.default_workspace_type), {"q": "test-workspace"})
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], workspace.pk)
+ response = self.client.post(
+ self.get_url(object.billing_project.name, object.name),
+ {"submit": ""},
+ follow=True,
+ )
+ messages = [m.message for m in get_messages(response.wsgi_request)]
+ self.assertEqual(len(messages), 1)
+ self.assertEqual(views.WorkspaceDelete.success_message, str(messages[0]))
- def test_returns_correct_object_starting_with_query(self):
- """Queryset returns the correct objects when query matches the beginning of the name."""
- workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="test-workspace")
+ def test_only_deletes_specified_pk(self):
+ """View only deletes the specified pk."""
+ object = factories.WorkspaceFactory.create()
+ other_object = factories.WorkspaceFactory.create()
+ api_url = self.get_api_url(object.billing_project.name, object.name)
+ self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.default_workspace_type), {"q": "test"})
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], workspace.pk)
+ response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(models.Workspace.objects.count(), 1)
+ self.assertQuerySetEqual(
+ models.Workspace.objects.all(),
+ models.Workspace.objects.filter(pk=other_object.pk),
+ )
- def test_returns_correct_object_containing_query(self):
- """Queryset returns the correct objects when the name contains the query."""
- workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="test-workspace")
+ def test_can_delete_workspace_with_auth_domain(self):
+ """A workspace can be deleted if it has an auth domain, and the auth domain group is not deleted."""
+ billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
+ object = factories.WorkspaceFactory.create(billing_project=billing_project, name="test-workspace")
+ auth_domain = factories.ManagedGroupFactory.create(name="test-group")
+ wad = models.WorkspaceAuthorizationDomain.objects.create(workspace=object, group=auth_domain)
+ # object.authorization_domains.add(auth_domain)
+ api_url = self.get_api_url(object.billing_project.name, object.name)
+ self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
+ self.client.force_login(self.user)
+ response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(models.Workspace.objects.count(), 0)
+ self.assertEqual(models.WorkspaceAuthorizationDomain.objects.count(), 0)
+ # The auth domain group still exists.
+ self.assertEqual(models.ManagedGroup.objects.count(), 1)
+ models.ManagedGroup.objects.get(pk=auth_domain.pk)
+ # History is added for workspace.
+ self.assertEqual(object.history.count(), 2)
+ self.assertEqual(object.history.latest().history_type, "-")
+ # History is added for auth domain.
+ self.assertEqual(wad.history.count(), 2)
+ self.assertEqual(wad.history.latest().history_type, "-")
+
+ def test_can_delete_workspace_that_has_been_shared_with_group(self):
+ """A workspace can be deleted if it has been shared with a group, and the group is not deleted."""
+ billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
+ object = factories.WorkspaceFactory.create(billing_project=billing_project, name="test-workspace")
+ group = factories.ManagedGroupFactory.create(name="test-group")
+ factories.WorkspaceGroupSharingFactory.create(workspace=object, group=group)
+ api_url = self.get_api_url(object.billing_project.name, object.name)
+ self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
+ self.client.force_login(self.user)
+ response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(models.Workspace.objects.count(), 0)
+ self.assertEqual(models.WorkspaceGroupSharing.objects.count(), 0)
+ # The group still exists.
+ self.assertEqual(models.ManagedGroup.objects.count(), 1)
+ models.ManagedGroup.objects.get(pk=group.pk)
+ # History is added for workspace.
+ self.assertEqual(object.history.count(), 2)
+ self.assertEqual(object.history.latest().history_type, "-")
+ # History is added for WorkspaceGroupSharing.
+ self.assertEqual(models.WorkspaceGroupSharing.history.count(), 2)
+ self.assertEqual(models.WorkspaceGroupSharing.history.latest().history_type, "-")
+
+ def test_success_url(self):
+ """Redirects to the expected page."""
+ object = factories.WorkspaceFactory.create()
+ # Need to use the client instead of RequestFactory to check redirection url.
+ api_url = self.get_api_url(object.billing_project.name, object.name)
+ self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
+ self.client.force_login(self.user)
+ response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:list",
+ args=[DefaultWorkspaceAdapter().get_type()],
+ ),
+ )
+
+ def test_adapter_success_url(self):
+ """Redirects to the expected page."""
+ # Register a new adapter.
+ workspace_adapter_registry.register(TestWorkspaceAdapter)
+ object = factories.WorkspaceFactory.create(workspace_type=TestWorkspaceAdapter().get_type())
+ # Need to use the client instead of RequestFactory to check redirection url.
+ api_url = self.get_api_url(object.billing_project.name, object.name)
+ self.anvil_response_mock.add(responses.DELETE, api_url, status=self.api_success_code)
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.default_workspace_type), {"q": "work"})
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], workspace.pk)
+ response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(
+ response,
+ reverse(
+ "anvil_consortium_manager:workspaces:list",
+ args=[TestWorkspaceAdapter().get_type()],
+ ),
+ )
- def test_returns_correct_object_case_insensitive(self):
- """Queryset returns the correct objects when query matches the beginning of the name."""
- workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="test-workspace")
+ def test_api_error(self):
+ """Shows a message if an AnVIL API error occurs."""
+ # Need a client to check messages.
+ object = factories.WorkspaceFactory.create()
+ api_url = self.get_api_url(object.billing_project.name, object.name)
+ self.anvil_response_mock.add(
+ responses.DELETE,
+ api_url,
+ status=500,
+ json={"message": "workspace delete test error"},
+ )
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.default_workspace_type), {"q": "TEST-WORKSPACE"})
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], workspace.pk)
+ response = self.client.post(self.get_url(object.billing_project.name, object.name), {"submit": ""})
+ self.assertEqual(response.status_code, 200)
+ messages = [m.message for m in get_messages(response.wsgi_request)]
+ self.assertEqual(len(messages), 1)
+ self.assertIn("AnVIL API Error: workspace delete test error", str(messages[0]))
+ # Make sure that the object still exists.
+ self.assertEqual(models.Workspace.objects.count(), 1)
- def test_only_specified_workspace_type(self):
- """Queryset returns only objects with the specified workspace type."""
- workspace = factories.DefaultWorkspaceDataFactory.create()
- other_workspace = TestWorkspaceDataFactory.create()
+ def test_post_does_not_delete_when_protected_fk_to_another_model(self):
+ """Workspace is not deleted when there is a protected foreign key reference to the workspace."""
+ object = factories.DefaultWorkspaceDataFactory.create()
+ app_models.ProtectedWorkspace.objects.create(workspace=object.workspace)
self.client.force_login(self.user)
- response = self.client.get(self.get_url(workspace.workspace.workspace_type))
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], workspace.pk)
- response = self.client.get(self.get_url(other_workspace.workspace.workspace_type))
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], other_workspace.pk)
+ response = self.client.post(
+ self.get_url(object.workspace.billing_project.name, object.workspace.name),
+ {"submit": ""},
+ follow=True,
+ )
+ self.assertRedirects(response, object.get_absolute_url())
+ # A message is added.
+ messages = list(response.context["messages"])
+ self.assertEqual(len(messages), 1)
+ self.assertEqual(
+ views.WorkspaceDelete.message_could_not_delete_workspace_from_app,
+ str(messages[0]),
+ )
+ # Make sure the group still exists.
+ self.assertEqual(models.Workspace.objects.count(), 1)
+ self.assertEqual(models.DefaultWorkspaceData.objects.count(), 1)
+ object.refresh_from_db()
- def test_custom_autocomplete_method(self):
- # Workspace that will match the custom autocomplete filtering.
- workspace_1 = TestWorkspaceDataFactory.create(workspace__name="TEST")
- # Workspace that should not match the custom autocomplete filtering.
- TestWorkspaceDataFactory.create(workspace__name="TEST-WORKSPACE")
+ def test_post_does_not_delete_when_workspace_data_has_protected_fk_to_another_model(
+ self,
+ ):
+ """Workspace is not deleted when there is a protected foreign key reference to the workspace data."""
+ workspace_data = factories.DefaultWorkspaceDataFactory()
+ object = workspace_data.workspace
+ app_models.ProtectedWorkspaceData.objects.create(workspace_data=workspace_data)
self.client.force_login(self.user)
- response = self.client.get(self.get_url(workspace_1.workspace.workspace_type), {"q": "TEST"})
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], workspace_1.pk)
+ response = self.client.post(
+ self.get_url(object.billing_project.name, object.name),
+ {"submit": ""},
+ follow=True,
+ )
+ self.assertRedirects(response, object.get_absolute_url())
+ # A message is added.
+ messages = list(response.context["messages"])
+ self.assertEqual(len(messages), 1)
+ self.assertEqual(
+ views.WorkspaceDelete.message_could_not_delete_workspace_from_app,
+ str(messages[0]),
+ )
+ # Make sure the group still exists.
+ self.assertEqual(models.Workspace.objects.count(), 1)
+ object.refresh_from_db()
- def test_custom_autocomplete_with_forwarded_value(self):
- # Workspace that will match the custom autocomplete filtering.
- workspace = TestWorkspaceDataFactory.create()
- # Workspace that should not match the custom autocomplete filtering.
- TestWorkspaceDataFactory.create()
+ def test_get_is_locked(self):
+ """View redirects with a get request if the workspace is locked."""
+ billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
+ object = factories.DefaultWorkspaceDataFactory.create(
+ workspace__billing_project=billing_project,
+ workspace__name="test-workspace",
+ workspace__is_locked=True,
+ )
self.client.force_login(self.user)
response = self.client.get(
- self.get_url(workspace.workspace.workspace_type),
- {"forward": json.dumps({"billing_project": workspace.workspace.billing_project.pk})},
+ self.get_url(object.workspace.billing_project.name, object.workspace.name),
+ follow=True,
)
- returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
- self.assertEqual(len(returned_ids), 1)
- self.assertEqual(returned_ids[0], workspace.pk)
+ # Make sure the workspace still exists.
+ self.assertIn(object.workspace, models.Workspace.objects.all())
+ self.assertIn(object, models.DefaultWorkspaceData.objects.all())
+ # Redirects to detail page.
+ self.assertRedirects(response, object.get_absolute_url())
+ # With a message.
+ messages = [m.message for m in get_messages(response.wsgi_request)]
+ self.assertEqual(len(messages), 1)
+ self.assertEqual(views.WorkspaceDelete.message_workspace_locked, str(messages[0]))
+ def test_post_is_locked(self):
+ """View redirects with a post request if the workspace is locked."""
+ billing_project = factories.BillingProjectFactory.create(name="test-billing-project")
+ object = factories.DefaultWorkspaceDataFactory.create(
+ workspace__billing_project=billing_project,
+ workspace__name="test-workspace",
+ workspace__is_locked=True,
+ )
+ self.client.force_login(self.user)
+ response = self.client.post(
+ self.get_url(object.workspace.billing_project.name, object.workspace.name),
+ {"submit": ""},
+ follow=True,
+ )
+ # Make sure the workspace still exists.
+ self.assertIn(object.workspace, models.Workspace.objects.all())
+ self.assertIn(object, models.DefaultWorkspaceData.objects.all())
+ # Redirects to detail page.
+ self.assertRedirects(response, object.get_absolute_url())
+ # With a message.
+ messages = [m.message for m in get_messages(response.wsgi_request)]
+ self.assertEqual(len(messages), 1)
+ self.assertEqual(views.WorkspaceDelete.message_workspace_locked, str(messages[0]))
-class WorkspaceAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests for the WorkspaceAudit view."""
+class WorkspaceAutocompleteTest(TestCase):
def setUp(self):
"""Set up test class."""
- super().setUp()
self.factory = RequestFactory()
- # Create a user with only view permission.
+ # Create a user with the correct permissions.
self.user = User.objects.create_user(username="test", password="test")
self.user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
@@ -12626,56 +11520,11 @@ def setUp(self):
def get_url(self, *args):
"""Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:workspaces:audit", args=args)
-
- def get_api_url(self):
- return self.api_client.rawls_entry_point + "/api/workspaces"
-
- def get_api_workspace_json(self, billing_project_name, workspace_name, access, auth_domains=[]):
- """Return the json dictionary for a single workspace on AnVIL."""
- return {
- "accessLevel": access,
- "workspace": {
- "name": workspace_name,
- "namespace": billing_project_name,
- "authorizationDomain": [{"membersGroupName": x} for x in auth_domains],
- "isLocked": False,
- },
- }
-
- def get_api_workspace_acl_url(self, billing_project_name, workspace_name):
- return (
- self.api_client.rawls_entry_point
- + "/api/workspaces/"
- + billing_project_name
- + "/"
- + workspace_name
- + "/acl"
- )
-
- def get_api_workspace_acl_response(self):
- """Return a json for the workspace/acl method where no one else can access."""
- return {
- "acl": {
- self.service_account_email: {
- "accessLevel": "OWNER",
- "canCompute": True,
- "canShare": True,
- "pending": False,
- }
- }
- }
-
- def get_api_bucket_options_url(self, billing_project_name, workspace_name):
- return self.api_client.rawls_entry_point + "/api/workspaces/" + billing_project_name + "/" + workspace_name
-
- def get_api_bucket_options_response(self):
- """Return a json for the workspace/acl method that is not requester pays."""
- return {"bucketOptions": {"requesterPays": False}}
+ return reverse("anvil_consortium_manager:workspaces:autocomplete", args=args)
def get_view(self):
"""Return the view being tested."""
- return views.WorkspaceAudit.as_view()
+ return views.WorkspaceAutocomplete.as_view()
def test_view_redirect_not_logged_in(self):
"View redirects to login view when user is not logged in."
@@ -12685,13 +11534,6 @@ def test_view_redirect_not_logged_in(self):
def test_status_code_with_user_permission(self):
"""Returns successful response code."""
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
self.client.force_login(self.user)
response = self.client.get(self.get_url())
self.assertEqual(response.status_code, 200)
@@ -12708,247 +11550,97 @@ def test_access_with_limited_view_permission(self):
self.get_view()(request)
def test_access_without_user_permission(self):
- """Raises permission denied if user has no permissions."""
- user_no_perms = User.objects.create_user(username="test-none", password="test-none")
- request = self.factory.get(self.get_url())
- request.user = user_no_perms
- with self.assertRaises(PermissionDenied):
- self.get_view()(request)
-
- def test_template(self):
- """Template loads successfully."""
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertEqual(response.status_code, 200)
-
- def test_audit_verified(self):
- """audit_verified is in the context data."""
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("verified_table", response.context_data)
- self.assertIsInstance(response.context_data["verified_table"], audit.VerifiedTable)
- self.assertEqual(len(response.context_data["verified_table"].rows), 0)
-
- def test_audit_verified_one_record(self):
- """audit_verified with one verified record."""
- workspace = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_workspace_json(workspace.billing_project.name, workspace.name, "OWNER")],
- )
- # Response to check workspace access.
- workspace_acl_url = self.get_api_workspace_acl_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_workspace_acl_response(),
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("verified_table", response.context_data)
- self.assertEqual(len(response.context_data["verified_table"].rows), 1)
-
- def test_audit_errors(self):
- """audit_errors is in the context data."""
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("error_table", response.context_data)
- self.assertIsInstance(response.context_data["error_table"], audit.ErrorTable)
- self.assertEqual(len(response.context_data["error_table"].rows), 0)
-
- def test_audit_errors_one_record(self):
- """audit_errors with one error record."""
- workspace = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- # Error - we are not an owner.
- json=[self.get_api_workspace_json(workspace.billing_project.name, workspace.name, "READER")],
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
+ """Raises permission denied if user has no permissions."""
+ user_no_perms = User.objects.create_user(username="test-none", password="test-none")
+ request = self.factory.get(self.get_url())
+ request.user = user_no_perms
+ with self.assertRaises(PermissionDenied):
+ self.get_view()(request)
+
+ def test_returns_all_objects(self):
+ """Queryset returns all objects when there is no query."""
+ groups = factories.WorkspaceFactory.create_batch(10)
self.client.force_login(self.user)
response = self.client.get(self.get_url())
- self.assertIn("error_table", response.context_data)
- self.assertEqual(len(response.context_data["error_table"].rows), 1)
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 10)
+ self.assertEqual(sorted(returned_ids), sorted([group.pk for group in groups]))
- def test_audit_not_in_app(self):
- """audit_not_in_app is in the context data."""
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
+ def test_returns_correct_object_match(self):
+ """Queryset returns the correct objects when query matches the name."""
+ workspace = factories.WorkspaceFactory.create(name="test-workspace")
self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("not_in_app_table", response.context_data)
- self.assertIsInstance(response.context_data["not_in_app_table"], audit.NotInAppTable)
- self.assertEqual(len(response.context_data["not_in_app_table"].rows), 0)
+ response = self.client.get(self.get_url(), {"q": "test-workspace"})
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], workspace.pk)
- def test_audit_not_in_app_one_record(self):
- """audit_not_in_app with one record not in app."""
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[self.get_api_workspace_json("foo", "bar", "OWNER")],
- )
+ def test_returns_correct_object_starting_with_query(self):
+ """Queryset returns the correct objects when query matches the beginning of the name."""
+ workspace = factories.WorkspaceFactory.create(name="test-workspace")
self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("not_in_app_table", response.context_data)
- self.assertEqual(len(response.context_data["not_in_app_table"].rows), 1)
+ response = self.client.get(self.get_url(), {"q": "test"})
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], workspace.pk)
- def test_audit_ok_is_ok(self):
- """audit_ok when audit_results.ok() is True."""
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- json=[],
- )
+ def test_returns_correct_object_containing_query(self):
+ """Queryset returns the correct objects when the name contains the query."""
+ workspace = factories.WorkspaceFactory.create(name="test-workspace")
self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], True)
+ response = self.client.get(self.get_url(), {"q": "work"})
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], workspace.pk)
- def test_audit_ok_is_not_ok(self):
- """audit_ok when audit_results.ok() is False."""
- workspace = factories.WorkspaceFactory.create()
- api_url = self.get_api_url()
- self.anvil_response_mock.add(
- responses.GET,
- api_url,
- status=200,
- # Error - we are not admin.
- json=[self.get_api_workspace_json(workspace.billing_project.name, workspace.name, "READER")],
- )
- # Response to check workspace bucket options.
- workspace_acl_url = self.get_api_bucket_options_url(workspace.billing_project.name, workspace.name)
- self.anvil_response_mock.add(
- responses.GET,
- workspace_acl_url,
- status=200,
- json=self.get_api_bucket_options_response(),
- )
+ def test_returns_correct_object_case_insensitive(self):
+ """Queryset returns the correct objects when query matches the beginning of the name."""
+ workspace = factories.WorkspaceFactory.create(name="test-workspace")
self.client.force_login(self.user)
- response = self.client.get(self.get_url())
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], False)
+ response = self.client.get(self.get_url(), {"q": "TEST-WORKSPACE"})
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], workspace.pk)
-class WorkspaceSharingAuditTest(AnVILAPIMockTestMixin, TestCase):
- """Tests for the WorkspaceSharingAudit view."""
+class WorkspaceAutocompleteByTypeTest(TestCase):
+ """Tests for the WorkspaceAutocompleteByType view."""
def setUp(self):
"""Set up test class."""
- super().setUp()
self.factory = RequestFactory()
- # Create a user with both view and edit permission.
+ # Create a user with the correct permissions.
self.user = User.objects.create_user(username="test", password="test")
self.user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.STAFF_VIEW_PERMISSION_CODENAME)
)
- # Set this variable here because it will include the service account.
- # Tests can update it with the update_api_response method.
- self.api_response = {"acl": {}}
- self.update_api_response(self.service_account_email, "OWNER", can_compute=True, can_share=True)
- # Create a workspace for use in tests.
- self.workspace = factories.WorkspaceFactory.create()
- self.api_url = (
- self.api_client.rawls_entry_point
- + "/api/workspaces/"
- + self.workspace.billing_project.name
- + "/"
- + self.workspace.name
- + "/acl"
- )
+ self.default_workspace_type = DefaultWorkspaceAdapter().get_type()
+ workspace_adapter_registry.register(TestWorkspaceAdapter)
+
+ def tearDown(self):
+ workspace_adapter_registry.unregister(TestWorkspaceAdapter)
def get_url(self, *args):
"""Get the url for the view being tested."""
- return reverse("anvil_consortium_manager:workspaces:audit_sharing", args=args)
-
- def update_api_response(self, email, access, can_compute=False, can_share=False):
- """Return a paired down json for a single ACL, including the service account."""
- self.api_response["acl"].update(
- {
- email: {
- "accessLevel": access,
- "canCompute": can_compute,
- "canShare": can_share,
- "pending": False,
- }
- }
- )
+ return reverse("anvil_consortium_manager:workspaces:autocomplete_by_type", args=args)
def get_view(self):
"""Return the view being tested."""
- return views.WorkspaceSharingAudit.as_view()
+ return views.WorkspaceAutocompleteByType.as_view()
def test_view_redirect_not_logged_in(self):
"View redirects to login view when user is not logged in."
# Need a client for redirects.
- response = self.client.get(self.get_url("foo", "bar"))
+ response = self.client.get(self.get_url(self.default_workspace_type))
self.assertRedirects(
response,
- resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url("foo", "bar"),
+ resolve_url(settings.LOGIN_URL) + "?next=" + self.get_url(self.default_workspace_type),
)
def test_status_code_with_user_permission(self):
"""Returns successful response code."""
- # Group membership API call.
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.workspace.billing_project.name, self.workspace.name))
+ response = self.client.get(self.get_url(self.default_workspace_type))
self.assertEqual(response.status_code, 200)
def test_access_with_limited_view_permission(self):
@@ -12957,7 +11649,7 @@ def test_access_with_limited_view_permission(self):
user.user_permissions.add(
Permission.objects.get(codename=models.AnVILProjectManagerAccess.VIEW_PERMISSION_CODENAME)
)
- request = self.factory.get(self.get_url("foo", "bar"))
+ request = self.factory.get(self.get_url(self.default_workspace_type))
request.user = user
with self.assertRaises(PermissionDenied):
self.get_view()(request)
@@ -12965,165 +11657,101 @@ def test_access_with_limited_view_permission(self):
def test_access_without_user_permission(self):
"""Raises permission denied if user has no permissions."""
user_no_perms = User.objects.create_user(username="test-none", password="test-none")
- request = self.factory.get(self.get_url("foo", "bar"))
+ request = self.factory.get(self.get_url(self.default_workspace_type))
request.user = user_no_perms
with self.assertRaises(PermissionDenied):
- self.get_view()(request, billing_project_slug="foo", workspace_slug="bar")
+ self.get_view()(request, workspace_type=self.default_workspace_type)
- def test_template(self):
- """Template loads successfully."""
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.workspace.billing_project.name, self.workspace.name))
- self.assertEqual(response.status_code, 200)
+ def test_404_with_unregistered_workspace_type(self):
+ """Raises 404 with get request if workspace type is not registered with adapter."""
+ request = self.factory.get(self.get_url("foo"))
+ request.user = self.user
+ with self.assertRaises(Http404):
+ self.get_view()(request, workspace_type="foo")
- def test_audit_verified(self):
- """audit_verified is in the context data."""
- # Group membership API call.
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.workspace.billing_project.name, self.workspace.name))
- self.assertIn("verified_table", response.context_data)
- self.assertIsInstance(response.context_data["verified_table"], audit.VerifiedTable)
- self.assertEqual(len(response.context_data["verified_table"].rows), 0)
-
- def test_audit_verified_one_record(self):
- """audit_verified with one verified record."""
- access = factories.WorkspaceGroupSharingFactory.create(workspace=self.workspace)
- self.update_api_response(access.group.email, "READER")
- # Group membership API call.
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
+ def test_returns_all_objects(self):
+ """Queryset returns all objects when there is no query."""
+ workspaces = factories.DefaultWorkspaceDataFactory.create_batch(10)
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.workspace.billing_project.name, self.workspace.name))
- self.assertIn("verified_table", response.context_data)
- self.assertEqual(len(response.context_data["verified_table"].rows), 1)
+ response = self.client.get(self.get_url(self.default_workspace_type))
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 10)
+ self.assertEqual(sorted(returned_ids), sorted([workspace.pk for workspace in workspaces]))
- def test_audit_errors(self):
- """audit_errors is in the context data."""
- # Group membership API call.
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
- self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.workspace.billing_project.name, self.workspace.name))
- self.assertIn("error_table", response.context_data)
- self.assertIsInstance(response.context_data["error_table"], audit.ErrorTable)
- self.assertEqual(len(response.context_data["error_table"].rows), 0)
-
- def test_audit_errors_one_record(self):
- """audit_errors with one error record."""
- access = factories.WorkspaceGroupSharingFactory.create(workspace=self.workspace)
- # Different access recorded.
- self.update_api_response(access.group.email, "WRITER")
- # Group membership API call.
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
+ def test_returns_correct_object_match(self):
+ """Queryset returns the correct objects when query matches the name."""
+ workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="test-workspace")
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.workspace.billing_project.name, self.workspace.name))
- self.assertIn("error_table", response.context_data)
- self.assertEqual(len(response.context_data["error_table"].rows), 1)
+ response = self.client.get(self.get_url(self.default_workspace_type), {"q": "test-workspace"})
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], workspace.pk)
- def test_audit_not_in_app(self):
- """audit_not_in_app is in the context data."""
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- # Admin insetad of member.
- # json=self.get_api_group_members_json_response(),
- )
+ def test_returns_correct_object_starting_with_query(self):
+ """Queryset returns the correct objects when query matches the beginning of the name."""
+ workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="test-workspace")
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.workspace.billing_project.name, self.workspace.name))
- self.assertIn("not_in_app_table", response.context_data)
- self.assertIsInstance(response.context_data["not_in_app_table"], audit.NotInAppTable)
- self.assertEqual(len(response.context_data["not_in_app_table"].rows), 0)
+ response = self.client.get(self.get_url(self.default_workspace_type), {"q": "test"})
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], workspace.pk)
- def test_audit_not_in_app_one_record(self):
- """audit_not_in_app with one record not in app."""
- self.update_api_response("foo@bar.com", "READER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- )
+ def test_returns_correct_object_containing_query(self):
+ """Queryset returns the correct objects when the name contains the query."""
+ workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="test-workspace")
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.workspace.billing_project.name, self.workspace.name))
- self.assertIn("not_in_app_table", response.context_data)
- self.assertEqual(len(response.context_data["not_in_app_table"].rows), 1)
+ response = self.client.get(self.get_url(self.default_workspace_type), {"q": "work"})
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], workspace.pk)
- def test_audit_ok_is_ok(self):
- """audit_ok when audit_results.ok() is True."""
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- # Admin insetad of member.
- # json=self.get_api_group_members_json_response(),
- )
+ def test_returns_correct_object_case_insensitive(self):
+ """Queryset returns the correct objects when query matches the beginning of the name."""
+ workspace = factories.DefaultWorkspaceDataFactory.create(workspace__name="test-workspace")
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.workspace.billing_project.name, self.workspace.name))
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], True)
+ response = self.client.get(self.get_url(self.default_workspace_type), {"q": "TEST-WORKSPACE"})
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], workspace.pk)
- def test_audit_ok_is_not_ok(self):
- """audit_ok when audit_results.ok() is False."""
- self.update_api_response("foo@bar.com", "READER")
- self.anvil_response_mock.add(
- responses.GET,
- self.api_url,
- status=200,
- json=self.api_response,
- # Not in app
- # json=self.get_api_group_members_json_response(members=["foo@bar.com"]),
- )
+ def test_only_specified_workspace_type(self):
+ """Queryset returns only objects with the specified workspace type."""
+ workspace = factories.DefaultWorkspaceDataFactory.create()
+ other_workspace = TestWorkspaceDataFactory.create()
self.client.force_login(self.user)
- response = self.client.get(self.get_url(self.workspace.billing_project.name, self.workspace.name))
- self.assertIn("audit_ok", response.context_data)
- self.assertEqual(response.context_data["audit_ok"], False)
+ response = self.client.get(self.get_url(workspace.workspace.workspace_type))
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], workspace.pk)
+ response = self.client.get(self.get_url(other_workspace.workspace.workspace_type))
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], other_workspace.pk)
- def test_workspace_does_not_exist_in_app(self):
- """Raises a 404 error with an invalid workspace slug."""
- billing_project = factories.BillingProjectFactory.create()
- request = self.factory.get(self.get_url(billing_project.name, self.workspace.name))
- request.user = self.user
- with self.assertRaises(Http404):
- self.get_view()(
- request,
- billing_project_slug=billing_project.name,
- workspace_slug=self.workspace.name,
- )
+ def test_custom_autocomplete_method(self):
+ # Workspace that will match the custom autocomplete filtering.
+ workspace_1 = TestWorkspaceDataFactory.create(workspace__name="TEST")
+ # Workspace that should not match the custom autocomplete filtering.
+ TestWorkspaceDataFactory.create(workspace__name="TEST-WORKSPACE")
+ self.client.force_login(self.user)
+ response = self.client.get(self.get_url(workspace_1.workspace.workspace_type), {"q": "TEST"})
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], workspace_1.pk)
- def test_billing_project_does_not_exist_in_app(self):
- """Raises a 404 error with an invalid billing project slug."""
- request = self.factory.get(self.get_url("foo", self.workspace.name))
- request.user = self.user
- with self.assertRaises(Http404):
- self.get_view()(request, billing_project_slug="foo", workspace_slug=self.workspace.name)
+ def test_custom_autocomplete_with_forwarded_value(self):
+ # Workspace that will match the custom autocomplete filtering.
+ workspace = TestWorkspaceDataFactory.create()
+ # Workspace that should not match the custom autocomplete filtering.
+ TestWorkspaceDataFactory.create()
+ self.client.force_login(self.user)
+ response = self.client.get(
+ self.get_url(workspace.workspace.workspace_type),
+ {"forward": json.dumps({"billing_project": workspace.workspace.billing_project.pk})},
+ )
+ returned_ids = [int(x["id"]) for x in json.loads(response.content.decode("utf-8"))["results"]]
+ self.assertEqual(len(returned_ids), 1)
+ self.assertEqual(returned_ids[0], workspace.pk)
class GroupGroupMembershipDetailTest(TestCase):
diff --git a/anvil_consortium_manager/urls.py b/anvil_consortium_manager/urls.py
index 999d5a5b..37976c4d 100644
--- a/anvil_consortium_manager/urls.py
+++ b/anvil_consortium_manager/urls.py
@@ -13,7 +13,6 @@
views.BillingProjectAutocomplete.as_view(),
name="autocomplete",
),
- path("audit/", views.BillingProjectAudit.as_view(), name="audit"),
path("/", views.BillingProjectDetail.as_view(), name="detail"),
path("/update/", views.BillingProjectUpdate.as_view(), name="update"),
],
@@ -56,7 +55,6 @@
views.GroupAccountMembershipCreateByAccount.as_view(),
name="add_to_group",
),
- path("audit/", views.AccountAudit.as_view(), name="audit"),
],
"accounts",
)
@@ -132,18 +130,12 @@
views.ManagedGroupAutocomplete.as_view(),
name="autocomplete",
),
- path("audit/", views.ManagedGroupAudit.as_view(), name="audit"),
path(
"visualization/",
views.ManagedGroupVisualization.as_view(),
name="visualization",
),
path("/", views.ManagedGroupDetail.as_view(), name="detail"),
- path(
- "/audit/",
- views.ManagedGroupMembershipAudit.as_view(),
- name="audit_membership",
- ),
path("/delete", views.ManagedGroupDelete.as_view(), name="delete"),
path("/member_groups/", include(member_group_patterns)),
path("/member_accounts/", include(member_account_patterns)),
@@ -223,7 +215,6 @@
# views.WorkspaceClone.as_view(),
# name="clone",
# ),
- path("audit/", views.WorkspaceAudit.as_view(), name="audit"),
path(
"//delete/",
views.WorkspaceDelete.as_view(),
@@ -234,11 +225,6 @@
views.WorkspaceDetail.as_view(),
name="detail",
),
- path(
- "//audit/",
- views.WorkspaceSharingAudit.as_view(),
- name="audit_sharing",
- ),
path(
"//sharing/",
include(workspace_sharing_patterns),
@@ -303,4 +289,5 @@
path("group_group_membership/", include(group_group_membership_patterns)),
path("group_account_membership/", include(group_account_membership_patterns)),
path("workspace_group_sharing/", include(workspace_group_sharing_patterns)),
+ path("audit/", include("anvil_consortium_manager.auditor.urls")),
]
diff --git a/anvil_consortium_manager/viewmixins.py b/anvil_consortium_manager/viewmixins.py
index 2b1b33ce..17f3e304 100644
--- a/anvil_consortium_manager/viewmixins.py
+++ b/anvil_consortium_manager/viewmixins.py
@@ -4,9 +4,7 @@
import numpy as np
import plotly
import plotly.graph_objects as go
-from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
-from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.views.generic.base import ContextMixin
from django.views.generic.detail import SingleObjectMixin
@@ -15,40 +13,6 @@
from .adapters.account import get_account_adapter
from .adapters.managed_group import get_managed_group_adapter
from .adapters.workspace import workspace_adapter_registry
-from .audit import audit
-
-
-class AnVILAuditMixin:
- """Mixin to display AnVIL audit results."""
-
- audit_class = None
-
- def get_audit_instance(self):
- if not self.audit_class:
- raise ImproperlyConfigured(
- "%(cls)s is missing an audit class. Define %(cls)s.audit_class or override "
- "%(cls)s.get_audit_instance()." % {"cls": self.__class__.__name__}
- )
- else:
- return self.audit_class()
-
- def run_audit(self):
- self.audit_results = self.get_audit_instance()
- self.audit_results.run_audit()
-
- def get(self, request, *args, **kwargs):
- self.run_audit()
- return super().get(request, *args, **kwargs)
-
- def get_context_data(self, *args, **kwargs):
- """Add audit results to the context data."""
- context = super().get_context_data(*args, **kwargs)
- context["audit_timestamp"] = timezone.now()
- context["audit_ok"] = self.audit_results.ok()
- context["verified_table"] = audit.VerifiedTable(self.audit_results.get_verified_results())
- context["error_table"] = audit.ErrorTable(self.audit_results.get_error_results())
- context["not_in_app_table"] = audit.NotInAppTable(self.audit_results.get_not_in_app_results())
- return context
class AccountAdapterMixin:
diff --git a/anvil_consortium_manager/views.py b/anvil_consortium_manager/views.py
index 1b785b59..61240fac 100644
--- a/anvil_consortium_manager/views.py
+++ b/anvil_consortium_manager/views.py
@@ -19,7 +19,6 @@
from .adapters.account import get_account_adapter
from .adapters.workspace import workspace_adapter_registry
from .anvil_api import AnVILAPIClient, AnVILAPIError
-from .audit import audit
from .tokens import account_verification_token
@@ -140,13 +139,6 @@ def get_queryset(self):
return qs
-class BillingProjectAudit(auth.AnVILConsortiumManagerStaffViewRequired, viewmixins.AnVILAuditMixin, TemplateView):
- """View to run an audit on Workspaces and display the results."""
-
- template_name = "anvil_consortium_manager/billing_project_audit.html"
- audit_class = audit.BillingProjectAudit
-
-
class AccountDetail(
auth.AnVILConsortiumManagerStaffViewRequired,
viewmixins.SingleAccountMixin,
@@ -619,13 +611,6 @@ def get_queryset(self):
return qs
-class AccountAudit(auth.AnVILConsortiumManagerStaffViewRequired, viewmixins.AnVILAuditMixin, TemplateView):
- """View to run an audit on Accounts and display the results."""
-
- template_name = "anvil_consortium_manager/account_audit.html"
- audit_class = audit.AccountAudit
-
-
class AccountUnlinkUser(
auth.AnVILConsortiumManagerStaffEditRequired, viewmixins.SingleAccountMixin, SuccessMessageMixin, FormView
):
@@ -894,44 +879,6 @@ def get_queryset(self):
return qs
-class ManagedGroupAudit(auth.AnVILConsortiumManagerStaffViewRequired, viewmixins.AnVILAuditMixin, TemplateView):
- """View to run an audit on ManagedGroups and display the results."""
-
- template_name = "anvil_consortium_manager/managedgroup_audit.html"
- audit_class = audit.ManagedGroupAudit
-
-
-class ManagedGroupMembershipAudit(
- auth.AnVILConsortiumManagerStaffViewRequired,
- SingleObjectMixin,
- viewmixins.AnVILAuditMixin,
- TemplateView,
-):
- """View to run an audit on ManagedGroups and display the results."""
-
- model = models.ManagedGroup
- slug_field = "name"
- template_name = "anvil_consortium_manager/managedgroup_membership_audit.html"
- message_not_managed_by_app = "Cannot audit membership because group is not managed by this app."
-
- def get(self, request, *args, **kwargs):
- self.object = self.get_object()
- # Check if managed by the app.
- if not self.object.is_managed_by_app:
- messages.add_message(
- self.request,
- messages.ERROR,
- self.message_not_managed_by_app,
- )
- # Redirect to the object detail page.
- return HttpResponseRedirect(self.object.get_absolute_url())
- # Otherwise, return the response.
- return super().get(request, *args, **kwargs)
-
- def get_audit_instance(self):
- return audit.ManagedGroupMembershipAudit(self.object)
-
-
class WorkspaceLandingPage(
auth.AnVILConsortiumManagerViewRequired,
viewmixins.RegisteredWorkspaceAdaptersMixin,
@@ -1656,53 +1603,6 @@ def form_valid(self, form):
return response
-class WorkspaceAudit(auth.AnVILConsortiumManagerStaffViewRequired, viewmixins.AnVILAuditMixin, TemplateView):
- """View to run an audit on Workspaces and display the results."""
-
- template_name = "anvil_consortium_manager/workspace_audit.html"
- audit_class = audit.WorkspaceAudit
-
-
-class WorkspaceSharingAudit(
- auth.AnVILConsortiumManagerStaffViewRequired,
- SingleObjectMixin,
- viewmixins.AnVILAuditMixin,
- TemplateView,
-):
- """View to run an audit on access to a specific Workspace and display the results."""
-
- model = models.Workspace
- template_name = "anvil_consortium_manager/workspace_sharing_audit.html"
-
- def get_object(self, queryset=None):
- """Return the object the view is displaying."""
-
- # Use a custom queryset if provided; this is required for subclasses
- # like DateDetailView
- if queryset is None:
- queryset = self.get_queryset()
- # Filter the queryset based on kwargs.
- billing_project_slug = self.kwargs.get("billing_project_slug", None)
- workspace_slug = self.kwargs.get("workspace_slug", None)
- queryset = queryset.filter(billing_project__name=billing_project_slug, name=workspace_slug)
- try:
- # Get the single item from the filtered queryset
- obj = queryset.get()
- except queryset.model.DoesNotExist:
- raise Http404(
- _("No %(verbose_name)s found matching the query") % {"verbose_name": queryset.model._meta.verbose_name}
- )
- return obj
-
- def get(self, request, *args, **kwargs):
- self.object = self.get_object()
- # Otherwise, return the response.
- return super().get(request, *args, **kwargs)
-
- def get_audit_instance(self):
- return audit.WorkspaceSharingAudit(self.object)
-
-
class WorkspaceAutocomplete(auth.AnVILConsortiumManagerStaffViewRequired, autocomplete.Select2QuerySetView):
"""View to provide autocompletion for Workspaces.
diff --git a/docs/Makefile b/docs/Makefile
index b45e4001..65487b75 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -21,7 +21,7 @@ livehtml:
# Outputs rst files from django application code
apidocs:
- sphinx-apidoc -T -e -o $(SOURCEDIR)/api $(APP) $(APP)/tests $(APP)/migrations
+ sphinx-apidoc -T -e -o $(SOURCEDIR)/api $(APP) $(APP)/tests $(APP)/migrations $(APP)/auditor/tests
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
diff --git a/docs/api/anvil_consortium_manager.adapters.managed_group.rst b/docs/api/anvil_consortium_manager.adapters.managed_group.rst
new file mode 100644
index 00000000..16085d4a
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.adapters.managed_group.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.adapters.managed\_group module
+=========================================================
+
+.. automodule:: anvil_consortium_manager.adapters.managed_group
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.adapters.rst b/docs/api/anvil_consortium_manager.adapters.rst
index c1f2b6ef..db92b601 100644
--- a/docs/api/anvil_consortium_manager.adapters.rst
+++ b/docs/api/anvil_consortium_manager.adapters.rst
@@ -9,6 +9,7 @@ Submodules
anvil_consortium_manager.adapters.account
anvil_consortium_manager.adapters.default
+ anvil_consortium_manager.adapters.managed_group
anvil_consortium_manager.adapters.workspace
Module contents
diff --git a/docs/api/anvil_consortium_manager.app_settings.rst b/docs/api/anvil_consortium_manager.app_settings.rst
new file mode 100644
index 00000000..eeb9177e
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.app_settings.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.app\_settings module
+===============================================
+
+.. automodule:: anvil_consortium_manager.app_settings
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.audit.audit.rst b/docs/api/anvil_consortium_manager.audit.audit.rst
deleted file mode 100644
index 47ece9d3..00000000
--- a/docs/api/anvil_consortium_manager.audit.audit.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-anvil\_consortium\_manager.audit.audit module
-=============================================
-
-.. automodule:: anvil_consortium_manager.audit.audit
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.audit.rst b/docs/api/anvil_consortium_manager.audit.rst
deleted file mode 100644
index 68eab8fd..00000000
--- a/docs/api/anvil_consortium_manager.audit.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-anvil\_consortium\_manager.audit package
-========================================
-
-Submodules
-----------
-
-.. toctree::
- :maxdepth: 4
-
- anvil_consortium_manager.audit.audit
-
-Module contents
----------------
-
-.. automodule:: anvil_consortium_manager.audit
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.admin.rst b/docs/api/anvil_consortium_manager.auditor.admin.rst
new file mode 100644
index 00000000..6b9072d4
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.admin.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.admin module
+===============================================
+
+.. automodule:: anvil_consortium_manager.auditor.admin
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.apps.rst b/docs/api/anvil_consortium_manager.auditor.apps.rst
new file mode 100644
index 00000000..2efa95a6
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.apps.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.apps module
+==============================================
+
+.. automodule:: anvil_consortium_manager.auditor.apps
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.audit.accounts.rst b/docs/api/anvil_consortium_manager.auditor.audit.accounts.rst
new file mode 100644
index 00000000..ae4a8580
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.audit.accounts.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.audit.accounts module
+========================================================
+
+.. automodule:: anvil_consortium_manager.auditor.audit.accounts
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.audit.base.rst b/docs/api/anvil_consortium_manager.auditor.audit.base.rst
new file mode 100644
index 00000000..e55f0904
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.audit.base.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.audit.base module
+====================================================
+
+.. automodule:: anvil_consortium_manager.auditor.audit.base
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.audit.billing_projects.rst b/docs/api/anvil_consortium_manager.auditor.audit.billing_projects.rst
new file mode 100644
index 00000000..5e39d7d6
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.audit.billing_projects.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.audit.billing\_projects module
+=================================================================
+
+.. automodule:: anvil_consortium_manager.auditor.audit.billing_projects
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.audit.managed_groups.rst b/docs/api/anvil_consortium_manager.auditor.audit.managed_groups.rst
new file mode 100644
index 00000000..cba6a64a
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.audit.managed_groups.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.audit.managed\_groups module
+===============================================================
+
+.. automodule:: anvil_consortium_manager.auditor.audit.managed_groups
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.audit.rst b/docs/api/anvil_consortium_manager.auditor.audit.rst
new file mode 100644
index 00000000..8f6d5d83
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.audit.rst
@@ -0,0 +1,22 @@
+anvil\_consortium\_manager.auditor.audit package
+================================================
+
+Submodules
+----------
+
+.. toctree::
+ :maxdepth: 4
+
+ anvil_consortium_manager.auditor.audit.accounts
+ anvil_consortium_manager.auditor.audit.base
+ anvil_consortium_manager.auditor.audit.billing_projects
+ anvil_consortium_manager.auditor.audit.managed_groups
+ anvil_consortium_manager.auditor.audit.workspaces
+
+Module contents
+---------------
+
+.. automodule:: anvil_consortium_manager.auditor.audit
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.audit.workspaces.rst b/docs/api/anvil_consortium_manager.auditor.audit.workspaces.rst
new file mode 100644
index 00000000..bdd6099b
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.audit.workspaces.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.audit.workspaces module
+==========================================================
+
+.. automodule:: anvil_consortium_manager.auditor.audit.workspaces
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.forms.rst b/docs/api/anvil_consortium_manager.auditor.forms.rst
new file mode 100644
index 00000000..3cadca4b
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.forms.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.forms module
+===============================================
+
+.. automodule:: anvil_consortium_manager.auditor.forms
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.migrations.0001_initial.rst b/docs/api/anvil_consortium_manager.auditor.migrations.0001_initial.rst
new file mode 100644
index 00000000..b27a73f6
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.migrations.0001_initial.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.migrations.0001\_initial module
+==================================================================
+
+.. automodule:: anvil_consortium_manager.auditor.migrations.0001_initial
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.migrations.rst b/docs/api/anvil_consortium_manager.auditor.migrations.rst
new file mode 100644
index 00000000..7787b769
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.migrations.rst
@@ -0,0 +1,18 @@
+anvil\_consortium\_manager.auditor.migrations package
+=====================================================
+
+Submodules
+----------
+
+.. toctree::
+ :maxdepth: 4
+
+ anvil_consortium_manager.auditor.migrations.0001_initial
+
+Module contents
+---------------
+
+.. automodule:: anvil_consortium_manager.auditor.migrations
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.models.rst b/docs/api/anvil_consortium_manager.auditor.models.rst
new file mode 100644
index 00000000..69fcd9da
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.models.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.models module
+================================================
+
+.. automodule:: anvil_consortium_manager.auditor.models
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.rst b/docs/api/anvil_consortium_manager.auditor.rst
new file mode 100644
index 00000000..9e4874cc
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.rst
@@ -0,0 +1,34 @@
+anvil\_consortium\_manager.auditor package
+==========================================
+
+Subpackages
+-----------
+
+.. toctree::
+ :maxdepth: 4
+
+ anvil_consortium_manager.auditor.audit
+ anvil_consortium_manager.auditor.migrations
+
+Submodules
+----------
+
+.. toctree::
+ :maxdepth: 4
+
+ anvil_consortium_manager.auditor.admin
+ anvil_consortium_manager.auditor.apps
+ anvil_consortium_manager.auditor.forms
+ anvil_consortium_manager.auditor.models
+ anvil_consortium_manager.auditor.tables
+ anvil_consortium_manager.auditor.urls
+ anvil_consortium_manager.auditor.viewmixins
+ anvil_consortium_manager.auditor.views
+
+Module contents
+---------------
+
+.. automodule:: anvil_consortium_manager.auditor
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.tables.rst b/docs/api/anvil_consortium_manager.auditor.tables.rst
new file mode 100644
index 00000000..d94d0334
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.tables.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.tables module
+================================================
+
+.. automodule:: anvil_consortium_manager.auditor.tables
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.urls.rst b/docs/api/anvil_consortium_manager.auditor.urls.rst
new file mode 100644
index 00000000..03f80437
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.urls.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.urls module
+==============================================
+
+.. automodule:: anvil_consortium_manager.auditor.urls
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.viewmixins.rst b/docs/api/anvil_consortium_manager.auditor.viewmixins.rst
new file mode 100644
index 00000000..b0122c7a
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.viewmixins.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.viewmixins module
+====================================================
+
+.. automodule:: anvil_consortium_manager.auditor.viewmixins
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.auditor.views.rst b/docs/api/anvil_consortium_manager.auditor.views.rst
new file mode 100644
index 00000000..c6ee3fe1
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.auditor.views.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.auditor.views module
+===============================================
+
+.. automodule:: anvil_consortium_manager.auditor.views
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.management.commands.convert_mariadb_uuid_fields.rst b/docs/api/anvil_consortium_manager.management.commands.convert_mariadb_uuid_fields.rst
new file mode 100644
index 00000000..d524595b
--- /dev/null
+++ b/docs/api/anvil_consortium_manager.management.commands.convert_mariadb_uuid_fields.rst
@@ -0,0 +1,7 @@
+anvil\_consortium\_manager.management.commands.convert\_mariadb\_uuid\_fields module
+====================================================================================
+
+.. automodule:: anvil_consortium_manager.management.commands.convert_mariadb_uuid_fields
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.management.commands.rst b/docs/api/anvil_consortium_manager.management.commands.rst
index 9dc1e85c..f6a42daa 100644
--- a/docs/api/anvil_consortium_manager.management.commands.rst
+++ b/docs/api/anvil_consortium_manager.management.commands.rst
@@ -7,7 +7,7 @@ Submodules
.. toctree::
:maxdepth: 4
- anvil_consortium_manager.management.commands.run_anvil_audit
+ anvil_consortium_manager.management.commands.convert_mariadb_uuid_fields
Module contents
---------------
diff --git a/docs/api/anvil_consortium_manager.management.commands.run_anvil_audit.rst b/docs/api/anvil_consortium_manager.management.commands.run_anvil_audit.rst
deleted file mode 100644
index 7fb9ce47..00000000
--- a/docs/api/anvil_consortium_manager.management.commands.run_anvil_audit.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-anvil\_consortium\_manager.management.commands.run\_anvil\_audit module
-=======================================================================
-
-.. automodule:: anvil_consortium_manager.management.commands.run_anvil_audit
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/anvil_consortium_manager.rst b/docs/api/anvil_consortium_manager.rst
index 139e6671..f9238712 100644
--- a/docs/api/anvil_consortium_manager.rst
+++ b/docs/api/anvil_consortium_manager.rst
@@ -8,7 +8,7 @@ Subpackages
:maxdepth: 4
anvil_consortium_manager.adapters
- anvil_consortium_manager.audit
+ anvil_consortium_manager.auditor
anvil_consortium_manager.management
Submodules
@@ -19,6 +19,7 @@ Submodules
anvil_consortium_manager.admin
anvil_consortium_manager.anvil_api
+ anvil_consortium_manager.app_settings
anvil_consortium_manager.apps
anvil_consortium_manager.auth
anvil_consortium_manager.exceptions
diff --git a/docs/auditing.rst b/docs/auditing.rst
index 9f4bf390..c68ca3f8 100644
--- a/docs/auditing.rst
+++ b/docs/auditing.rst
@@ -3,19 +3,19 @@
Auditing information in the app
===============================
-Periodically, you should verify that the information in the app matches what's actually on AnVIL.
-The app provides a number of model methods, typically named `anvil_audit`, to help with this auditing, as well as objects that store the results of an audit.
+The :mod:`~anvil_consortium_manager.auditor` app provides functionality to audit information stored in the app against what is actually on AnVIL.
Audit results classes
---------------------
-Results from an audit are returned as an object that is a subclass of :class:`~anvil_consortium_manager.anvil_audit.AnVILAuditResults`.
-The subclasses have a method :meth:`~anvil_consortium_manager.anvil_audit.AnVILAuditResults.ok` that indicates if the audit was successful or if any errors were detected.
-It also can list the set of model instances in the app that were audited against AnVIL using :meth:`~anvil_consortium_manager.anvil_audit.AnVILAuditResults.get_verified`;
-a dictionary of model instances with detected errors and the errors themselves using :meth:`~anvil_consortium_manager.anvil_audit.AnVILAuditResults.get_errors`;
-and the set of records that exist on AnVIL but are not in the app using :meth:`~anvil_consortium_manager.anvil_audit.AnVILAuditResults.get_not_in_app`.
+Results from an audit are returned as an object that is a subclass of :class:`~anvil_consortium_manager.auditor.audit.base.AnVILAudit`.
+The subclasses have a method :meth:`~anvil_consortium_manager.auditor.audit.base.AnVILAudit.ok` that indicates if the audit was successful or if any errors were detected.
+It also can list the set of model instances in the app that were audited against AnVIL using :meth:`~anvil_consortium_manager.auditor.audit.base.AnVILAudit.get_verified_results`;
+a dictionary of model instances with detected errors and the errors themselves using :meth:`~anvil_consortium_manager.auditor.audit.base.AnVILAudit.get_error_results`;
+and the set of records that exist on AnVIL but are not in the app using :meth:`~anvil_consortium_manager.auditor.audit.base.AnVILAudit.get_not_in_app_results`;
+and any "not in app" records that have been marked as ignored :meth:`~anvil_consortium_manager.auditor.audit.base.AnVILAudit.get_ignored_results`.
-Different models check different things and have different potential errors.
+Audits for different models check different things and will report different potential errors.
Model-specific auditing
@@ -24,8 +24,7 @@ Model-specific auditing
Billing project auditing
~~~~~~~~~~~~~~~~~~~~~~~~
-The :class:`~anvil_consortium_manager.models.BillingProject` model provides a class method :meth:`~anvil_consortium_manager.models.BillingProject.anvil_audit` that runs on all :class:`~anvil_consortium_manager.models.BillingProject` model instances in the app.
-This method runs the following checks:
+The :class:`~anvil_consortium_manager.auditor.audit.billing_projects.BillingProjectAudit` class can be used to audit all :class:`~anvil_consortium_manager.models.BillingProject` model instances in the app. It runs the following checks:
1. All :class:`~anvil_consortium_manager.models.BillingProject` model instances in the app also exist on AnVIL.
@@ -35,8 +34,7 @@ It does not check if there are Billing Projects on AnVIL that don't have a recor
Account auditing
~~~~~~~~~~~~~~~~
-The :class:`~anvil_consortium_manager.models.Account` model provides a class method :meth:`~anvil_consortium_manager.models.Account.anvil_audit` that runs on all :class:`~anvil_consortium_manager.models.Account` model instances in the app.
-This method runs the following checks:
+The :class:`~anvil_consortium_manager.auditor.audit.accounts.AccountAudit` class can be used to audit all :class:`~anvil_consortium_manager.models.Account` model instances in the app. It runs the following checks:
1. All :class:`~anvil_consortium_manager.models.Account` model instances in the app also exist on AnVIL.
@@ -45,16 +43,14 @@ It does not check if there are Accounts on AnVIL that don't have a record in the
Managed Group auditing
~~~~~~~~~~~~~~~~~~~~~~
-The :class:`~anvil_consortium_manager.models.ManagedGroup` model provides two options for auditing: an instance method :meth:`~anvil_consortium_manager.models.ManagedGroup.anvil_audit` to check membership for a single :class:`~anvil_consortium_manager.models.ManagedGroup`, and a class method :meth:`~anvil_consortium_manager.models.ManagedGroup.anvil_audit` that runs on all :class:`~anvil_consortium_manager.models.ManagedGroup` model instances in the app.
-
-The :meth:`~anvil_consortium_manager.models.ManagedGroup.anvil_audit_membership` method runs the following checks:
+The :class:`~anvil_consortium_manager.auditor.audit.managed_groups.ManagedGroupAudit` class can be used to audit all :class:`~anvil_consortium_manager.models.ManagedGroup` model instances in the app. It runs the following checks:
1. All :class:`~anvil_consortium_manager.models.ManagedGroup` model instances in the app also exist on AnVIL.
2. The service account running the app has the same role (admin vs member) in the app as on AnVIL.
- 3. The membership of each group in the app matches the membership on AnVIL (using :meth:`~anvil_consortium_manager.models.ManagedGroup.anvil_audit_membership` method for each ManagedGroup).
+ 3. The membership of each group in the app matches the membership on AnVIL (by running an :class:`~anvil_consortium_manager.auditor.audit.managed_groups.ManagedGroupMembershipAudit` audit for each ManagedGroup).
4. No groups that have the app service account as an Admin exist on AnVIL.
-The :meth:`~anvil_consortium_manager.models.ManagedGroup.anvil_audit_membership` method runs the following checks for a single :class:`~anvil_consortium_manager.models.ManagedGroup` instance:
+Membership auditing for a single group can be done using the :class:`~anvil_consortium_manager.auditor.audit.managed_groups.ManagedGroupMembershipAudit` class. This class performs the following checks:
1. All account members of this :class:`~anvil_consortium_manager.models.ManagedGroup` in the app are also members in AnVIL.
2. All account admin of this :class:`~anvil_consortium_manager.models.ManagedGroup` in the app are also admin in AnVIL.
@@ -64,30 +60,35 @@ The :meth:`~anvil_consortium_manager.models.ManagedGroup.anvil_audit_membership`
6. All members in AnVIL are also recorded in the app.
+If desired, specific membership records can be ignored by creating an :class:`~anvil_consortium_manager.auditor.models.IgnoredManagedGroupMembership` instance in the app.
+Ignored records will be included in the audit results, but will not be considered errors.
+
+
Workspace auditing
~~~~~~~~~~~~~~~~~~
-As for ManagedGroups, the :class:`~anvil_consortium_manager.models.Workspace` model provides two options for auditing: an instance method :meth:`~anvil_consortium_manager.models.Workspace.anvil_audit` to check access for a single :class:`~anvil_consortium_manager.models.Workspace`, and a class method :meth:`~anvil_consortium_manager.models.Workspace.anvil_audit` that runs on all :class:`~anvil_consortium_manager.models.Workspace` model instances in the app.
-The :meth:`~anvil_consortium_manager.models.Workspace.anvil_audit` method runs the following checks:
+
+The :class:`~anvil_consortium_manager.auditor.audit.workspaces.WorkspaceAudit` class can be used to audit all :class:`~anvil_consortium_manager.models.Workspace` model instances in the app. It runs the following checks:
1. All :class:`~anvil_consortium_manager.models.Workspace` model instances in the app also exist on AnVIL.
2. The service account running the app is an owner on AnVIL of all the :class:`~anvil_consortium_manager.models.Workspace` model instances.
3. The :class:`~anvil_consortium_manager.models.Workspace` has the same authorization domains in the app as on AnVIL.
- 4. The access to each :class:`~anvil_consortium_manager.models.Workspace` in the app matches the access on AnVIL (using :meth:`~anvil_consortium_manager.models.Workspace.anvil_audit_access` method for each Workspace).
+ 4. The access to each :class:`~anvil_consortium_manager.models.Workspace` in the app matches the access on AnVIL (by running an :class:`~anvil_consortium_manager.auditor.audit.workspaces.WorkspaceSharingAudit` audit for each Workspace).
5. No workspaces that have the app service account as an owner exist on AnVIL.
6. The workspace ``is_locked`` status matches AnVIL.
7. The workspace ``is_requester_pays`` status matches AnVIL.
-The :meth:`~anvil_consortium_manager.models.Workspace.anvil_audit_membership` method runs the following checks for a single :class:`~anvil_consortium_manager.models.Workspace` instance:
+Sharing for a workspace can be audited using the :class:`~anvil_consortium_manager.auditor.audit.workspaces.WorkspaceSharingAudit` class. This class performs the following checks:
1. All groups that have access in the app also have access in AnVIL.
2. Each :class:`~anvil_consortium_manager.models.ManagedGroup` that has access in the app has the same access in AnVIL.
3. The :attr:`~anvil_consortium_manager.models.WorkspaceGroupSharing.can_compute` value is the same in the app and on AnVIL.
- 4. The :attr:`~anvil_consortium_manager.models.WorkspaceGroupSharing.can_share` value is the same in the app and on AnVIL.
+ 4. The ``can_share`` value is as expected on AnVIL based on the group's ``role``.
5. No groups or accounts on AnVIL have access to the workspace that are not recorded in the app.
+
Running audits
--------------
@@ -96,15 +97,15 @@ Auditing views
The app provides a number of views for auditing various models.
- - :class:`~anvil_consortium_manager.models.BillingProject`: :class:`~anvil_consortium_manager.views.BillingProjectAudit` (accessible from default navbar)
- - :class:`~anvil_consortium_manager.models.Account`: :class:`~anvil_consortium_manager.views.AccountAudit` (accessible from default navbar)
- - :class:`~anvil_consortium_manager.models.ManagedGroup`: :class:`~anvil_consortium_manager.views.ManagedGroupAudit` (accessible from default navbar)
- - :class:`~anvil_consortium_manager.models.Workspace`: :class:`~anvil_consortium_manager.views.WorkspaceAudit` (accessible from default navbar)
+ - :class:`~anvil_consortium_manager.models.BillingProject`: :class:`~anvil_consortium_manager.auditor.views.BillingProjectAudit` (accessible from default navbar)
+ - :class:`~anvil_consortium_manager.models.Account`: :class:`~anvil_consortium_manager.views.auditor.accounts.AccountAudit` (accessible from default navbar)
+ - :class:`~anvil_consortium_manager.models.ManagedGroup`: :class:`~anvil_consortium_manager.views.auditor.managed_groups.ManagedGroupAudit` (accessible from default navbar)
+ - :class:`~anvil_consortium_manager.models.Workspace`: :class:`~anvil_consortium_manager.views.auditor.workspaces.WorkspaceAudit` (accessible from default navbar)
Workspaces and ManagedGroups have additional audit views that can audit the sharing and membership, respectively.
-- :class:`~anvil_consortium_manager.models.ManagedGroup` membership: :class:`~anvil_consortium_manager.views.ManagedGroupMembershipAudit` (accessible from Managed Group detail page)
-- :class:`~anvil_consortium_manager.models.Workspace` sharing: :class:`~anvil_consortium_manager.views.WorkspaceSharingAudit` (accessible from the Workspace detail page)
+- :class:`~anvil_consortium_manager.models.ManagedGroup` membership: :class:`~anvil_consortium_manager.auditor.views.ManagedGroupMembershipAudit` (accessible from Managed Group detail page)
+- :class:`~anvil_consortium_manager.models.Workspace` sharing: :class:`~anvil_consortium_manager.auditor.views.WorkspaceSharingAudit` (accessible from the Workspace detail page)
Auditing via management command
@@ -127,3 +128,8 @@ Here are some examples of calling this command:
python manage.py run_anvil_audit --models BillingProject Account
More information can be found in the help for ``run_anvil_audit``.
+
+.. code-block:: bash
+
+ # To audit all models and print a report to the terminal.
+ python manage.py run_anvil_audit --help
diff --git a/docs/index.rst b/docs/index.rst
index c667aa9a..76f0d007 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -20,7 +20,7 @@ Welcome to django-anvil-consortium-manager's documentation!
management_commands
.. toctree::
- :maxdepth: 6
+ :maxdepth: 3
:caption: Reference
api/anvil_consortium_manager
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index 05b7a337..95625da6 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -34,6 +34,8 @@ Required Settings
# This app:
"anvil_consortium_manager",
+ # The associated app for auditing information against AnVIL (required):
+ "anvil_consortium_manager.auditor",
]
2. Set the ``ANVIL_API_SERVICE_ACCOUNT_FILE`` setting to the path to the service account credentials file.
diff --git a/example_site/settings.py b/example_site/settings.py
index b2552e62..432e32bc 100644
--- a/example_site/settings.py
+++ b/example_site/settings.py
@@ -92,6 +92,7 @@
"django_filters",
# This app.
"anvil_consortium_manager",
+ "anvil_consortium_manager.auditor",
# Autocomplete.
# note these are supposed to come before django.contrib.admin.
"dal",
diff --git a/pyproject.toml b/pyproject.toml
index 695ecd53..0bf13423 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -90,7 +90,7 @@ exclude_lines = ["@abstractproperty"]
[tool.pytest.ini_options]
addopts = "--ignore=anvil_consortium_manager/tests/test_app --ds=anvil_consortium_manager.tests.settings.test"
-python_files = ["anvil_consortium_manager/tests/test*.py"]
+python_files = ["anvil_consortium_manager/tests/test*.py", "anvil_consortium_manager/auditor/tests/test*.py"]
python_classes = ["!TestWorkspaceDataFactory"]
# HATCH