@@ -33,12 +33,17 @@ def setUp(self) -> None:
3333 self .project3 = self .create_project (organization = self .org2 )
3434
3535 self .user2 = self .create_user (is_superuser = False )
36+ self .user3 = self .create_user (is_superuser = False )
3637 self .create_member (user = self .user2 , organization = self .organization , role = "member" , teams = [])
3738 self .create_member (user = self .user2 , organization = self .org3 , role = "member" , teams = [])
3839 self .project4 = self .create_project (
3940 name = "users2sproj" ,
4041 teams = [self .create_team (organization = self .org , members = [self .user2 ])],
4142 )
43+ self .project5 = self .create_project (
44+ name = "users3sproj" ,
45+ teams = [self .create_team (organization = self .org , members = [self .user3 ])],
46+ )
4247
4348 self .store_outcomes (
4449 {
@@ -85,6 +90,18 @@ def setUp(self) -> None:
8590 "quantity" : 1 ,
8691 }
8792 )
93+ self .store_outcomes (
94+ {
95+ "org_id" : self .org .id ,
96+ "timestamp" : self ._now - timedelta (hours = 1 ),
97+ "project_id" : self .project5 .id ,
98+ "outcome" : Outcome .ACCEPTED ,
99+ "reason" : "none" ,
100+ "category" : DataCategory .ERROR ,
101+ "quantity" : 1 ,
102+ },
103+ 2 ,
104+ )
88105
89106 # Add profile_duration outcome data
90107 self .store_outcomes (
@@ -284,7 +301,7 @@ def test_timeseries_interval(self) -> None:
284301 isoformat_z (floor_to_utc_day (self ._now )),
285302 ],
286303 "groups" : [
287- {"by" : {}, "series" : {"sum(quantity)" : [0 , 6 ]}, "totals" : {"sum(quantity)" : 6 }}
304+ {"by" : {}, "series" : {"sum(quantity)" : [0 , 8 ]}, "totals" : {"sum(quantity)" : 8 }}
288305 ],
289306 "start" : isoformat_z (floor_to_utc_day (self ._now ) - timedelta (days = 1 )),
290307 "end" : isoformat_z (floor_to_utc_day (self ._now ) + timedelta (days = 1 )),
@@ -312,8 +329,8 @@ def test_timeseries_interval(self) -> None:
312329 "groups" : [
313330 {
314331 "by" : {},
315- "series" : {"sum(quantity)" : [0 , 0 , 0 , 6 , 0 ]},
316- "totals" : {"sum(quantity)" : 6 },
332+ "series" : {"sum(quantity)" : [0 , 0 , 0 , 8 , 0 ]},
333+ "totals" : {"sum(quantity)" : 8 },
317334 }
318335 ],
319336 "start" : isoformat_z (
@@ -344,7 +361,7 @@ def test_user_org_total_all_accessible(self) -> None:
344361 isoformat_z (floor_to_utc_day (self ._now )),
345362 ],
346363 "groups" : [
347- {"by" : {}, "series" : {"sum(quantity)" : [0 , 7 ]}, "totals" : {"sum(quantity)" : 7 }}
364+ {"by" : {}, "series" : {"sum(quantity)" : [0 , 9 ]}, "totals" : {"sum(quantity)" : 9 }}
348365 ],
349366 }
350367
@@ -450,6 +467,10 @@ def test_open_membership_semantics(self) -> None:
450467 "by" : {"project" : self .project2 .id },
451468 "totals" : {"sum(quantity)" : 1 },
452469 },
470+ {
471+ "by" : {"project" : self .project5 .id },
472+ "totals" : {"sum(quantity)" : 2 },
473+ },
453474 ],
454475 }
455476
@@ -973,6 +994,118 @@ def test_profile_duration_groupby(self) -> None:
973994 ],
974995 }
975996
997+ @freeze_time (_now )
998+ def test_project_filtering_with_all_projects (self ) -> None :
999+ """Test that project=-1 aggregates data across all projects in the org"""
1000+ response = self .do_request (
1001+ {
1002+ "project" : [- 1 ],
1003+ "statsPeriod" : "1d" ,
1004+ "interval" : "1d" ,
1005+ "field" : ["sum(quantity)" ],
1006+ "category" : ["error" , "transaction" ],
1007+ },
1008+ status_code = 200 ,
1009+ )
1010+
1011+ assert response .data ["groups" ] == [
1012+ {
1013+ "by" : {},
1014+ "totals" : {"sum(quantity)" : 9 },
1015+ "series" : {"sum(quantity)" : [0 , 9 ]},
1016+ }
1017+ ]
1018+
1019+ @freeze_time (_now )
1020+ def test_project_filtering_without_project_param (self ) -> None :
1021+ """Test that when no project parameter is provided, it filters by user's projects (my projects)"""
1022+ response = self .do_request (
1023+ {
1024+ "statsPeriod" : "1d" ,
1025+ "interval" : "1d" ,
1026+ "field" : ["sum(quantity)" ],
1027+ "category" : ["error" , "transaction" ],
1028+ },
1029+ status_code = 200 ,
1030+ )
1031+
1032+ assert response .data ["groups" ] == [
1033+ {
1034+ "by" : {},
1035+ "totals" : {"sum(quantity)" : 7 },
1036+ "series" : {"sum(quantity)" : [0 , 7 ]},
1037+ }
1038+ ]
1039+
1040+ @freeze_time (_now )
1041+ def test_project_filtering_with_specific_project (self ) -> None :
1042+ """Test that when a specific project id is provided, it filters by that project only"""
1043+ response = self .do_request (
1044+ {
1045+ "project" : [self .project .id ],
1046+ "statsPeriod" : "1d" ,
1047+ "interval" : "1d" ,
1048+ "field" : ["sum(quantity)" ],
1049+ "category" : ["error" , "transaction" ],
1050+ },
1051+ status_code = 200 ,
1052+ )
1053+
1054+ assert response .data ["groups" ] == [
1055+ {
1056+ "by" : {},
1057+ "totals" : {"sum(quantity)" : 6 },
1058+ "series" : {"sum(quantity)" : [0 , 6 ]},
1059+ }
1060+ ]
1061+
1062+ @freeze_time (_now )
1063+ def test_project_filtering_with_multiple_specific_projects (self ) -> None :
1064+ """Test filtering with multiple specific project IDs"""
1065+ response = self .do_request (
1066+ {
1067+ "project" : [self .project .id , self .project2 .id ],
1068+ "statsPeriod" : "1d" ,
1069+ "interval" : "1d" ,
1070+ "field" : ["sum(quantity)" ],
1071+ "category" : ["error" , "transaction" ],
1072+ },
1073+ status_code = 200 ,
1074+ )
1075+
1076+ assert response .data ["groups" ] == [
1077+ {
1078+ "by" : {},
1079+ "totals" : {"sum(quantity)" : 7 },
1080+ "series" : {"sum(quantity)" : [0 , 7 ]},
1081+ }
1082+ ]
1083+
1084+ @freeze_time (_now )
1085+ def test_with_groupby_project (self ) -> None :
1086+ """Test that groupBy=project shows individual project stats"""
1087+ response = self .do_request (
1088+ {
1089+ "statsPeriod" : "1d" ,
1090+ "interval" : "1d" ,
1091+ "field" : ["sum(quantity)" ],
1092+ "category" : ["error" , "transaction" ],
1093+ "groupBy" : ["project" ],
1094+ },
1095+ status_code = 200 ,
1096+ )
1097+
1098+ assert response .data ["groups" ] == [
1099+ {
1100+ "by" : {"project" : self .project .id },
1101+ "totals" : {"sum(quantity)" : 6 },
1102+ },
1103+ {
1104+ "by" : {"project" : self .project2 .id },
1105+ "totals" : {"sum(quantity)" : 1 },
1106+ },
1107+ ]
1108+
9761109
9771110def result_sorted (result ):
9781111 """sort the groups of the results array by the `by` object, ensuring a stable order"""
0 commit comments