@@ -866,3 +866,112 @@ async def test_non_manager_cannot_set_project_members(
866866 json = body ,
867867 )
868868 assert response .status_code == 403
869+
870+
871+ class TestListUserProjectsService :
872+ """Test the service-level functions for backward compatibility"""
873+
874+ @pytest .mark .asyncio
875+ @pytest .mark .parametrize ("test_db" , ["sqlite" , "postgres" ], indirect = True )
876+ async def test_list_user_projects_only_returns_member_projects (
877+ self , test_db , session : AsyncSession , client : AsyncClient
878+ ):
879+ # Create project owner
880+ owner = await create_user (
881+ session = session ,
882+ name = "owner" ,
883+ created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ),
884+ global_role = GlobalRole .USER ,
885+ )
886+
887+ # Create a different user who is not a member
888+ non_member = await create_user (
889+ session = session ,
890+ name = "non_member" ,
891+ created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ),
892+ global_role = GlobalRole .USER ,
893+ )
894+
895+ # Create a public project
896+ public_project = await create_project (
897+ session = session ,
898+ owner = owner ,
899+ name = "public_project" ,
900+ created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ),
901+ is_public = True ,
902+ )
903+
904+ # Add owner as admin
905+ await add_project_member (
906+ session = session , project = public_project , user = owner , project_role = ProjectRole .ADMIN
907+ )
908+
909+ # Test: list_user_projects should NOT return public projects for non-members
910+ from dstack ._internal .server .services .projects import list_user_projects
911+ projects = await list_user_projects (session = session , user = non_member )
912+ assert len (projects ) == 0 # Non-member should see NO projects
913+
914+ # Test: list_user_projects should return projects where user IS a member
915+ projects = await list_user_projects (session = session , user = owner )
916+ assert len (projects ) == 1
917+ assert projects [0 ].project_name == "public_project"
918+
919+ @pytest .mark .asyncio
920+ @pytest .mark .parametrize ("test_db" , ["sqlite" , "postgres" ], indirect = True )
921+ async def test_list_user_accessible_projects_returns_member_and_public_projects (
922+ self , test_db , session : AsyncSession , client : AsyncClient
923+ ):
924+ # Create project owner
925+ owner = await create_user (
926+ session = session ,
927+ name = "owner" ,
928+ created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ),
929+ global_role = GlobalRole .USER ,
930+ )
931+
932+ # Create a different user who is not a member
933+ non_member = await create_user (
934+ session = session ,
935+ name = "non_member" ,
936+ created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ),
937+ global_role = GlobalRole .USER ,
938+ )
939+
940+ # Create a public project
941+ public_project = await create_project (
942+ session = session ,
943+ owner = owner ,
944+ name = "public_project" ,
945+ created_at = datetime (2023 , 1 , 2 , 3 , 4 , tzinfo = timezone .utc ),
946+ is_public = True ,
947+ )
948+
949+ # Create a private project
950+ private_project = await create_project (
951+ session = session ,
952+ owner = owner ,
953+ name = "private_project" ,
954+ created_at = datetime (2023 , 1 , 2 , 3 , 5 , tzinfo = timezone .utc ),
955+ is_public = False ,
956+ )
957+
958+ # Add owner as admin to both projects
959+ await add_project_member (
960+ session = session , project = public_project , user = owner , project_role = ProjectRole .ADMIN
961+ )
962+ await add_project_member (
963+ session = session , project = private_project , user = owner , project_role = ProjectRole .ADMIN
964+ )
965+
966+ # Test: list_user_accessible_projects should return public projects for non-members
967+ from dstack ._internal .server .services .projects import list_user_accessible_projects
968+ projects = await list_user_accessible_projects (session = session , user = non_member )
969+ assert len (projects ) == 1 # Should see only the public project
970+ assert projects [0 ].project_name == "public_project"
971+
972+ # Test: list_user_accessible_projects should return ALL projects for members
973+ projects = await list_user_accessible_projects (session = session , user = owner )
974+ assert len (projects ) == 2 # Should see both projects
975+ project_names = [p .project_name for p in projects ]
976+ assert "public_project" in project_names
977+ assert "private_project" in project_names
0 commit comments