Skip to content

Commit 9049efb

Browse files
feat(codecov): Use term query param for test results endpoint (#95179)
1 parent b16336f commit 9049efb

File tree

3 files changed

+123
-85
lines changed

3 files changed

+123
-85
lines changed

src/sentry/apidocs/parameters.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,3 +1122,10 @@ class PreventParams:
11221122
description="""The cursor to start the query from. Will return results after the cursor if used with `first` or before the cursor if used with `last`.
11231123
""",
11241124
)
1125+
TERM = OpenApiParameter(
1126+
name="term",
1127+
location="query",
1128+
required=False,
1129+
type=str,
1130+
description="""The term substring to filter test name strings by using the `contains` operator.""",
1131+
)

src/sentry/codecov/endpoints/TestResults/test_results.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def has_pagination(self, response):
4141
PreventParams.FIRST,
4242
PreventParams.LAST,
4343
PreventParams.CURSOR,
44+
PreventParams.TERM,
4445
],
4546
request=None,
4647
responses={
@@ -95,7 +96,7 @@ def get(
9596
request.query_params.get("interval", MeasurementInterval.INTERVAL_30_DAY.value)
9697
),
9798
"flags": None,
98-
"term": None,
99+
"term": request.query_params.get("term"),
99100
"test_suites": None,
100101
},
101102
"ordering": {

tests/sentry/codecov/endpoints/test_test_results.py

Lines changed: 114 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,73 @@
77
)
88
from sentry.testutils.cases import APITestCase
99

10+
mock_graphql_response_empty = {
11+
"data": {
12+
"owner": {
13+
"repository": {
14+
"__typename": "Repository",
15+
"testAnalytics": {
16+
"testResults": {
17+
"edges": [],
18+
"pageInfo": {"endCursor": None, "hasNextPage": False},
19+
"totalCount": 0,
20+
}
21+
},
22+
}
23+
}
24+
}
25+
}
26+
27+
mock_graphql_response_populated = {
28+
"data": {
29+
"owner": {
30+
"repository": {
31+
"__typename": "Repository",
32+
"testAnalytics": {
33+
"testResults": {
34+
"edges": [
35+
{
36+
"node": {
37+
"updatedAt": "2025-05-22T16:21:18.763951+00:00",
38+
"avgDuration": 0.04066228070175437,
39+
"totalDuration": 1.0,
40+
"lastDuration": 0.04066228070175437,
41+
"name": "../usr/local/lib/python3.13/site-packages/asgiref/sync.py::GetFinalYamlInteractorTest::test_when_commit_has_no_yaml",
42+
"failureRate": 0.0,
43+
"flakeRate": 0.0,
44+
"commitsFailed": 0,
45+
"totalFailCount": 0,
46+
"totalFlakyFailCount": 0,
47+
"totalSkipCount": 0,
48+
"totalPassCount": 70,
49+
}
50+
},
51+
{
52+
"node": {
53+
"updatedAt": "2025-05-22T16:21:18.763961+00:00",
54+
"avgDuration": 0.034125877192982455,
55+
"totalDuration": 1.0,
56+
"lastDuration": 0.034125877192982455,
57+
"name": "../usr/local/lib/python3.13/site-packages/asgiref/sync.py::GetFinalYamlInteractorTest::test_when_commit_has_yaml",
58+
"failureRate": 0.0,
59+
"flakeRate": 0.0,
60+
"commitsFailed": 0,
61+
"totalFailCount": 0,
62+
"totalFlakyFailCount": 0,
63+
"totalSkipCount": 0,
64+
"totalPassCount": 70,
65+
}
66+
},
67+
],
68+
"pageInfo": {"endCursor": "cursor123", "hasNextPage": False},
69+
"totalCount": 2,
70+
}
71+
},
72+
}
73+
}
74+
}
75+
}
76+
1077

1178
class TestResultsEndpointTest(APITestCase):
1279
endpoint = "sentry-api-0-test-results"
@@ -28,58 +95,9 @@ def reverse_url(self, owner="testowner", repository="testrepo"):
2895

2996
@patch("sentry.codecov.endpoints.TestResults.test_results.CodecovApiClient")
3097
def test_get_returns_mock_response_with_default_variables(self, mock_codecov_client_class):
31-
mock_graphql_response = {
32-
"data": {
33-
"owner": {
34-
"repository": {
35-
"__typename": "Repository",
36-
"testAnalytics": {
37-
"testResults": {
38-
"edges": [
39-
{
40-
"node": {
41-
"updatedAt": "2025-05-22T16:21:18.763951+00:00",
42-
"avgDuration": 0.04066228070175437,
43-
"totalDuration": 1.0,
44-
"lastDuration": 0.04066228070175437,
45-
"name": "../usr/local/lib/python3.13/site-packages/asgiref/sync.py::GetFinalYamlInteractorTest::test_when_commit_has_no_yaml",
46-
"failureRate": 0.0,
47-
"flakeRate": 0.0,
48-
"commitsFailed": 0,
49-
"totalFailCount": 0,
50-
"totalFlakyFailCount": 0,
51-
"totalSkipCount": 0,
52-
"totalPassCount": 70,
53-
}
54-
},
55-
{
56-
"node": {
57-
"updatedAt": "2025-05-22T16:21:18.763961+00:00",
58-
"avgDuration": 0.034125877192982455,
59-
"totalDuration": 1.0,
60-
"lastDuration": 0.034125877192982455,
61-
"name": "../usr/local/lib/python3.13/site-packages/asgiref/sync.py::GetFinalYamlInteractorTest::test_when_commit_has_yaml",
62-
"failureRate": 0.0,
63-
"flakeRate": 0.0,
64-
"commitsFailed": 0,
65-
"totalFailCount": 0,
66-
"totalFlakyFailCount": 0,
67-
"totalSkipCount": 0,
68-
"totalPassCount": 70,
69-
}
70-
},
71-
],
72-
"pageInfo": {"endCursor": "cursor123", "hasNextPage": False},
73-
"totalCount": 2,
74-
}
75-
},
76-
}
77-
}
78-
}
79-
}
8098
mock_codecov_client_instance = Mock()
8199
mock_response = Mock()
82-
mock_response.json.return_value = mock_graphql_response
100+
mock_response.json.return_value = mock_graphql_response_populated
83101
mock_codecov_client_instance.query.return_value = mock_response
84102
mock_codecov_client_class.return_value = mock_codecov_client_instance
85103

@@ -129,25 +147,9 @@ def test_get_returns_mock_response_with_default_variables(self, mock_codecov_cli
129147

130148
@patch("sentry.codecov.endpoints.TestResults.test_results.CodecovApiClient")
131149
def test_get_with_query_parameters(self, mock_codecov_client_class):
132-
mock_graphql_response = {
133-
"data": {
134-
"owner": {
135-
"repository": {
136-
"__typename": "Repository",
137-
"testAnalytics": {
138-
"testResults": {
139-
"edges": [],
140-
"pageInfo": {"endCursor": None, "hasNextPage": False},
141-
"totalCount": 0,
142-
}
143-
},
144-
}
145-
}
146-
}
147-
}
148150
mock_codecov_client_instance = Mock()
149151
mock_response = Mock()
150-
mock_response.json.return_value = mock_graphql_response
152+
mock_response.json.return_value = mock_graphql_response_empty
151153
mock_codecov_client_instance.query.return_value = mock_response
152154
mock_codecov_client_class.return_value = mock_codecov_client_instance
153155

@@ -189,25 +191,9 @@ def test_get_with_query_parameters(self, mock_codecov_client_class):
189191

190192
@patch("sentry.codecov.endpoints.TestResults.test_results.CodecovApiClient")
191193
def test_get_with_last_parameter(self, mock_codecov_client_class):
192-
mock_graphql_response = {
193-
"data": {
194-
"owner": {
195-
"repository": {
196-
"__typename": "Repository",
197-
"testAnalytics": {
198-
"testResults": {
199-
"edges": [],
200-
"pageInfo": {"endCursor": None, "hasNextPage": False},
201-
"totalCount": 0,
202-
}
203-
},
204-
}
205-
}
206-
}
207-
}
208194
mock_codecov_client_instance = Mock()
209195
mock_response = Mock()
210-
mock_response.json.return_value = mock_graphql_response
196+
mock_response.json.return_value = mock_graphql_response_empty
211197
mock_codecov_client_instance.query.return_value = mock_response
212198
mock_codecov_client_class.return_value = mock_codecov_client_instance
213199

@@ -249,3 +235,47 @@ def test_get_with_both_first_and_last_returns_bad_request(self):
249235

250236
assert response.status_code == 400
251237
assert response.data == {"details": "Cannot specify both `first` and `last`"}
238+
239+
@patch("sentry.codecov.endpoints.TestResults.test_results.CodecovApiClient")
240+
def test_get_with_term_filter(self, mock_codecov_client_class):
241+
mock_codecov_client_instance = Mock()
242+
mock_response = Mock()
243+
mock_response.json.return_value = mock_graphql_response_empty
244+
mock_codecov_client_instance.query.return_value = mock_response
245+
mock_codecov_client_class.return_value = mock_codecov_client_instance
246+
247+
url = self.reverse_url()
248+
query_params = {
249+
"term": "test::function_with_underscores-and-dashes",
250+
"branch": "develop",
251+
"filterBy": "FLAKY_TESTS",
252+
"sortBy": "AVG_DURATION",
253+
"interval": "INTERVAL_7_DAY",
254+
"first": "15",
255+
}
256+
response = self.client.get(url, query_params)
257+
258+
expected_variables = {
259+
"owner": "testowner",
260+
"repo": "testrepo",
261+
"filters": {
262+
"branch": "develop",
263+
"parameter": "FLAKY_TESTS",
264+
"interval": "INTERVAL_7_DAY",
265+
"flags": None,
266+
"term": "test::function_with_underscores-and-dashes",
267+
"test_suites": None,
268+
},
269+
"ordering": {
270+
"direction": "ASC",
271+
"parameter": "AVG_DURATION",
272+
},
273+
"first": 15,
274+
"last": None,
275+
"before": None,
276+
"after": None,
277+
}
278+
279+
call_args = mock_codecov_client_instance.query.call_args
280+
assert call_args[1]["variables"] == expected_variables
281+
assert response.status_code == 200

0 commit comments

Comments
 (0)