@@ -628,22 +628,28 @@ async def test_global_admin_manager_can_set_project_admins(
628628
629629 @pytest .mark .asyncio
630630 @pytest .mark .parametrize ("test_db" , ["sqlite" , "postgres" ], indirect = True )
631- async def test_add_member_returns_404_for_nonexistent_user (self , test_db , session : AsyncSession , client : AsyncClient ):
631+ async def test_add_member_skips_nonexistent_user (self , test_db , session : AsyncSession , client : AsyncClient ):
632632 # Setup project and admin
633633 project = await create_project (session = session , created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ))
634634 admin = await create_user (session = session , created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ))
635635 await add_project_member (session = session , project = project , user = admin , project_role = ProjectRole .ADMIN )
636636
637- # Try to add non-existent user
637+ # Try to add non-existent user - should succeed but skip the non-existent user
638638 body = {"members" : {"username" : "nonexistent" , "project_role" : "user" }}
639639 response = await client .post (
640640 f"/api/projects/{ project .name } /add_member" ,
641641 headers = get_auth_headers (admin .token ),
642642 json = body ,
643643 )
644644
645- # The service layer returns 400 for non-existent users, not 404
646- assert response .status_code == 400
645+ # Operation succeeds but non-existent user is silently skipped
646+ assert response .status_code == 200
647+ response_data = response .json ()
648+
649+ # Only the admin should be present (non-existent user was skipped)
650+ member_usernames = [member ["user" ]["username" ] for member in response_data ["members" ]]
651+ assert "nonexistent" not in member_usernames
652+ assert admin .name in member_usernames
647653
648654 @pytest .mark .asyncio
649655 @pytest .mark .parametrize ("test_db" , ["sqlite" , "postgres" ], indirect = True )
@@ -782,22 +788,28 @@ async def test_add_member_updates_existing_role(self, test_db, session: AsyncSes
782788
783789 @pytest .mark .asyncio
784790 @pytest .mark .parametrize ("test_db" , ["sqlite" , "postgres" ], indirect = True )
785- async def test_add_member_returns_404_for_nonexistent_user (self , test_db , session : AsyncSession , client : AsyncClient ):
791+ async def test_add_member_skips_nonexistent_user (self , test_db , session : AsyncSession , client : AsyncClient ):
786792 # Setup project and admin
787793 project = await create_project (session = session , created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ))
788794 admin = await create_user (session = session , created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ))
789795 await add_project_member (session = session , project = project , user = admin , project_role = ProjectRole .ADMIN )
790796
791- # Try to add non-existent user
797+ # Try to add non-existent user - should succeed but skip the non-existent user
792798 body = {"members" : {"username" : "nonexistent" , "project_role" : "user" }}
793799 response = await client .post (
794800 f"/api/projects/{ project .name } /add_member" ,
795801 headers = get_auth_headers (admin .token ),
796802 json = body ,
797803 )
798804
799- # The service layer returns 400 for non-existent users, not 404
800- assert response .status_code == 400
805+ # Operation succeeds but non-existent user is silently skipped
806+ assert response .status_code == 200
807+ response_data = response .json ()
808+
809+ # Only the admin should be present (non-existent user was skipped)
810+ member_usernames = [member ["user" ]["username" ] for member in response_data ["members" ]]
811+ assert "nonexistent" not in member_usernames
812+ assert admin .name in member_usernames
801813
802814 @pytest .mark .asyncio
803815 @pytest .mark .parametrize ("test_db" , ["sqlite" , "postgres" ], indirect = True )
@@ -882,26 +894,26 @@ async def test_remove_member_by_email(self, test_db, session: AsyncSession, clie
882894
883895 @pytest .mark .asyncio
884896 @pytest .mark .parametrize ("test_db" , ["sqlite" , "postgres" ], indirect = True )
885- async def test_remove_member_returns_404_for_nonexistent_user (self , test_db , session : AsyncSession , client : AsyncClient ):
897+ async def test_remove_member_skips_nonexistent_user (self , test_db , session : AsyncSession , client : AsyncClient ):
886898 # Setup project and admin
887899 project = await create_project (session = session , created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ))
888900 admin = await create_user (session = session , created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ))
889901 await add_project_member (session = session , project = project , user = admin , project_role = ProjectRole .ADMIN )
890902
891- # Try to remove non-existent user
903+ # Try to remove non-existent user - should succeed but skip the non-existent user
892904 body = {"usernames" : "nonexistent" }
893905 response = await client .post (
894906 f"/api/projects/{ project .name } /remove_member" ,
895907 headers = get_auth_headers (admin .token ),
896908 json = body ,
897909 )
898910
899- # The service layer returns 400 for non-existent users, not 404
900- assert response .status_code == 400
911+ # Operation succeeds but non-existent user is silently skipped
912+ assert response .status_code == 200
901913
902914 @pytest .mark .asyncio
903915 @pytest .mark .parametrize ("test_db" , ["sqlite" , "postgres" ], indirect = True )
904- async def test_remove_member_returns_400_for_non_members (self , test_db , session : AsyncSession , client : AsyncClient ):
916+ async def test_remove_member_skips_non_members (self , test_db , session : AsyncSession , client : AsyncClient ):
905917 # Setup project and admin
906918 project = await create_project (session = session , created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ))
907919 admin = await create_user (session = session , created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ))
@@ -910,15 +922,16 @@ async def test_remove_member_returns_400_for_non_members(self, test_db, session:
910922 # Create user who is NOT a member
911923 non_member = await create_user (session = session , name = "nonmember" , created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ))
912924
913- # Try to remove non-member
925+ # Try to remove non-member - should succeed but skip the non-member
914926 body = {"usernames" : "nonmember" }
915927 response = await client .post (
916928 f"/api/projects/{ project .name } /remove_member" ,
917929 headers = get_auth_headers (admin .token ),
918930 json = body ,
919931 )
920932
921- assert response .status_code == 400
933+ # Operation succeeds but non-member is silently skipped
934+ assert response .status_code == 200
922935
923936 @pytest .mark .asyncio
924937 @pytest .mark .parametrize ("test_db" , ["sqlite" , "postgres" ], indirect = True )
@@ -938,8 +951,8 @@ async def test_remove_member_prevents_removing_last_admin(self, test_db, session
938951
939952 assert response .status_code == 400
940953 response_json = response .json ()
941- # The actual error message format includes details in an array
942- assert "Cannot remove the last project admin " in str (response_json )
954+ # Check for the actual error message we use
955+ assert "Cannot remove all project admins " in str (response_json )
943956
944957 @pytest .mark .asyncio
945958 @pytest .mark .parametrize ("test_db" , ["sqlite" , "postgres" ], indirect = True )
0 commit comments