@@ -3445,6 +3445,71 @@ def test_taxon_detail_visible_when_excluded_from_list(self):
34453445 self .assertEqual (res .status_code , status .HTTP_200_OK )
34463446
34473447
3448+ class TaxaListViewSetPermissionTestCase (TestCase ):
3449+ """Test TaxaListViewSet CRUD permissions for project members vs anonymous users."""
3450+
3451+ def setUp (self ):
3452+ self .owner = User .objects .create_user (email = "owner@example.com" , password = "testpass" )
3453+ self .member = User .objects .create_user (email = "member@example.com" , password = "testpass" )
3454+ self .non_member = User .objects .create_user (email = "nonmember@example.com" , password = "testpass" )
3455+ self .project = Project .objects .create (name = "Test Project" , owner = self .owner )
3456+ self .project .members .add (self .member )
3457+ self .taxa_list = TaxaList .objects .create (name = "Existing List" , description = "A list" )
3458+ self .taxa_list .projects .add (self .project )
3459+ self .client = APIClient ()
3460+ self .list_url = f"/api/v2/taxa/lists/?project_id={ self .project .pk } "
3461+ self .detail_url = f"/api/v2/taxa/lists/{ self .taxa_list .pk } /?project_id={ self .project .pk } "
3462+
3463+ def test_member_can_list_taxa_lists (self ):
3464+ self .client .force_authenticate (self .member )
3465+ response = self .client .get (self .list_url )
3466+ self .assertEqual (response .status_code , status .HTTP_200_OK )
3467+
3468+ def test_member_can_create_taxa_list (self ):
3469+ self .client .force_authenticate (self .member )
3470+ response = self .client .post (self .list_url , {"name" : "New List" })
3471+ self .assertEqual (response .status_code , status .HTTP_201_CREATED )
3472+
3473+ def test_member_can_update_taxa_list (self ):
3474+ self .client .force_authenticate (self .member )
3475+ response = self .client .patch (self .detail_url , {"name" : "Renamed" })
3476+ self .assertEqual (response .status_code , status .HTTP_200_OK )
3477+ self .taxa_list .refresh_from_db ()
3478+ self .assertEqual (self .taxa_list .name , "Renamed" )
3479+
3480+ def test_member_can_delete_taxa_list (self ):
3481+ self .client .force_authenticate (self .member )
3482+ response = self .client .delete (self .detail_url )
3483+ self .assertEqual (response .status_code , status .HTTP_204_NO_CONTENT )
3484+
3485+ def test_anonymous_can_read_taxa_lists (self ):
3486+ response = self .client .get (self .list_url )
3487+ self .assertEqual (response .status_code , status .HTTP_200_OK )
3488+
3489+ def test_anonymous_cannot_create_taxa_list (self ):
3490+ response = self .client .post (self .list_url , {"name" : "Anon List" })
3491+ self .assertIn (response .status_code , [status .HTTP_401_UNAUTHORIZED , status .HTTP_403_FORBIDDEN ])
3492+
3493+ def test_anonymous_cannot_update_taxa_list (self ):
3494+ response = self .client .patch (self .detail_url , {"name" : "Hacked" })
3495+ self .assertIn (response .status_code , [status .HTTP_401_UNAUTHORIZED , status .HTTP_403_FORBIDDEN ])
3496+
3497+ def test_non_member_cannot_create_taxa_list (self ):
3498+ self .client .force_authenticate (self .non_member )
3499+ response = self .client .post (self .list_url , {"name" : "Intruder List" })
3500+ self .assertEqual (response .status_code , status .HTTP_403_FORBIDDEN )
3501+
3502+ def test_non_member_cannot_update_taxa_list (self ):
3503+ self .client .force_authenticate (self .non_member )
3504+ response = self .client .patch (self .detail_url , {"name" : "Hacked" })
3505+ self .assertEqual (response .status_code , status .HTTP_403_FORBIDDEN )
3506+
3507+ def test_owner_can_manage_taxa_list (self ):
3508+ self .client .force_authenticate (self .owner )
3509+ response = self .client .post (self .list_url , {"name" : "Owner List" })
3510+ self .assertEqual (response .status_code , status .HTTP_201_CREATED )
3511+
3512+
34483513class TaxaListTaxonAPITestCase (TestCase ):
34493514 """Test TaxaList taxa management operations via API."""
34503515
0 commit comments