1
1
from __future__ import annotations
2
2
3
3
import functools
4
- from datetime import datetime , timedelta , timezone
4
+ from datetime import UTC , datetime , timedelta
5
5
from typing import Any
6
6
7
7
from django .urls import reverse
8
8
9
9
from sentry .constants import DataCategory
10
10
from sentry .testutils .cases import APITestCase , OutcomesSnubaTest
11
11
from sentry .testutils .helpers .datetime import freeze_time
12
+ from sentry .utils .dates import floor_to_utc_day
12
13
from sentry .utils .outcomes import Outcome
13
14
14
15
16
+ def _iso_z (dt : datetime ) -> str :
17
+ assert dt .tzinfo == UTC
18
+ return f"{ dt .isoformat ().removesuffix ('+00:00' )} Z"
19
+
20
+
15
21
class OrganizationStatsSummaryTest (APITestCase , OutcomesSnubaTest ):
22
+ _now = datetime .now (UTC ).replace (hour = 12 , minute = 27 , second = 28 , microsecond = 0 )
23
+
16
24
def setUp (self ):
17
25
super ().setUp ()
18
- self .now = datetime (2021 , 3 , 14 , 12 , 27 , 28 , tzinfo = timezone .utc )
19
26
20
27
self .login_as (user = self .user )
21
28
@@ -45,7 +52,7 @@ def setUp(self):
45
52
self .store_outcomes (
46
53
{
47
54
"org_id" : self .org .id ,
48
- "timestamp" : self .now - timedelta (hours = 1 ),
55
+ "timestamp" : self ._now - timedelta (hours = 1 ),
49
56
"project_id" : self .project .id ,
50
57
"outcome" : Outcome .ACCEPTED ,
51
58
"reason" : "none" ,
@@ -57,7 +64,7 @@ def setUp(self):
57
64
self .store_outcomes (
58
65
{
59
66
"org_id" : self .org .id ,
60
- "timestamp" : self .now - timedelta (hours = 1 ),
67
+ "timestamp" : self ._now - timedelta (hours = 1 ),
61
68
"project_id" : self .project .id ,
62
69
"outcome" : Outcome .ACCEPTED ,
63
70
"reason" : "none" ,
@@ -69,7 +76,7 @@ def setUp(self):
69
76
self .store_outcomes (
70
77
{
71
78
"org_id" : self .org .id ,
72
- "timestamp" : self .now - timedelta (hours = 1 ),
79
+ "timestamp" : self ._now - timedelta (hours = 1 ),
73
80
"project_id" : self .project .id ,
74
81
"outcome" : Outcome .RATE_LIMITED ,
75
82
"reason" : "smart_rate_limit" ,
@@ -80,7 +87,7 @@ def setUp(self):
80
87
self .store_outcomes (
81
88
{
82
89
"org_id" : self .org .id ,
83
- "timestamp" : self .now - timedelta (hours = 1 ),
90
+ "timestamp" : self ._now - timedelta (hours = 1 ),
84
91
"project_id" : self .project2 .id ,
85
92
"outcome" : Outcome .RATE_LIMITED ,
86
93
"reason" : "smart_rate_limit" ,
@@ -142,27 +149,31 @@ def test_unknown_field(self):
142
149
143
150
def test_no_end_param (self ):
144
151
response = self .do_request (
145
- {"field" : ["sum(quantity)" ], "interval" : "1d" , "start" : "2021-03-14T00:00:00Z" }
152
+ {
153
+ "field" : ["sum(quantity)" ],
154
+ "interval" : "1d" ,
155
+ "start" : floor_to_utc_day (self ._now ).isoformat (),
156
+ }
146
157
)
147
158
148
159
assert response .status_code == 400 , response .content
149
160
assert response .data == {"detail" : "start and end are both required" }
150
161
151
- @freeze_time (datetime ( 2021 , 3 , 14 , 12 , 27 , 28 , tzinfo = timezone . utc ) )
162
+ @freeze_time (_now )
152
163
def test_future_request (self ):
153
164
response = self .do_request (
154
165
{
155
166
"field" : ["sum(quantity)" ],
156
167
"interval" : "1h" ,
157
168
"category" : ["error" ],
158
- "start" : "2021-03-14T15:30:00" ,
159
- "end" : "2021-03-14T16:30:00" ,
169
+ "start" : self . _now . replace ( hour = 15 , minute = 30 , second = 0 ). isoformat () ,
170
+ "end" : self . _now . replace ( hour = 16 , minute = 30 , second = 0 ). isoformat () ,
160
171
}
161
172
)
162
173
assert response .status_code == 200 , response .content
163
174
assert response .data == {
164
- "start" : "2021-03-14T12:00:00Z" ,
165
- "end" : "2021-03-14T17:00:00Z" ,
175
+ "start" : _iso_z ( self . _now . replace ( hour = 12 , minute = 0 , second = 0 )) ,
176
+ "end" : _iso_z ( self . _now . replace ( hour = 17 , minute = 0 , second = 0 )) ,
166
177
"projects" : [],
167
178
}
168
179
@@ -212,7 +223,7 @@ def test_resolution_invalid(self):
212
223
213
224
assert response .status_code == 400 , response .content
214
225
215
- @freeze_time ("2021-03-14T12:27:28.303Z" )
226
+ @freeze_time (_now )
216
227
def test_attachment_filter_only (self ):
217
228
response = self .do_request (
218
229
{
@@ -229,7 +240,7 @@ def test_attachment_filter_only(self):
229
240
"detail" : "if filtering by attachment no other category may be present"
230
241
}
231
242
232
- @freeze_time ("2021-03-14T12:27:28.303Z" )
243
+ @freeze_time (_now )
233
244
def test_user_all_accessible (self ):
234
245
response = self .do_request (
235
246
{
@@ -256,12 +267,12 @@ def test_user_all_accessible(self):
256
267
257
268
assert response .status_code == 200
258
269
assert response .data == {
259
- "start" : "2021-03-13T00:00:00Z" ,
260
- "end" : "2021-03-15T00:00:00Z" ,
270
+ "start" : _iso_z ( floor_to_utc_day ( self . _now ) - timedelta ( days = 1 )) ,
271
+ "end" : _iso_z ( floor_to_utc_day ( self . _now ) + timedelta ( days = 1 )) ,
261
272
"projects" : [],
262
273
}
263
274
264
- @freeze_time ("2021-03-14T12:27:28.303Z" )
275
+ @freeze_time (_now )
265
276
def test_no_project_access (self ):
266
277
user = self .create_user (is_superuser = False )
267
278
self .create_member (user = user , organization = self .organization , role = "member" , teams = [])
@@ -281,7 +292,7 @@ def test_no_project_access(self):
281
292
assert response .status_code == 403 , response .content
282
293
assert response .data == {"detail" : "You do not have permission to perform this action." }
283
294
284
- @freeze_time ("2021-03-14T12:27:28.303Z" )
295
+ @freeze_time (_now )
285
296
def test_open_membership_semantics (self ):
286
297
self .org .flags .allow_joinleave = True
287
298
self .org .save ()
@@ -299,8 +310,8 @@ def test_open_membership_semantics(self):
299
310
300
311
assert response .status_code == 200
301
312
assert response .data == {
302
- "start" : "2021-03-13T00:00:00Z" ,
303
- "end" : "2021-03-15T00:00:00Z" ,
313
+ "start" : _iso_z ( floor_to_utc_day ( self . _now ) - timedelta ( days = 1 )) ,
314
+ "end" : _iso_z ( floor_to_utc_day ( self . _now ) + timedelta ( days = 1 )) ,
304
315
"projects" : [
305
316
{
306
317
"id" : self .project .id ,
@@ -343,7 +354,7 @@ def test_open_membership_semantics(self):
343
354
],
344
355
}
345
356
346
- @freeze_time ("2021-03-14T12:27:28.303Z" )
357
+ @freeze_time (_now )
347
358
def test_org_simple (self ):
348
359
make_request = functools .partial (
349
360
self .client .get ,
@@ -359,8 +370,8 @@ def test_org_simple(self):
359
370
360
371
assert response .status_code == 200 , response .content
361
372
assert response .data == {
362
- "start" : "2021-03-12T00:00:00Z" ,
363
- "end" : "2021-03-15T00:00:00Z" ,
373
+ "start" : _iso_z ( floor_to_utc_day ( self . _now ) - timedelta ( days = 2 )) ,
374
+ "end" : _iso_z ( floor_to_utc_day ( self . _now ) + timedelta ( days = 1 )) ,
364
375
"projects" : [
365
376
{
366
377
"id" : self .project .id ,
@@ -416,7 +427,7 @@ def test_org_simple(self):
416
427
],
417
428
}
418
429
419
- @freeze_time ("2021-03-14T12:27:28.303Z" )
430
+ @freeze_time (_now )
420
431
def test_org_multiple_fields (self ):
421
432
make_request = functools .partial (
422
433
self .client .get ,
@@ -432,8 +443,8 @@ def test_org_multiple_fields(self):
432
443
433
444
assert response .status_code == 200 , response .content
434
445
assert response .data == {
435
- "start" : "2021-03-12T00:00:00Z" ,
436
- "end" : "2021-03-15T00:00:00Z" ,
446
+ "start" : _iso_z ( floor_to_utc_day ( self . _now ) - timedelta ( days = 2 )) ,
447
+ "end" : _iso_z ( floor_to_utc_day ( self . _now ) + timedelta ( days = 1 )) ,
437
448
"projects" : [
438
449
{
439
450
"id" : self .project .id ,
@@ -493,7 +504,7 @@ def test_org_multiple_fields(self):
493
504
],
494
505
}
495
506
496
- @freeze_time ("2021-03-14T12:27:28.303Z" )
507
+ @freeze_time (_now )
497
508
def test_org_project_totals_per_project (self ):
498
509
make_request = functools .partial (
499
510
self .client .get ,
@@ -510,8 +521,8 @@ def test_org_project_totals_per_project(self):
510
521
511
522
assert response_per_group .status_code == 200 , response_per_group .content
512
523
assert response_per_group .data == {
513
- "start" : "2021-03-13T12:00:00Z" ,
514
- "end" : "2021-03-14T13:00:00Z" ,
524
+ "start" : _iso_z (( self . _now - timedelta ( days = 1 )). replace ( hour = 12 , minute = 0 , second = 0 )) ,
525
+ "end" : _iso_z ( self . _now . replace ( hour = 13 , minute = 0 , second = 0 )) ,
515
526
"projects" : [
516
527
{
517
528
"id" : self .project .id ,
@@ -554,7 +565,7 @@ def test_org_project_totals_per_project(self):
554
565
],
555
566
}
556
567
557
- @freeze_time ("2021-03-14T12:27:28.303Z" )
568
+ @freeze_time (_now )
558
569
def test_project_filter (self ):
559
570
make_request = functools .partial (
560
571
self .client .get ,
@@ -572,8 +583,8 @@ def test_project_filter(self):
572
583
573
584
assert response .status_code == 200 , response .content
574
585
assert response .data == {
575
- "start" : "2021-03-13T00:00:00Z" ,
576
- "end" : "2021-03-15T00:00:00Z" ,
586
+ "start" : _iso_z ( floor_to_utc_day ( self . _now ) - timedelta ( days = 1 )) ,
587
+ "end" : _iso_z ( floor_to_utc_day ( self . _now ) + timedelta ( days = 1 )) ,
577
588
"projects" : [
578
589
{
579
590
"id" : self .project .id ,
@@ -597,7 +608,7 @@ def test_project_filter(self):
597
608
],
598
609
}
599
610
600
- @freeze_time ("2021-03-14T12:27:28.303Z" )
611
+ @freeze_time (_now )
601
612
def test_reason_filter (self ):
602
613
make_request = functools .partial (
603
614
self .client .get ,
@@ -615,8 +626,8 @@ def test_reason_filter(self):
615
626
616
627
assert response .status_code == 200 , response .content
617
628
assert response .data == {
618
- "start" : "2021-03-13T00:00:00Z" ,
619
- "end" : "2021-03-15T00:00:00Z" ,
629
+ "start" : _iso_z ( floor_to_utc_day ( self . _now ) - timedelta ( days = 1 )) ,
630
+ "end" : _iso_z ( floor_to_utc_day ( self . _now ) + timedelta ( days = 1 )) ,
620
631
"projects" : [
621
632
{
622
633
"id" : self .project .id ,
@@ -661,7 +672,7 @@ def test_reason_filter(self):
661
672
],
662
673
}
663
674
664
- @freeze_time ("2021-03-14T12:27:28.303Z" )
675
+ @freeze_time (_now )
665
676
def test_outcome_filter (self ):
666
677
make_request = functools .partial (
667
678
self .client .get ,
@@ -678,8 +689,8 @@ def test_outcome_filter(self):
678
689
)
679
690
assert response .status_code == 200 , response .content
680
691
assert response .data == {
681
- "start" : "2021-03-13T00:00:00Z" ,
682
- "end" : "2021-03-15T00:00:00Z" ,
692
+ "start" : _iso_z ( floor_to_utc_day ( self . _now ) - timedelta ( days = 1 )) ,
693
+ "end" : _iso_z ( floor_to_utc_day ( self . _now ) + timedelta ( days = 1 )) ,
683
694
"projects" : [
684
695
{
685
696
"id" : self .project .id ,
@@ -697,7 +708,7 @@ def test_outcome_filter(self):
697
708
],
698
709
}
699
710
700
- @freeze_time ("2021-03-14T12:27:28.303Z" )
711
+ @freeze_time (_now )
701
712
def test_category_filter (self ):
702
713
make_request = functools .partial (
703
714
self .client .get ,
@@ -713,8 +724,8 @@ def test_category_filter(self):
713
724
)
714
725
assert response .status_code == 200 , response .content
715
726
assert response .data == {
716
- "start" : "2021-03-13T00:00:00Z" ,
717
- "end" : "2021-03-15T00:00:00Z" ,
727
+ "start" : _iso_z ( floor_to_utc_day ( self . _now ) - timedelta ( days = 1 )) ,
728
+ "end" : _iso_z ( floor_to_utc_day ( self . _now ) + timedelta ( days = 1 )) ,
718
729
"projects" : [
719
730
{
720
731
"id" : self .project .id ,
0 commit comments