File: test_detection_config.py

package info (click to toggle)
python-azure 20201208%2Bgit-6
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 1,437,920 kB
  • sloc: python: 4,287,452; javascript: 269; makefile: 198; sh: 187; xml: 106
file content (806 lines) | stat: -rw-r--r-- 54,684 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
# coding=utf-8
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------

import pytest
from azure.core.exceptions import ResourceNotFoundError

from azure.ai.metricsadvisor.models import (
    MetricDetectionCondition,
    MetricSeriesGroupDetectionCondition,
    MetricSingleSeriesDetectionCondition,
    SmartDetectionCondition,
    SuppressCondition,
    ChangeThresholdCondition,
    HardThresholdCondition,
)
from base_testcase import TestMetricsAdvisorAdministrationClientBase


class TestMetricsAdvisorAdministrationClient(TestMetricsAdvisorAdministrationClientBase):

    def test_create_ad_config_whole_series_detection(self):

        data_feed = self._create_data_feed("adconfig")
        try:
            detection_config_name = self.create_random_name("testdetectionconfig")
            config = self.admin_client.create_detection_configuration(
                name=detection_config_name,
                metric_id=data_feed.metric_ids[0],
                description="My test metric anomaly detection configuration",
                whole_series_detection_condition=MetricDetectionCondition(
                    cross_conditions_operator="OR",
                    smart_detection_condition=SmartDetectionCondition(
                        sensitivity=50,
                        anomaly_detector_direction="Both",
                        suppress_condition=SuppressCondition(
                            min_number=50,
                            min_ratio=50
                        )
                    ),
                    hard_threshold_condition=HardThresholdCondition(
                        anomaly_detector_direction="Both",
                        suppress_condition=SuppressCondition(
                            min_number=5,
                            min_ratio=5
                        ),
                        lower_bound=0,
                        upper_bound=100
                    ),
                    change_threshold_condition=ChangeThresholdCondition(
                        change_percentage=50,
                        shift_point=30,
                        within_range=True,
                        anomaly_detector_direction="Both",
                        suppress_condition=SuppressCondition(
                            min_number=2,
                            min_ratio=2
                        )
                    )
                )
            )
            self.assertIsNotNone(config.id)
            self.assertEqual(config.metric_id, data_feed.metric_ids[0])
            self.assertEqual(config.description, "My test metric anomaly detection configuration")
            self.assertIsNotNone(config.name)
            self.assertIsNone(config.series_detection_conditions)
            self.assertIsNone(config.series_group_detection_conditions)
            self.assertEqual(config.whole_series_detection_condition.cross_conditions_operator, "OR")
            self.assertEqual(
                config.whole_series_detection_condition.change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(config.whole_series_detection_condition.change_threshold_condition.change_percentage, 50)
            self.assertEqual(config.whole_series_detection_condition.change_threshold_condition.shift_point, 30)
            self.assertTrue(config.whole_series_detection_condition.change_threshold_condition.within_range)
            self.assertEqual(
                config.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_number, 2)
            self.assertEqual(
                config.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(
                config.whole_series_detection_condition.hard_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(config.whole_series_detection_condition.hard_threshold_condition.lower_bound, 0)
            self.assertEqual(config.whole_series_detection_condition.hard_threshold_condition.upper_bound, 100)
            self.assertEqual(
                config.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(
                config.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_ratio, 5)
            self.assertEqual(
                config.whole_series_detection_condition.smart_detection_condition.anomaly_detector_direction, "Both")
            self.assertEqual(config.whole_series_detection_condition.smart_detection_condition.sensitivity, 50)
            self.assertEqual(
                config.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_number, 50)
            self.assertEqual(
                config.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_ratio, 50)

            self.admin_client.delete_detection_configuration(config.id)

            with self.assertRaises(ResourceNotFoundError):
                self.admin_client.get_detection_configuration(config.id)
        finally:
            self.admin_client.delete_data_feed(data_feed.id)

    def test_create_ad_config_with_series_and_group_conds(self):
        data_feed = self._create_data_feed("adconfigget")
        try:
            detection_config_name = self.create_random_name("testdetectionconfiget")
            detection_config = self.admin_client.create_detection_configuration(
                name=detection_config_name,
                metric_id=data_feed.metric_ids[0],
                description="My test metric anomaly detection configuration",
                whole_series_detection_condition=MetricDetectionCondition(
                    cross_conditions_operator="AND",
                    smart_detection_condition=SmartDetectionCondition(
                        sensitivity=50,
                        anomaly_detector_direction="Both",
                        suppress_condition=SuppressCondition(
                            min_number=50,
                            min_ratio=50
                        )
                    ),
                    hard_threshold_condition=HardThresholdCondition(
                        anomaly_detector_direction="Both",
                        suppress_condition=SuppressCondition(
                            min_number=5,
                            min_ratio=5
                        ),
                        lower_bound=0,
                        upper_bound=100
                    ),
                    change_threshold_condition=ChangeThresholdCondition(
                        change_percentage=50,
                        shift_point=30,
                        within_range=True,
                        anomaly_detector_direction="Both",
                        suppress_condition=SuppressCondition(
                            min_number=2,
                            min_ratio=2
                        )
                    )
                ),
                series_detection_conditions=[MetricSingleSeriesDetectionCondition(
                    series_key={"city": "Shenzhen", "category": "Jewelry"},
                    smart_detection_condition=SmartDetectionCondition(
                        anomaly_detector_direction="Both",
                        sensitivity=63,
                        suppress_condition=SuppressCondition(
                            min_number=1,
                            min_ratio=100
                        )
                    )
                )],
                series_group_detection_conditions=[MetricSeriesGroupDetectionCondition(
                    series_group_key={"city": "Sao Paulo"},
                    smart_detection_condition=SmartDetectionCondition(
                        anomaly_detector_direction="Both",
                        sensitivity=63,
                        suppress_condition=SuppressCondition(
                            min_number=1,
                            min_ratio=100
                        )
                    )
                )]
            )

            self.assertIsNotNone(detection_config.id)
            self.assertEqual(detection_config.metric_id, data_feed.metric_ids[0])
            self.assertEqual(detection_config.description, "My test metric anomaly detection configuration")
            self.assertIsNotNone(detection_config.name)
            self.assertEqual(detection_config.whole_series_detection_condition.cross_conditions_operator, "AND")
            self.assertEqual(
                detection_config.whole_series_detection_condition.change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(detection_config.whole_series_detection_condition.change_threshold_condition.change_percentage, 50)
            self.assertEqual(detection_config.whole_series_detection_condition.change_threshold_condition.shift_point, 30)
            self.assertTrue(detection_config.whole_series_detection_condition.change_threshold_condition.within_range)
            self.assertEqual(
                detection_config.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_number, 2)
            self.assertEqual(
                detection_config.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(
                detection_config.whole_series_detection_condition.hard_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(detection_config.whole_series_detection_condition.hard_threshold_condition.lower_bound, 0)
            self.assertEqual(detection_config.whole_series_detection_condition.hard_threshold_condition.upper_bound, 100)
            self.assertEqual(
                detection_config.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(
                detection_config.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_ratio, 5)
            self.assertEqual(
                detection_config.whole_series_detection_condition.smart_detection_condition.anomaly_detector_direction, "Both")
            self.assertEqual(detection_config.whole_series_detection_condition.smart_detection_condition.sensitivity, 50)
            self.assertEqual(
                detection_config.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_number, 50)
            self.assertEqual(
                detection_config.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_ratio, 50)
            self.assertEqual(
                detection_config.series_detection_conditions[0].smart_detection_condition.suppress_condition.min_ratio, 100)
            self.assertEqual(
                detection_config.series_detection_conditions[0].smart_detection_condition.suppress_condition.min_number, 1)
            self.assertEqual(
                detection_config.series_detection_conditions[0].smart_detection_condition.sensitivity, 63)
            self.assertEqual(
                detection_config.series_detection_conditions[0].smart_detection_condition.anomaly_detector_direction, "Both")
            self.assertEqual(
                detection_config.series_detection_conditions[0].series_key, {'city': 'Shenzhen', 'category': 'Jewelry'})
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].smart_detection_condition.suppress_condition.min_ratio, 100)
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].smart_detection_condition.suppress_condition.min_number, 1)
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].smart_detection_condition.sensitivity, 63)
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].smart_detection_condition.anomaly_detector_direction, "Both")
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].series_group_key, {'city': 'Sao Paulo'})

        finally:
            self.admin_client.delete_data_feed(data_feed.id)

    def test_create_ad_config_multiple_series_and_group_conds(self):
        data_feed = self._create_data_feed("datafeedforconfig")
        try:
            detection_config_name = self.create_random_name("multipledetectionconfigs")
            detection_config = self.admin_client.create_detection_configuration(
                name=detection_config_name,
                metric_id=data_feed.metric_ids[0],
                description="My test metric anomaly detection configuration",
                whole_series_detection_condition=MetricDetectionCondition(
                    cross_conditions_operator="AND",
                    smart_detection_condition=SmartDetectionCondition(
                        sensitivity=50,
                        anomaly_detector_direction="Both",
                        suppress_condition=SuppressCondition(
                            min_number=50,
                            min_ratio=50
                        )
                    ),
                    hard_threshold_condition=HardThresholdCondition(
                        anomaly_detector_direction="Both",
                        suppress_condition=SuppressCondition(
                            min_number=5,
                            min_ratio=5
                        ),
                        lower_bound=0,
                        upper_bound=100
                    ),
                    change_threshold_condition=ChangeThresholdCondition(
                        change_percentage=50,
                        shift_point=30,
                        within_range=True,
                        anomaly_detector_direction="Both",
                        suppress_condition=SuppressCondition(
                            min_number=2,
                            min_ratio=2
                        )
                    )
                ),
                series_detection_conditions=[
                    MetricSingleSeriesDetectionCondition(
                        series_key={"city": "Shenzhen", "category": "Jewelry"},
                        cross_conditions_operator="AND",
                        smart_detection_condition=SmartDetectionCondition(
                            anomaly_detector_direction="Both",
                            sensitivity=63,
                            suppress_condition=SuppressCondition(
                                min_number=1,
                                min_ratio=100
                            )
                        ),
                        hard_threshold_condition=HardThresholdCondition(
                            anomaly_detector_direction="Both",
                            suppress_condition=SuppressCondition(
                                min_number=5,
                                min_ratio=5
                            ),
                            lower_bound=0,
                            upper_bound=100
                        ),
                        change_threshold_condition=ChangeThresholdCondition(
                            change_percentage=50,
                            shift_point=30,
                            within_range=True,
                            anomaly_detector_direction="Both",
                            suppress_condition=SuppressCondition(
                                min_number=2,
                                min_ratio=2
                            )
                        )
                    ),
                    MetricSingleSeriesDetectionCondition(
                        series_key={"city": "Osaka", "category": "Cell Phones"},
                        cross_conditions_operator="AND",
                        smart_detection_condition=SmartDetectionCondition(
                            anomaly_detector_direction="Both",
                            sensitivity=63,
                            suppress_condition=SuppressCondition(
                                min_number=1,
                                min_ratio=100
                            )
                        )
                    )
                ],
                series_group_detection_conditions=[
                    MetricSeriesGroupDetectionCondition(
                        series_group_key={"city": "Sao Paulo"},
                        cross_conditions_operator="AND",
                        smart_detection_condition=SmartDetectionCondition(
                            anomaly_detector_direction="Both",
                            sensitivity=63,
                            suppress_condition=SuppressCondition(
                                min_number=1,
                                min_ratio=100
                            )
                        ),
                        hard_threshold_condition=HardThresholdCondition(
                            anomaly_detector_direction="Both",
                            suppress_condition=SuppressCondition(
                                min_number=5,
                                min_ratio=5
                            ),
                            lower_bound=0,
                            upper_bound=100
                        ),
                        change_threshold_condition=ChangeThresholdCondition(
                            change_percentage=50,
                            shift_point=30,
                            within_range=True,
                            anomaly_detector_direction="Both",
                            suppress_condition=SuppressCondition(
                                min_number=2,
                                min_ratio=2
                            )
                        )
                    ),
                    MetricSeriesGroupDetectionCondition(
                        series_group_key={"city": "Seoul"},
                        cross_conditions_operator="AND",
                        smart_detection_condition=SmartDetectionCondition(
                            anomaly_detector_direction="Both",
                            sensitivity=63,
                            suppress_condition=SuppressCondition(
                                min_number=1,
                                min_ratio=100
                            )
                        )
                    )
                ]
            )

            self.assertIsNotNone(detection_config.id)
            self.assertEqual(detection_config.metric_id, data_feed.metric_ids[0])
            self.assertEqual(detection_config.description, "My test metric anomaly detection configuration")
            self.assertIsNotNone(detection_config.name)

            # whole series detection condition
            self.assertEqual(detection_config.whole_series_detection_condition.cross_conditions_operator, "AND")
            self.assertEqual(
                detection_config.whole_series_detection_condition.change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(detection_config.whole_series_detection_condition.change_threshold_condition.change_percentage, 50)
            self.assertEqual(detection_config.whole_series_detection_condition.change_threshold_condition.shift_point, 30)
            self.assertTrue(detection_config.whole_series_detection_condition.change_threshold_condition.within_range)
            self.assertEqual(
                detection_config.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_number, 2)
            self.assertEqual(
                detection_config.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(
                detection_config.whole_series_detection_condition.hard_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(detection_config.whole_series_detection_condition.hard_threshold_condition.lower_bound, 0)
            self.assertEqual(detection_config.whole_series_detection_condition.hard_threshold_condition.upper_bound, 100)
            self.assertEqual(
                detection_config.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(
                detection_config.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_ratio, 5)
            self.assertEqual(
                detection_config.whole_series_detection_condition.smart_detection_condition.anomaly_detector_direction, "Both")
            self.assertEqual(detection_config.whole_series_detection_condition.smart_detection_condition.sensitivity, 50)
            self.assertEqual(
                detection_config.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_number, 50)
            self.assertEqual(
                detection_config.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_ratio, 50)

            # series detection conditions
            self.assertEqual(
                detection_config.series_detection_conditions[0].series_key, {'city': 'Shenzhen', 'category': 'Jewelry'})
            self.assertEqual(detection_config.series_detection_conditions[0].cross_conditions_operator, "AND")
            self.assertEqual(
                detection_config.series_detection_conditions[0].smart_detection_condition.suppress_condition.min_ratio, 100)
            self.assertEqual(
                detection_config.series_detection_conditions[0].smart_detection_condition.suppress_condition.min_number, 1)
            self.assertEqual(
                detection_config.series_detection_conditions[0].smart_detection_condition.sensitivity, 63)
            self.assertEqual(
                detection_config.series_detection_conditions[0].smart_detection_condition.anomaly_detector_direction, "Both")
            self.assertEqual(
                detection_config.series_detection_conditions[0].change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(detection_config.series_detection_conditions[0].change_threshold_condition.change_percentage, 50)
            self.assertEqual(detection_config.series_detection_conditions[0].change_threshold_condition.shift_point, 30)
            self.assertTrue(detection_config.series_detection_conditions[0].change_threshold_condition.within_range)
            self.assertEqual(
                detection_config.series_detection_conditions[0].change_threshold_condition.suppress_condition.min_number, 2)
            self.assertEqual(
                detection_config.series_detection_conditions[0].change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(
                detection_config.series_detection_conditions[0].hard_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(detection_config.series_detection_conditions[0].hard_threshold_condition.lower_bound, 0)
            self.assertEqual(detection_config.series_detection_conditions[0].hard_threshold_condition.upper_bound, 100)
            self.assertEqual(
                detection_config.series_detection_conditions[0].hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(
                detection_config.series_detection_conditions[0].hard_threshold_condition.suppress_condition.min_ratio, 5)
            self.assertEqual(
                detection_config.series_detection_conditions[1].series_key, {"city": "Osaka", "category": "Cell Phones"})
            self.assertEqual(
                detection_config.series_detection_conditions[1].smart_detection_condition.suppress_condition.min_ratio, 100)
            self.assertEqual(
                detection_config.series_detection_conditions[1].smart_detection_condition.suppress_condition.min_number, 1)
            self.assertEqual(
                detection_config.series_detection_conditions[1].smart_detection_condition.sensitivity, 63)
            self.assertEqual(
                detection_config.series_detection_conditions[1].smart_detection_condition.anomaly_detector_direction, "Both")

            # series group detection conditions
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].series_group_key, {"city": "Sao Paulo"})
            self.assertEqual(detection_config.series_group_detection_conditions[0].cross_conditions_operator, "AND")
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].smart_detection_condition.suppress_condition.min_ratio, 100)
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].smart_detection_condition.suppress_condition.min_number, 1)
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].smart_detection_condition.sensitivity, 63)
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].smart_detection_condition.anomaly_detector_direction, "Both")
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(detection_config.series_group_detection_conditions[0].change_threshold_condition.change_percentage, 50)
            self.assertEqual(detection_config.series_group_detection_conditions[0].change_threshold_condition.shift_point, 30)
            self.assertTrue(detection_config.series_group_detection_conditions[0].change_threshold_condition.within_range)
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].change_threshold_condition.suppress_condition.min_number, 2)
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].hard_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(detection_config.series_group_detection_conditions[0].hard_threshold_condition.lower_bound, 0)
            self.assertEqual(detection_config.series_group_detection_conditions[0].hard_threshold_condition.upper_bound, 100)
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(
                detection_config.series_group_detection_conditions[0].hard_threshold_condition.suppress_condition.min_ratio, 5)
            self.assertEqual(
                detection_config.series_group_detection_conditions[1].series_group_key, {"city": "Seoul"})
            self.assertEqual(
                detection_config.series_group_detection_conditions[1].smart_detection_condition.suppress_condition.min_ratio, 100)
            self.assertEqual(
                detection_config.series_group_detection_conditions[1].smart_detection_condition.suppress_condition.min_number, 1)
            self.assertEqual(
                detection_config.series_group_detection_conditions[1].smart_detection_condition.sensitivity, 63)
            self.assertEqual(
                detection_config.series_group_detection_conditions[1].smart_detection_condition.anomaly_detector_direction, "Both")

        finally:
            self.admin_client.delete_data_feed(data_feed.id)

    def test_list_detection_configs(self):
        configs = self.admin_client.list_detection_configurations(metric_id=self.metric_id)
        assert len(list(configs)) > 0

    def test_update_detection_config_with_model(self):
        try:
            detection_config, data_feed = self._create_detection_config_for_update("updatedetection")

            detection_config.name = "updated"
            detection_config.description = "updated"
            change_threshold_condition = ChangeThresholdCondition(
                anomaly_detector_direction="Both",
                change_percentage=20,
                shift_point=10,
                within_range=True,
                suppress_condition=SuppressCondition(
                    min_number=5,
                    min_ratio=2
                )
            )
            hard_threshold_condition = HardThresholdCondition(
                anomaly_detector_direction="Up",
                upper_bound=100,
                suppress_condition=SuppressCondition(
                    min_number=5,
                    min_ratio=2
                )
            )
            smart_detection_condition = SmartDetectionCondition(
                anomaly_detector_direction="Up",
                sensitivity=10,
                suppress_condition=SuppressCondition(
                    min_number=5,
                    min_ratio=2
                )
            )
            detection_config.series_detection_conditions[0].change_threshold_condition = change_threshold_condition
            detection_config.series_detection_conditions[0].hard_threshold_condition = hard_threshold_condition
            detection_config.series_detection_conditions[0].smart_detection_condition = smart_detection_condition
            detection_config.series_detection_conditions[0].cross_conditions_operator = "AND"
            detection_config.series_group_detection_conditions[0].change_threshold_condition = change_threshold_condition
            detection_config.series_group_detection_conditions[0].hard_threshold_condition = hard_threshold_condition
            detection_config.series_group_detection_conditions[0].smart_detection_condition = smart_detection_condition
            detection_config.series_group_detection_conditions[0].cross_conditions_operator = "AND"
            detection_config.whole_series_detection_condition.hard_threshold_condition = hard_threshold_condition
            detection_config.whole_series_detection_condition.smart_detection_condition = smart_detection_condition
            detection_config.whole_series_detection_condition.change_threshold_condition = change_threshold_condition
            detection_config.whole_series_detection_condition.cross_conditions_operator = "OR"

            updated = self.admin_client.update_detection_configuration(detection_config)

            self.assertEqual(updated.name, "updated")
            self.assertEqual(updated.description, "updated")
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.change_percentage, 20)
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.shift_point, 10)
            self.assertTrue(updated.series_detection_conditions[0].change_threshold_condition.within_range)
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.upper_bound, 100)
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.sensitivity, 10)
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_detection_conditions[0].cross_conditions_operator, "AND")

            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.change_percentage, 20)
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.shift_point, 10)
            self.assertTrue(updated.series_group_detection_conditions[0].change_threshold_condition.within_range)
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.upper_bound, 100)
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.sensitivity, 10)
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_group_detection_conditions[0].cross_conditions_operator, "AND")

            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.change_percentage, 20)
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.shift_point, 10)
            self.assertTrue(updated.whole_series_detection_condition.change_threshold_condition.within_range)
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.upper_bound, 100)
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.sensitivity, 10)
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.whole_series_detection_condition.cross_conditions_operator, "OR")
        finally:
            self.admin_client.delete_data_feed(data_feed.id)

    def test_update_detection_config_with_kwargs(self):
        try:
            detection_config, data_feed = self._create_detection_config_for_update("updatedetection")
            change_threshold_condition = ChangeThresholdCondition(
                anomaly_detector_direction="Both",
                change_percentage=20,
                shift_point=10,
                within_range=True,
                suppress_condition=SuppressCondition(
                    min_number=5,
                    min_ratio=2
                )
            )
            hard_threshold_condition = HardThresholdCondition(
                anomaly_detector_direction="Up",
                upper_bound=100,
                suppress_condition=SuppressCondition(
                    min_number=5,
                    min_ratio=2
                )
            )
            smart_detection_condition = SmartDetectionCondition(
                anomaly_detector_direction="Up",
                sensitivity=10,
                suppress_condition=SuppressCondition(
                    min_number=5,
                    min_ratio=2
                )
            )
            updated = self.admin_client.update_detection_configuration(
                detection_config.id,
                name="updated",
                description="updated",
                whole_series_detection_condition=MetricDetectionCondition(
                    cross_conditions_operator="OR",
                    smart_detection_condition=smart_detection_condition,
                    hard_threshold_condition=hard_threshold_condition,
                    change_threshold_condition=change_threshold_condition
                ),
                series_detection_conditions=[MetricSingleSeriesDetectionCondition(
                    series_key={"city": "San Paulo", "category": "Jewelry"},
                    cross_conditions_operator="AND",
                    smart_detection_condition=smart_detection_condition,
                    hard_threshold_condition=hard_threshold_condition,
                    change_threshold_condition=change_threshold_condition
                )],
                series_group_detection_conditions=[MetricSeriesGroupDetectionCondition(
                    series_group_key={"city": "Shenzen"},
                    cross_conditions_operator="AND",
                    smart_detection_condition=smart_detection_condition,
                    hard_threshold_condition=hard_threshold_condition,
                    change_threshold_condition=change_threshold_condition
                )]
            )

            self.assertEqual(updated.name, "updated")
            self.assertEqual(updated.description, "updated")
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.change_percentage, 20)
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.shift_point, 10)
            self.assertTrue(updated.series_detection_conditions[0].change_threshold_condition.within_range)
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.upper_bound, 100)
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.sensitivity, 10)
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_detection_conditions[0].cross_conditions_operator, "AND")
            self.assertEqual(updated.series_detection_conditions[0].series_key, {"city": "San Paulo", "category": "Jewelry"})

            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.change_percentage, 20)
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.shift_point, 10)
            self.assertTrue(updated.series_group_detection_conditions[0].change_threshold_condition.within_range)
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.upper_bound, 100)
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.sensitivity, 10)
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_group_detection_conditions[0].cross_conditions_operator, "AND")
            self.assertEqual(updated.series_group_detection_conditions[0].series_group_key, {"city": "Shenzen"})

            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.change_percentage, 20)
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.shift_point, 10)
            self.assertTrue(updated.whole_series_detection_condition.change_threshold_condition.within_range)
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.upper_bound, 100)
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.sensitivity, 10)
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.whole_series_detection_condition.cross_conditions_operator, "OR")
        finally:
            self.admin_client.delete_data_feed(data_feed.id)

    def test_update_detection_config_with_model_and_kwargs(self):
        try:
            detection_config, data_feed = self._create_detection_config_for_update("updatedetection")
            change_threshold_condition = ChangeThresholdCondition(
                anomaly_detector_direction="Both",
                change_percentage=20,
                shift_point=10,
                within_range=True,
                suppress_condition=SuppressCondition(
                    min_number=5,
                    min_ratio=2
                )
            )
            hard_threshold_condition = HardThresholdCondition(
                anomaly_detector_direction="Up",
                upper_bound=100,
                suppress_condition=SuppressCondition(
                    min_number=5,
                    min_ratio=2
                )
            )
            smart_detection_condition = SmartDetectionCondition(
                anomaly_detector_direction="Up",
                sensitivity=10,
                suppress_condition=SuppressCondition(
                    min_number=5,
                    min_ratio=2
                )
            )

            detection_config.name = "updateMe"
            detection_config.description = "updateMe"
            updated = self.admin_client.update_detection_configuration(
                detection_config,
                whole_series_detection_condition=MetricDetectionCondition(
                    cross_conditions_operator="OR",
                    smart_detection_condition=smart_detection_condition,
                    hard_threshold_condition=hard_threshold_condition,
                    change_threshold_condition=change_threshold_condition
                ),
                series_detection_conditions=[MetricSingleSeriesDetectionCondition(
                    series_key={"city": "San Paulo", "category": "Jewelry"},
                    cross_conditions_operator="AND",
                    smart_detection_condition=smart_detection_condition,
                    hard_threshold_condition=hard_threshold_condition,
                    change_threshold_condition=change_threshold_condition
                )],
                series_group_detection_conditions=[MetricSeriesGroupDetectionCondition(
                    series_group_key={"city": "Shenzen"},
                    cross_conditions_operator="AND",
                    smart_detection_condition=smart_detection_condition,
                    hard_threshold_condition=hard_threshold_condition,
                    change_threshold_condition=change_threshold_condition
                )]
            )

            self.assertEqual(updated.name, "updateMe")
            self.assertEqual(updated.description, "updateMe")
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.change_percentage, 20)
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.shift_point, 10)
            self.assertTrue(updated.series_detection_conditions[0].change_threshold_condition.within_range)
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_detection_conditions[0].change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.upper_bound, 100)
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_detection_conditions[0].hard_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.sensitivity, 10)
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_detection_conditions[0].smart_detection_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_detection_conditions[0].cross_conditions_operator, "AND")
            self.assertEqual(updated.series_detection_conditions[0].series_key, {"city": "San Paulo", "category": "Jewelry"})

            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.change_percentage, 20)
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.shift_point, 10)
            self.assertTrue(updated.series_group_detection_conditions[0].change_threshold_condition.within_range)
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_group_detection_conditions[0].change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.upper_bound, 100)
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_group_detection_conditions[0].hard_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.sensitivity, 10)
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.series_group_detection_conditions[0].smart_detection_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.series_group_detection_conditions[0].cross_conditions_operator, "AND")
            self.assertEqual(updated.series_group_detection_conditions[0].series_group_key, {"city": "Shenzen"})

            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.anomaly_detector_direction, "Both")
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.change_percentage, 20)
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.shift_point, 10)
            self.assertTrue(updated.whole_series_detection_condition.change_threshold_condition.within_range)
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.whole_series_detection_condition.change_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.upper_bound, 100)
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.whole_series_detection_condition.hard_threshold_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.anomaly_detector_direction, "Up")
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.sensitivity, 10)
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_number, 5)
            self.assertEqual(updated.whole_series_detection_condition.smart_detection_condition.suppress_condition.min_ratio, 2)
            self.assertEqual(updated.whole_series_detection_condition.cross_conditions_operator, "OR")
        finally:
            self.admin_client.delete_data_feed(data_feed.id)

    def test_update_detection_config_by_resetting_properties(self):
        try:
            detection_config, data_feed = self._create_detection_config_for_update("updatedetection")

            updated = self.admin_client.update_detection_configuration(
                detection_config.id,
                name="reset",
                description="",
                # series_detection_conditions=None,
                # series_group_detection_conditions=None
            )

            self.assertEqual(updated.name, "reset")
            self.assertEqual(updated.description, "")  # currently won't update with None

            # service bug says these are required
            # self.assertEqual(updated.series_detection_conditions, None)
            # self.assertEqual(updated.series_group_detection_conditions, None)

        finally:
            self.admin_client.delete_data_feed(data_feed.id)