|
1 | 1 | import datetime |
| 2 | +from base64 import b64encode |
2 | 3 |
|
3 | 4 | from django.test import TransactionTestCase |
4 | 5 | from shared.django_apps.reports.tests.factories import FlakeFactory |
|
17 | 18 | from .helper import GraphQLTestHelper |
18 | 19 |
|
19 | 20 |
|
| 21 | +def base64_encode_string(x: str) -> str: |
| 22 | + return b64encode(x.encode()).decode("utf-8") |
| 23 | + |
| 24 | + |
20 | 25 | class TestAnalyticsTestCase(GraphQLTestHelper, TransactionTestCase): |
21 | 26 | def setUp(self) -> None: |
22 | 27 | self.owner = OwnerFactory(username="codecov-user") |
@@ -119,6 +124,28 @@ def test_branch_filter_on_test_results(self) -> None: |
119 | 124 | ) |
120 | 125 | assert res["testResults"] == {"edges": [{"node": {"name": test.name}}]} |
121 | 126 |
|
| 127 | + def test_interval_filter_on_test_results(self) -> None: |
| 128 | + repo = RepositoryFactory(author=self.owner, active=True, private=True) |
| 129 | + test = TestFactory(repository=repo) |
| 130 | + test2 = TestFactory(repository=repo) |
| 131 | + _ = DailyTestRollupFactory( |
| 132 | + test=test, |
| 133 | + date=datetime.datetime.now() - datetime.timedelta(days=7), |
| 134 | + repoid=repo.repoid, |
| 135 | + branch="main", |
| 136 | + ) |
| 137 | + _ = DailyTestRollupFactory( |
| 138 | + test=test2, |
| 139 | + date=datetime.datetime.now(), |
| 140 | + repoid=repo.repoid, |
| 141 | + branch="feature", |
| 142 | + ) |
| 143 | + res = self.fetch_test_analytics( |
| 144 | + repo.name, |
| 145 | + """testResults(filters: { interval: INTERVAL_1_DAY }) { edges { node { name } } }""", |
| 146 | + ) |
| 147 | + assert res["testResults"] == {"edges": [{"node": {"name": test2.name}}]} |
| 148 | + |
122 | 149 | def test_flaky_filter_on_test_results(self) -> None: |
123 | 150 | repo = RepositoryFactory(author=self.owner, active=True, private=True) |
124 | 151 | test = TestFactory(repository=repo) |
@@ -167,6 +194,7 @@ def test_failed_filter_on_test_results(self) -> None: |
167 | 194 | assert res["testResults"] == {"edges": [{"node": {"name": test2.name}}]} |
168 | 195 |
|
169 | 196 | def test_skipped_filter_on_test_results(self) -> None: |
| 197 | + # note - this test guards against division by zero errors for the failure/flake rate |
170 | 198 | repo = RepositoryFactory(author=self.owner, active=True, private=True) |
171 | 199 | test = TestFactory(repository=repo) |
172 | 200 | test2 = TestFactory(repository=repo) |
@@ -367,8 +395,8 @@ def test_last_duration_ordering_on_test_results(self) -> None: |
367 | 395 | ) |
368 | 396 | assert res["testResults"] == { |
369 | 397 | "edges": [ |
370 | | - {"node": {"name": test.name, "lastDuration": 0.0}}, |
371 | | - {"node": {"name": test_2.name, "lastDuration": 0.0}}, |
| 398 | + {"node": {"name": test.name, "lastDuration": 2.0}}, |
| 399 | + {"node": {"name": test_2.name, "lastDuration": 3.0}}, |
372 | 400 | ] |
373 | 401 | } |
374 | 402 |
|
@@ -402,8 +430,8 @@ def test_desc_last_duration_ordering_on_test_results(self) -> None: |
402 | 430 | ) |
403 | 431 | assert res["testResults"] == { |
404 | 432 | "edges": [ |
405 | | - {"node": {"name": test_2.name, "lastDuration": 0.0}}, |
406 | | - {"node": {"name": test.name, "lastDuration": 0.0}}, |
| 433 | + {"node": {"name": test_2.name, "lastDuration": 3.0}}, |
| 434 | + {"node": {"name": test.name, "lastDuration": 2.0}}, |
407 | 435 | ] |
408 | 436 | } |
409 | 437 |
|
@@ -548,6 +576,142 @@ def test_desc_failure_rate_ordering_on_test_results(self) -> None: |
548 | 576 | ] |
549 | 577 | } |
550 | 578 |
|
| 579 | + def test_desc_failure_rate_ordering_on_test_results_with_after(self) -> None: |
| 580 | + repo = RepositoryFactory(author=self.owner, active=True, private=True) |
| 581 | + test = TestFactory(repository=repo) |
| 582 | + _ = DailyTestRollupFactory( |
| 583 | + test=test, |
| 584 | + date=datetime.date.today() - datetime.timedelta(days=1), |
| 585 | + repoid=repo.repoid, |
| 586 | + pass_count=1, |
| 587 | + fail_count=1, |
| 588 | + ) |
| 589 | + _ = DailyTestRollupFactory( |
| 590 | + test=test, |
| 591 | + date=datetime.date.today(), |
| 592 | + repoid=repo.repoid, |
| 593 | + pass_count=3, |
| 594 | + fail_count=0, |
| 595 | + ) |
| 596 | + test_2 = TestFactory(repository=repo) |
| 597 | + _ = DailyTestRollupFactory( |
| 598 | + test=test_2, |
| 599 | + date=datetime.date.today(), |
| 600 | + repoid=repo.repoid, |
| 601 | + pass_count=2, |
| 602 | + fail_count=3, |
| 603 | + ) |
| 604 | + res = self.fetch_test_analytics( |
| 605 | + repo.name, |
| 606 | + """testResults(ordering: { parameter: FAILURE_RATE, direction: DESC }, first: 1) { edges { node { name failureRate } }, pageInfo { hasNextPage, hasPreviousPage, startCursor, endCursor }, totalCount }""", |
| 607 | + ) |
| 608 | + |
| 609 | + assert res["testResults"] == { |
| 610 | + "edges": [ |
| 611 | + {"node": {"name": test_2.name, "failureRate": 0.6}}, |
| 612 | + ], |
| 613 | + "pageInfo": { |
| 614 | + "endCursor": base64_encode_string(f"0.6|{test_2.name}"), |
| 615 | + "hasNextPage": True, |
| 616 | + "hasPreviousPage": False, |
| 617 | + "startCursor": base64_encode_string(f"0.6|{test_2.name}"), |
| 618 | + }, |
| 619 | + "totalCount": 2, |
| 620 | + } |
| 621 | + |
| 622 | + res = self.fetch_test_analytics( |
| 623 | + repo.name, |
| 624 | + """testResults(ordering: { parameter: FAILURE_RATE, direction: DESC }, first: 1, after: "%s") { edges { node { name failureRate } }, pageInfo { hasNextPage, hasPreviousPage, startCursor, endCursor }, totalCount }""" |
| 625 | + % res["testResults"]["pageInfo"]["endCursor"], |
| 626 | + ) |
| 627 | + |
| 628 | + assert res["testResults"] == { |
| 629 | + "edges": [ |
| 630 | + {"node": {"name": test.name, "failureRate": 0.2}}, |
| 631 | + ], |
| 632 | + "pageInfo": { |
| 633 | + "endCursor": base64_encode_string(f"0.2|{test.name}"), |
| 634 | + "hasNextPage": False, |
| 635 | + "hasPreviousPage": False, |
| 636 | + "startCursor": base64_encode_string(f"0.2|{test.name}"), |
| 637 | + }, |
| 638 | + "totalCount": 2, |
| 639 | + } |
| 640 | + |
| 641 | + res = self.fetch_test_analytics( |
| 642 | + repo.name, |
| 643 | + """testResults(ordering: { parameter: FAILURE_RATE, direction: ASC }, first: 1) { edges { node { name failureRate } }, pageInfo { hasNextPage, hasPreviousPage, startCursor, endCursor }, totalCount }""", |
| 644 | + ) |
| 645 | + |
| 646 | + assert res["testResults"] == { |
| 647 | + "edges": [ |
| 648 | + {"node": {"name": test.name, "failureRate": 0.2}}, |
| 649 | + ], |
| 650 | + "pageInfo": { |
| 651 | + "endCursor": base64_encode_string(f"0.2|{test.name}"), |
| 652 | + "hasNextPage": True, |
| 653 | + "hasPreviousPage": False, |
| 654 | + "startCursor": base64_encode_string(f"0.2|{test.name}"), |
| 655 | + }, |
| 656 | + "totalCount": 2, |
| 657 | + } |
| 658 | + |
| 659 | + res = self.fetch_test_analytics( |
| 660 | + repo.name, |
| 661 | + """testResults(ordering: { parameter: FAILURE_RATE, direction: ASC }, first: 1, after: "%s") { edges { node { name failureRate } }, pageInfo { hasNextPage, hasPreviousPage, startCursor, endCursor }, totalCount }""" |
| 662 | + % res["testResults"]["pageInfo"]["endCursor"], |
| 663 | + ) |
| 664 | + |
| 665 | + assert res["testResults"] == { |
| 666 | + "edges": [ |
| 667 | + {"node": {"name": test_2.name, "failureRate": 0.6}}, |
| 668 | + ], |
| 669 | + "pageInfo": { |
| 670 | + "endCursor": base64_encode_string(f"0.6|{test_2.name}"), |
| 671 | + "hasNextPage": False, |
| 672 | + "hasPreviousPage": False, |
| 673 | + "startCursor": base64_encode_string(f"0.6|{test_2.name}"), |
| 674 | + }, |
| 675 | + "totalCount": 2, |
| 676 | + } |
| 677 | + |
| 678 | + res = self.fetch_test_analytics( |
| 679 | + repo.name, |
| 680 | + """testResults(ordering: { parameter: FAILURE_RATE, direction: ASC }, last: 2) { edges { node { name failureRate } }, pageInfo { hasNextPage, hasPreviousPage, startCursor, endCursor }, totalCount }""", |
| 681 | + ) |
| 682 | + |
| 683 | + assert res["testResults"] == { |
| 684 | + "edges": [ |
| 685 | + {"node": {"name": test_2.name, "failureRate": 0.6}}, |
| 686 | + {"node": {"name": test.name, "failureRate": 0.2}}, |
| 687 | + ], |
| 688 | + "pageInfo": { |
| 689 | + "endCursor": base64_encode_string(f"0.2|{test.name}"), |
| 690 | + "hasNextPage": False, |
| 691 | + "hasPreviousPage": False, |
| 692 | + "startCursor": base64_encode_string(f"0.6|{test_2.name}"), |
| 693 | + }, |
| 694 | + "totalCount": 2, |
| 695 | + } |
| 696 | + |
| 697 | + res = self.fetch_test_analytics( |
| 698 | + repo.name, |
| 699 | + """testResults(ordering: { parameter: FAILURE_RATE, direction: ASC }, last: 1) { edges { node { name failureRate } }, pageInfo { hasNextPage, hasPreviousPage, startCursor, endCursor }, totalCount }""", |
| 700 | + ) |
| 701 | + |
| 702 | + assert res["testResults"] == { |
| 703 | + "edges": [ |
| 704 | + {"node": {"name": test_2.name, "failureRate": 0.6}}, |
| 705 | + ], |
| 706 | + "pageInfo": { |
| 707 | + "endCursor": base64_encode_string(f"0.6|{test_2.name}"), |
| 708 | + "hasNextPage": False, |
| 709 | + "hasPreviousPage": True, |
| 710 | + "startCursor": base64_encode_string(f"0.6|{test_2.name}"), |
| 711 | + }, |
| 712 | + "totalCount": 2, |
| 713 | + } |
| 714 | + |
551 | 715 | def test_flake_rate_filtering_on_test_results(self) -> None: |
552 | 716 | repo = RepositoryFactory(author=self.owner, active=True, private=True) |
553 | 717 | test = TestFactory(repository=repo) |
@@ -753,6 +917,43 @@ def test_test_results_aggregates_no_history(self) -> None: |
753 | 917 | "totalSlowTestsPercentChange": None, |
754 | 918 | } |
755 | 919 |
|
| 920 | + def test_test_results_aggregates_no_history_7_days(self) -> None: |
| 921 | + repo = RepositoryFactory( |
| 922 | + author=self.owner, active=True, private=True, branch="main" |
| 923 | + ) |
| 924 | + |
| 925 | + for i in range(0, 7): |
| 926 | + test = TestFactory(repository=repo) |
| 927 | + _ = DailyTestRollupFactory( |
| 928 | + test=test, |
| 929 | + repoid=repo.repoid, |
| 930 | + branch="main", |
| 931 | + fail_count=1 if i % 3 == 0 else 0, |
| 932 | + skip_count=1 if i % 6 == 0 else 0, |
| 933 | + pass_count=1, |
| 934 | + avg_duration_seconds=float(i), |
| 935 | + last_duration_seconds=float(i), |
| 936 | + date=datetime.date.today() - datetime.timedelta(days=i), |
| 937 | + ) |
| 938 | + |
| 939 | + res = self.fetch_test_analytics( |
| 940 | + repo.name, |
| 941 | + """testResultsAggregates(interval: INTERVAL_7_DAY) { totalDuration, slowestTestsDuration, totalFails, totalSkips, totalSlowTests, totalDurationPercentChange, slowestTestsDurationPercentChange, totalFailsPercentChange, totalSkipsPercentChange, totalSlowTestsPercentChange }""", |
| 942 | + ) |
| 943 | + |
| 944 | + assert res["testResultsAggregates"] == { |
| 945 | + "totalDuration": 30.0, |
| 946 | + "totalDurationPercentChange": None, |
| 947 | + "slowestTestsDuration": 12.0, |
| 948 | + "slowestTestsDurationPercentChange": None, |
| 949 | + "totalFails": 3, |
| 950 | + "totalFailsPercentChange": None, |
| 951 | + "totalSkips": 2, |
| 952 | + "totalSkipsPercentChange": None, |
| 953 | + "totalSlowTests": 1, |
| 954 | + "totalSlowTestsPercentChange": None, |
| 955 | + } |
| 956 | + |
756 | 957 | def test_flake_aggregates(self) -> None: |
757 | 958 | repo = RepositoryFactory( |
758 | 959 | author=self.owner, active=True, private=True, branch="main" |
@@ -924,7 +1125,7 @@ def test_flake_aggregates_7_days(self) -> None: |
924 | 1125 |
|
925 | 1126 | res = self.fetch_test_analytics( |
926 | 1127 | repo.name, |
927 | | - """flakeAggregates(history: INTERVAL_7_DAY) { flakeCount, flakeRate, flakeCountPercentChange, flakeRatePercentChange }""", |
| 1128 | + """flakeAggregates(interval: INTERVAL_7_DAY) { flakeCount, flakeRate, flakeCountPercentChange, flakeRatePercentChange }""", |
928 | 1129 | ) |
929 | 1130 |
|
930 | 1131 | assert res["flakeAggregates"] == { |
|
0 commit comments