@@ -876,6 +876,100 @@ mod tests {
876876 Ok ( ( ) )
877877 }
878878
879+ #[ actix_web:: test]
880+ #[ serial]
881+ async fn test_refresh_group_isolation_across_groups ( ) -> Result < ( ) > {
882+ let _ = env_logger:: try_init ( ) ;
883+ log:: info!( "Testing refresh isolation across groups" ) ;
884+
885+ let _path = init_test_backend ( "test_refresh_group_isolation" ) . await ?;
886+
887+ // Create two groups with distinct repos/files.
888+ let ( group_a, repo_a, group_b, _repo_b, file_a, file_b) = {
889+ use server:: get_backend;
890+ let backend = get_backend ( ) . await ?;
891+
892+ let mut group_a = backend. create_group ( ) . await ?;
893+ group_a. set_name ( "Group A" ) . await ?;
894+ let repo_a = group_a. create_repo ( ) . await ?;
895+ repo_a. set_name ( "Repo A" ) . await ?;
896+ let file_a = "group_a_only.txt" ;
897+ repo_a. upload ( file_a, b"alpha" . to_vec ( ) ) . await ?;
898+
899+ let mut group_b = backend. create_group ( ) . await ?;
900+ group_b. set_name ( "Group B" ) . await ?;
901+ let repo_b = group_b. create_repo ( ) . await ?;
902+ repo_b. set_name ( "Repo B" ) . await ?;
903+ let file_b = "group_b_only.txt" ;
904+ repo_b. upload ( file_b, b"beta" . to_vec ( ) ) . await ?;
905+
906+ ( group_a, repo_a, group_b, repo_b, file_a, file_b)
907+ } ;
908+
909+ let app = test:: init_service (
910+ App :: new ( )
911+ . service ( status)
912+ . service ( health)
913+ . service ( web:: scope ( "/api" ) . service ( groups:: scope ( ) ) ) ,
914+ )
915+ . await ;
916+
917+ // Refresh group B and verify it only reports its own files.
918+ let refresh_req = test:: TestRequest :: post ( )
919+ . uri ( & format ! ( "/api/groups/{}/refresh" , group_b. id( ) ) )
920+ . to_request ( ) ;
921+ let refresh_resp = test:: call_service ( & app, refresh_req) . await ;
922+ assert ! (
923+ refresh_resp. status( ) . is_success( ) ,
924+ "Refresh should succeed for group B"
925+ ) ;
926+ let refresh_data: serde_json:: Value = test:: read_body_json ( refresh_resp) . await ;
927+ assert_eq ! ( refresh_data[ "status" ] , "success" ) ;
928+
929+ let repos = refresh_data[ "repos" ] . as_array ( ) . expect ( "repos should be an array" ) ;
930+ assert ! ( !repos. is_empty( ) , "Group B should have at least one repo" ) ;
931+
932+ // Aggregate all files reported by refresh for group B.
933+ let mut group_b_files = Vec :: new ( ) ;
934+ for repo_data in repos {
935+ let files = repo_data[ "all_files" ]
936+ . as_array ( )
937+ . expect ( "all_files should be an array" ) ;
938+ for file in files {
939+ if let Some ( name) = file. as_str ( ) {
940+ group_b_files. push ( name. to_string ( ) ) ;
941+ }
942+ }
943+ }
944+
945+ assert ! (
946+ group_b_files. iter( ) . any( |f| f == file_b) ,
947+ "Group B refresh should include its own file"
948+ ) ;
949+ assert ! (
950+ !group_b_files. iter( ) . any( |f| f == file_a) ,
951+ "Group B refresh should not include Group A file"
952+ ) ;
953+
954+ // Sanity: group A file remains accessible in group A.
955+ let get_a_file_req = test:: TestRequest :: get ( )
956+ . uri ( & format ! (
957+ "/api/groups/{}/repos/{}/media/{}" ,
958+ group_a. id( ) ,
959+ repo_a. id( ) ,
960+ file_a
961+ ) )
962+ . to_request ( ) ;
963+ let get_a_file_resp = test:: call_service ( & app, get_a_file_req) . await ;
964+ assert ! (
965+ get_a_file_resp. status( ) . is_success( ) ,
966+ "Group A file should remain accessible in Group A"
967+ ) ;
968+
969+ cleanup_test_resources ( ) . await ?;
970+ Ok ( ( ) )
971+ }
972+
879973 // P2P tests: use in-test retries (like save-dweb-backend) so nextest retries + internal
880974 // retry loops give the Veilid network time to converge.
881975 #[ actix_web:: test]
0 commit comments