Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 51 additions & 8 deletions graphql_api/tests/test_test_analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,45 @@ def test_flake_rate_filtering_on_test_results(self) -> None:
]
}

def test_flake_rate_filtering_by_term(self) -> None:
repo = RepositoryFactory(author=self.owner, active=True, private=True)
test = TestFactory(repository=repo, name="hello")
_ = DailyTestRollupFactory(
test=test,
date=datetime.date.today() - datetime.timedelta(days=1),
repoid=repo.repoid,
pass_count=1,
fail_count=1,
flaky_fail_count=1,
)
_ = DailyTestRollupFactory(
test=test,
date=datetime.date.today(),
repoid=repo.repoid,
pass_count=3,
fail_count=0,
flaky_fail_count=0,
)
test_2 = TestFactory(repository=repo, name="world")
_ = DailyTestRollupFactory(
test=test_2,
date=datetime.date.today(),
repoid=repo.repoid,
pass_count=2,
fail_count=3,
flaky_fail_count=1,
)
res = self.fetch_test_analytics(
repo.name,
"""testResults(filters: { term: "hello" }) { edges { node { name failureRate } } }""",
)

assert res["testResults"] == {
"edges": [
{"node": {"name": test.name, "failureRate": 0.2}},
]
}

def test_desc_flake_rate_ordering_on_test_results(self) -> None:
repo = RepositoryFactory(author=self.owner, active=True, private=True)
test = TestFactory(repository=repo)
Expand Down Expand Up @@ -662,17 +701,19 @@ def test_test_results_aggregates(self) -> None:
)
res = self.fetch_test_analytics(
repo.name,
"""testResultsAggregates { totalDuration, slowestTestsDuration, totalFails, totalSkips, totalDurationPercentChange, slowestTestsDurationPercentChange, totalFailsPercentChange, totalSkipsPercentChange }""",
"""testResultsAggregates { totalDuration, slowestTestsDuration, totalFails, totalSkips, totalSlowTests, totalDurationPercentChange, slowestTestsDurationPercentChange, totalFailsPercentChange, totalSkipsPercentChange, totalSlowTestsPercentChange }""",
)
assert res["testResultsAggregates"] == {
"totalDuration": 570.0,
"slowestTestsDuration": 29.0,
"totalFails": 10,
"totalSkips": 5,
"totalDurationPercentChange": -63.1068,
"slowestTestsDuration": 29.0,
"slowestTestsDurationPercentChange": -50.84746,
"totalFails": 10,
"totalFailsPercentChange": 100.0,
"totalSkips": 5,
"totalSkipsPercentChange": -50.0,
"totalSlowTests": 1,
"totalSlowTestsPercentChange": 0.0,
}

def test_test_results_aggregates_no_history(self) -> None:
Expand All @@ -696,18 +737,20 @@ def test_test_results_aggregates_no_history(self) -> None:

res = self.fetch_test_analytics(
repo.name,
"""testResultsAggregates { totalDuration, slowestTestsDuration, totalFails, totalSkips, totalDurationPercentChange, slowestTestsDurationPercentChange, totalFailsPercentChange, totalSkipsPercentChange }""",
"""testResultsAggregates { totalDuration, slowestTestsDuration, totalFails, totalSkips, totalSlowTests, totalDurationPercentChange, slowestTestsDurationPercentChange, totalFailsPercentChange, totalSkipsPercentChange, totalSlowTestsPercentChange }""",
)

assert res["testResultsAggregates"] == {
"totalDuration": 570.0,
"slowestTestsDuration": 29.0,
"totalFails": 10,
"totalSkips": 5,
"totalDurationPercentChange": None,
"slowestTestsDuration": 29.0,
"slowestTestsDurationPercentChange": None,
"totalFails": 10,
"totalFailsPercentChange": None,
"totalSkips": 5,
"totalSkipsPercentChange": None,
"totalSlowTests": 1,
"totalSlowTestsPercentChange": None,
}

def test_flake_aggregates(self) -> None:
Expand Down
93 changes: 93 additions & 0 deletions graphql_api/tests/test_test_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,96 @@ def test_fetch_test_result_avg_duration(self) -> None:
assert result["owner"]["repository"]["testAnalytics"]["testResults"]["edges"][
0
]["node"]["avgDuration"] == (5.6 / 3)

def test_fetch_test_result_total_fail_count(self) -> None:
query = """
query {
owner(username: "%s") {
repository(name: "%s") {
... on Repository {
testAnalytics {
testResults {
edges {
node {
totalFailCount
}
}
}
}
}
}
}
}
""" % (self.owner.username, self.repository.name)

result = self.gql_request(query, owner=self.owner)

assert "errors" not in result
assert (
result["owner"]["repository"]["testAnalytics"]["testResults"]["edges"][0][
"node"
]["totalFailCount"]
== 9
)

def test_fetch_test_result_total_skip_count(self) -> None:
query = """
query {
owner(username: "%s") {
repository(name: "%s") {
... on Repository {
testAnalytics {
testResults {
edges {
node {
totalSkipCount
}
}
}
}
}
}
}
}
""" % (self.owner.username, self.repository.name)

result = self.gql_request(query, owner=self.owner)

assert "errors" not in result
assert (
result["owner"]["repository"]["testAnalytics"]["testResults"]["edges"][0][
"node"
]["totalSkipCount"]
== 6
)

def test_fetch_test_result_total_pass_count(self) -> None:
query = """
query {
owner(username: "%s") {
repository(name: "%s") {
... on Repository {
testAnalytics {
testResults {
edges {
node {
totalPassCount
}
}
}
}
}
}
}
}
""" % (self.owner.username, self.repository.name)

result = self.gql_request(query, owner=self.owner)

assert "errors" not in result
assert (
result["owner"]["repository"]["testAnalytics"]["testResults"]["edges"][0][
"node"
]["totalPassCount"]
== 3
)
1 change: 1 addition & 0 deletions graphql_api/types/inputs/test_results_filters.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ input TestResultsFilters {
test_suites: [String!]
flags: [String!]
history: MeasurementInterval
term: String
}

input TestResultsOrdering {
Expand Down
1 change: 1 addition & 0 deletions graphql_api/types/test_analytics/test_analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ async def resolve_test_results(
parameter=parameter,
testsuites=filters.get("test_suites") if filters else None,
flags=filters.get("flags") if filters else None,
term=filters.get("term") if filters else None,
)

return await queryset_to_connection(
Expand Down
3 changes: 3 additions & 0 deletions graphql_api/types/test_results/test_results.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ type TestResult {
flakeRate: Float!
avgDuration: Float!
lastDuration: Float!
totalFailCount: Int!
totalSkipCount: Int!
totalPassCount: Int!
}
18 changes: 18 additions & 0 deletions graphql_api/types/test_results/test_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ class TestDict(TypedDict):
avg_duration: float
last_duration: float
flake_rate: float
total_fail_count: int
total_skip_count: int
total_pass_count: int


test_result_bindable = ObjectType("TestResult")
Expand Down Expand Up @@ -51,3 +54,18 @@ def resolve_avg_duration(test: TestDict, _: GraphQLResolveInfo) -> float:
@test_result_bindable.field("lastDuration")
def resolve_last_duration(test: TestDict, _: GraphQLResolveInfo) -> float:
return test["last_duration"]


@test_result_bindable.field("totalFailCount")
def resolve_total_fail_count(test: TestDict, _: GraphQLResolveInfo) -> int:
return test["total_fail_count"]


@test_result_bindable.field("totalSkipCount")
def resolve_total_skip_count(test: TestDict, _: GraphQLResolveInfo) -> int:
return test["total_skip_count"]


@test_result_bindable.field("totalPassCount")
def resolve_total_pass_count(test: TestDict, _: GraphQLResolveInfo) -> int:
return test["total_pass_count"]
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ type TestResultsAggregates {
totalFailsPercentChange: Float
totalSkips: Int!
totalSkipsPercentChange: Float
totalSlowTests: Int!
totalSlowTestsPercentChange: Float
}
15 changes: 14 additions & 1 deletion utils/test_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def generate_test_results(
parameter: GENERATE_TEST_RESULT_PARAM | None = None,
testsuites: list[str] | None = None,
flags: list[str] | None = None,
term: str | None = None,
) -> QuerySet:
"""
Function that retrieves aggregated information about all tests in a given repository, for a given time range, optionally filtered by branch name.
Expand Down Expand Up @@ -103,6 +104,9 @@ def generate_test_results(

totals = totals.filter(test_id__in=test_ids)

if term is not None:
totals = totals.filter(test__name__icontains=term)

match parameter:
case GENERATE_TEST_RESULT_PARAM.FLAKY:
flakes = Flake.objects.filter(
Expand Down Expand Up @@ -172,6 +176,8 @@ def generate_test_results(
total_flaky_fail_count=Cast(
Sum(F("flaky_fail_count")), output_field=FloatField()
),
total_skip_count=Cast(Sum(F("skip_count")), output_field=FloatField()),
total_pass_count=Cast(Sum(F("pass_count")), output_field=FloatField()),
failure_rate=Case(
When(
total_test_count=0,
Expand Down Expand Up @@ -262,6 +268,7 @@ def get_test_results_aggregate_numbers(
slowest_tests_duration=slowest_tests_duration,
skips=Sum("skip_count"),
fails=Sum("fail_count"),
total_slow_tests=Value(slow_test_threshold(num_tests)),
)

return test_headers[0] if len(test_headers) > 0 else {}
Expand All @@ -280,7 +287,13 @@ def generate_test_results_aggregates(
past_numbers = get_test_results_aggregate_numbers(repo, double_time_ago, since)

return curr_numbers | get_percent_change(
["total_duration", "slowest_tests_duration", "skips", "fails"],
[
"total_duration",
"slowest_tests_duration",
"skips",
"fails",
"total_slow_tests",
],
curr_numbers,
past_numbers,
)
Expand Down
Loading