1
+ import sentry_sdk
2
+ from rest_framework import serializers
1
3
from rest_framework .request import Request
2
4
from rest_framework .response import Response
3
5
62
64
}
63
65
"""
64
66
65
- # NOTE: There is no testResult resolver in GQL atm so if we need this, will need to build it.
66
- get_query = """query GetTestResult(
67
- $owner: String!
68
- $repo: String!
69
- $testResultId: String!
70
- ) {
67
+
68
+ class TestResultNodeSerializer (serializers .Serializer ):
69
+ """
70
+ Serializer for individual test result nodes from GraphQL response
71
+ """
72
+
73
+ updatedAt = serializers .CharField ()
74
+ avgDuration = serializers .FloatField ()
75
+ name = serializers .CharField ()
76
+ failureRate = serializers .FloatField ()
77
+ flakeRate = serializers .FloatField ()
78
+ commitsFailed = serializers .IntegerField ()
79
+ totalFailCount = serializers .IntegerField ()
80
+ totalFlakyFailCount = serializers .IntegerField ()
81
+ totalSkipCount = serializers .IntegerField ()
82
+ totalPassCount = serializers .IntegerField ()
83
+
84
+
85
+ class TestResultSerializer (serializers .Serializer ):
86
+ """
87
+ Serializer to transform GraphQL response to client format
88
+ """
89
+
90
+ def transform_graphql_response (self , graphql_response ):
91
+ """
92
+ Transform the GraphQL response format to the expected client format
93
+ """
94
+ try :
95
+ # Extract test result nodes from the nested GraphQL structure
96
+ test_results = graphql_response ["data" ]["owner" ]["repository" ]["testAnalytics" ][
97
+ "testResults"
98
+ ]["edges" ]
99
+
100
+ # Transform each node to the expected client format
101
+ transformed_results = []
102
+ for edge in test_results :
103
+ node = edge ["node" ]
104
+ # Note: lastDuration is not in the GraphQL response, using avgDuration as fallback
105
+ transformed_result = {
106
+ "updatedAt" : node ["updatedAt" ],
107
+ "name" : node ["name" ],
108
+ "commitsFailed" : node ["commitsFailed" ],
109
+ "failureRate" : node ["failureRate" ],
110
+ "flakeRate" : node ["flakeRate" ],
111
+ "avgDuration" : node ["avgDuration" ],
112
+ "lastDuration" : node .get (
113
+ "lastDuration" , node ["avgDuration" ]
114
+ ), # fallback to avgDuration
115
+ "totalFailCount" : node ["totalFailCount" ],
116
+ "totalFlakyFailCount" : node ["totalFlakyFailCount" ],
117
+ "totalSkipCount" : node ["totalSkipCount" ],
118
+ "totalPassCount" : node ["totalPassCount" ],
119
+ }
120
+ transformed_results .append (transformed_result )
121
+
122
+ return transformed_results
123
+
124
+ except (KeyError , TypeError ) as e :
125
+ # Handle malformed GraphQL response
126
+ sentry_sdk .capture_exception (e )
127
+
128
+ return []
129
+
130
+
131
+ # Sample GraphQL response structure for reference
132
+ sample_graphql_response = {
133
+ "data" : {
134
+ "owner" : {
135
+ "repository" : {
136
+ "__typename" : "Repository" ,
137
+ "testAnalytics" : {
138
+ "testResults" : {
139
+ "edges" : [
140
+ {
141
+ "node" : {
142
+ "updatedAt" : "2025-05-22T16:21:18.763951+00:00" ,
143
+ "avgDuration" : 0.04066228070175437 ,
144
+ "name" : "../usr/local/lib/python3.13/site-packages/asgiref/sync.py::GetFinalYamlInteractorTest::test_when_commit_has_no_yaml" ,
145
+ "failureRate" : 0.0 ,
146
+ "flakeRate" : 0.0 ,
147
+ "commitsFailed" : 0 ,
148
+ "totalFailCount" : 0 ,
149
+ "totalFlakyFailCount" : 0 ,
150
+ "totalSkipCount" : 0 ,
151
+ "totalPassCount" : 70 ,
152
+ }
153
+ },
154
+ {
155
+ "node" : {
156
+ "updatedAt" : "2025-05-22T16:21:18.763961+00:00" ,
157
+ "avgDuration" : 0.034125877192982455 ,
158
+ "name" : "../usr/local/lib/python3.13/site-packages/asgiref/sync.py::GetFinalYamlInteractorTest::test_when_commit_has_yaml" ,
159
+ "failureRate" : 0.0 ,
160
+ "flakeRate" : 0.0 ,
161
+ "commitsFailed" : 0 ,
162
+ "totalFailCount" : 0 ,
163
+ "totalFlakyFailCount" : 0 ,
164
+ "totalSkipCount" : 0 ,
165
+ "totalPassCount" : 70 ,
166
+ }
167
+ },
168
+ ],
169
+ }
170
+ },
171
+ }
172
+ }
173
+ }
71
174
}
72
- """
73
175
74
176
75
177
@region_silo_endpoint
@@ -79,79 +181,30 @@ class TestResultsEndpoint(CodecovEndpoint):
79
181
"GET" : ApiPublishStatus .PUBLIC ,
80
182
}
81
183
82
- def get (self , request : Request ) -> Response :
184
+ # Disable pagination requirement for this endpoint
185
+ def has_pagination (self , response ):
186
+ return True
187
+
188
+ def get (self , request : Request , owner : str , repository : str , commit : str ) -> Response :
83
189
"""Retrieves the list of test results for a given commit. If a test result id is also
84
190
provided, the endpoint will return the test result with that id."""
85
191
86
- test_result_id = request .GET .get ("test_result_id" )
87
- owner = request .GET .get ("owner" )
88
- repo = request .GET .get ("repo" )
89
- commit = request .GET .get ("commit" )
90
-
91
192
variables = {
92
193
"owner" : owner ,
93
- "repo" : repo ,
194
+ "repo" : repository ,
94
195
"commit" : commit ,
95
196
}
96
197
97
- assert variables # just to get rid of lint error
98
-
99
- if test_result_id :
100
- # TODO: graphQL Query
101
- # Passing in query into codecov client means we need the query to be structured by the time we call it.
102
-
103
- # res = CodecovClient.query(get_query, variables)
104
- # transformed_res = CodecovClient.transform_response(res, serializer)
105
-
106
- return Response (
107
- {
108
- "updatedAt" : "2021-01-01T00:00:00Z" ,
109
- "name" : "test" ,
110
- "commitsFailed" : 1 ,
111
- "failureRate" : 0.01 ,
112
- "flakeRate" : 100 ,
113
- "avgDuration" : 100 ,
114
- "lastDuration" : 100 ,
115
- "totalFailCount" : 1 ,
116
- "totalFlakyFailCount" : 1 ,
117
- "totalSkipCount" : 0 ,
118
- "totalPassCount" : 0 ,
119
- }
120
- )
121
-
122
- # CodecovClient.query(list_query, variables)
123
-
124
- # TODO: Response filtering
125
-
126
- return Response (
127
- {
128
- [
129
- {
130
- "updatedAt" : "2021-01-01T00:00:00Z" ,
131
- "name" : "test" ,
132
- "commitsFailed" : 1 ,
133
- "failureRate" : 0.01 ,
134
- "flakeRate" : 100 ,
135
- "avgDuration" : 100 ,
136
- "lastDuration" : 100 ,
137
- "totalFailCount" : 1 ,
138
- "totalFlakyFailCount" : 1 ,
139
- "totalSkipCount" : 0 ,
140
- "totalPassCount" : 0 ,
141
- },
142
- {
143
- "updatedAt" : "2021-01-01T00:00:00Z" ,
144
- "name" : "test" ,
145
- "commitsFailed" : 4 ,
146
- "failureRate" : 0.5 ,
147
- "flakeRate" : 0.2 ,
148
- "avgDuration" : 100 ,
149
- "lastDuration" : 100 ,
150
- "totalFailCount" : 4 ,
151
- "totalFlakyFailCount" : 0 ,
152
- "totalSkipCount" : 0 ,
153
- "totalPassCount" : 0 ,
154
- },
155
- ]
156
- }
157
- )
198
+ assert variables
199
+
200
+ # TODO: Uncomment when CodecovClient is available
201
+ # graphql_response = CodecovClient.query(list_query, variables)
202
+
203
+ # For now, use the sample response for demonstration
204
+ graphql_response = sample_graphql_response
205
+
206
+ # Transform the GraphQL response to client format
207
+ serializer = TestResultSerializer ()
208
+ test_results = serializer .transform_graphql_response (graphql_response )
209
+
210
+ return Response (test_results )
0 commit comments