Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.

Commit e14d14d

Browse files
feat(gql): Add testSuites and flags to TestAnalytics (#883)
1 parent 7857053 commit e14d14d

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

graphql_api/tests/test_test_analytics.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,3 +1134,93 @@ def test_flake_aggregates_7_days(self) -> None:
11341134
"flakeCountPercentChange": -50.0,
11351135
"flakeRatePercentChange": -43.75,
11361136
}
1137+
1138+
def test_test_suites(self) -> None:
1139+
repo = RepositoryFactory(author=self.owner, active=True, private=True)
1140+
test = TestFactory(repository=repo, testsuite="hello_world")
1141+
test2 = TestFactory(repository=repo, testsuite="goodbye_world")
1142+
1143+
repo_flag = RepositoryFlagFactory(repository=repo, flag_name="hello_world")
1144+
1145+
_ = TestFlagBridgeFactory(flag=repo_flag, test=test)
1146+
_ = DailyTestRollupFactory(
1147+
test=test,
1148+
created_at=datetime.datetime.now(),
1149+
repoid=repo.repoid,
1150+
branch="main",
1151+
avg_duration_seconds=0.1,
1152+
)
1153+
_ = DailyTestRollupFactory(
1154+
test=test2,
1155+
created_at=datetime.datetime.now(),
1156+
repoid=repo.repoid,
1157+
branch="main",
1158+
avg_duration_seconds=20.0,
1159+
)
1160+
res = self.fetch_test_analytics(
1161+
repo.name,
1162+
"""testSuites(term: "hello")""",
1163+
)
1164+
assert res["testSuites"] == ["hello_world"]
1165+
1166+
def test_test_suites_no_term(self) -> None:
1167+
repo = RepositoryFactory(author=self.owner, active=True, private=True)
1168+
test = TestFactory(repository=repo, testsuite="hello_world")
1169+
test2 = TestFactory(repository=repo, testsuite="goodbye_world")
1170+
1171+
repo_flag = RepositoryFlagFactory(repository=repo, flag_name="hello_world")
1172+
1173+
_ = TestFlagBridgeFactory(flag=repo_flag, test=test)
1174+
_ = DailyTestRollupFactory(
1175+
test=test,
1176+
created_at=datetime.datetime.now(),
1177+
repoid=repo.repoid,
1178+
branch="main",
1179+
avg_duration_seconds=0.1,
1180+
)
1181+
_ = DailyTestRollupFactory(
1182+
test=test2,
1183+
created_at=datetime.datetime.now(),
1184+
repoid=repo.repoid,
1185+
branch="main",
1186+
avg_duration_seconds=20.0,
1187+
)
1188+
res = self.fetch_test_analytics(
1189+
repo.name,
1190+
"""testSuites""",
1191+
)
1192+
assert sorted(res["testSuites"]) == ["goodbye_world", "hello_world"]
1193+
1194+
def test_flags(self) -> None:
1195+
repo = RepositoryFactory(author=self.owner, active=True, private=True)
1196+
test = TestFactory(repository=repo)
1197+
test2 = TestFactory(repository=repo)
1198+
1199+
repo_flag = RepositoryFlagFactory(repository=repo, flag_name="hello_world")
1200+
repo_flag2 = RepositoryFlagFactory(repository=repo, flag_name="goodbye_world")
1201+
1202+
_ = TestFlagBridgeFactory(flag=repo_flag, test=test)
1203+
_ = TestFlagBridgeFactory(flag=repo_flag2, test=test2)
1204+
1205+
res = self.fetch_test_analytics(
1206+
repo.name,
1207+
"""flags(term: "hello")""",
1208+
)
1209+
assert res["flags"] == ["hello_world"]
1210+
1211+
def test_flags_no_term(self) -> None:
1212+
repo = RepositoryFactory(author=self.owner, active=True, private=True)
1213+
test = TestFactory(repository=repo)
1214+
test2 = TestFactory(repository=repo)
1215+
1216+
repo_flag = RepositoryFlagFactory(repository=repo, flag_name="hello_world")
1217+
repo_flag2 = RepositoryFlagFactory(repository=repo, flag_name="goodbye_world")
1218+
1219+
_ = TestFlagBridgeFactory(flag=repo_flag, test=test)
1220+
_ = TestFlagBridgeFactory(flag=repo_flag2, test=test2)
1221+
1222+
res = self.fetch_test_analytics(
1223+
repo.name,
1224+
"""flags""",
1225+
)
1226+
assert sorted(res["flags"]) == ["goodbye_world", "hello_world"]

graphql_api/types/test_analytics/test_analytics.graphql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ type TestAnalytics {
1717

1818
"Flake aggregates are flake totals across all tests"
1919
flakeAggregates(interval: MeasurementInterval): FlakeAggregates
20+
21+
testSuites(term: String): [String!]!
22+
23+
"Only flag names relevant to Test Analytics"
24+
flags(term: String): [String!]!
2025
}
2126

2227
type TestResultConnection {

graphql_api/types/test_analytics/test_analytics.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
generate_flake_aggregates,
1414
generate_test_results,
1515
generate_test_results_aggregates,
16+
get_flags,
17+
get_test_suites,
1618
)
1719

1820
log = logging.getLogger(__name__)
@@ -88,6 +90,20 @@ async def resolve_flake_aggregates(
8890
)
8991

9092

93+
@test_analytics_bindable.field("testSuites")
94+
async def resolve_test_suites(
95+
repository: Repository, info: GraphQLResolveInfo, term: str | None = None, **_
96+
):
97+
return await sync_to_async(get_test_suites)(repository.repoid, term)
98+
99+
100+
@test_analytics_bindable.field("flags")
101+
async def resolve_flags(
102+
repository: Repository, info: GraphQLResolveInfo, term: str | None = None, **_
103+
):
104+
return await sync_to_async(get_flags)(repository.repoid, term)
105+
106+
91107
def convert_interval_to_timedelta(interval: MeasurementInterval | None) -> timedelta:
92108
if interval is None:
93109
return timedelta(days=30)

utils/test_results.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from shared.django_apps.reports.models import (
1717
DailyTestRollup,
1818
Flake,
19+
Test,
1920
TestFlagBridge,
2021
)
2122

@@ -623,3 +624,37 @@ def generate_flake_aggregates(
623624
curr_numbers,
624625
past_numbers,
625626
)
627+
628+
629+
def get_test_suites(repoid: int, term: str | None = None) -> list[str]:
630+
if term:
631+
return list(
632+
Test.objects.filter(repository_id=repoid, testsuite__icontains=term)
633+
.values_list("testsuite", flat=True)
634+
.distinct()
635+
)
636+
else:
637+
return list(
638+
Test.objects.filter(repository_id=repoid)
639+
.values_list("testsuite", flat=True)
640+
.distinct()
641+
)
642+
643+
644+
def get_flags(repoid: int, term: str | None = None) -> list[str]:
645+
if term:
646+
return list(
647+
TestFlagBridge.objects.filter(
648+
test__repository_id=repoid, flag__flag_name__icontains=term
649+
)
650+
.select_related("flag")
651+
.values_list("flag__flag_name", flat=True)
652+
.distinct()
653+
)
654+
else:
655+
return list(
656+
TestFlagBridge.objects.filter(test__repository_id=repoid)
657+
.select_related("flag")
658+
.values_list("flag__flag_name", flat=True)
659+
.distinct()
660+
)

0 commit comments

Comments
 (0)