diff --git a/graphql_api/tests/test_test_analytics.py b/graphql_api/tests/test_test_analytics.py index af224ff571..e6e7329392 100644 --- a/graphql_api/tests/test_test_analytics.py +++ b/graphql_api/tests/test_test_analytics.py @@ -1134,3 +1134,93 @@ 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="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(term: "hello")""", + ) + 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) + 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(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"] diff --git a/graphql_api/types/test_analytics/test_analytics.graphql b/graphql_api/types/test_analytics/test_analytics.graphql index dc83b8eef5..efa71bfde0 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(term: String): [String!]! + + "Only flag names relevant to Test Analytics" + 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 158518acde..7625d3225a 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,20 @@ async def resolve_flake_aggregates( ) +@test_analytics_bindable.field("testSuites") +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, term: str | None = None, **_ +): + return await sync_to_async(get_flags)(repository.repoid, term) + + 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..c303016f8b 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,37 @@ def generate_flake_aggregates( curr_numbers, past_numbers, ) + + +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, 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() + )