From 48985eb9bc9cdec149e3eafc2fe6f65c0583f7a1 Mon Sep 17 00:00:00 2001 From: joseph-sentry Date: Fri, 11 Oct 2024 13:23:12 -0400 Subject: [PATCH 1/3] feat(gql): add testSuites, flags to TestAnalytics --- graphql_api/tests/test_test_analytics.py | 56 +++++++++++++++++++ .../test_analytics/test_analytics.graphql | 5 ++ .../types/test_analytics/test_analytics.py | 12 ++++ utils/test_results.py | 18 ++++++ 4 files changed, 91 insertions(+) diff --git a/graphql_api/tests/test_test_analytics.py b/graphql_api/tests/test_test_analytics.py index af224ff571..1f4d690ca9 100644 --- a/graphql_api/tests/test_test_analytics.py +++ b/graphql_api/tests/test_test_analytics.py @@ -1134,3 +1134,59 @@ def test_flake_aggregates_7_days(self) -> None: "flakeCountPercentChange": -50.0, "flakeRatePercentChange": -43.75, } + + def test_test_suites(self) -> None: + repo = RepositoryFactory(author=self.owner, active=True, private=True) + test = TestFactory(repository=repo, testsuite="test_suite_1") + test2 = TestFactory(repository=repo, testsuite="test_suite_2") + + repo_flag = RepositoryFlagFactory(repository=repo, flag_name="hello_world") + + _ = TestFlagBridgeFactory(flag=repo_flag, test=test) + _ = DailyTestRollupFactory( + test=test, + created_at=datetime.datetime.now(), + repoid=repo.repoid, + branch="main", + avg_duration_seconds=0.1, + ) + _ = DailyTestRollupFactory( + test=test2, + created_at=datetime.datetime.now(), + repoid=repo.repoid, + branch="main", + avg_duration_seconds=20.0, + ) + res = self.fetch_test_analytics( + repo.name, + """testSuites""", + ) + assert res["testSuites"] == ["test_suite_1", "test_suite_2"] + + def test_flags(self) -> None: + repo = RepositoryFactory(author=self.owner, active=True, private=True) + test = TestFactory(repository=repo) + test2 = TestFactory(repository=repo) + + repo_flag = RepositoryFlagFactory(repository=repo, flag_name="hello_world") + + _ = TestFlagBridgeFactory(flag=repo_flag, test=test) + _ = DailyTestRollupFactory( + test=test, + created_at=datetime.datetime.now(), + repoid=repo.repoid, + branch="main", + avg_duration_seconds=0.1, + ) + _ = DailyTestRollupFactory( + test=test2, + created_at=datetime.datetime.now(), + repoid=repo.repoid, + branch="main", + avg_duration_seconds=20.0, + ) + res = self.fetch_test_analytics( + repo.name, + """flags""", + ) + assert res["flags"] == ["hello_world"] diff --git a/graphql_api/types/test_analytics/test_analytics.graphql b/graphql_api/types/test_analytics/test_analytics.graphql index dc83b8eef5..c1e39b8a90 100644 --- a/graphql_api/types/test_analytics/test_analytics.graphql +++ b/graphql_api/types/test_analytics/test_analytics.graphql @@ -17,6 +17,11 @@ type TestAnalytics { "Flake aggregates are flake totals across all tests" flakeAggregates(interval: MeasurementInterval): FlakeAggregates + + testSuites: [String!]! + + "Only flag names relevant to Test Analytics" + flags: [String!]! } type TestResultConnection { diff --git a/graphql_api/types/test_analytics/test_analytics.py b/graphql_api/types/test_analytics/test_analytics.py index 158518acde..e0fb137bb3 100644 --- a/graphql_api/types/test_analytics/test_analytics.py +++ b/graphql_api/types/test_analytics/test_analytics.py @@ -13,6 +13,8 @@ generate_flake_aggregates, generate_test_results, generate_test_results_aggregates, + get_flags, + get_test_suites, ) log = logging.getLogger(__name__) @@ -88,6 +90,16 @@ async def resolve_flake_aggregates( ) +@test_analytics_bindable.field("testSuites") +async def resolve_test_suites(repository: Repository, info: GraphQLResolveInfo, **_): + return await sync_to_async(get_test_suites)(repository.repoid) + + +@test_analytics_bindable.field("flags") +async def resolve_flags(repository: Repository, info: GraphQLResolveInfo, **_): + return await sync_to_async(get_flags)(repository.repoid) + + def convert_interval_to_timedelta(interval: MeasurementInterval | None) -> timedelta: if interval is None: return timedelta(days=30) diff --git a/utils/test_results.py b/utils/test_results.py index 3e3f6d08a4..240551f944 100644 --- a/utils/test_results.py +++ b/utils/test_results.py @@ -16,6 +16,7 @@ from shared.django_apps.reports.models import ( DailyTestRollup, Flake, + Test, TestFlagBridge, ) @@ -623,3 +624,20 @@ def generate_flake_aggregates( curr_numbers, past_numbers, ) + + +def get_test_suites(repoid: int) -> list[str]: + return list( + Test.objects.filter(repository_id=repoid) + .values_list("testsuite", flat=True) + .distinct() + ) + + +def get_flags(repoid: int) -> list[str]: + return list( + TestFlagBridge.objects.filter(test__repository_id=repoid) + .select_related("flag") + .values_list("flag__flag_name", flat=True) + .distinct() + ) From ca2cd18ae94e13707aa78f69a8c17a55c2b9e40f Mon Sep 17 00:00:00 2001 From: joseph-sentry Date: Fri, 11 Oct 2024 15:42:16 -0400 Subject: [PATCH 2/3] fix: add search term --- graphql_api/tests/test_test_analytics.py | 27 ++++-------- .../test_analytics/test_analytics.graphql | 4 +- .../types/test_analytics/test_analytics.py | 12 ++++-- utils/test_results.py | 43 +++++++++++++------ 4 files changed, 48 insertions(+), 38 deletions(-) diff --git a/graphql_api/tests/test_test_analytics.py b/graphql_api/tests/test_test_analytics.py index 1f4d690ca9..142bbcee7b 100644 --- a/graphql_api/tests/test_test_analytics.py +++ b/graphql_api/tests/test_test_analytics.py @@ -1137,8 +1137,8 @@ def test_flake_aggregates_7_days(self) -> None: def test_test_suites(self) -> None: repo = RepositoryFactory(author=self.owner, active=True, private=True) - test = TestFactory(repository=repo, testsuite="test_suite_1") - test2 = TestFactory(repository=repo, testsuite="test_suite_2") + test = TestFactory(repository=repo, testsuite="hello_world") + test2 = TestFactory(repository=repo, testsuite="goodbye_world") repo_flag = RepositoryFlagFactory(repository=repo, flag_name="hello_world") @@ -1159,9 +1159,9 @@ def test_test_suites(self) -> None: ) res = self.fetch_test_analytics( repo.name, - """testSuites""", + """testSuites(term: "hello")""", ) - assert res["testSuites"] == ["test_suite_1", "test_suite_2"] + assert res["testSuites"] == ["hello_world"] def test_flags(self) -> None: repo = RepositoryFactory(author=self.owner, active=True, private=True) @@ -1169,24 +1169,13 @@ def test_flags(self) -> None: test2 = TestFactory(repository=repo) repo_flag = RepositoryFlagFactory(repository=repo, flag_name="hello_world") + repo_flag2 = RepositoryFlagFactory(repository=repo, flag_name="goodbye_world") _ = TestFlagBridgeFactory(flag=repo_flag, test=test) - _ = DailyTestRollupFactory( - test=test, - created_at=datetime.datetime.now(), - repoid=repo.repoid, - branch="main", - avg_duration_seconds=0.1, - ) - _ = DailyTestRollupFactory( - test=test2, - created_at=datetime.datetime.now(), - repoid=repo.repoid, - branch="main", - avg_duration_seconds=20.0, - ) + _ = TestFlagBridgeFactory(flag=repo_flag2, test=test2) + res = self.fetch_test_analytics( repo.name, - """flags""", + """flags(term: "hello")""", ) assert res["flags"] == ["hello_world"] diff --git a/graphql_api/types/test_analytics/test_analytics.graphql b/graphql_api/types/test_analytics/test_analytics.graphql index c1e39b8a90..efa71bfde0 100644 --- a/graphql_api/types/test_analytics/test_analytics.graphql +++ b/graphql_api/types/test_analytics/test_analytics.graphql @@ -18,10 +18,10 @@ type TestAnalytics { "Flake aggregates are flake totals across all tests" flakeAggregates(interval: MeasurementInterval): FlakeAggregates - testSuites: [String!]! + testSuites(term: String): [String!]! "Only flag names relevant to Test Analytics" - flags: [String!]! + flags(term: String): [String!]! } type TestResultConnection { diff --git a/graphql_api/types/test_analytics/test_analytics.py b/graphql_api/types/test_analytics/test_analytics.py index e0fb137bb3..7625d3225a 100644 --- a/graphql_api/types/test_analytics/test_analytics.py +++ b/graphql_api/types/test_analytics/test_analytics.py @@ -91,13 +91,17 @@ async def resolve_flake_aggregates( @test_analytics_bindable.field("testSuites") -async def resolve_test_suites(repository: Repository, info: GraphQLResolveInfo, **_): - return await sync_to_async(get_test_suites)(repository.repoid) +async def resolve_test_suites( + repository: Repository, info: GraphQLResolveInfo, term: str | None = None, **_ +): + return await sync_to_async(get_test_suites)(repository.repoid, term) @test_analytics_bindable.field("flags") -async def resolve_flags(repository: Repository, info: GraphQLResolveInfo, **_): - return await sync_to_async(get_flags)(repository.repoid) +async def resolve_flags( + repository: Repository, info: GraphQLResolveInfo, term: str | None = None, **_ +): + return await sync_to_async(get_flags)(repository.repoid, term) def convert_interval_to_timedelta(interval: MeasurementInterval | None) -> timedelta: diff --git a/utils/test_results.py b/utils/test_results.py index 240551f944..c303016f8b 100644 --- a/utils/test_results.py +++ b/utils/test_results.py @@ -626,18 +626,35 @@ def generate_flake_aggregates( ) -def get_test_suites(repoid: int) -> list[str]: - return list( - Test.objects.filter(repository_id=repoid) - .values_list("testsuite", flat=True) - .distinct() - ) +def get_test_suites(repoid: int, term: str | None = None) -> list[str]: + if term: + return list( + Test.objects.filter(repository_id=repoid, testsuite__icontains=term) + .values_list("testsuite", flat=True) + .distinct() + ) + else: + return list( + Test.objects.filter(repository_id=repoid) + .values_list("testsuite", flat=True) + .distinct() + ) -def get_flags(repoid: int) -> list[str]: - return list( - TestFlagBridge.objects.filter(test__repository_id=repoid) - .select_related("flag") - .values_list("flag__flag_name", flat=True) - .distinct() - ) +def get_flags(repoid: int, term: str | None = None) -> list[str]: + if term: + return list( + TestFlagBridge.objects.filter( + test__repository_id=repoid, flag__flag_name__icontains=term + ) + .select_related("flag") + .values_list("flag__flag_name", flat=True) + .distinct() + ) + else: + return list( + TestFlagBridge.objects.filter(test__repository_id=repoid) + .select_related("flag") + .values_list("flag__flag_name", flat=True) + .distinct() + ) From 8cac79162774d108578918f20f4495f7b227b2d1 Mon Sep 17 00:00:00 2001 From: joseph-sentry Date: Fri, 18 Oct 2024 17:45:13 -0400 Subject: [PATCH 3/3] test: add some tests without filters for testuites and flags --- graphql_api/tests/test_test_analytics.py | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/graphql_api/tests/test_test_analytics.py b/graphql_api/tests/test_test_analytics.py index 142bbcee7b..e6e7329392 100644 --- a/graphql_api/tests/test_test_analytics.py +++ b/graphql_api/tests/test_test_analytics.py @@ -1163,6 +1163,34 @@ def test_test_suites(self) -> None: ) assert res["testSuites"] == ["hello_world"] + def test_test_suites_no_term(self) -> None: + repo = RepositoryFactory(author=self.owner, active=True, private=True) + test = TestFactory(repository=repo, testsuite="hello_world") + test2 = TestFactory(repository=repo, testsuite="goodbye_world") + + repo_flag = RepositoryFlagFactory(repository=repo, flag_name="hello_world") + + _ = TestFlagBridgeFactory(flag=repo_flag, test=test) + _ = DailyTestRollupFactory( + test=test, + created_at=datetime.datetime.now(), + repoid=repo.repoid, + branch="main", + avg_duration_seconds=0.1, + ) + _ = DailyTestRollupFactory( + test=test2, + created_at=datetime.datetime.now(), + repoid=repo.repoid, + branch="main", + avg_duration_seconds=20.0, + ) + res = self.fetch_test_analytics( + repo.name, + """testSuites""", + ) + assert sorted(res["testSuites"]) == ["goodbye_world", "hello_world"] + def test_flags(self) -> None: repo = RepositoryFactory(author=self.owner, active=True, private=True) test = TestFactory(repository=repo) @@ -1179,3 +1207,20 @@ def test_flags(self) -> None: """flags(term: "hello")""", ) assert res["flags"] == ["hello_world"] + + def test_flags_no_term(self) -> None: + repo = RepositoryFactory(author=self.owner, active=True, private=True) + test = TestFactory(repository=repo) + test2 = TestFactory(repository=repo) + + repo_flag = RepositoryFlagFactory(repository=repo, flag_name="hello_world") + repo_flag2 = RepositoryFlagFactory(repository=repo, flag_name="goodbye_world") + + _ = TestFlagBridgeFactory(flag=repo_flag, test=test) + _ = TestFlagBridgeFactory(flag=repo_flag2, test=test2) + + res = self.fetch_test_analytics( + repo.name, + """flags""", + ) + assert sorted(res["flags"]) == ["goodbye_world", "hello_world"]