diff --git a/gracedb/api/v1/superevents/tests/test_access.py b/gracedb/api/v1/superevents/tests/test_access.py
index d15824859a072e728968aa7871665afe974de6cf..82a1cbd45daa0e3eb558c4572bce23536918a22e 100644
--- a/gracedb/api/v1/superevents/tests/test_access.py
+++ b/gracedb/api/v1/superevents/tests/test_access.py
@@ -40,7 +40,7 @@ class TestSupereventListGet(SupereventSetup, GraceDbApiTestBase):
         super(TestSupereventListGet, cls).setUpClass()
         cls.url = v_reverse('superevents:superevent-list')
 
-    def test_internal_user(self):
+    def test_internal_user_get_superevent_list(self):
         """Internal user sees all superevents"""
         response = self.request_as_user(self.url, "GET", self.internal_user)
         self.assertEqual(response.status_code, 200)
@@ -48,11 +48,11 @@ class TestSupereventListGet(SupereventSetup, GraceDbApiTestBase):
         # Check response data - should have both superevents
         data = response.data
         superevent_ids = [s['superevent_id'] for s in data['superevents']]
-        self.assertEqual(len(data['superevents']), 2)
+        self.assertEqual(len(data['superevents']), 3)
         for s in Superevent.objects.all():
             self.assertIn(s.superevent_id, superevent_ids)
 
-    def test_lvem_user(self):
+    def test_lvem_user_get_superevent_list(self):
         """LV-EM user sees only permitted superevents"""
         # Note that object permissions were already assigned
         # Get response and check code
@@ -62,15 +62,26 @@ class TestSupereventListGet(SupereventSetup, GraceDbApiTestBase):
         # Check response data - should have only lvem superevent
         data = response.data
         superevent_ids = [s['superevent_id'] for s in data['superevents']]
-        self.assertEqual(len(data['superevents']), 1)
+        self.assertEqual(len(data['superevents']), 2)
         self.assertNotIn(self.internal_superevent.superevent_id,
             superevent_ids)
         self.assertIn(self.lvem_superevent.superevent_id, superevent_ids)
+        self.assertIn(self.public_superevent.superevent_id, superevent_ids)
 
-    def test_public_user(self):
+    def test_public_user_get_superevent_list(self):
         """Public user sees only permitted superevents"""
-        # NOTE: this will change in the near future
-        pass
+        response = self.request_as_user(self.url, "GET")
+        self.assertEqual(response.status_code, 200)
+
+        # Check response data - should have only public superevent
+        data = response.data
+        superevent_ids = [s['superevent_id'] for s in data['superevents']]
+        self.assertEqual(len(data['superevents']), 1)
+        self.assertNotIn(self.internal_superevent.superevent_id,
+            superevent_ids)
+        self.assertNotIn(self.lvem_superevent.superevent_id,
+            superevent_ids)
+        self.assertIn(self.public_superevent.superevent_id, superevent_ids)
 
 
 class TestSupereventListPost(SupereventManagersGroupAndUserSetup,
@@ -79,7 +90,7 @@ class TestSupereventListPost(SupereventManagersGroupAndUserSetup,
     Tests permissions for POST requests to the superevents list (i.e.,
     superevent creation) for internal, LV-EM, and public users.  Also
     tests permissions for different superevent categories (production,
-    test, MDC).  Note also tha there are separate tests for "basic" internal
+    test, MDC).  Note also that there are separate tests for "basic" internal
     users and "privileged" internal users, since "basic" internal users
     should only be able to create Test superevents.
     """
@@ -123,21 +134,21 @@ class TestSupereventListPost(SupereventManagersGroupAndUserSetup,
         super(TestSupereventListPost, cls).setUpClass()
         cls.url = v_reverse('superevents:superevent-list')
 
-    def test_basic_internal_production(self):
+    def test_basic_internal_user_create_production_superevent(self):
         """Basic internal user can't create a production superevent"""
         # Send response
         response = self.request_as_user(self.url, "POST", self.internal_user,
             data=self.production_superevent_data)
         self.assertEqual(response.status_code, 403)
 
-    def test_basic_internal_mdc(self):
+    def test_basic_internal_user_create_mdc_superevent(self):
         """Basic internal user can't create an MDC superevent"""
         # Send response
         response = self.request_as_user(self.url, "POST", self.internal_user,
             data=self.mdc_superevent_data)
         self.assertEqual(response.status_code, 403)
 
-    def test_basic_internal_test(self):
+    def test_basic_internal_user_create_test_superevent(self):
         """Basic internal user can create a Test superevent"""
         # Send response
         response = self.request_as_user(self.url, "POST", self.internal_user,
@@ -150,7 +161,7 @@ class TestSupereventListPost(SupereventManagersGroupAndUserSetup,
         self.assertEqual(response.data['t_0'],
             self.test_superevent_data['t_0'])
 
-    def test_privileged_internal_production(self):
+    def test_privileged_internal_user_create_production_superevent(self):
         """Privileged internal user can create a production superevent"""
         # Send response
         response = self.request_as_user(self.url, "POST", self.sm_user,
@@ -163,7 +174,7 @@ class TestSupereventListPost(SupereventManagersGroupAndUserSetup,
         self.assertEqual(response.data['t_0'],
             self.production_superevent_data['t_0'])
 
-    def test_privileged_internal_mdc(self):
+    def test_privileged_internal_user_create_mdc_superevent(self):
         """Privileged internal user can create an MDC superevent"""
         # Send response
         response = self.request_as_user(self.url, "POST", self.sm_user,
@@ -176,7 +187,7 @@ class TestSupereventListPost(SupereventManagersGroupAndUserSetup,
         self.assertEqual(response.data['t_0'],
             self.mdc_superevent_data['t_0'])
 
-    def test_privileged_internal_test(self):
+    def test_privileged_internal_user_create_test_superevent(self):
         """Privileged internal user can create a test superevent"""
         # Send response
         response = self.request_as_user(self.url, "POST", self.sm_user,
@@ -189,7 +200,7 @@ class TestSupereventListPost(SupereventManagersGroupAndUserSetup,
         self.assertEqual(response.data['t_0'],
             self.test_superevent_data['t_0'])
 
-    def test_lvem_user(self):
+    def test_lvem_user_create_superevents(self):
         """LV-EM user can't create any superevents"""
         # Production
         response = self.request_as_user(self.url, "POST", self.lvem_user,
@@ -211,7 +222,7 @@ class TestSupereventListPost(SupereventManagersGroupAndUserSetup,
             data={"data": "NOTHING"})
         self.assertEqual(response.status_code, 403)
 
-    def test_public_user(self):
+    def test_public_user_create_superevents(self):
         """Public user can't create any superevents"""
         # Production
         response = self.request_as_user(self.url, "POST",
@@ -242,7 +253,7 @@ class TestSupereventDetail(SupereventSetup, GraceDbApiTestBase):
     required permissions are category-dependent.
     """
 
-    def test_internal_get(self):
+    def test_internal_get_superevent_detail(self):
         """Internal user can get all superevent details"""
         for s in Superevent.objects.all():
             # Set up URL
@@ -255,7 +266,7 @@ class TestSupereventDetail(SupereventSetup, GraceDbApiTestBase):
             # Check data on page
             self.assertEqual(response.data['superevent_id'], s.superevent_id)
 
-    def test_lvem_get_no_view_perms(self):
+    def test_lvem_get_superevent_detail_no_view_perms(self):
         """
         LV-EM user cannot GET this superevent since object permissions
         have not been added
@@ -269,7 +280,7 @@ class TestSupereventDetail(SupereventSetup, GraceDbApiTestBase):
         # not included in the queryset due to filtering
         self.assertEqual(response.status_code, 404)
 
-    def test_lvem_get_with_perms(self):
+    def test_lvem_get_superevent_detail_with_view_perms(self):
         """
         LV-EM user can GET this superevent since permissions have been added
         """
@@ -283,21 +294,40 @@ class TestSupereventDetail(SupereventSetup, GraceDbApiTestBase):
         self.assertEqual(response.data['superevent_id'],
             self.lvem_superevent.superevent_id)
 
-    def test_public_get_no_view_perms(self):
+    def test_public_get_superevent_detail_no_view_perms(self):
         """
         Public user cannot GET this URL since no permissions have been added
         """
-        # TODO
-        pass
+        # Set up URL
+        url = v_reverse('superevents:superevent-detail',
+            args=[self.internal_superevent.superevent_id])
+        # Get response and check code
+        response = self.request_as_user(url, "GET")
+        # Expect 404 response instead of 403, since this superevent is just
+        # not included in the queryset due to filtering
+        self.assertEqual(response.status_code, 404)
+
+        # Repeat with lvem superevent
+        url = v_reverse('superevents:superevent-detail',
+            args=[self.lvem_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 404)
 
-    def test_public_get_with_perms(self):
+    def test_public_get_superevent_detail_with_view_perms(self):
         """
         Public user can GET this URL since permissions have been added
         """
-        # TODO: public access
-        pass
+        # Set up URL
+        url = v_reverse('superevents:superevent-detail',
+            args=[self.public_superevent.superevent_id])
+        # Get response and check code
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 200)
+        # Check data
+        self.assertEqual(response.data['superevent_id'],
+            self.public_superevent.superevent_id)
 
-    def test_basic_internal_patch_production(self):
+    def test_basic_internal_patch_production_superevent(self):
         """Basic internal user can't update production superevents"""
         # Define url, make request, and check response
         url = v_reverse('superevents:superevent-detail',
@@ -306,7 +336,7 @@ class TestSupereventDetail(SupereventSetup, GraceDbApiTestBase):
             data={'t_0': 1234})
         self.assertEqual(response.status_code, 403)
 
-    def test_basic_internal_patch_mdc(self):
+    def test_basic_internal_patch_mdc_superevent(self):
         """Basic internal user can't update MDC superevents"""
         # Create mdc superevent
         s = self.create_superevent(self.internal_user, event_search='MDC',
@@ -318,7 +348,7 @@ class TestSupereventDetail(SupereventSetup, GraceDbApiTestBase):
             data={'t_0': 1234})
         self.assertEqual(response.status_code, 403)
 
-    def test_basic_internal_patch_test(self):
+    def test_basic_internal_patch_test_superevent(self):
         """Basic internal user can update test superevents"""
         # Create test superevent
         s = self.create_superevent(self.internal_user, event_group='Test',
@@ -331,6 +361,52 @@ class TestSupereventDetail(SupereventSetup, GraceDbApiTestBase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.data['t_0'], 1234)
 
+    def test_lvem_patch_superevents(self):
+        """LV-EM user can't update superevents"""
+        # Try to patch internal-only superevent
+        url = v_reverse('superevents:superevent-detail',
+            args=[self.internal_superevent.superevent_id])
+        response = self.request_as_user(url, "PATCH", self.lvem_user,
+            data={'t_0': 1234})
+        self.assertEqual(response.status_code, 404)
+
+        # Try to patch lv-em superevent
+        url = v_reverse('superevents:superevent-detail',
+            args=[self.lvem_superevent.superevent_id])
+        response = self.request_as_user(url, "PATCH", self.lvem_user,
+            data={'t_0': 1234})
+        self.assertEqual(response.status_code, 403)
+
+        # Try to patch public superevent
+        url = v_reverse('superevents:superevent-detail',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "PATCH", self.lvem_user,
+            data={'t_0': 1234})
+        self.assertEqual(response.status_code, 403)
+
+    def test_public_patch_superevents(self):
+        """Public user can't update superevents"""
+        # Try to patch internal-only superevent
+        url = v_reverse('superevents:superevent-detail',
+            args=[self.internal_superevent.superevent_id])
+        response = self.request_as_user(url, "PATCH", data={'t_0': 1234})
+        # Response code is 403: since user isn't authenticated, non-safe
+        # HTTP methods are rejected before we even try to filter the
+        # superevent queryset for the user, which would give a 404
+        self.assertEqual(response.status_code, 403)
+
+        # Try to patch lvem superevent
+        url = v_reverse('superevents:superevent-detail',
+            args=[self.lvem_superevent.superevent_id])
+        response = self.request_as_user(url, "PATCH", data={'t_0': 1234})
+        self.assertEqual(response.status_code, 403)
+
+        # Try to patch public superevent
+        url = v_reverse('superevents:superevent-detail',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "PATCH", data={'t_0': 1234})
+        self.assertEqual(response.status_code, 403)
+
 
 class TestSupereventConfirmAsGw(SupereventManagersGroupAndUserSetup,
     SupereventCreateMixin, GraceDbApiTestBase):
@@ -360,21 +436,21 @@ class TestSupereventConfirmAsGw(SupereventManagersGroupAndUserSetup,
         cls.mdc_superevent = cls.create_superevent(cls.sm_user,
             event_search='MDC', category=Superevent.SUPEREVENT_CATEGORY_MDC)
 
-    def test_basic_internal_user_confirm_production(self):
+    def test_basic_internal_user_confirm_production_superevent(self):
         """Basic internal user can't confirm production superevent as GW"""
         url = v_reverse('superevents:superevent-confirm-as-gw',
             args=[self.production_superevent.superevent_id])
         response = self.request_as_user(url, "POST", self.internal_user)
         self.assertEqual(response.status_code, 403)
 
-    def test_basic_internal_user_confirm_mdc(self):
+    def test_basic_internal_user_confirm_mdc_superevent(self):
         """Basic internal user can't confirm MDC superevent as GW"""
         url = v_reverse('superevents:superevent-confirm-as-gw',
             args=[self.mdc_superevent.superevent_id])
         response = self.request_as_user(url, "POST", self.internal_user)
         self.assertEqual(response.status_code, 403)
 
-    def test_basic_internal_user_confirm_test(self):
+    def test_basic_internal_user_confirm_test_superevent(self):
         """Basic internal user can confirm test superevent as GW"""
         url = v_reverse('superevents:superevent-confirm-as-gw',
             args=[self.test_superevent.superevent_id])
@@ -384,7 +460,7 @@ class TestSupereventConfirmAsGw(SupereventManagersGroupAndUserSetup,
         # Check data
         self.assertTrue('GW' in response.data['superevent_id'])
 
-    def test_privileged_internal_user_confirm_production(self):
+    def test_privileged_internal_user_confirm_production_superevent(self):
         """Privileged internal user can confirm production superevent as GW"""
         url = v_reverse('superevents:superevent-confirm-as-gw',
             args=[self.production_superevent.superevent_id])
@@ -394,7 +470,7 @@ class TestSupereventConfirmAsGw(SupereventManagersGroupAndUserSetup,
         # Check data
         self.assertTrue('GW' in response.data['superevent_id'])
 
-    def test_privileged_internal_user_confirm_mdc(self):
+    def test_privileged_internal_user_confirm_mdc_superevent(self):
         """Privileged internal user can confirm MDC superevent as GW"""
         url = v_reverse('superevents:superevent-confirm-as-gw',
             args=[self.mdc_superevent.superevent_id])
@@ -404,7 +480,7 @@ class TestSupereventConfirmAsGw(SupereventManagersGroupAndUserSetup,
         # Check data
         self.assertTrue('GW' in response.data['superevent_id'])
 
-    def test_privileged_internal_user_confirm_test(self):
+    def test_privileged_internal_user_confirm_test_superevent(self):
         """Privileged internal user can confirm test superevent as GW"""
         url = v_reverse('superevents:superevent-confirm-as-gw',
             args=[self.test_superevent.superevent_id])
@@ -414,7 +490,7 @@ class TestSupereventConfirmAsGw(SupereventManagersGroupAndUserSetup,
         # Check data
         self.assertTrue('GW' in response.data['superevent_id'])
 
-    def test_lvem_user_confirm_production(self):
+    def test_lvem_user_confirm_production_superevent(self):
         """LV-EM user can't confirm production superevent as GW"""
         url = v_reverse('superevents:superevent-confirm-as-gw',
             args=[self.production_superevent.superevent_id])
@@ -428,8 +504,10 @@ class TestSupereventConfirmAsGw(SupereventManagersGroupAndUserSetup,
             obj=self.production_superevent)
         response = self.request_as_user(url, "POST", self.lvem_user)
         self.assertEqual(response.status_code, 403)
+        self.assertIn('not allowed to confirm superevents as GWs',
+            response.content)
 
-    def test_lvem_user_confirm_mdc(self):
+    def test_lvem_user_confirm_mdc_superevent(self):
         """LV-EM user can't confirm mdc superevent as GW"""
         url = v_reverse('superevents:superevent-confirm-as-gw',
             args=[self.mdc_superevent.superevent_id])
@@ -443,8 +521,10 @@ class TestSupereventConfirmAsGw(SupereventManagersGroupAndUserSetup,
             obj=self.mdc_superevent)
         response = self.request_as_user(url, "POST", self.lvem_user)
         self.assertEqual(response.status_code, 403)
+        self.assertIn('not allowed to confirm MDC superevents as GWs',
+            response.content)
 
-    def test_lvem_user_confirm_test(self):
+    def test_lvem_user_confirm_test_superevent(self):
         """LV-EM user can't confirm test superevent as GW"""
         url = v_reverse('superevents:superevent-confirm-as-gw',
             args=[self.test_superevent.superevent_id])
@@ -458,17 +538,29 @@ class TestSupereventConfirmAsGw(SupereventManagersGroupAndUserSetup,
             obj=self.test_superevent)
         response = self.request_as_user(url, "POST", self.lvem_user)
         self.assertEqual(response.status_code, 403)
+        self.assertIn('not allowed to confirm test superevents as GWs',
+            response.content)
 
-    def test_public_user_confirm(self):
+    def test_public_user_confirm_superevents(self):
         """Public user can't confirm any superevents as GWs"""
-        # TODO: this will have to be updated in the future
         superevents = [self.production_superevent, self.test_superevent,
             self.mdc_superevent]
         for s in superevents:
+            # We get 403 because credentials are checked before any
+            # superevent queryset filtering for the user
             url = v_reverse('superevents:superevent-confirm-as-gw',
                 args=[s.superevent_id])
             response = self.request_as_user(url, "POST")
             self.assertEqual(response.status_code, 403)
+            self.assertIn('Authentication credentials', response.content)
+
+            # Expose it, should still get same 403 since the permission
+            # checking process still fails at the outset
+            assign_perm('superevents.view_superevent', self.public_group,
+                obj=self.test_superevent)
+            response = self.request_as_user(url, "POST")
+            self.assertEqual(response.status_code, 403)
+            self.assertIn('Authentication credentials', response.content)
 
 
 class TestSupereventLabelList(SupereventSetup, GraceDbApiTestBase):
@@ -492,8 +584,10 @@ class TestSupereventLabelList(SupereventSetup, GraceDbApiTestBase):
             cls.internal_superevent, label=l2)
         Labelling.objects.create(creator=cls.internal_user, superevent=
             cls.lvem_superevent, label=l1)
+        Labelling.objects.create(creator=cls.internal_user, superevent=
+            cls.public_superevent, label=l2)
 
-    def test_internal_get(self):
+    def test_internal_user_get_label_list(self):
         """Internal user sees all labels for all superevents"""
         for s in Superevent.objects.all():
             # Set up URL
@@ -510,7 +604,7 @@ class TestSupereventLabelList(SupereventSetup, GraceDbApiTestBase):
             for l in s.labels.all():
                 self.assertIn(l.name, label_list)
 
-    def test_lvem_get_no_view_perms(self):
+    def test_lvem_user_get_label_list_no_view_perms(self):
         """LV-EM user can't see labels for internal-only superevent"""
         url = v_reverse('superevents:superevent-label-list',
             args=[self.internal_superevent.superevent_id])
@@ -519,8 +613,9 @@ class TestSupereventLabelList(SupereventSetup, GraceDbApiTestBase):
         # should be excluded from queryset
         self.assertEqual(response.status_code, 404)
 
-    def test_lvem_get_with_view_perms(self):
-        """LV-EM user can see labels for exposed superevent"""
+    def test_lvem_user_get_label_list_with_view_perms(self):
+        """LV-EM user can see labels for exposed superevents"""
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-label-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.lvem_user)
@@ -532,31 +627,51 @@ class TestSupereventLabelList(SupereventSetup, GraceDbApiTestBase):
         for l in self.lvem_superevent.labels.all():
             self.assertIn(l.name, api_label_names)
 
-    def test_public_get_no_view_perms(self):
+        # Public superevent
+        url = v_reverse('superevents:superevent-label-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET", self.lvem_user)
+        self.assertEqual(response.status_code, 200)
+        # Check response data - should have all labels
+        data = response.data
+        self.assertEqual(len(data['labels']), 1)
+        api_label_names = [l['name'] for l in data['labels']]
+        for l in self.public_superevent.labels.all():
+            self.assertIn(l.name, api_label_names)
+
+    def test_public_user_get_label_list_no_view_perms(self):
         """Public user can't see labels for non-public superevents"""
-        # TODO: these errors will be 404 in the future
-        # Test internal superevent
+        # Internal superevent
         url = v_reverse('superevents:superevent-label-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "GET")
         # Should get 404 response - because of filtering, this object
         # should be excluded from queryset
-        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.status_code, 404)
 
-        # Test LV-EM superevent
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-label-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "GET")
         # Should get 404 response - because of filtering, this object
         # should be excluded from queryset
-        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.status_code, 404)
 
-    def test_public_get_with_view_perms(self):
+    def test_public_user_get_label_list_with_view_perms(self):
         """Public user can see labels for public superevents"""
-        # TODO
-        pass
+        # Test public superevent
+        url = v_reverse('superevents:superevent-label-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 200)
+        # Check response data - should have all labels
+        data = response.data
+        self.assertEqual(len(data['labels']), 1)
+        api_label_names = [l['name'] for l in data['labels']]
+        for l in self.public_superevent.labels.all():
+            self.assertIn(l.name, api_label_names)
 
-    def test_internal_post(self):
+    def test_internal_user_add_label(self):
         """Internal user can add labels to all superevents"""
         label, _ = Label.objects.get_or_create(name='NEW_LABEL')
         for s in Superevent.objects.all():
@@ -570,7 +685,7 @@ class TestSupereventLabelList(SupereventSetup, GraceDbApiTestBase):
             self.assertEqual(response.status_code, 201)
             self.assertEqual(response.data['name'], label.name)
 
-    def test_lvem_post(self):
+    def test_lvem_user_add_label(self):
         """LV-EM user cannot add labels to any superevents"""
         label, _ = Label.objects.get_or_create(name='NEW_LABEL')
 
@@ -590,25 +705,41 @@ class TestSupereventLabelList(SupereventSetup, GraceDbApiTestBase):
             data=data)
         self.assertEqual(response.status_code, 403)
 
-    def test_public_post(self):
+        # Public superevent - should get 403
+        url = v_reverse('superevents:superevent-label-list',
+            args=[self.public_superevent.superevent_id])
+        data = {'name': label.name}
+        response = self.request_as_user(url, "POST", self.lvem_user,
+            data=data)
+        self.assertEqual(response.status_code, 403)
+
+    def test_public_user_add_label(self):
         """Public user cannot add labels to any superevents"""
         label, _ = Label.objects.get_or_create(name='NEW_LABEL')
 
-        # Internal-only superevent - should get 404
-        # TODO: eventually this will be a 404, 403 for now
+        # Internal-only superevent - we get a 404 since looking up
+        # the parent superevent fails before permissions are checked.
         url = v_reverse('superevents:superevent-label-list',
             args=[self.internal_superevent.superevent_id])
         data = {'name': label.name}
         response = self.request_as_user(url, "POST", data=data)
-        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.status_code, 404)
 
-        # LV-EM superevent - should get 404
-        # TODO: eventually this will be a 404, 403 for now
+        # LV-EM superevent - we get a 404 since looking up
+        # the parent superevent fails before permissions are checked.
         url = v_reverse('superevents:superevent-label-list',
             args=[self.lvem_superevent.superevent_id])
         data = {'name': label.name}
         response = self.request_as_user(url, "POST", data=data)
+        self.assertEqual(response.status_code, 404)
+
+        # Public superevent - should get 403
+        url = v_reverse('superevents:superevent-label-list',
+            args=[self.public_superevent.superevent_id])
+        data = {'name': label.name}
+        response = self.request_as_user(url, "POST", data=data)
         self.assertEqual(response.status_code, 403)
+        self.assertIn('Authentication credentials', response.content)
 
 
 class TestSupereventLabelDetail(SupereventSetup, GraceDbApiTestBase):
@@ -632,8 +763,10 @@ class TestSupereventLabelDetail(SupereventSetup, GraceDbApiTestBase):
             cls.internal_superevent, label=l2)
         Labelling.objects.create(creator=cls.internal_user, superevent=
             cls.lvem_superevent, label=l1)
+        Labelling.objects.create(creator=cls.internal_user, superevent=
+            cls.public_superevent, label=l1)
 
-    def test_internal_get(self):
+    def test_internal_user_get_label_detail(self):
         """Internal user sees all labels for all superevents"""
         for s in Superevent.objects.all():
             for l in s.labels.all():
@@ -647,7 +780,7 @@ class TestSupereventLabelDetail(SupereventSetup, GraceDbApiTestBase):
                 # Check number of labels
                 self.assertEqual(response.data['name'], l.name)
 
-    def test_lvem_get_no_view_perms(self):
+    def test_lvem_get_label_detail_no_view_perms(self):
         """LV-EM user can't see labels for internal-only superevent"""
         for l in self.internal_superevent.labels.all():
             url = v_reverse('superevents:superevent-label-detail',
@@ -657,8 +790,9 @@ class TestSupereventLabelDetail(SupereventSetup, GraceDbApiTestBase):
             # should be excluded from queryset
             self.assertEqual(response.status_code, 404)
 
-    def test_lvem_get_with_view_perms(self):
-        """LV-EM user can see all labels for exposed superevent"""
+    def test_lvem_get_label_detail_with_view_perms(self):
+        """LV-EM user can see all labels for exposed superevents"""
+        # LV-EM superevent
         for l in self.lvem_superevent.labels.all():
             url = v_reverse('superevents:superevent-label-detail',
                 args=[self.lvem_superevent.superevent_id, l.name])
@@ -667,29 +801,41 @@ class TestSupereventLabelDetail(SupereventSetup, GraceDbApiTestBase):
             # Check response data
             self.assertEqual(response.data['name'], l.name)
 
-    def test_public_get_no_view_perms(self):
+        # public superevent
+        for l in self.public_superevent.labels.all():
+            url = v_reverse('superevents:superevent-label-detail',
+                args=[self.public_superevent.superevent_id, l.name])
+            response = self.request_as_user(url, "GET", self.lvem_user)
+            self.assertEqual(response.status_code, 200)
+            # Check response data
+            self.assertEqual(response.data['name'], l.name)
+
+    def test_public_get_label_detail_no_view_perms(self):
         """Public user can't see labels for non-public superevents"""
-        # TODO: these errors will be 404 in the future
         # Test internal superevent
         for l in self.internal_superevent.labels.all():
             url = v_reverse('superevents:superevent-label-detail',
                 args=[self.internal_superevent.superevent_id, l.name])
             response = self.request_as_user(url, "GET")
-            self.assertEqual(response.status_code, 403)
+            self.assertEqual(response.status_code, 404)
         
         # Test LV-EM superevent
         for l in self.lvem_superevent.labels.all():
             url = v_reverse('superevents:superevent-label-detail',
                 args=[self.lvem_superevent.superevent_id, l.name])
             response = self.request_as_user(url, "GET")
-            self.assertEqual(response.status_code, 403)
+            self.assertEqual(response.status_code, 404)
 
     def test_public_get_with_view_perms(self):
         """Public user can see all labels for public superevents"""
-        # TODO
-        pass
+        for l in self.public_superevent.labels.all():
+            url = v_reverse('superevents:superevent-label-detail',
+                args=[self.public_superevent.superevent_id, l.name])
+            response = self.request_as_user(url, "GET")
+            self.assertEqual(response.status_code, 200)
+            self.assertEqual(response.data['name'], l.name)
 
-    def test_internal_delete(self):
+    def test_internal_user_delete_label(self):
         """Internal user can remove labels from all superevents"""
         for s in Superevent.objects.all():
             for l in s.labels.all():
@@ -700,7 +846,7 @@ class TestSupereventLabelDetail(SupereventSetup, GraceDbApiTestBase):
                 self.assertEqual(response.status_code, 204)
                 self.assertEqual(response.data, None)
             
-    def test_lvem_delete(self):
+    def test_lvem_user_delete_label(self):
         """LV-EM user cannot remove labels from any superevents"""
         # Internal superevent - should get 404
         for l in self.internal_superevent.labels.all():
@@ -722,30 +868,45 @@ class TestSupereventLabelDetail(SupereventSetup, GraceDbApiTestBase):
             response = self.request_as_user(url, "DELETE", self.lvem_user)
             self.assertEqual(response.status_code, 403)
 
-    def test_public_delete(self):
+        # Public superevent - should get 403
+        for l in self.public_superevent.labels.all():
+            url = v_reverse('superevents:superevent-label-detail',
+                args=[self.public_superevent.superevent_id, l.name])
+            response = self.request_as_user(url, "DELETE", self.lvem_user)
+            self.assertEqual(response.status_code, 403)
+            self.assertIn('You do not have permission to remove labels',
+                response.content)
+
+    def test_public_user_delete_label(self):
         """Public user cannot remove labels from any superevents"""
-        # Internal superevent - should get 404 (TODO: 403 for now)
+        # Internal superevent - should get 404
         for l in self.internal_superevent.labels.all():
             url = v_reverse('superevents:superevent-label-detail',
                 args=[self.internal_superevent.superevent_id, l.name])
             response = self.request_as_user(url, "DELETE")
-            self.assertEqual(response.status_code, 403)
+            self.assertEqual(response.status_code, 404)
 
         # Try to delete a label that doesn't exist to ensure there is no
-        # information leaked that way. (TODO: 403 for now, will be 404)
+        # information leaked that way.
         url = v_reverse('superevents:superevent-label-detail',
             args=[self.internal_superevent.superevent_id, 'FAKE_LABEL'])
         response = self.request_as_user(url, "DELETE")
-        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.status_code, 404)
 
-        # LV-EM superevent - should get 404 (TODO: 403 for now)
+        # LV-EM superevent - should get 404
         for l in self.lvem_superevent.labels.all():
             url = v_reverse('superevents:superevent-label-detail',
                 args=[self.lvem_superevent.superevent_id, l.name])
             response = self.request_as_user(url, "DELETE")
-            self.assertEqual(response.status_code, 403)
+            self.assertEqual(response.status_code, 404)
 
-        # TODO: test on a public superevent
+        # Public superevent
+        for l in self.public_superevent.labels.all():
+            url = v_reverse('superevents:superevent-label-detail',
+                args=[self.public_superevent.superevent_id, l.name])
+            response = self.request_as_user(url, "DELETE")
+            self.assertEqual(response.status_code, 403)
+            self.assertIn('Authentication credentials', response.content)
 
 
 class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
@@ -766,10 +927,13 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
             search_name='search1', user=cls.internal_user)
         cls.event2 = cls.create_event('group2', 'pipeline2',
             search_name='search2', user=cls.internal_user)
+        cls.event3 = cls.create_event('group3', 'pipeline3',
+            search_name='search3', user=cls.internal_user)
         cls.internal_superevent.events.add(cls.event1)
         cls.lvem_superevent.events.add(cls.event2)
+        cls.public_superevent.events.add(cls.event3)
 
-    def test_internal_get(self):
+    def test_internal_get_event_list(self):
         """Internal user sees all events for superevents"""
         for s in Superevent.objects.all():
             # Set up URL
@@ -784,7 +948,7 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
             for ev in s.events.all():
                 self.assertIn(ev.graceid, graceid_list)
 
-    def test_lvem_get_no_view_perms(self):
+    def test_lvem_get_event_list_no_view_perms(self):
         """LV-EM user can't see events for internal-only superevent"""
         url = v_reverse('superevents:superevent-event-list',
             args=[self.internal_superevent.superevent_id])
@@ -793,10 +957,9 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
         # should be excluded from queryset
         self.assertEqual(response.status_code, 404)
 
-    def test_lvem_get_with_view_perms(self):
-        """LV-EM user can see events for exposed superevent"""
-        # TODO: do we deal with permissions on events too? i.e.,
-        #       don't show events in the list if they aren't exposed?
+    def test_lvem_get_event_list_with_view_perms(self):
+        """LV-EM user can see events for exposed superevents"""
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-event-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.lvem_user)
@@ -809,7 +972,20 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
         for ev in self.lvem_superevent.events.all():
             self.assertIn(ev.graceid, graceid_list)
 
-    def test_public_get_no_view_perms(self):
+        # Public superevent
+        url = v_reverse('superevents:superevent-event-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET", self.lvem_user)
+        self.assertEqual(response.status_code, 200)
+        # Check response data
+        data = response.data
+        self.assertEqual(len(data['events']),
+            self.public_superevent.events.count())
+        graceid_list = [ev['graceid'] for ev in data['events']]
+        for ev in self.public_superevent.events.all():
+            self.assertIn(ev.graceid, graceid_list)
+
+    def test_public_get_event_list_no_view_perms(self):
         """Public user can't see events for non-public superevents"""
         # Internal superevent
         url = v_reverse('superevents:superevent-event-list',
@@ -817,7 +993,7 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
         response = self.request_as_user(url, "GET")
         # Should get 404 response - because of filtering, this object
         # should be excluded from queryset
-        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.status_code, 404)
 
         # LV-EM superevent
         url = v_reverse('superevents:superevent-event-list',
@@ -825,15 +1001,23 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
         response = self.request_as_user(url, "GET")
         # Should get 404 response - because of filtering, this object
         # should be excluded from queryset
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be a 404 error in the future, not 403
+        self.assertEqual(response.status_code, 404)
 
-    def test_public_get_with_view_perms(self):
+    def test_public_get_event_list_with_view_perms(self):
         """Public user can see events for public superevents"""
-        # TODO
-        pass
+        url = v_reverse('superevents:superevent-event-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 200)
+        # Check response data
+        data = response.data
+        self.assertEqual(len(data['events']),
+            self.public_superevent.events.count())
+        graceid_list = [ev['graceid'] for ev in data['events']]
+        for ev in self.public_superevent.events.all():
+            self.assertIn(ev.graceid, graceid_list)
 
-    def test_basic_internal_post_production(self):
+    def test_basic_internal_add_event_to_production_superevent(self):
         """Basic internal user can't add events to production superevents"""
         # Create production event
         ev = self.create_event('EventGroup', 'EventPipeline',
@@ -846,7 +1030,7 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
             data={'event': ev.graceid})
         self.assertEqual(response.status_code, 403)
 
-    def test_basic_internal_post_mdc(self):
+    def test_basic_internal_add_event_to_mdc_superevent(self):
         """Basic internal user can't add events to mdc superevents"""
         # Create MDC superevent and event
         s = self.create_superevent(self.internal_user, event_search='MDC',
@@ -861,7 +1045,7 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
             data={'event': ev.graceid})
         self.assertEqual(response.status_code, 403)
 
-    def test_basic_internal_post_test(self):
+    def test_basic_internal_add_event_to_test_superevent(self):
         """Basic internal user can add events to test superevents"""
         # Create test superevent and event
         s = self.create_superevent(self.internal_user, event_group='Test',
@@ -877,7 +1061,7 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
         self.assertEqual(response.status_code, 201)
         self.assertEqual(response.data['graceid'], ev.graceid)
 
-    def test_privileged_internal_post_production(self):
+    def test_privileged_internal_add_event_to_production_superevent(self):
         """Privileged internal user can add events to production superevents"""
         # Create production event
         ev = self.create_event('EventGroup', 'EventPipeline',
@@ -891,7 +1075,7 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
         self.assertEqual(response.status_code, 201)
         self.assertEqual(response.data['graceid'], ev.graceid)
 
-    def test_privileged_internal_post_mdc(self):
+    def test_privileged_internal_add_event_to_mdc_superevent(self):
         """Privileged internal user can add events to mdc superevents"""
         # Create MDC superevent and event
         s = self.create_superevent(self.internal_user, event_search='MDC',
@@ -907,7 +1091,7 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
         self.assertEqual(response.status_code, 201)
         self.assertEqual(response.data['graceid'], ev.graceid)
 
-    def test_privileged_internal_post_test(self):
+    def test_privileged_internal_add_event_to_test_superevent(self):
         """Privileged internal user can add events to test superevents"""
         # Create test superevent and event
         s = self.create_superevent(self.internal_user, event_group='Test',
@@ -923,7 +1107,7 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
         self.assertEqual(response.status_code, 201)
         self.assertEqual(response.data['graceid'], ev.graceid)
 
-    def test_lvem_user_post(self):
+    def test_lvem_user_add_event_to_superevents(self):
         """LV-EM user can't add events to superevents"""
         # Create an event
         ev = self.create_event('EventGroup', 'EventPipeline',
@@ -937,14 +1121,16 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
             data={'event': ev.graceid})
         self.assertEqual(response.status_code, 404)
 
-        # Expose superevent and retry, should get 403 now
-        assign_perm('superevent.view_superevent', self.lvem_group,
-            obj=self.internal_superevent)
+        # Try with exposed superevent
+        url = v_reverse('superevents:superevent-event-list',
+            args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "POST", self.lvem_user,
             data={'event': ev.graceid})
         self.assertEqual(response.status_code, 403)
+        self.assertIn('not allowed to add events to superevents',
+            response.content)
 
-    def test_public_user_post(self):
+    def test_public_user_add_event_to_superevent(self):
         """Public user can't add events to superevents"""
         # Create an event
         ev = self.create_event('EventGroup', 'EventPipeline',
@@ -956,10 +1142,15 @@ class TestSupereventEventList(SupereventManagersGroupAndUserSetup,
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "POST",
             data={'event': ev.graceid})
-        self.assertEqual(response.status_code, 403)
-        # TODO: this is 403 for now, will be 404 in the future
+        self.assertEqual(response.status_code, 404)
 
-        # TODO: expose to public and retry, should get 403
+        # Try with public superevent - 403 due to not authenticated
+        url = v_reverse('superevents:superevent-event-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "POST",
+            data={'event': ev.graceid})
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided', response.content)
 
 
 class TestSupereventEventDetail(SupereventManagersGroupAndUserSetup,
@@ -975,12 +1166,15 @@ class TestSupereventEventDetail(SupereventManagersGroupAndUserSetup,
             search_name='search1', user=cls.internal_user)
         cls.event2 = cls.create_event('group2', 'pipeline2',
             search_name='search2', user=cls.internal_user)
+        cls.event3 = cls.create_event('group3', 'pipeline3',
+            search_name='search3', user=cls.internal_user)
 
-        # add events to superevent
+        # Add events to superevents
         cls.internal_superevent.events.add(cls.event1)
         cls.lvem_superevent.events.add(cls.event2)
+        cls.public_superevent.events.add(cls.event3)
 
-    def test_internal_get(self):
+    def test_internal_user_get_events_list(self):
         """Internal user sees all events for superevents"""
         for s in Superevent.objects.all():
             for ev in s.events.all():
@@ -993,7 +1187,7 @@ class TestSupereventEventDetail(SupereventManagersGroupAndUserSetup,
                 # Check event number and graceids
                 self.assertEqual(response.data['graceid'], ev.graceid)
 
-    def test_lvem_get_no_view_perms(self):
+    def test_lvem_user_get_events_list_no_view_perms(self):
         """LV-EM user can't see events for internal-only superevent"""
         for ev in self.internal_superevent.events.all():
             url = v_reverse('superevents:superevent-event-detail',
@@ -1003,10 +1197,9 @@ class TestSupereventEventDetail(SupereventManagersGroupAndUserSetup,
             # should be excluded from the queryset
             self.assertEqual(response.status_code, 404)
 
-    def test_lvem_get_with_view_perms(self):
-        """LV-EM user can see events for exposed superevent"""
-        # TODO: do we deal with permissions on events too? i.e.,
-        #       don't show events in the list if they aren't exposed?
+    def test_lvem_user_get_events_list_with_view_perms(self):
+        """LV-EM user can see events for exposed superevents"""
+        # LV-EM superevent
         for ev in self.lvem_superevent.events.all():
             url = v_reverse('superevents:superevent-event-detail',
                 args=[self.lvem_superevent.superevent_id, ev.graceid])
@@ -1015,7 +1208,16 @@ class TestSupereventEventDetail(SupereventManagersGroupAndUserSetup,
             # Check response data
             self.assertEqual(response.data['graceid'], ev.graceid)
 
-    def test_public_get_no_view_perms(self):
+        # Public superevent
+        for ev in self.public_superevent.events.all():
+            url = v_reverse('superevents:superevent-event-detail',
+                args=[self.public_superevent.superevent_id, ev.graceid])
+            response = self.request_as_user(url, "GET", self.lvem_user)
+            self.assertEqual(response.status_code, 200)
+            # Check response data
+            self.assertEqual(response.data['graceid'], ev.graceid)
+
+    def test_public_user_get_event_list_no_view_perms(self):
         """Public user can't see events for non-public superevents"""
         # Internal superevent
         for ev in self.internal_superevent.events.all():
@@ -1024,22 +1226,26 @@ class TestSupereventEventDetail(SupereventManagersGroupAndUserSetup,
             response = self.request_as_user(url, "GET")
             # Should get 404 response - because of filtering, this superevent
             # should be excluded from the queryset
-            self.assertEqual(response.status_code, 403)
-            # NOTE: will be a 404 error in the future
+            self.assertEqual(response.status_code, 404)
 
+        # LV-EM superevent
         for ev in self.lvem_superevent.events.all():
             url = v_reverse('superevents:superevent-event-detail',
                 args=[self.lvem_superevent.superevent_id, ev.graceid])
             response = self.request_as_user(url, "GET")
             # Should get 404 response - because of filtering, this superevent
             # should be excluded from the queryset
-            self.assertEqual(response.status_code, 403)
-            # NOTE: will be a 404 error in the future
+            self.assertEqual(response.status_code, 404)
 
-    def test_public_get_with_view_perms(self):
+    def test_public_user_get_event_list_with_view_perms(self):
         """Public user can see events for public superevents"""
-        # TODO
-        pass
+        for ev in self.public_superevent.events.all():
+            url = v_reverse('superevents:superevent-event-detail',
+                args=[self.public_superevent.superevent_id, ev.graceid])
+            response = self.request_as_user(url, "GET")
+            self.assertEqual(response.status_code, 200)
+            # Check response data
+            self.assertEqual(response.data['graceid'], ev.graceid)
 
     def test_basic_internal_user_remove_event_from_production_superevent(self):
         """
@@ -1144,6 +1350,8 @@ class TestSupereventEventDetail(SupereventManagersGroupAndUserSetup,
             self.event2.graceid])
         response = self.request_as_user(url, "DELETE", self.lvem_user)
         self.assertEqual(response.status_code, 403)
+        self.assertIn('not allowed to remove events from superevents',
+            response.content)
 
     def test_public_user_remove_event_from_superevent(self):
         """
@@ -1154,35 +1362,71 @@ class TestSupereventEventDetail(SupereventManagersGroupAndUserSetup,
             args=[self.internal_superevent.superevent_id,
             self.event1.graceid])
         response = self.request_as_user(url, "DELETE")
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be 404 in the future
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        url = v_reverse('superevents:superevent-event-detail',
+            args=[self.lvem_superevent.superevent_id,
+            self.event2.graceid])
+        response = self.request_as_user(url, "DELETE")
+        self.assertEqual(response.status_code, 404)
 
-        # TODO: Exposed superevent
+        # Public superevent
+        url = v_reverse('superevents:superevent-event-detail',
+            args=[self.public_superevent.superevent_id,
+            self.event2.graceid])
+        response = self.request_as_user(url, "DELETE")
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided', response.content)
 
 
 class TestSupereventLogList(AccessManagersGroupAndUserSetup,
     SupereventSetup, GraceDbApiTestBase):
 
+    @classmethod
+    def setUpClass(cls):
+        super(TestSupereventLogList, cls).setUpClass()
+        cls.num_logs = 3
+        cls.lvem_log_index = 1
+        cls.public_log_index = 2
+
     @classmethod
     def setUpTestData(cls):
         super(TestSupereventLogList, cls).setUpTestData()
-
+        
         # Add logs to superevents
-        cls.num_logs = 3
+        comment = "test comment {n}"
         for i in range(cls.num_logs):
             Log.objects.create(superevent=cls.internal_superevent,
-                issuer=cls.internal_user, comment='test comment')
+                issuer=cls.internal_user, comment=comment.format(n=i))
             Log.objects.create(superevent=cls.lvem_superevent,
-                issuer=cls.internal_user, comment='test comment')
-
-        # Expose one log on each superevent to LV-EM
-        assign_perm('superevents.view_log', cls.lvem_group,
-            obj=cls.internal_superevent.log_set.first())
-        assign_perm('superevents.view_log', cls.lvem_group,
-            obj=cls.lvem_superevent.log_set.first())
-        # TODO: expose one log on each superevent to public
-
-    def test_internal_user_get(self):
+                issuer=cls.internal_user, comment=comment.format(n=i))
+            Log.objects.create(superevent=cls.public_superevent,
+                issuer=cls.internal_user, comment=comment.format(n=i))
+
+        # Compile a dict of logs for tracking things accurately
+        cls.log_dict = {}
+        cls.log_dict['internal_lvem'] = cls.internal_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['lvem_lvem'] = cls.lvem_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['public_lvem'] = cls.public_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['internal_public'] = cls.internal_superevent.log_set.get(
+            N=cls.public_log_index)
+        cls.log_dict['lvem_public'] = cls.lvem_superevent.log_set.get(
+            N=cls.public_log_index)
+        cls.log_dict['public_public'] = cls.public_superevent.log_set.get(
+            N=cls.public_log_index)
+
+        # Expose one log on each superevent to LV-EM only
+        # Expose one log on each superevent to both LV-EM and public
+        for k, v in cls.log_dict.items():
+            expose_log_to_lvem(v)
+            if k.endswith('public'):
+                expose_log_to_public(v)
+
+    def test_internal_user_get_log_list(self):
         """Internal user can see all logs for a superevent"""
         url = v_reverse('superevents:superevent-log-list',
             args=[self.internal_superevent.superevent_id])
@@ -1190,8 +1434,9 @@ class TestSupereventLogList(AccessManagersGroupAndUserSetup,
         self.assertEqual(response.status_code, 200)
         self.assertEqual(len(response.data['log']),
             self.internal_superevent.log_set.count())
+        self.assertEqual(len(response.data['log']), self.num_logs)
 
-    def test_lvem_user_get_for_hidden_superevent(self):
+    def test_lvem_user_get_log_list_for_hidden_superevent(self):
         """LV-EM user can't see any logs for hidden superevent"""
         # Internal superevent even has one log exposed to LV-EM,
         # but still shouldn't be able to see it
@@ -1200,20 +1445,59 @@ class TestSupereventLogList(AccessManagersGroupAndUserSetup,
         response = self.request_as_user(url, "GET", self.lvem_user)
         self.assertEqual(response.status_code, 404)
 
-    def test_lvem_user_get_for_exposed_superevent(self):
-        """LV-EM user can see only exposed logs for exposed superevent"""
+    def test_lvem_user_get_log_list_for_exposed_superevent(self):
+        """LV-EM user can see only exposed logs for exposed superevents"""
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-log-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.lvem_user)
         self.assertEqual(response.status_code, 200)
-        self.assertEqual(len(response.data['log']), 1)
-        self.assertEqual(response.data['log'][0]['N'],
-            self.lvem_superevent.log_set.first().N)
+        self.assertEqual(len(response.data['log']), 2)
+
+        # Exposed log N values
+        N_values = [log['N'] for log in response.data['log']]
+
+        # Make sure they are what we expect 
+        self.assertIn(self.log_dict['lvem_lvem'].N, N_values)
+        self.assertIn(self.log_dict['lvem_public'].N, N_values)
+
+        # Public superevent
+        url = v_reverse('superevents:superevent-log-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET", self.lvem_user)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['log']), 2)
+        # Exposed log N values
+        N_values = [log['N'] for log in response.data['log']]
+        # Make sure they are what we expect 
+        self.assertIn(self.log_dict['public_lvem'].N, N_values)
+        self.assertIn(self.log_dict['public_public'].N, N_values)
+
+    def test_public_user_get_log_list_for_hidden_superevent(self):
+        """Public user can't see any logs for hidden superevents"""
+        # Internal superevent even has one log exposed to public,
+        # but still shouldn't be able to see it
+        url = v_reverse('superevents:superevent-log-list',
+            args=[self.internal_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent even has one log exposed to public,
+        # but still shouldn't be able to see it
+        url = v_reverse('superevents:superevent-log-list',
+            args=[self.lvem_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 404)
 
-    def test_public_user_get_for_hidden_superevent(self):
-        """Public user can't see any logs for hidden superevent"""
-        # TODO
-        pass
+    def test_public_user_get_log_list_for_exposed_superevent(self):
+        """Public user can see only exposed logs for exposed superevent"""
+        url = v_reverse('superevents:superevent-log-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['log']), 1)
+        self.assertEqual(self.log_dict['public_public'].N,
+            response.data['log'][0]['N'])
 
     def test_internal_user_create_log(self):
         """Internal user can create logs for all superevents"""
@@ -1346,7 +1630,7 @@ class TestSupereventLogList(AccessManagersGroupAndUserSetup,
         # full queryset
         self.assertEqual(response.status_code, 404)
 
-        # LV-EM exposed superevent
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-log-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "POST", self.lvem_user,
@@ -1354,6 +1638,19 @@ class TestSupereventLogList(AccessManagersGroupAndUserSetup,
         # Check response and data
         self.assertEqual(response.status_code, 201)
         self.assertEqual(response.data['comment'], log_data['comment'])
+        self.assertIn(settings.EXTERNAL_ACCESS_TAGNAME,
+            response.data['tag_names'])
+
+        # Public superevent
+        url = v_reverse('superevents:superevent-log-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "POST", self.lvem_user,
+            data=log_data)
+        # Check response and data
+        self.assertEqual(response.status_code, 201)
+        self.assertEqual(response.data['comment'], log_data['comment'])
+        self.assertIn(settings.EXTERNAL_ACCESS_TAGNAME,
+            response.data['tag_names'])
 
     def test_lvem_user_create_log_with_tag(self):
         """LV-EM user can't create log with tags"""
@@ -1379,38 +1676,78 @@ class TestSupereventLogList(AccessManagersGroupAndUserSetup,
 
     def test_public_user_create_log(self):
         """Public user can't create any logs"""
-        # TODO
-        pass
+        log_data = {'comment': 'test comment', 'tagname': ['test_tag']}
 
+        # Post to internal superevent, should get 404
+        url = v_reverse('superevents:superevent-log-list',
+            args=[self.internal_superevent.superevent_id])
+        response = self.request_as_user(url, "POST", data=log_data)
+        # Check response and data
+        self.assertEqual(response.status_code, 404)
 
-class TestSupereventLogDetail(SupereventSetup, GraceDbApiTestBase):
+        # Post to LV-EM superevent, should get 404
+        url = v_reverse('superevents:superevent-log-list',
+            args=[self.lvem_superevent.superevent_id])
+        response = self.request_as_user(url, "POST", data=log_data)
+        # Check response and data
+        self.assertEqual(response.status_code, 404)
 
-    @classmethod
-    def setUpTestData(cls):
-        super(TestSupereventLogDetail, cls).setUpTestData()
+        # Post to public superevent, should get 403
+        url = v_reverse('superevents:superevent-log-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "POST", data=log_data)
+        # Check response and data
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided', response.data['detail'])
 
-        # Add logs to superevents
+
+class TestSupereventLogDetail(SupereventSetup, GraceDbApiTestBase):
+    """Test GET-ing superevent log detail pages for all user classes"""
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestSupereventLogDetail, cls).setUpClass()
         cls.num_logs = 3
+        cls.lvem_log_index = 1
+        cls.public_log_index = 2
+
+    @classmethod
+    def setUpTestData(cls):
+        super(TestSupereventLogDetail, cls).setUpTestData()
+        
+        # Add logs to superevents
+        comment = "test comment {n}"
         for i in range(cls.num_logs):
             Log.objects.create(superevent=cls.internal_superevent,
-                issuer=cls.internal_user, comment='test comment')
+                issuer=cls.internal_user, comment=comment.format(n=i))
             Log.objects.create(superevent=cls.lvem_superevent,
-                issuer=cls.internal_user, comment='test comment')
-
-        # Expose one log on each superevent to LV-EM and store them on the
-        # class for easy access
-        cls.internal_superevent_exposed_log = cls.internal_superevent \
-            .log_set.first()
-        cls.lvem_superevent_exposed_log = cls.lvem_superevent \
-            .log_set.first()
-        assign_perm('superevents.view_log', cls.lvem_group,
-            obj=cls.internal_superevent_exposed_log)
-        assign_perm('superevents.view_log', cls.lvem_group,
-            obj=cls.lvem_superevent_exposed_log)
-
-        # TODO: expose one log on each superevent to public
-
-    def test_internal_user_get(self):
+                issuer=cls.internal_user, comment=comment.format(n=i))
+            Log.objects.create(superevent=cls.public_superevent,
+                issuer=cls.internal_user, comment=comment.format(n=i))
+
+        # Compile a dict of logs for tracking things accurately
+        cls.log_dict = {}
+        cls.log_dict['internal_lvem'] = cls.internal_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['lvem_lvem'] = cls.lvem_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['public_lvem'] = cls.public_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['internal_public'] = cls.internal_superevent.log_set.get(
+            N=cls.public_log_index)
+        cls.log_dict['lvem_public'] = cls.lvem_superevent.log_set.get(
+            N=cls.public_log_index)
+        cls.log_dict['public_public'] = cls.public_superevent.log_set.get(
+            N=cls.public_log_index)
+
+        # Expose one log on each superevent to LV-EM only
+        # Expose one log on each superevent to both LV-EM and public
+        for k, v in cls.log_dict.items():
+            expose_log_to_lvem(v)
+            if k.endswith('public'):
+                expose_log_to_public(v)
+
+    def test_internal_user_get_log_detail(self):
         """Internal user can see all logs for all superevents"""
         for s in Superevent.objects.all():
             for l in s.log_set.all():
@@ -1422,7 +1759,7 @@ class TestSupereventLogDetail(SupereventSetup, GraceDbApiTestBase):
                 self.assertEqual(response.data['comment'], l.comment)
                 self.assertEqual(response.data['N'], l.N)
             
-    def test_lvem_user_get_for_hidden_superevent(self):
+    def test_lvem_user_get_log_detail_for_hidden_superevent(self):
         """LV-EM user can't get log detail for hidden superevent"""
         # Internal superevent even has one log exposed to LV-EM,
         # but still shouldn't be able to see it
@@ -1432,74 +1769,114 @@ class TestSupereventLogDetail(SupereventSetup, GraceDbApiTestBase):
             response = self.request_as_user(url, "GET", self.lvem_user)
             self.assertEqual(response.status_code, 404)
 
-    def test_lvem_user_get_for_exposed_superevent(self):
-        """LV-EM user can see only exposed logs for exposed superevent"""
+    def test_lvem_user_get_log_detail_for_exposed_superevent(self):
+        """LV-EM user can see only exposed logs for exposed superevents"""
+        # LV-EM superevent
         for l in self.lvem_superevent.log_set.all():
             url = v_reverse('superevents:superevent-log-detail',
                 args=[self.lvem_superevent.superevent_id, l.N])
             response = self.request_as_user(url, "GET", self.lvem_user)
 
-            if l == self.lvem_superevent_exposed_log:
+            log_is_viewable = False
+            log = None
+            if (l == self.log_dict['lvem_lvem']):
+                log_is_viewable = True
+                log = self.log_dict['lvem_lvem']
+            elif (l == self.log_dict['lvem_public']):
+                log_is_viewable = True
+                log = self.log_dict['lvem_public']
+            if log_is_viewable:
                 self.assertEqual(response.status_code, 200)
-                self.assertEqual(response.data['comment'],
-                    self.lvem_superevent_exposed_log.comment)
-                self.assertEqual(response.data['N'],
-                    self.lvem_superevent_exposed_log.N)
+                self.assertEqual(response.data['comment'], log.comment)
+                self.assertEqual(response.data['N'], log.N)
             else:
                 self.assertEqual(response.status_code, 404)
 
-    def test_public_user_get_for_hidden_superevent(self):
-        """Public user can't get log detail for hidden superevent"""
-        # TODO: add public log to superevent
+    def test_public_user_get_log_detail_for_hidden_superevent(self):
+        """Public user can't get log detail for hidden superevents"""
+        # Internal superevent
         for l in self.internal_superevent.log_set.all():
             url = v_reverse('superevents:superevent-log-detail',
                 args=[self.internal_superevent.superevent_id, l.N])
             response = self.request_as_user(url, "GET")
-            # TODO: will be 404 in the future
-            self.assertEqual(response.status_code, 403)
+            self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        for l in self.lvem_superevent.log_set.all():
+            url = v_reverse('superevents:superevent-log-detail',
+                args=[self.lvem_superevent.superevent_id, l.N])
+            response = self.request_as_user(url, "GET")
+            self.assertEqual(response.status_code, 404)
 
-    def test_public_user_get_for_exposed_superevent(self):
+    def test_public_user_get_log_detail_for_exposed_superevent(self):
         """Public user can see only exposed logs for exposed superevent"""
-        # TODO
-        pass
+        log = self.log_dict['public_public']
+        for l in self.public_superevent.log_set.all():
+            url = v_reverse('superevents:superevent-log-detail',
+                args=[self.public_superevent.superevent_id, l.N])
+            response = self.request_as_user(url, "GET")
+            if (l == log):
+                self.assertEqual(response.status_code, 200)
+                self.assertEqual(response.data['comment'], log.comment)
+                self.assertEqual(response.data['N'], log.N)
+            else:
+                self.assertEqual(response.status_code, 404)
 
 
 class TestSupereventLogTagList(AccessManagersGroupAndUserSetup,
     SupereventSetup, GraceDbApiTestBase):
+    """Test getting log tag lists and adding tags to logs"""
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestSupereventLogTagList, cls).setUpClass()
+        cls.num_logs = 3
+        cls.lvem_log_index = 1
+        cls.public_log_index = 2
 
     @classmethod
     def setUpTestData(cls):
         super(TestSupereventLogTagList, cls).setUpTestData()
-
+        
         # Add logs to superevents
-        cls.num_logs = 3
+        comment = "test comment {n}"
         for i in range(cls.num_logs):
             Log.objects.create(superevent=cls.internal_superevent,
-                issuer=cls.internal_user, comment='test comment')
+                issuer=cls.internal_user, comment=comment.format(n=i))
             Log.objects.create(superevent=cls.lvem_superevent,
-                issuer=cls.internal_user, comment='test comment')
-
-        # Expose one log on each superevent to LV-EM and store them on the
-        # class for easy access
-        cls.internal_superevent_exposed_log = cls.internal_superevent \
-            .log_set.first()
-        cls.lvem_superevent_exposed_log = cls.lvem_superevent \
-            .log_set.first()
-        assign_perm('superevents.view_log', cls.lvem_group,
-            obj=cls.internal_superevent_exposed_log)
-        assign_perm('superevents.view_log', cls.lvem_group,
-            obj=cls.lvem_superevent_exposed_log)
-        # TODO: expose one log on each superevent to public
+                issuer=cls.internal_user, comment=comment.format(n=i))
+            Log.objects.create(superevent=cls.public_superevent,
+                issuer=cls.internal_user, comment=comment.format(n=i))
+
+        # Compile a dict of logs for tracking things accurately
+        cls.log_dict = {}
+        cls.log_dict['internal_lvem'] = cls.internal_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['lvem_lvem'] = cls.lvem_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['public_lvem'] = cls.public_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['internal_public'] = cls.internal_superevent.log_set.get(
+            N=cls.public_log_index)
+        cls.log_dict['lvem_public'] = cls.lvem_superevent.log_set.get(
+            N=cls.public_log_index)
+        cls.log_dict['public_public'] = cls.public_superevent.log_set.get(
+            N=cls.public_log_index)
 
         # Create some tags
         cls.tag1 = Tag.objects.create(name='test_tag1')
         cls.tag2 = Tag.objects.create(name='test_tag2')
 
-        # Add tags to the exposed logs
-        cls.internal_superevent_exposed_log.tags.add(*(cls.tag1, cls.tag2))
-        cls.lvem_superevent_exposed_log.tags.add(*(cls.tag1, cls.tag2))
+        # Expose one log on each superevent to LV-EM only
+        # Expose one log on each superevent to both LV-EM and public
+        # Add both tags to each exposed log
+        for k, v in cls.log_dict.items():
+            expose_log_to_lvem(v)
+            v.tags.add(*(cls.tag1, cls.tag2))
+            if k.endswith('public'):
+                expose_log_to_public(v)
 
-    def test_internal_get(self):
+    def test_internal_user_get_log_tag_list(self):
         """Internal user can get all tags for all logs for all superevents"""
         for s in Superevent.objects.all():
             for l in s.log_set.all():
@@ -1509,17 +1886,12 @@ class TestSupereventLogTagList(AccessManagersGroupAndUserSetup,
                 response = self.request_as_user(url, "GET", self.internal_user)
                 # Check response code and data
                 self.assertEqual(response.status_code, 200)
-                if (l == self.internal_superevent_exposed_log or
-                    l == self.lvem_superevent_exposed_log):
-                    self.assertEqual(len(response.data['tags']),
-                        l.tags.count())
-                    response_tags = [t['name'] for t in response.data['tags']]
-                    self.assertIn(self.tag1.name, response_tags)
-                    self.assertIn(self.tag2.name, response_tags)
-                else:
-                    self.assertEqual(response.data['tags'], [])
+                # Lists of log tags from 
+                log_tags = [t.name for t in l.tags.all()]
+                self.assertItemsEqual(
+                    [t['name'] for t in response.data['tags']], log_tags)
 
-    def test_lvem_get_for_hidden_superevent(self):
+    def test_lvem_user_get_log_tag_list_for_hidden_superevent(self):
         """LV-EM user can't get tags for any logs on hidden superevent"""
         for l in self.internal_superevent.log_set.all():
             url = v_reverse('superevents:superevent-log-tag-list',
@@ -1529,17 +1901,20 @@ class TestSupereventLogTagList(AccessManagersGroupAndUserSetup,
             # Check response code and data
             self.assertEqual(response.status_code, 404)
 
-    def test_lvem_get_for_exposed_superevent(self):
+    def test_lvem_user_get_log_tag_list_for_exposed_superevent(self):
         """
         LV-EM user can only get tags for exposed logs on exposed superevent
         """
+        # LV-EM superevent
         for l in self.lvem_superevent.log_set.all():
             url = v_reverse('superevents:superevent-log-tag-list',
                 args=[self.lvem_superevent.superevent_id, l.N])
             # Make request
             response = self.request_as_user(url, "GET", self.lvem_user)
-            # Check response code and data
-            if (l == self.lvem_superevent_exposed_log):
+            # Check response code and data. Should be "OK" for the logs
+            # exposed to LV-EM and public
+            if (l in [self.log_dict['lvem_lvem'], 
+                self.log_dict['lvem_public']]):
                 self.assertEqual(response.status_code, 200)
                 self.assertEqual(len(response.data['tags']),
                     l.tags.count())
@@ -1549,23 +1924,66 @@ class TestSupereventLogTagList(AccessManagersGroupAndUserSetup,
             else:
                 self.assertEqual(response.status_code, 404)
 
-    def test_public_get_for_hidden_superevent(self):
+        # Public superevent
+        for l in self.public_superevent.log_set.all():
+            url = v_reverse('superevents:superevent-log-tag-list',
+                args=[self.public_superevent.superevent_id, l.N])
+            # Make request
+            response = self.request_as_user(url, "GET", self.lvem_user)
+            # Check response code and data. Should be "OK" for the logs
+            # exposed to LV-EM and public
+            if (l in [self.log_dict['public_lvem'],
+                self.log_dict['public_public']]):
+                self.assertEqual(response.status_code, 200)
+                self.assertEqual(len(response.data['tags']),
+                    l.tags.count())
+                response_tags = [t['name'] for t in response.data['tags']]
+                self.assertIn(self.tag1.name, response_tags)
+                self.assertIn(self.tag2.name, response_tags)
+            else:
+                self.assertEqual(response.status_code, 404)
+
+    def test_public_user_get_log_tag_list_for_hidden_superevent(self):
         """Public user can't get tags for any logs on hidden superevent"""
+        # Internal superevent
         for l in self.internal_superevent.log_set.all():
             url = v_reverse('superevents:superevent-log-tag-list',
                 args=[self.internal_superevent.superevent_id, l.N])
             # Make request
             response = self.request_as_user(url, "GET")
             # Check response code and data
-            # TODO: will be 404 in the future
-            self.assertEqual(response.status_code, 403)
+            self.assertEqual(response.status_code, 404)
 
-    def test_public_get_for_exposed_superevent(self):
+        # LV-EM superevent
+        for l in self.lvem_superevent.log_set.all():
+            url = v_reverse('superevents:superevent-log-tag-list',
+                args=[self.lvem_superevent.superevent_id, l.N])
+            # Make request
+            response = self.request_as_user(url, "GET")
+            # Check response code and data
+            self.assertEqual(response.status_code, 404)
+
+    def test_public_user_get_log_tag_list_for_exposed_superevent(self):
         """
         Public user can only get tags for exposed logs on exposed superevent
         """
-        # TODO
-        pass
+        # Public superevent
+        for l in self.public_superevent.log_set.all():
+            url = v_reverse('superevents:superevent-log-tag-list',
+                args=[self.public_superevent.superevent_id, l.N])
+            # Make request
+            response = self.request_as_user(url, "GET")
+            # Check response code and data. Should be "OK" for the logs
+            # exposed to LV-EM and public
+            if (l == self.log_dict['public_public']):
+                self.assertEqual(response.status_code, 200)
+                self.assertEqual(len(response.data['tags']),
+                    l.tags.count())
+                response_tags = [t['name'] for t in response.data['tags']]
+                self.assertIn(self.tag1.name, response_tags)
+                self.assertIn(self.tag2.name, response_tags)
+            else:
+                self.assertEqual(response.status_code, 404)
 
     def test_internal_user_tag_log(self):
         """Internal user can tag logs for all superevents"""
@@ -1679,7 +2097,7 @@ class TestSupereventLogTagList(AccessManagersGroupAndUserSetup,
         # leaks out that way.
         url = v_reverse('superevents:superevent-log-tag-list',
             args=[self.internal_superevent.superevent_id,
-            self.internal_superevent_exposed_log.N])
+            self.log_dict['internal_lvem'].N])
         response = self.request_as_user(url, "POST", self.lvem_user,
             data={'name': self.tag1.name})
         # Check response and data
@@ -1688,7 +2106,7 @@ class TestSupereventLogTagList(AccessManagersGroupAndUserSetup,
         # LV-EM exposed superevent
         url = v_reverse('superevents:superevent-log-tag-list',
             args=[self.lvem_superevent.superevent_id,
-            self.lvem_superevent_exposed_log.N])
+            self.log_dict['lvem_lvem'].N])
         response = self.request_as_user(url, "POST", self.lvem_user,
             data={'name': self.tag1.name})
         # Check response and data
@@ -1707,51 +2125,69 @@ class TestSupereventLogTagList(AccessManagersGroupAndUserSetup,
             data={'name': self.tag1.name})
         # Check response and data - 404 due to superevent not included in
         # full queryset
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be 404 in the future
+        self.assertEqual(response.status_code, 404)
 
         # Internal-only superevent again, with tag already applied,
         # to make sure error message is what we expect and that nothing
         # leaks out that way.
         url = v_reverse('superevents:superevent-log-tag-list',
             args=[self.internal_superevent.superevent_id,
-            self.internal_superevent_exposed_log.N])
+            self.log_dict['internal_public'].N])
         response = self.request_as_user(url, "POST",
             data={'name': self.tag1.name})
         # Check response and data
-        self.assertEqual(response.status_code, 403)
-        # TODO: this will be 404 in the future
+        self.assertEqual(response.status_code, 404)
 
-        # Test publicly-exposed superevent
-        # TODO
+        # Public superevent
+        url = v_reverse('superevents:superevent-log-tag-list',
+            args=[self.public_superevent.superevent_id,
+            self.log_dict['public_public'].N])
+        response = self.request_as_user(url, "POST",
+            data={'name': self.tag1.name})
+        # Check response and data
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided', response.content)
 
 
 class TestSupereventLogTagDetail(AccessManagersGroupAndUserSetup,
     SupereventSetup, GraceDbApiTestBase):
+    """Test getting log tag details and deleting tags"""
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestSupereventLogTagDetail, cls).setUpClass()
+        cls.num_logs = 3
+        cls.lvem_log_index = 1
+        cls.public_log_index = 2
 
     @classmethod
     def setUpTestData(cls):
         super(TestSupereventLogTagDetail, cls).setUpTestData()
-
+        
         # Add logs to superevents
-        cls.num_logs = 3
+        comment = "test comment {n}"
         for i in range(cls.num_logs):
             Log.objects.create(superevent=cls.internal_superevent,
-                issuer=cls.internal_user, comment='test comment')
+                issuer=cls.internal_user, comment=comment.format(n=i))
             Log.objects.create(superevent=cls.lvem_superevent,
-                issuer=cls.internal_user, comment='test comment')
-
-        # Expose one log on each superevent to LV-EM and store them on the
-        # class for easy access
-        cls.internal_superevent_exposed_log = cls.internal_superevent \
-            .log_set.first()
-        cls.lvem_superevent_exposed_log = cls.lvem_superevent \
-            .log_set.first()
-        assign_perm('superevents.view_log', cls.lvem_group,
-            obj=cls.internal_superevent_exposed_log)
-        assign_perm('superevents.view_log', cls.lvem_group,
-            obj=cls.lvem_superevent_exposed_log)
-        # TODO: expose one log on each superevent to public
+                issuer=cls.internal_user, comment=comment.format(n=i))
+            Log.objects.create(superevent=cls.public_superevent,
+                issuer=cls.internal_user, comment=comment.format(n=i))
+
+        # Compile a dict of logs for tracking things accurately
+        cls.log_dict = {}
+        cls.log_dict['internal_lvem'] = cls.internal_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['lvem_lvem'] = cls.lvem_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['public_lvem'] = cls.public_superevent.log_set.get(
+            N=cls.lvem_log_index)
+        cls.log_dict['internal_public'] = cls.internal_superevent.log_set.get(
+            N=cls.public_log_index)
+        cls.log_dict['lvem_public'] = cls.lvem_superevent.log_set.get(
+            N=cls.public_log_index)
+        cls.log_dict['public_public'] = cls.public_superevent.log_set.get(
+            N=cls.public_log_index)
 
         # Create some tags
         cls.tag1 = Tag.objects.create(name='test_tag1')
@@ -1761,72 +2197,105 @@ class TestSupereventLogTagDetail(AccessManagersGroupAndUserSetup,
         cls.public_tag = Tag.objects.create(
             name=settings.PUBLIC_ACCESS_TAGNAME)
 
-        # Add tags to the exposed logs
-        cls.internal_superevent_exposed_log.tags.add(*(cls.tag1, cls.tag2))
-        cls.lvem_superevent_exposed_log.tags.add(*(cls.tag1, cls.tag2))
+        # Expose one log on each superevent to LV-EM only
+        # Expose one log on each superevent to both LV-EM and public
+        # Add both tags to each exposed log
+        for k, v in cls.log_dict.items():
+            expose_log_to_lvem(v)
+            v.tags.add(*(cls.tag1, cls.tag2))
+            if k.endswith('public'):
+                expose_log_to_public(v)
 
-    def test_internal_user_get(self):
+    def test_internal_user_get_log_tag_detail(self):
         """Internal user can get all tags for all logs for all superevents"""
         for s in Superevent.objects.all():
             for l in s.log_set.all():
                 for t in l.tags.all():
-                    url = reverse(('api:default:superevents:'
-                        'superevent-log-tag-detail'),
+                    url = v_reverse('superevents:superevent-log-tag-detail',
                         args=[s.superevent_id, l.N, t.name])
                     response = self.request_as_user(url, "GET",
                         self.internal_user)
                     self.assertEqual(response.status_code, 200)
                     self.assertEqual(response.data['name'], t.name)
 
-    def test_lvem_user_get_tags_for_hidden_superevent(self):
+    def test_lvem_user_get_log_tag_detail_for_hidden_superevent(self):
         """LV-EM user can't get tags for any logs on hidden superevent"""
         for l in self.internal_superevent.log_set.all():
             for t in l.tags.all():
-                url = reverse(('api:default:superevents:'
-                    'superevent-log-tag-detail'),
+                url = v_reverse('superevents:superevent-log-tag-detail',
                     args=[self.internal_superevent.superevent_id, l.N, t.name])
                 response = self.request_as_user(url, "GET", self.lvem_user)
                 self.assertEqual(response.status_code, 404)
 
-    def test_lvem_user_get_tags_for_exposed_superevent(self):
+    def test_lvem_user_get_log_tag_detail_for_exposed_superevent(self):
         """
-        LV-EM user can only get tags for exposed logs on exposed superevent
+        LV-EM user can only get tags for exposed logs on exposed superevents
         """
+        # LV-EM superevent
         for l in self.lvem_superevent.log_set.all():
             for t in l.tags.all():
-                url = reverse(('api:default:superevents:'
-                    'superevent-log-tag-detail'),
+                url = v_reverse('superevents:superevent-log-tag-detail',
                     args=[self.lvem_superevent.superevent_id, l.N, t.name])
                 response = self.request_as_user(url, "GET", self.lvem_user)
-                if (l == self.lvem_superevent_exposed_log):
+                if (l in [self.log_dict['lvem_lvem'],
+                    self.log_dict['lvem_public']]):
+                    self.assertEqual(response.status_code, 200)
+                    self.assertEqual(response.data['name'], t.name)
+                else:
+                    self.assertEqual(response.status_code, 404)
+
+        # Public superevent
+        for l in self.public_superevent.log_set.all():
+            for t in l.tags.all():
+                url = v_reverse('superevents:superevent-log-tag-detail',
+                    args=[self.public_superevent.superevent_id, l.N, t.name])
+                response = self.request_as_user(url, "GET", self.lvem_user)
+                if (l in [self.log_dict['public_lvem'],
+                    self.log_dict['public_public']]):
                     self.assertEqual(response.status_code, 200)
                     self.assertEqual(response.data['name'], t.name)
                 else:
                     self.assertEqual(response.status_code, 404)
 
-    def test_public_user_get_tags_for_hidden_superevent(self):
+    def test_public_user_get_log_tag_detail_for_hidden_superevent(self):
         """Public user can't get tags for any logs for hidden superevent"""
+        # Internal superevent
         for l in self.internal_superevent.log_set.all():
             for t in l.tags.all():
-                url = reverse(('api:default:superevents:'
-                    'superevent-log-tag-detail'),
+                url = v_reverse('superevents:superevent-log-tag-detail',
                     args=[self.internal_superevent.superevent_id, l.N, t.name])
                 response = self.request_as_user(url, "GET")
-                self.assertEqual(response.status_code, 403)
-                # TODO: this will be 404 in the future
+                self.assertEqual(response.status_code, 404)
 
-    def test_public_user_get_tags_for_exposed_superevent(self):
+        # LV-EM superevent
+        for l in self.lvem_superevent.log_set.all():
+            for t in l.tags.all():
+                url = v_reverse('superevents:superevent-log-tag-detail',
+                    args=[self.lvem_superevent.superevent_id, l.N, t.name])
+                response = self.request_as_user(url, "GET")
+                self.assertEqual(response.status_code, 404)
+
+    def test_public_user_get_log_tag_detail_for_exposed_superevent(self):
         """
         Public user can only get tags for exposed logs on exposed superevent
         """
-        # TODO
-        pass
+        # Public superevent
+        for l in self.public_superevent.log_set.all():
+            for t in l.tags.all():
+                url = v_reverse('superevents:superevent-log-tag-detail',
+                    args=[self.public_superevent.superevent_id, l.N, t.name])
+                response = self.request_as_user(url, "GET")
+                if (l == self.log_dict['public_public']):
+                    self.assertEqual(response.status_code, 200)
+                    self.assertEqual(response.data['name'], t.name)
+                else:
+                    self.assertEqual(response.status_code, 404)
 
-    def test_internal_user_remove_tag(self):
+    def test_internal_user_remove_log_tag(self):
         """Internal user can remove tags from superevent logs"""
         url = v_reverse('superevents:superevent-log-tag-detail',
             args=[self.internal_superevent.superevent_id,
-            self.internal_superevent_exposed_log.N, self.tag1.name])
+            self.log_dict['internal_lvem'].N, self.tag1.name])
         response = self.request_as_user(url, "DELETE", self.internal_user)
         self.assertEqual(response.status_code, 204)
 
@@ -1883,7 +2352,7 @@ class TestSupereventLogTagDetail(AccessManagersGroupAndUserSetup,
         # Make request and check response data
         url = v_reverse('superevents:superevent-log-tag-detail',
             args=[self.internal_superevent.superevent_id,
-            self.internal_superevent_exposed_log.N, self.tag1.name])
+            self.log_dict['internal_lvem'].N, self.tag1.name])
         response = self.request_as_user(url, "DELETE", self.lvem_user)
         self.assertEqual(response.status_code, 404)
 
@@ -1907,7 +2376,7 @@ class TestSupereventLogTagDetail(AccessManagersGroupAndUserSetup,
         # Exposed log on exposed superevent
         url = v_reverse('superevents:superevent-log-tag-detail',
             args=[self.lvem_superevent.superevent_id,
-            self.lvem_superevent_exposed_log.N, self.tag1.name])
+            self.log_dict['lvem_lvem'].N, self.tag1.name])
         response = self.request_as_user(url, "DELETE", self.lvem_user)
         self.assertEqual(response.status_code, 403)
 
@@ -1916,18 +2385,23 @@ class TestSupereventLogTagDetail(AccessManagersGroupAndUserSetup,
         # Make request and check response data
         url = v_reverse('superevents:superevent-log-tag-detail',
             args=[self.internal_superevent.superevent_id,
-            self.internal_superevent_exposed_log.N, self.tag1.name])
+            self.log_dict['internal_public'].N, self.tag1.name])
         response = self.request_as_user(url, "DELETE")
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be 404 in the future
+        self.assertEqual(response.status_code, 404)
 
     def test_public_user_remove_tag_from_log_on_exposed_superevent(self):
         """Public user can't remove tag from logs on exposed superevent"""
-        # TODO
-        pass
+        # Make request and check response data
+        url = v_reverse('superevents:superevent-log-tag-detail',
+            args=[self.public_superevent.superevent_id,
+            self.log_dict['public_public'].N, self.tag1.name])
+        response = self.request_as_user(url, "DELETE")
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided', response.content)
 
 
 class TestSupereventVOEventList(SupereventSetup, GraceDbApiTestBase):
+    """Test getting VOEvent list and creating VOEvents"""
 
     @classmethod
     def setUpTestData(cls):
@@ -1942,6 +2416,10 @@ class TestSupereventVOEventList(SupereventSetup, GraceDbApiTestBase):
             superevent=cls.lvem_superevent, voevent_type='IN')
         voevent4 = VOEvent.objects.create(issuer=cls.internal_user,
             superevent=cls.lvem_superevent, voevent_type='UP')
+        voevent5 = VOEvent.objects.create(issuer=cls.internal_user,
+            superevent=cls.public_superevent, voevent_type='IN')
+        voevent6 = VOEvent.objects.create(issuer=cls.internal_user,
+            superevent=cls.public_superevent, voevent_type='UP')
 
         # Need the 'em_follow' tag since it is automatically applied
         # to voevent-related log messages
@@ -1961,7 +2439,7 @@ class TestSupereventVOEventList(SupereventSetup, GraceDbApiTestBase):
             'CoincComment': False,
         }
 
-    def test_internal_user_get_list(self):
+    def test_internal_user_get_voevent_list(self):
         """Internal user can get all VOEvents for all superevents"""
         for s in Superevent.objects.all():
             url = v_reverse('superevents:superevent-voevent-list',
@@ -1976,15 +2454,16 @@ class TestSupereventVOEventList(SupereventSetup, GraceDbApiTestBase):
             for v in s.voevent_set.all():
                 self.assertIn(v.N, voevent_nums)
 
-    def test_lvem_user_get_list_for_hidden_superevent(self):
+    def test_lvem_user_get_voevent_list_for_hidden_superevent(self):
         """LV-EM user can't get any VOEvents for hidden superevents"""
         url = v_reverse('superevents:superevent-voevent-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.lvem_user)
         self.assertEqual(response.status_code, 404)
 
-    def test_lvem_user_get_list_for_exposed_superevent(self):
+    def test_lvem_user_get_voevent_list_for_exposed_superevent(self):
         """LV-EM user can get all VOEvents for exposed superevents"""
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-voevent-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.lvem_user)
@@ -1996,18 +2475,45 @@ class TestSupereventVOEventList(SupereventSetup, GraceDbApiTestBase):
         for v in self.lvem_superevent.voevent_set.all():
             self.assertIn(v.N, voevent_nums)
 
-    def test_public_user_get_list_for_hidden_superevent(self):
+        # Public superevent
+        url = v_reverse('superevents:superevent-voevent-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET", self.lvem_user)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['voevents']),
+            self.public_superevent.voevent_set.count())
+        # Check individual voevent presence by N values
+        voevent_nums = [v['N'] for v in response.data['voevents']]
+        for v in self.public_superevent.voevent_set.all():
+            self.assertIn(v.N, voevent_nums)
+
+    def test_public_user_get_voevent_list_for_hidden_superevent(self):
         """Public user can't get any VOEvents for hidden superevents"""
+        # Internal superevent
         url = v_reverse('superevents:superevent-voevent-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "GET")
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be 404 in the future
+        self.assertEqual(response.status_code, 404)
 
-    def test_public_user_get_list_for_exposed_superevent(self):
+        # LV-EM superevent
+        url = v_reverse('superevents:superevent-voevent-list',
+            args=[self.lvem_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 404)
+
+    def test_public_user_get_voevent_list_for_exposed_superevent(self):
         """Public user can get all VOEvents for exposed superevents"""
-        # TODO
-        pass
+        # Public superevent
+        url = v_reverse('superevents:superevent-voevent-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['voevents']),
+            self.public_superevent.voevent_set.count())
+        # Check individual voevent presence by N values
+        voevent_nums = [v['N'] for v in response.data['voevents']]
+        for v in self.public_superevent.voevent_set.all():
+            self.assertIn(v.N, voevent_nums)
 
     def test_internal_user_create_voevent(self):
         """Internal user can create VOEvents for all superevents"""
@@ -2042,16 +2548,20 @@ class TestSupereventVOEventList(SupereventSetup, GraceDbApiTestBase):
         url = v_reverse('superevents:superevent-voevent-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "POST", data=self.voevent_data)
-        self.assertEqual(response.status_code, 403)
-        # TODO: this will be 404 in the future
+        self.assertEqual(response.status_code, 404)
 
     def test_public_user_create_voevent_for_exposed_superevent(self):
         """Public user can't create VOEvents for exposed superevents"""
-        # TODO
-        pass
+        url = v_reverse('superevents:superevent-voevent-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "POST", data=self.voevent_data)
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided',
+            response.data['detail'])
 
 
 class TestSupereventVOEventDetail(SupereventSetup, GraceDbApiTestBase):
+    """Test getting VOEvent details"""
 
     @classmethod
     def setUpTestData(cls):
@@ -2066,13 +2576,16 @@ class TestSupereventVOEventDetail(SupereventSetup, GraceDbApiTestBase):
             superevent=cls.lvem_superevent, voevent_type='IN')
         voevent4 = VOEvent.objects.create(issuer=cls.internal_user,
             superevent=cls.lvem_superevent, voevent_type='UP')
+        voevent5 = VOEvent.objects.create(issuer=cls.internal_user,
+            superevent=cls.public_superevent, voevent_type='IN')
+        voevent6 = VOEvent.objects.create(issuer=cls.internal_user,
+            superevent=cls.public_superevent, voevent_type='UP')
 
     def test_internal_user_get_detail(self):
         """Internal user can get all VOEvent details for all superevents"""
         for s in Superevent.objects.all():
             for v in s.voevent_set.all():
-                url = reverse(('api:default:superevents:'
-                    'superevent-voevent-detail'),
+                url = v_reverse('superevents:superevent-voevent-detail',
                     args=[s.superevent_id, v.N])
                 response = self.request_as_user(url, "GET", self.internal_user)
                 self.assertEqual(response.status_code, 200)
@@ -2089,6 +2602,7 @@ class TestSupereventVOEventDetail(SupereventSetup, GraceDbApiTestBase):
 
     def test_lvem_user_get_detail_for_exposed_superevent(self):
         """LV-EM user can get all VOEvent details for exposed superevents"""
+        # LV-EM superevent
         for v in self.lvem_superevent.voevent_set.all():
             url = v_reverse('superevents:superevent-voevent-detail',
                 args=[self.lvem_superevent.superevent_id, v.N])
@@ -2097,22 +2611,44 @@ class TestSupereventVOEventDetail(SupereventSetup, GraceDbApiTestBase):
             self.assertEqual(response.data['N'], v.N)
             self.assertEqual(response.data['voevent_type'], v.voevent_type)
 
+        # Public superevent
+        for v in self.public_superevent.voevent_set.all():
+            url = v_reverse('superevents:superevent-voevent-detail',
+                args=[self.public_superevent.superevent_id, v.N])
+            response = self.request_as_user(url, "GET", self.lvem_user)
+            self.assertEqual(response.status_code, 200)
+            self.assertEqual(response.data['N'], v.N)
+            self.assertEqual(response.data['voevent_type'], v.voevent_type)
+
     def test_public_user_get_list_for_hidden_superevent(self):
         """Public user can't get any VOEvent details for hidden superevents"""
+        # Internal superevent
         v = self.internal_superevent.voevent_set.first()
         url = v_reverse('superevents:superevent-voevent-detail',
             args=[self.internal_superevent.superevent_id, v.N])
         response = self.request_as_user(url, "GET")
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be 404 in the future
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        v = self.lvem_superevent.voevent_set.first()
+        url = v_reverse('superevents:superevent-voevent-detail',
+            args=[self.lvem_superevent.superevent_id, v.N])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 404)
 
     def test_public_user_get_list_for_exposed_superevent(self):
         """Public user can get all VOEvent details for exposed superevents"""
-        # TODO: should this be the case?
-        pass
+        for v in self.public_superevent.voevent_set.all():
+            url = v_reverse('superevents:superevent-voevent-detail',
+                args=[self.public_superevent.superevent_id, v.N])
+            response = self.request_as_user(url, "GET", self.lvem_user)
+            self.assertEqual(response.status_code, 200)
+            self.assertEqual(response.data['N'], v.N)
+            self.assertEqual(response.data['voevent_type'], v.voevent_type)
 
 
 class TestSupereventEMObservationList(SupereventSetup, GraceDbApiTestBase):
+    """Test getting EMObservation list and creating EMObservations"""
 
     @classmethod
     def setUpTestData(cls):
@@ -2130,6 +2666,10 @@ class TestSupereventEMObservationList(SupereventSetup, GraceDbApiTestBase):
             superevent=cls.lvem_superevent, group=cls.emgroup)
         emo4 = EMObservation.objects.create(submitter=cls.internal_user,
             superevent=cls.lvem_superevent, group=cls.emgroup)
+        emo5 = EMObservation.objects.create(submitter=cls.internal_user,
+            superevent=cls.public_superevent, group=cls.emgroup)
+        emo6 = EMObservation.objects.create(submitter=cls.internal_user,
+            superevent=cls.public_superevent, group=cls.emgroup)
 
     @classmethod
     def setUpClass(cls):
@@ -2154,8 +2694,7 @@ class TestSupereventEMObservationList(SupereventSetup, GraceDbApiTestBase):
     def test_internal_user_get_list(self):
         """Internal user can get all EMObservations for all superevents"""
         for s in Superevent.objects.all():
-            url = reverse(('api:default:superevents:'
-                'superevent-emobservation-list'),
+            url = v_reverse('superevents:superevent-emobservation-list',
                 args=[s.superevent_id])
             response = self.request_as_user(url, "GET", self.internal_user)
             self.assertEqual(response.status_code, 200)
@@ -2176,6 +2715,7 @@ class TestSupereventEMObservationList(SupereventSetup, GraceDbApiTestBase):
 
     def test_lvem_user_get_list_for_exposed_superevent(self):
         """LV-EM user can get all EMObservations for exposed superevents"""
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-emobservation-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.lvem_user)
@@ -2187,18 +2727,46 @@ class TestSupereventEMObservationList(SupereventSetup, GraceDbApiTestBase):
         for emo in self.lvem_superevent.emobservation_set.all():
             self.assertIn(emo.N, emo_nums)
 
+        # Public superevent
+        url = v_reverse('superevents:superevent-emobservation-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET", self.lvem_user)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['observations']),
+            self.public_superevent.emobservation_set.count())
+        # Check individual voevent presence by N values
+        api_emo_nums = [emo['N'] for emo in response.data['observations']]
+        db_emo_nums = [emo.N for emo in
+            self.public_superevent.emobservation_set.all()]
+        self.assertItemsEqual(api_emo_nums, db_emo_nums)
+
     def test_public_user_get_list_for_hidden_superevent(self):
         """Public user can't get any EMObservations for hidden superevents"""
+        # Internal superevent
         url = v_reverse('superevents:superevent-emobservation-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "GET")
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be 404 in the future
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        url = v_reverse('superevents:superevent-emobservation-list',
+            args=[self.lvem_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 404)
 
     def test_public_user_get_list_for_exposed_superevent(self):
         """Public user can get all EMObservations for exposed superevents"""
-        # TODO: do we really want this to be the case?
-        pass
+        url = v_reverse('superevents:superevent-emobservation-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['observations']),
+            self.public_superevent.emobservation_set.count())
+        # Check individual voevent presence by N values
+        api_emo_nums = [emo['N'] for emo in response.data['observations']]
+        db_emo_nums = [emo.N for emo in
+            self.public_superevent.emobservation_set.all()]
+        self.assertItemsEqual(api_emo_nums, db_emo_nums)
 
     def test_internal_user_create_emobservation(self):
         """Internal user can create EMObservations for all superevents"""
@@ -2226,6 +2794,8 @@ class TestSupereventEMObservationList(SupereventSetup, GraceDbApiTestBase):
         # document the EMObservation creation and it will be tagged
         # with 'lvem' since it was created by an external user.
         Tag.objects.create(name=settings.EXTERNAL_ACCESS_TAGNAME)
+
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-emobservation-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "POST", self.lvem_user,
@@ -2236,22 +2806,45 @@ class TestSupereventEMObservationList(SupereventSetup, GraceDbApiTestBase):
         self.assertEqual(response.data['group'],
             self.emobservation_data['group'])
 
+        # Public superevent
+        url = v_reverse('superevents:superevent-emobservation-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "POST", self.lvem_user,
+            data=self.emobservation_data)
+        self.assertEqual(response.status_code, 201)
+        self.assertEqual(response.data['comment'],
+            self.emobservation_data['comment'])
+        self.assertEqual(response.data['group'],
+            self.emobservation_data['group'])
+
     def test_public_user_create_emobservation_for_hidden_superevent(self):
         """Public user can't create EMObservations for hidden superevents"""
+        # Internal superevent
         url = v_reverse('superevents:superevent-emobservation-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "POST",
             data=self.emobservation_data)
-        self.assertEqual(response.status_code, 403)
-        # TODO: this will be 404 in the future
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        url = v_reverse('superevents:superevent-emobservation-list',
+            args=[self.lvem_superevent.superevent_id])
+        response = self.request_as_user(url, "POST",
+            data=self.emobservation_data)
+        self.assertEqual(response.status_code, 404)
 
     def test_public_user_create_emobservation_for_exposed_superevent(self):
         """Public user can't create EMObservation for exposed superevents"""
-        # TODO
-        pass
+        url = v_reverse('superevents:superevent-emobservation-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "POST",
+            data=self.emobservation_data)
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided', response.data['detail'])
 
 
 class TestSupereventEMObservationDetail(SupereventSetup, GraceDbApiTestBase):
+    """Test getting EMObservation details"""
 
     @classmethod
     def setUpTestData(cls):
@@ -2274,8 +2867,7 @@ class TestSupereventEMObservationDetail(SupereventSetup, GraceDbApiTestBase):
         """Internal user can get EMObservaion details for all superevents"""
         for s in Superevent.objects.all():
             for emo in s.emobservation_set.all():
-                url = reverse(('api:default:superevents:'
-                    'superevent-emobservation-detail'),
+                url = v_reverse('superevents:superevent-emobservation-detail',
                     args=[s.superevent_id, emo.N])
                 response = self.request_as_user(url, "GET", self.internal_user)
                 self.assertEqual(response.status_code, 200)
@@ -2291,36 +2883,57 @@ class TestSupereventEMObservationDetail(SupereventSetup, GraceDbApiTestBase):
 
     def test_lvem_user_get_detail_for_exposed_superevent(self):
         """LV-EM user can get EMObservation details for exposed superevents"""
+        # LV-EM superevent
         for emo in self.lvem_superevent.emobservation_set.all():
-            url = reverse(('api:default:superevents:'
-                'superevent-emobservation-detail'),
+            url = v_reverse('superevents:superevent-emobservation-detail',
                 args=[self.lvem_superevent.superevent_id, emo.N])
             response = self.request_as_user(url, "GET", self.lvem_user)
             self.assertEqual(response.status_code, 200)
             self.assertEqual(response.data['N'], emo.N)
             self.assertEqual(response.data['group'], emo.group.name)
 
+        # Public superevent
+        for emo in self.public_superevent.emobservation_set.all():
+            url = v_reverse('superevents:superevent-emobservation-detail',
+                args=[self.public_superevent.superevent_id, emo.N])
+            response = self.request_as_user(url, "GET", self.lvem_user)
+            self.assertEqual(response.status_code, 200)
+            self.assertEqual(response.data['N'], emo.N)
+            self.assertEqual(response.data['group'], emo.group.name)
+
     def test_public_user_get_detail_for_hidden_superevent(self):
         """
         Public user can't get any EMObservation details for hidden superevents
         """
+        # Internal superevent
         emo = self.internal_superevent.emobservation_set.first()
-        url = reverse(('api:default:superevents:'
-            'superevent-emobservation-detail'),
+        url = v_reverse('superevents:superevent-emobservation-detail',
             args=[self.internal_superevent.superevent_id, emo.N])
         response = self.request_as_user(url, "GET")
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be 404 in the future
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        emo = self.lvem_superevent.emobservation_set.first()
+        url = v_reverse('superevents:superevent-emobservation-detail',
+            args=[self.lvem_superevent.superevent_id, emo.N])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 404)
 
     def test_public_user_get_detail_for_exposed_superevent(self):
         """
         Public user can get all EMObservation details for exposed superevents
         """
-        # TODO: should this be the case?
-        pass
+        for emo in self.public_superevent.emobservation_set.all():
+            url = v_reverse('superevents:superevent-emobservation-detail',
+                args=[self.public_superevent.superevent_id, emo.N])
+            response = self.request_as_user(url, "GET")
+            self.assertEqual(response.status_code, 200)
+            self.assertEqual(response.data['N'], emo.N)
+            self.assertEqual(response.data['group'], emo.group.name)
 
 
 class TestSupereventFileList(SupereventSetup, GraceDbApiTestBase):
+    """Test getting file list"""
 
     @classmethod
     def setUpTestData(cls):
@@ -2342,8 +2955,14 @@ class TestSupereventFileList(SupereventSetup, GraceDbApiTestBase):
             log4 = create_log(cls.internal_user, 'upload file2',
                 cls.lvem_superevent, filename=cls.file2['filename'],
                 data_file=SimpleUploadedFile.from_dict(cls.file2))
+            log5 = create_log(cls.internal_user, 'upload file1',
+                cls.public_superevent, filename=cls.file1['filename'],
+                data_file=SimpleUploadedFile.from_dict(cls.file1))
+            log6 = create_log(cls.internal_user, 'upload file2',
+                cls.public_superevent, filename=cls.file2['filename'],
+                data_file=SimpleUploadedFile.from_dict(cls.file2))
 
-    def test_internal_get_file_list_for_superevent(self):
+    def test_internal_user_get_list_for_superevent(self):
         """Internal user can see all files for all superevents"""
         for s in Superevent.objects.all():
             url = v_reverse('superevents:superevent-file-list',
@@ -2359,7 +2978,7 @@ class TestSupereventFileList(SupereventSetup, GraceDbApiTestBase):
             for f in file_list:
                 self.assertIn(f, response.data)
 
-    def test_lvem_get_file_list_for_hidden_superevent(self):
+    def test_lvem_user_get_list_for_hidden_superevent(self):
         """LV-EM user can't get file list for hidden superevents"""
         url = v_reverse('superevents:superevent-file-list',
             args=[self.internal_superevent.superevent_id])
@@ -2372,7 +2991,7 @@ class TestSupereventFileList(SupereventSetup, GraceDbApiTestBase):
         response = self.request_as_user(url, "GET", self.lvem_user)
         self.assertEqual(response.status_code, 404)
 
-    def test_lvem_get_file_list_for_exposed_superevent(self):
+    def test_lvem_user_get_list_for_exposed_superevent(self):
         """LV-EM user can get exposed file list for exposed superevents"""
         # Expose a non-symlinked log
         log = self.lvem_superevent.log_set.get(filename=self.file1['filename'],
@@ -2387,7 +3006,7 @@ class TestSupereventFileList(SupereventSetup, GraceDbApiTestBase):
         self.assertEqual(len(response.data), 1)
         self.assertIn(log.versioned_filename, response.data)
 
-    def test_lvem_get_file_list_for_exposed_superevent_symlinked_file(self):
+    def test_lvem_user_get_list_for_exposed_superevent_symlinked_file(self):
         """
         LV-EM user can get exposed file list for exposed superevents, including
         symlinks
@@ -2407,34 +3026,57 @@ class TestSupereventFileList(SupereventSetup, GraceDbApiTestBase):
         self.assertIn(log.versioned_filename, response.data)
         self.assertIn(log.filename, response.data)
 
-    def test_public_get_file_list_for_hidden_superevent(self):
+    def test_public_user_get_list_for_hidden_superevent(self):
         """Public user can't get file list for hidden superevents"""
         url = v_reverse('superevents:superevent-file-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "GET")
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be 404 error in the future
+        self.assertEqual(response.status_code, 404)
 
         # Try exposing a log and make sure it's still a 404
         log = self.internal_superevent.log_set.exclude(filename='').first()
         expose_log_to_public(log)
         response = self.request_as_user(url, "GET")
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be 404 error in the future
+        self.assertEqual(response.status_code, 404)
 
-    def test_public_get_file_list_for_exposed_superevent(self):
+    def test_public_user_get_list_for_exposed_superevent(self):
         """Public user can get exposed file list for exposed superevents"""
-        # TODO
+        # Expose a non-symlinked log
+        log = self.public_superevent.log_set.get(
+            filename=self.file1['filename'], file_version=1)
+        expose_log_to_public(log)
+
+        # Make request and check response
+        url = v_reverse('superevents:superevent-file-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data), 1)
+        self.assertIn(log.versioned_filename, response.data)
 
-    def test_public_get_file_list_for_exposed_superevent_symlinked_file(self):
+    def test_public_user_get_list_for_exposed_superevent_symlinked_file(self):
         """
-        Public user can get exposed file list for exposed superevents, including
-        symlinks
+        Public user can get exposed file list for exposed superevents,
+        including symlinks
         """
-        # TODO
+        # Expose the most recent version of a file, make sure both the
+        # versioned file and non-versioned symlink are shown
+        log = self.public_superevent.log_set.get(
+            filename=self.file1['filename'], file_version=3)
+        expose_log_to_public(log)
+
+        # Make request and get response
+        url = v_reverse('superevents:superevent-file-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data), 2)
+        self.assertIn(log.versioned_filename, response.data)
+        self.assertIn(log.filename, response.data)
 
 
 class TestSupereventFileDetail(SupereventSetup, GraceDbApiTestBase):
+    """Test file downloads"""
 
     @classmethod
     def setUpTestData(cls):
@@ -2456,8 +3098,14 @@ class TestSupereventFileDetail(SupereventSetup, GraceDbApiTestBase):
             log4 = create_log(cls.internal_user, 'upload file2',
                 cls.lvem_superevent, filename=cls.file2['filename'],
                 data_file=SimpleUploadedFile.from_dict(cls.file2))
+            log5 = create_log(cls.internal_user, 'upload file1',
+                cls.public_superevent, filename=cls.file1['filename'],
+                data_file=SimpleUploadedFile.from_dict(cls.file1))
+            log6 = create_log(cls.internal_user, 'upload file2',
+                cls.public_superevent, filename=cls.file2['filename'],
+                data_file=SimpleUploadedFile.from_dict(cls.file2))
 
-    def test_internal_get_file_detail_for_superevent(self):
+    def test_internal_user_get_file_for_superevent(self):
         """Internal user can see all files for all superevents"""
         for s in Superevent.objects.all():
             file_logs = s.log_set.exclude(filename='')
@@ -2470,7 +3118,7 @@ class TestSupereventFileDetail(SupereventSetup, GraceDbApiTestBase):
                 response = self.request_as_user(url, "GET", self.internal_user)
                 self.assertEqual(response.status_code, 200)
 
-    def test_lvem_get_file_detail_for_hidden_superevent(self):
+    def test_lvem_user_get_file_for_hidden_superevent(self):
         """LV-EM user can't get file detail for hidden superevents"""
         file_logs = self.internal_superevent.log_set.exclude(filename='')
         file_list = [l.versioned_filename for l in file_logs]
@@ -2482,7 +3130,7 @@ class TestSupereventFileDetail(SupereventSetup, GraceDbApiTestBase):
             response = self.request_as_user(url, "GET", self.lvem_user)
             self.assertEqual(response.status_code, 404)
 
-    def test_lvem_get_file_detail_for_exposed_superevent(self):
+    def test_lvem_user_get_file_for_exposed_superevent(self):
         """LV-EM user can get only exposed files for exposed superevents"""
         # Expose a non-symlinked log
         log = self.lvem_superevent.log_set.get(filename=self.file1['filename'],
@@ -2505,7 +3153,7 @@ class TestSupereventFileDetail(SupereventSetup, GraceDbApiTestBase):
         response = self.request_as_user(url, "GET", self.lvem_user)
         self.assertEqual(response.status_code, 404)
 
-    def test_lvem_get_file_detail_for_exposed_superevent_symlinked_file(self):
+    def test_lvem_get_symlinked_file_for_exposed_superevent(self):
         """
         LV-EM user can get exposed files for exposed superevents, including
         symlinks
@@ -2529,8 +3177,9 @@ class TestSupereventFileDetail(SupereventSetup, GraceDbApiTestBase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.data, data1)
 
-    def test_public_get_file_detail_for_hidden_superevent(self):
+    def test_public_get_file_for_hidden_superevent(self):
         """Public user can't get files for hidden superevents"""
+        # Internal superevent
         file_logs = self.internal_superevent.log_set.exclude(filename='')
         file_list = [l.versioned_filename for l in file_logs]
         symlinks = list(set([fl.filename for fl in file_logs]))
@@ -2539,27 +3188,77 @@ class TestSupereventFileDetail(SupereventSetup, GraceDbApiTestBase):
             url = v_reverse('superevents:superevent-file-detail',
                 args=[self.internal_superevent.superevent_id, f])
             response = self.request_as_user(url, "GET")
-            self.assertEqual(response.status_code, 403)
-            # TODO: will be 404 in the future
+            self.assertEqual(response.status_code, 404)
 
-    def test_public_get_file_detail_for_exposed_superevent(self):
+        # LV-EM superevent
+        file_logs = self.lvem_superevent.log_set.exclude(filename='')
+        file_list = [l.versioned_filename for l in file_logs]
+        symlinks = list(set([fl.filename for fl in file_logs]))
+        file_list.extend(symlinks)
+        for f in file_list:
+            url = v_reverse('superevents:superevent-file-detail',
+                args=[self.lvem_superevent.superevent_id, f])
+            response = self.request_as_user(url, "GET")
+            self.assertEqual(response.status_code, 404)
+
+    def test_public_get_file_for_exposed_superevent(self):
         """Public user can get exposed files for exposed superevents"""
-        # TODO
+        # Expose a non-symlinked log
+        log = self.public_superevent.log_set.get(
+            filename=self.file1['filename'], file_version=1)
+        expose_log_to_public(log)
 
-    def test_public_get_file_detail_for_exposed_superevent_symlinked_file(self):
+        # Make request and check response
+        url = v_reverse('superevents:superevent-file-detail',
+            args=[self.public_superevent.superevent_id,
+            log.versioned_filename])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 200)
+        data1 = response.data
+
+        # Try with a different version (not exposed)
+        log2 = self.public_superevent.log_set.get(filename=
+            self.file1['filename'], file_version=0)
+        # Make request and check response
+        url = v_reverse('superevents:superevent-file-detail',
+            args=[self.public_superevent.superevent_id,
+            log2.versioned_filename])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 404)
+
+    def test_public_get_symlinked_file_for_exposed_superevent(self):
         """
         Public user can get exposed files for exposed superevents, including
         symlinks
         """
-        # TODO
+        # Expose a non-symlinked log
+        log = self.public_superevent.log_set.get(
+            filename=self.file1['filename'], file_version=3)
+        expose_log_to_public(log)
+
+        # Make request and check response
+        url = v_reverse('superevents:superevent-file-detail',
+            args=[self.public_superevent.superevent_id,
+            log.versioned_filename])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 200)
+        data1 = response.data
+
+        # Repeat with non-versioned filename
+        url = v_reverse('superevents:superevent-file-detail',
+            args=[self.public_superevent.superevent_id, log.filename])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.data, data1)
 
 
 class TestSupereventGroupObjectPermissionList(SupereventSetup,
     GraceDbApiTestBase):
+    """Test viewing superevent permissions"""
 
     def test_internal_user_get_permissions(self):
         """Internal user can view permissions list for all superevents"""
-        # Internal
+        # Internal superevent
         url = v_reverse('superevents:superevent-permission-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.internal_user)
@@ -2567,7 +3266,7 @@ class TestSupereventGroupObjectPermissionList(SupereventSetup,
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.data['permissions'], [])
 
-        # Exposed
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-permission-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.internal_user)
@@ -2575,25 +3274,47 @@ class TestSupereventGroupObjectPermissionList(SupereventSetup,
         data = response.data['permissions']
         groups = [p['group'] for p in data]
         self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(data), 2)
+        self.assertEqual(groups.count(settings.LVEM_OBSERVERS_GROUP), 2)
+
+        # Public superevent
+        url = v_reverse('superevents:superevent-permission-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET", self.internal_user)
+        # Check response and data
+        data = response.data['permissions']
+        groups = [p['group'] for p in data]
+        self.assertEqual(response.status_code, 200)
         self.assertEqual(len(data), 3)
         self.assertEqual(groups.count(settings.PUBLIC_GROUP), 1)
         self.assertEqual(groups.count(settings.LVEM_OBSERVERS_GROUP), 2)
 
     def test_lvem_user_get_permissions(self):
         """LV-EM user can't get permission list"""
-        # Internal
+        # Internal superevent
         url = v_reverse('superevents:superevent-permission-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.lvem_user)
         # Check response and data
         self.assertEqual(response.status_code, 404)
 
-        # Exposed
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-permission-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.lvem_user)
         # Check response and data
         self.assertEqual(response.status_code, 403)
+        self.assertIn('not allowed to view superevent permissions',
+            response.content)
+
+        # Public superevent
+        url = v_reverse('superevents:superevent-permission-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET", self.lvem_user)
+        # Check response and data
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('not allowed to view superevent permissions',
+            response.content)
 
     def test_public_user_get_permissions(self):
         """Public user can't get permission list"""
@@ -2602,14 +3323,27 @@ class TestSupereventGroupObjectPermissionList(SupereventSetup,
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "GET")
         # Check response and data
-        self.assertEqual(response.status_code, 403)
-        # TODO: this will be 404 in the future
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        url = v_reverse('superevents:superevent-permission-list',
+            args=[self.lvem_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        # Check response and data
+        self.assertEqual(response.status_code, 404)
 
-        # Exposed: TODO
+        # Public superevent
+        url = v_reverse('superevents:superevent-permission-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        # Check response and data
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided', response.content)
 
 
 class TestSupereventGroupObjectPermissionModify(SupereventSetup,
     AccessManagersGroupAndUserSetup, GraceDbApiTestBase):
+    """Test modifying superevent permissions"""
 
     def test_internal_user_expose_internal_superevent(self):
         """Internal user can't modify permissions to expose superevent"""
@@ -2656,9 +3390,9 @@ class TestSupereventGroupObjectPermissionModify(SupereventSetup,
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.data, [])
 
-    def test_lvem_user_permissions_modify(self):
+    def test_lvem_user_modify_permissions(self):
         """LV-EM user can't modify permissions"""
-        # Internal - expose
+        # Internal superevent - expose
         url = v_reverse('superevents:superevent-permission-modify',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "POST", self.lvem_user,
@@ -2666,27 +3400,35 @@ class TestSupereventGroupObjectPermissionModify(SupereventSetup,
         # Check response and data
         self.assertEqual(response.status_code, 404)
 
-        # Exposed
+        # LV-EM superevent - hide
         url = v_reverse('superevents:superevent-permission-modify',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "POST", self.lvem_user,
             data={'action': 'hide'})
         # Check response and data
         self.assertEqual(response.status_code, 403)
+        self.assertIn('not allowed to hide superevent', response.content)
 
-    def test_public_user_get_permissions(self):
-        # Internal
+    def test_public_user_modify_permissions(self):
+        """Public user can't modify permissions"""
+        # Internal superevent - expose
         url = v_reverse('superevents:superevent-permission-modify',
             args=[self.internal_superevent.superevent_id])
-        response = self.request_as_user(url, "GET")
+        response = self.request_as_user(url, "POST", data={'action': 'expose'})
         # Check response and data
-        self.assertEqual(response.status_code, 403)
-        # TODO: this will be 404 in the future
+        self.assertEqual(response.status_code, 404)
 
-        # Exposed: TODO
+        # Public superevent - hide
+        url = v_reverse('superevents:superevent-permission-modify',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "POST", data={'action': 'hide'})
+        # Check response and data
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided', response.content)
 
 
 class TestSupereventSignoffList(SupereventSetup, GraceDbApiTestBase):
+    """Test getting signoff list"""
 
     @classmethod
     def setUpTestData(cls):
@@ -2703,8 +3445,13 @@ class TestSupereventSignoffList(SupereventSetup, GraceDbApiTestBase):
             status=Signoff.OPERATOR_STATUS_OK,
             instrument=Signoff.INSTRUMENT_H1,
             signoff_type=Signoff.SIGNOFF_TYPE_OPERATOR)
+        cls.public_signoff = Signoff.objects.create(
+            submitter=cls.internal_user, superevent=cls.public_superevent,
+            status=Signoff.OPERATOR_STATUS_OK,
+            instrument=Signoff.INSTRUMENT_H1,
+            signoff_type=Signoff.SIGNOFF_TYPE_OPERATOR)
 
-    def test_internal_get_signoff_list(self):
+    def test_internal_user_get_signoff_list(self):
         """Internal user can view list of signoffs"""
         url = v_reverse('superevents:superevent-signoff-list',
             args=[self.internal_superevent.superevent_id])
@@ -2713,15 +3460,16 @@ class TestSupereventSignoffList(SupereventSetup, GraceDbApiTestBase):
         self.assertEqual(len(response.data['signoffs']),
             self.internal_superevent.signoff_set.count())
 
-    def test_lvem_get_hidden_signoff_list(self):
+    def test_lvem_user_get_hidden_signoff_list(self):
         """LV-EM user can't view list of signoffs for hidden superevent"""
         url = v_reverse('superevents:superevent-signoff-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.lvem_user)
         self.assertEqual(response.status_code, 404)
 
-    def test_lvem_get_exposed_signoff_list(self):
-        """LV-EM user can't view list of signoffs for exposed superevent"""
+    def test_lvem_user_get_signoff_list_for_exposed_superevent(self):
+        """LV-EM user can't view list of signoffs for exposed superevents"""
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-signoff-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "GET", self.lvem_user)
@@ -2729,21 +3477,41 @@ class TestSupereventSignoffList(SupereventSetup, GraceDbApiTestBase):
         self.assertIn('do not have permission to view superevent signoffs',
             response.data['detail'])
 
+        # Public superevent
+        url = v_reverse('superevents:superevent-signoff-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET", self.lvem_user)
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('do not have permission to view superevent signoffs',
+            response.data['detail'])
+
     def test_public_get_hidden_signoff_list(self):
         """Public user can't view list of signoffs for hidden superevent"""
+        # Internal superevent
         url = v_reverse('superevents:superevent-signoff-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "GET")
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be 404 error in the future
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        url = v_reverse('superevents:superevent-signoff-list',
+            args=[self.lvem_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 404)
 
     def test_public_get_exposed_signoff_list(self):
         """Public user can't view list of signoffs for exposed superevent"""
-        # TODO
-        pass
+        # Public superevent
+        url = v_reverse('superevents:superevent-signoff-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided',
+            response.data['detail'])
 
 
 class TestSupereventSignoffDetail(SupereventSetup, GraceDbApiTestBase):
+    """Test getting signoff details"""
 
     @classmethod
     def setUpTestData(cls):
@@ -2760,6 +3528,11 @@ class TestSupereventSignoffDetail(SupereventSetup, GraceDbApiTestBase):
             status=Signoff.OPERATOR_STATUS_OK,
             instrument=Signoff.INSTRUMENT_H1,
             signoff_type=Signoff.SIGNOFF_TYPE_OPERATOR)
+        cls.public_signoff = Signoff.objects.create(
+            submitter=cls.internal_user, superevent=cls.public_superevent,
+            status=Signoff.OPERATOR_STATUS_OK,
+            instrument=Signoff.INSTRUMENT_H1,
+            signoff_type=Signoff.SIGNOFF_TYPE_OPERATOR)
 
     def test_internal_user_get_signoff_detail(self):
         """Internal user can view signoff detail"""
@@ -2786,32 +3559,59 @@ class TestSupereventSignoffDetail(SupereventSetup, GraceDbApiTestBase):
         self.assertEqual(response.status_code, 404)
         
     def test_lvem_user_get_signoff_detail_for_exposed_superevent(self):
-        """LV-EM user can't view signoff detail for exposed superevent"""
+        """LV-EM user can't view signoff detail for exposed superevents"""
+        # LV-EM superevent
         signoff = self.lvem_superevent.signoff_set.first()
         url = v_reverse('superevents:superevent-signoff-detail',
             args=[self.lvem_superevent.superevent_id,
             signoff.signoff_type + signoff.instrument])
         response = self.request_as_user(url, "GET", self.lvem_user)
         self.assertEqual(response.status_code, 403)
-        
+        self.assertIn('do not have permission to view superevent signoffs',
+            response.content)
+
+        # Public superevent
+        signoff = self.public_superevent.signoff_set.first()
+        url = v_reverse('superevents:superevent-signoff-detail',
+            args=[self.public_superevent.superevent_id,
+            signoff.signoff_type + signoff.instrument])
+        response = self.request_as_user(url, "GET", self.lvem_user)
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('do not have permission to view superevent signoffs',
+            response.content)
+
     def test_public_user_get_signoff_detail_for_hidden_superevent(self):
         """Public user can't view signoff detail for hidden superevent"""
+        # Internal superevent
         signoff = self.internal_superevent.signoff_set.first()
         url = v_reverse('superevents:superevent-signoff-detail',
             args=[self.internal_superevent.superevent_id,
             signoff.signoff_type + signoff.instrument])
         response = self.request_as_user(url, "GET")
-        self.assertEqual(response.status_code, 403)
-        # TODO: will be 404 in the future
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        signoff = self.lvem_superevent.signoff_set.first()
+        url = v_reverse('superevents:superevent-signoff-detail',
+            args=[self.lvem_superevent.superevent_id,
+            signoff.signoff_type + signoff.instrument])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 404)
         
     def test_public_user_get_signoff_detail_for_exposed_superevent(self):
         """Public user can't view signoff detail for exposed superevent"""
-        # TODO
-        pass
+        signoff = self.public_superevent.signoff_set.first()
+        url = v_reverse('superevents:superevent-signoff-detail',
+            args=[self.public_superevent.superevent_id,
+            signoff.signoff_type + signoff.instrument])
+        response = self.request_as_user(url, "GET")
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided', response.content)
 
 
 class TestSupereventSignoffCreation(SignoffGroupsAndUsersSetup,
     SupereventSetup, GraceDbApiTestBase):
+    """Test signoff creation"""
 
     @classmethod
     def setUpTestData(cls):
@@ -2902,6 +3702,7 @@ class TestSupereventSignoffCreation(SignoffGroupsAndUsersSetup,
 
     def test_lvem_user_create_signoff_for_exposed_superevent(self):
         """LV-EM user can't create signoffs for exposed superevents"""
+        # LV-EM superevent
         url = v_reverse('superevents:superevent-signoff-list',
             args=[self.lvem_superevent.superevent_id])
         response = self.request_as_user(url, "POST", self.lvem_user,
@@ -2910,22 +3711,42 @@ class TestSupereventSignoffCreation(SignoffGroupsAndUsersSetup,
         self.assertIn('do not have permission to create superevent signoffs',
             response.data['detail'])
 
+        # Public superevent
+        url = v_reverse('superevents:superevent-signoff-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "POST", self.lvem_user,
+            data=self.signoff_data)
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('do not have permission to create superevent signoffs',
+            response.data['detail'])
+
     def test_public_user_create_signoff_for_hidden_superevent(self):
         """Public user can't create signoffs for hidden superevents"""
+        # Internal superevent
         url = v_reverse('superevents:superevent-signoff-list',
             args=[self.internal_superevent.superevent_id])
         response = self.request_as_user(url, "POST", data=self.signoff_data)
-        self.assertEqual(response.status_code, 403)
-        # TODO: this will be 404 in the future
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        url = v_reverse('superevents:superevent-signoff-list',
+            args=[self.lvem_superevent.superevent_id])
+        response = self.request_as_user(url, "POST", data=self.signoff_data)
+        self.assertEqual(response.status_code, 404)
 
     def test_public_user_create_signoff_for_exposed_superevent(self):
         """Public user can't create signoffs for exposed superevents"""
-        # TODO
-        pass
+        url = v_reverse('superevents:superevent-signoff-list',
+            args=[self.public_superevent.superevent_id])
+        response = self.request_as_user(url, "POST", data=self.signoff_data)
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided',
+            response.data['detail'])
 
 
 class TestSupereventSignoffUpdate(SignoffGroupsAndUsersSetup,
     SupereventSetup, GraceDbApiTestBase):
+    """Test signoff updates"""
 
     @classmethod
     def setUpTestData(cls):
@@ -2942,6 +3763,11 @@ class TestSupereventSignoffUpdate(SignoffGroupsAndUsersSetup,
             status=Signoff.OPERATOR_STATUS_OK,
             instrument=Signoff.INSTRUMENT_H1,
             signoff_type=Signoff.SIGNOFF_TYPE_OPERATOR)
+        cls.public_signoff = Signoff.objects.create(
+            submitter=cls.internal_user, superevent=cls.public_superevent,
+            status=Signoff.OPERATOR_STATUS_OK,
+            instrument=Signoff.INSTRUMENT_H1,
+            signoff_type=Signoff.SIGNOFF_TYPE_OPERATOR)
 
         # Create a few labels for testing
         Label.objects.get_or_create(name='H1OPS')
@@ -2954,10 +3780,12 @@ class TestSupereventSignoffUpdate(SignoffGroupsAndUsersSetup,
         # Add H1OPS and ADVREQ to superevents so that signoffs can be created
         Labelling.objects.create(superevent=cls.internal_superevent,
             label=h1ok, creator=cls.internal_user)
-        Labelling.objects.create(superevent=cls.lvem_superevent,
-            label=h1ok, creator=cls.internal_user)
         Labelling.objects.create(superevent=cls.internal_superevent,
             label=advok, creator=cls.internal_user)
+        Labelling.objects.create(superevent=cls.lvem_superevent,
+            label=h1ok, creator=cls.internal_user)
+        Labelling.objects.create(superevent=cls.public_superevent,
+            label=h1ok, creator=cls.internal_user)
 
         # Also need em_follow tag since signoff-related log messages are
         # tagged with it
@@ -3042,6 +3870,7 @@ class TestSupereventSignoffUpdate(SignoffGroupsAndUsersSetup,
 
     def test_lvem_user_update_signoff_for_exposed_superevent(self):
         """LV-EM user can't update signoffs for exposed superevents"""
+        # LV-EM superevent
         signoff = self.lvem_signoff
         url = v_reverse('superevents:superevent-signoff-detail',
             args=[signoff.superevent.superevent_id,
@@ -3052,24 +3881,49 @@ class TestSupereventSignoffUpdate(SignoffGroupsAndUsersSetup,
         self.assertIn('do not have permission to change superevent signoffs',
             response.data['detail'])
 
+        # Public superevent
+        signoff = self.public_signoff
+        url = v_reverse('superevents:superevent-signoff-detail',
+            args=[signoff.superevent.superevent_id,
+            signoff.signoff_type + signoff.instrument])
+        response = self.request_as_user(url, "PATCH", self.lvem_user,
+            data=self.signoff_data)
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('do not have permission to change superevent signoffs',
+            response.data['detail'])
+
     def test_public_user_update_signoff_for_hidden_superevent(self):
         """Public user can't update signoffs for hidden superevents"""
+        # Internal superevent
         signoff = self.internal_signoff
         url = v_reverse('superevents:superevent-signoff-detail',
             args=[signoff.superevent.superevent_id,
             signoff.signoff_type + signoff.instrument])
         response = self.request_as_user(url, "PATCH", data=self.signoff_data)
-        self.assertEqual(response.status_code, 403)
-        # TODO: this will be 404 in the future
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        signoff = self.lvem_signoff
+        url = v_reverse('superevents:superevent-signoff-detail',
+            args=[signoff.superevent.superevent_id,
+            signoff.signoff_type + signoff.instrument])
+        response = self.request_as_user(url, "PATCH", data=self.signoff_data)
+        self.assertEqual(response.status_code, 404)
 
     def test_public_user_update_signoff_for_exposed_superevent(self):
         """Public user can't update signoffs for exposed superevents"""
-        # TODO
-        pass
+        signoff = self.public_signoff
+        url = v_reverse('superevents:superevent-signoff-detail',
+            args=[signoff.superevent.superevent_id,
+            signoff.signoff_type + signoff.instrument])
+        response = self.request_as_user(url, "PATCH", data=self.signoff_data)
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided', response.data['detail'])
 
 
 class TestSupereventSignoffDeletion(SignoffGroupsAndUsersSetup,
     SupereventSetup, GraceDbApiTestBase):
+    """Test signoff deletion"""
 
     @classmethod
     def setUpTestData(cls):
@@ -3086,6 +3940,11 @@ class TestSupereventSignoffDeletion(SignoffGroupsAndUsersSetup,
             status=Signoff.OPERATOR_STATUS_OK,
             instrument=Signoff.INSTRUMENT_H1,
             signoff_type=Signoff.SIGNOFF_TYPE_OPERATOR)
+        cls.public_signoff = Signoff.objects.create(
+            submitter=cls.internal_user, superevent=cls.public_superevent,
+            status=Signoff.OPERATOR_STATUS_OK,
+            instrument=Signoff.INSTRUMENT_H1,
+            signoff_type=Signoff.SIGNOFF_TYPE_OPERATOR)
 
         # Create a few labels for testing
         Label.objects.get_or_create(name='H1OPS')
@@ -3098,10 +3957,12 @@ class TestSupereventSignoffDeletion(SignoffGroupsAndUsersSetup,
         # Add H1OPS and ADVREQ to superevents so that signoffs can be created
         Labelling.objects.create(superevent=cls.internal_superevent,
             label=h1ok, creator=cls.internal_user)
-        Labelling.objects.create(superevent=cls.lvem_superevent,
-            label=h1ok, creator=cls.internal_user)
         Labelling.objects.create(superevent=cls.internal_superevent,
             label=advok, creator=cls.internal_user)
+        Labelling.objects.create(superevent=cls.lvem_superevent,
+            label=h1ok, creator=cls.internal_user)
+        Labelling.objects.create(superevent=cls.public_superevent,
+            label=h1ok, creator=cls.internal_user)
 
         # Also need em_follow tag since signoff-related log messages are
         # tagged with it
@@ -3167,6 +4028,7 @@ class TestSupereventSignoffDeletion(SignoffGroupsAndUsersSetup,
 
     def test_lvem_user_delete_signoff_for_exposed_superevent(self):
         """LV-EM user can't delete signoffs for exposed superevents"""
+        # LV-EM superevent
         signoff = self.lvem_signoff
         url = v_reverse('superevents:superevent-signoff-detail',
             args=[signoff.superevent.superevent_id,
@@ -3176,17 +4038,40 @@ class TestSupereventSignoffDeletion(SignoffGroupsAndUsersSetup,
         self.assertIn('do not have permission to delete superevent signoffs',
             response.data['detail'])
 
+        # Public superevent
+        signoff = self.public_signoff
+        url = v_reverse('superevents:superevent-signoff-detail',
+            args=[signoff.superevent.superevent_id,
+            signoff.signoff_type + signoff.instrument])
+        response = self.request_as_user(url, "DELETE", self.lvem_user)
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('do not have permission to delete superevent signoffs',
+            response.data['detail'])
+
     def test_public_user_delete_signoff_for_hidden_superevent(self):
         """Public user can't delete signoffs for hidden superevents"""
+        # Internal superevent
         signoff = self.internal_signoff
         url = v_reverse('superevents:superevent-signoff-detail',
             args=[signoff.superevent.superevent_id,
             signoff.signoff_type + signoff.instrument])
         response = self.request_as_user(url, "DELETE")
-        self.assertEqual(response.status_code, 403)
-        # TODO: this will be 404 in the future
+        self.assertEqual(response.status_code, 404)
+
+        # LV-EM superevent
+        signoff = self.lvem_signoff
+        url = v_reverse('superevents:superevent-signoff-detail',
+            args=[signoff.superevent.superevent_id,
+            signoff.signoff_type + signoff.instrument])
+        response = self.request_as_user(url, "DELETE")
+        self.assertEqual(response.status_code, 404)
 
     def test_public_user_delete_signoff_for_exposed_superevent(self):
         """Public user can't delete signoffs for exposed superevents"""
-        # TODO
-        pass
+        signoff = self.public_signoff
+        url = v_reverse('superevents:superevent-signoff-detail',
+            args=[signoff.superevent.superevent_id,
+            signoff.signoff_type + signoff.instrument])
+        response = self.request_as_user(url, "DELETE")
+        self.assertEqual(response.status_code, 403)
+        self.assertIn('credentials were not provided', response.data['detail'])
diff --git a/gracedb/superevents/tests/mixins.py b/gracedb/superevents/tests/mixins.py
index 29e7a6eaea48cf02afa5ef9f1d536855a24fe67c..b4b8b47466e99c2bcb118874107fa07d8679e9ad 100644
--- a/gracedb/superevents/tests/mixins.py
+++ b/gracedb/superevents/tests/mixins.py
@@ -1,10 +1,11 @@
 from __future__ import absolute_import
 import os
 
+from core.permission_utils import assign_perms_to_obj
 from core.tests.utils import GraceDbTestBase
 from events.tests.mixins import EventCreateMixin
 from superevents.models import Superevent
-from superevents.utils import expose_superevent
+from superevents.utils import expose_superevent, SUPEREVENT_PERMS
 
 
 class SupereventCreateMixin(EventCreateMixin):
@@ -39,10 +40,19 @@ class SupereventSetup(GraceDbTestBase, SupereventCreateMixin):
     def setUpTestData(cls):
         super(SupereventSetup, cls).setUpTestData()
 
-        # Create two superevents
+        # Create three superevents
         cls.internal_superevent = cls.create_superevent(cls.internal_user)
         cls.lvem_superevent = cls.create_superevent(cls.internal_user)
+        cls.public_superevent = cls.create_superevent(cls.internal_user)
+
+        # Expose one to LV-EM only - a little hacky since our utility
+        # function for exposing a superevent only is capable of
+        # exposing it to both LV-EM and the public.
+        assign_perms_to_obj(SUPEREVENT_PERMS[cls.lvem_group.name],
+            cls.lvem_group, cls.lvem_superevent)
+        cls.lvem_superevent.is_exposed = True
+        cls.lvem_superevent.save(update_fields=['is_exposed'])
 
         # Expose one to LV-EM and public, and assign relevant permissions
-        expose_superevent(cls.lvem_superevent, cls.internal_user,
+        expose_superevent(cls.public_superevent, cls.internal_user,
             add_log_message=False, issue_alert=False)