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

Commit dba1525

Browse files
committed
feat: add ability to read new file format
i'm introducing 2 things here: - the ability to read the output of the new ta cache rollups code in the new location in GCS - the ability to read multiple versions of the new rollup file - its probable that the "no version" file never exists in prod GCS but i'd like to establish this pattern in the code nonetheless i also add metrics
1 parent defc6a5 commit dba1525

File tree

6 files changed

+389
-49
lines changed

6 files changed

+389
-49
lines changed

docker-compose.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ services:
1616
- RUN_ENV=DEV
1717
# Improves pytest-cov performance in python 3.12
1818
# https://github.com/nedbat/coveragepy/issues/1665#issuecomment-1937075835
19-
- COVERAGE_CORE=sysmon
19+
- COVERAGE_CORE=sysmon
2020
env_file:
2121
- .testenv
2222

@@ -42,3 +42,16 @@ services:
4242
redis:
4343
image: redis:6-alpine
4444

45+
minio:
46+
image: minio/minio:latest
47+
command: server /export
48+
ports:
49+
- "${MINIO_PORT:-9000}:9000"
50+
environment:
51+
- MINIO_ACCESS_KEY=codecov-default-key
52+
- MINIO_SECRET_KEY=codecov-default-secret
53+
volumes:
54+
- type: tmpfs
55+
target: /export
56+
tmpfs:
57+
size: 256M
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
[
2+
{
3+
"cursor": "MC44fHRlc3Q0",
4+
"node": {
5+
"name": "test4",
6+
"failureRate": 0.8,
7+
"flakeRate": 0.0,
8+
"avgDuration": 100.0,
9+
"totalFailCount": 20,
10+
"totalFlakyFailCount": 0,
11+
"totalPassCount": 5,
12+
"totalSkipCount": 5,
13+
"commitsFailed": 5,
14+
"lastDuration": 100.0
15+
}
16+
},
17+
{
18+
"cursor": "MC43NXx0ZXN0Mw==",
19+
"node": {
20+
"name": "test3",
21+
"failureRate": 0.75,
22+
"flakeRate": 0.0,
23+
"avgDuration": 100.0,
24+
"totalFailCount": 15,
25+
"totalFlakyFailCount": 0,
26+
"totalPassCount": 5,
27+
"totalSkipCount": 5,
28+
"commitsFailed": 5,
29+
"lastDuration": 100.0
30+
}
31+
},
32+
{
33+
"cursor": "MC42NjY2NjY2NjY2NjY2NjY2fHRlc3Qy",
34+
"node": {
35+
"name": "test2",
36+
"failureRate": 0.6666666666666666,
37+
"flakeRate": 0.0,
38+
"avgDuration": 100.0,
39+
"totalFailCount": 10,
40+
"totalFlakyFailCount": 0,
41+
"totalPassCount": 5,
42+
"totalSkipCount": 5,
43+
"commitsFailed": 5,
44+
"lastDuration": 100.0
45+
}
46+
},
47+
{
48+
"cursor": "MC41fHRlc3Qx",
49+
"node": {
50+
"name": "test1",
51+
"failureRate": 0.5,
52+
"flakeRate": 0.5,
53+
"avgDuration": 100.0,
54+
"totalFailCount": 5,
55+
"totalFlakyFailCount": 5,
56+
"totalPassCount": 5,
57+
"totalSkipCount": 5,
58+
"commitsFailed": 5,
59+
"lastDuration": 100.0
60+
}
61+
},
62+
{
63+
"cursor": "MC4wfHRlc3Qw",
64+
"node": {
65+
"name": "test0",
66+
"failureRate": 0.0,
67+
"flakeRate": 0.0,
68+
"avgDuration": 100.0,
69+
"totalFailCount": 0,
70+
"totalFlakyFailCount": 0,
71+
"totalPassCount": 5,
72+
"totalSkipCount": 5,
73+
"commitsFailed": 5,
74+
"lastDuration": 100.0
75+
}
76+
}
77+
]

graphql_api/tests/test_test_analytics.py

Lines changed: 120 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import datetime
22
from base64 import b64encode
3+
from itertools import chain
34
from typing import Any
45

56
import polars as pl
67
import pytest
8+
from django.conf import settings
79
from shared.django_apps.codecov_auth.tests.factories import OwnerFactory
810
from shared.django_apps.core.tests.factories import RepositoryFactory
11+
from shared.storage import get_appropriate_storage_service
912
from shared.storage.exceptions import BucketAlreadyExistsError
10-
from shared.storage.memory import MemoryStorageService
1113

1214
from graphql_api.types.enums import (
1315
OrderingDirection,
@@ -48,12 +50,32 @@ def __call__(self, updated_at: datetime.datetime) -> dict[str, Any]:
4850
}
4951

5052

51-
@pytest.fixture
52-
def mock_storage(mocker):
53-
m = mocker.patch("utils.test_results.get_appropriate_storage_service")
54-
storage_server = MemoryStorageService({})
55-
m.return_value = storage_server
56-
yield storage_server
53+
def create_no_version_row(updated_at: datetime.datetime) -> list[dict[str, Any]]:
54+
return [
55+
{
56+
"timestamp_bin": datetime.datetime(
57+
updated_at.year, updated_at.month, updated_at.day
58+
),
59+
"computed_name": f"test{i}",
60+
"flags": [f"flag{i}"],
61+
"updated_at": updated_at,
62+
"avg_duration": 100.0,
63+
"fail_count": i,
64+
"flaky_fail_count": 1 if i == 1 else 0,
65+
"pass_count": 1,
66+
"skip_count": 1,
67+
"failing_commits": 1,
68+
"last_duration": 100.0,
69+
}
70+
for i in range(5)
71+
]
72+
73+
74+
def create_v1_row(updated_at: datetime.datetime) -> list[dict[str, Any]]:
75+
return [
76+
{**row, "testsuite": f"testsuite{i}"}
77+
for i, row in enumerate(create_no_version_row(updated_at))
78+
]
5779

5880

5981
base_gql_query = """
@@ -80,6 +102,15 @@ def mock_storage(mocker):
80102
for i in range(0, len(rows_with_duplicate_names) - 1, 2):
81103
rows_with_duplicate_names[i]["name"] = rows_with_duplicate_names[i + 1]["name"]
82104

105+
no_version_rows = list(
106+
chain.from_iterable(
107+
create_no_version_row(datetime.datetime.now()) for i in range(5)
108+
)
109+
)
110+
v1_rows = list(
111+
chain.from_iterable(create_v1_row(datetime.datetime.now()) for i in range(5))
112+
)
113+
83114

84115
def dedup(rows: list[dict]) -> list[dict]:
85116
by_name = {}
@@ -135,6 +166,8 @@ def row_to_camel_case(row: dict) -> dict:
135166

136167
test_results_table = pl.DataFrame(rows)
137168
test_results_table_with_duplicate_names = pl.DataFrame(rows_with_duplicate_names)
169+
test_results_table_no_version = pl.DataFrame(no_version_rows)
170+
test_results_table_v1 = pl.DataFrame(v1_rows)
138171

139172

140173
def base64_encode_string(x: str) -> str:
@@ -169,23 +202,23 @@ def store_in_redis(repository):
169202

170203

171204
@pytest.fixture
172-
def store_in_storage(repository, mock_storage):
173-
from django.conf import settings
205+
def store_in_storage(repository):
206+
storage = get_appropriate_storage_service()
174207

175208
try:
176-
mock_storage.create_root_storage(settings.GCS_BUCKET_NAME)
209+
storage.create_root_storage(settings.GCS_BUCKET_NAME)
177210
except BucketAlreadyExistsError:
178211
pass
179212

180-
mock_storage.write_file(
213+
storage.write_file(
181214
settings.GCS_BUCKET_NAME,
182215
f"test_results/rollups/{repository.repoid}/{repository.branch}/30",
183216
test_results_table.write_ipc(None).getvalue(),
184217
)
185218

186219
yield
187220

188-
mock_storage.delete_file(
221+
storage.delete_file(
189222
settings.GCS_BUCKET_NAME,
190223
f"test_results/rollups/{repository.repoid}/{repository.branch}/30",
191224
)
@@ -215,20 +248,17 @@ def test_get_test_results(
215248
repository,
216249
store_in_redis,
217250
store_in_storage,
218-
mock_storage,
219251
):
220252
results = get_results(repository.repoid, repository.branch, 30)
221253
assert results is not None
222254

223255
assert results.equals(dedup_table(test_results_table))
224256

225-
def test_get_test_results_no_storage(
226-
self, transactional_db, repository, mock_storage
227-
):
257+
def test_get_test_results_no_storage(self, transactional_db, repository):
228258
assert get_results(repository.repoid, repository.branch, 30) is None
229259

230260
def test_get_test_results_no_redis(
231-
self, mocker, transactional_db, repository, store_in_storage, mock_storage
261+
self, mocker, transactional_db, repository, store_in_storage
232262
):
233263
m = mocker.patch("services.task.TaskService.cache_test_results_redis")
234264
results = get_results(repository.repoid, repository.branch, 30)
@@ -237,9 +267,7 @@ def test_get_test_results_no_redis(
237267

238268
m.assert_called_once_with(repository.repoid, repository.branch)
239269

240-
def test_test_results(
241-
self, transactional_db, repository, store_in_redis, mock_storage, snapshot
242-
):
270+
def test_test_results(self, transactional_db, repository, store_in_redis, snapshot):
243271
test_results = generate_test_results(
244272
repoid=repository.repoid,
245273
ordering=TestResultsOrderingParameter.UPDATED_AT,
@@ -261,7 +289,7 @@ def test_test_results(
261289
]
262290

263291
def test_test_results_asc(
264-
self, transactional_db, repository, store_in_redis, mock_storage, snapshot
292+
self, transactional_db, repository, store_in_redis, snapshot
265293
):
266294
test_results = generate_test_results(
267295
repoid=repository.repoid,
@@ -373,7 +401,6 @@ def test_test_results_pagination(
373401
end_cursor,
374402
repository,
375403
store_in_redis,
376-
mock_storage,
377404
snapshot,
378405
):
379406
test_results = generate_test_results(
@@ -489,7 +516,6 @@ def test_test_results_pagination_asc(
489516
end_cursor,
490517
repository,
491518
store_in_redis,
492-
mock_storage,
493519
snapshot,
494520
):
495521
test_results = generate_test_results(
@@ -515,9 +541,7 @@ def test_test_results_pagination_asc(
515541
if isinstance(row["node"], TestResultsRow)
516542
]
517543

518-
def test_test_analytics_term_filter(
519-
self, repository, store_in_redis, mock_storage, snapshot
520-
):
544+
def test_test_analytics_term_filter(self, repository, store_in_redis, snapshot):
521545
test_results = generate_test_results(
522546
repoid=repository.repoid,
523547
term=rows[0]["name"][2:],
@@ -563,9 +587,7 @@ def test_test_analytics_testsuite_filter(
563587
if isinstance(row["node"], TestResultsRow)
564588
]
565589

566-
def test_test_analytics_flag_filter(
567-
self, repository, store_in_redis, mock_storage, snapshot
568-
):
590+
def test_test_analytics_flag_filter(self, repository, store_in_redis, snapshot):
569591
test_results = generate_test_results(
570592
repoid=repository.repoid,
571593
flags=[rows[0]["flags"][0]],
@@ -588,7 +610,7 @@ def test_test_analytics_flag_filter(
588610
if isinstance(row["node"], TestResultsRow)
589611
]
590612

591-
def test_gql_query(self, repository, store_in_redis, mock_storage):
613+
def test_gql_query(self, repository, store_in_redis):
592614
query = base_gql_query % (
593615
repository.author.username,
594616
repository.name,
@@ -632,7 +654,7 @@ def test_gql_query(self, repository, store_in_redis, mock_storage):
632654
]
633655

634656
def test_gql_query_with_duplicate_names(
635-
self, repository, store_in_redis_with_duplicate_names, mock_storage
657+
self, repository, store_in_redis_with_duplicate_names
636658
):
637659
query = base_gql_query % (
638660
repository.author.username,
@@ -676,7 +698,7 @@ def test_gql_query_with_duplicate_names(
676698
for row in dedup(rows_with_duplicate_names)
677699
]
678700

679-
def test_gql_query_aggregates(self, repository, store_in_redis, mock_storage):
701+
def test_gql_query_aggregates(self, repository, store_in_redis):
680702
query = base_gql_query % (
681703
repository.author.username,
682704
repository.name,
@@ -703,7 +725,7 @@ def test_gql_query_aggregates(self, repository, store_in_redis, mock_storage):
703725
"totalSlowTests": 1,
704726
}
705727

706-
def test_gql_query_flake_aggregates(self, repository, store_in_redis, mock_storage):
728+
def test_gql_query_flake_aggregates(self, repository, store_in_redis):
707729
query = base_gql_query % (
708730
repository.author.username,
709731
repository.name,
@@ -721,3 +743,68 @@ def test_gql_query_flake_aggregates(self, repository, store_in_redis, mock_stora
721743
"flakeRate": 0.1,
722744
"flakeCount": 1,
723745
}
746+
747+
def test_gql_query_with_new_ta(self, mocker, repository, snapshot):
748+
# set the feature flag
749+
mocker.patch("rollouts.READ_NEW_TA.check_value", return_value=True)
750+
751+
# read file from samples
752+
storage = get_appropriate_storage_service()
753+
try:
754+
storage.create_root_storage(settings.GCS_BUCKET_NAME)
755+
except BucketAlreadyExistsError:
756+
pass
757+
storage.write_file(
758+
settings.GCS_BUCKET_NAME,
759+
f"test_analytics/branch_rollups/{repository.repoid}/{repository.branch}.arrow",
760+
test_results_table_no_version.write_ipc(None).getvalue(),
761+
)
762+
763+
# run the GQL query
764+
query = base_gql_query % (
765+
repository.author.username,
766+
repository.name,
767+
"""
768+
testResults(ordering: { parameter: FAILURE_RATE, direction: DESC } ) {
769+
totalCount
770+
edges {
771+
cursor
772+
node {
773+
name
774+
failureRate
775+
flakeRate
776+
updatedAt
777+
avgDuration
778+
totalFailCount
779+
totalFlakyFailCount
780+
totalPassCount
781+
totalSkipCount
782+
commitsFailed
783+
lastDuration
784+
}
785+
}
786+
}
787+
""",
788+
)
789+
790+
result = self.gql_request(query, owner=repository.author)
791+
792+
# take a snapshot of the results
793+
assert (
794+
result["owner"]["repository"]["testAnalytics"]["testResults"]["totalCount"]
795+
== 5
796+
)
797+
assert snapshot("json") == [
798+
{
799+
**edge,
800+
"node": {k: v for k, v in edge["node"].items() if k != "updatedAt"},
801+
}
802+
for edge in result["owner"]["repository"]["testAnalytics"]["testResults"][
803+
"edges"
804+
]
805+
]
806+
807+
storage.delete_file(
808+
settings.GCS_BUCKET_NAME,
809+
f"test_analytics/branch_rollups/{repository.repoid}/{repository.branch}.arrow",
810+
)

0 commit comments

Comments
 (0)