Skip to content

Commit 8f2e329

Browse files
authored
Merge pull request #3155 from athenianco/test-health-metrics
[DEV-5569] Add unit tests for internal account data health metrics
2 parents 3507900 + 6c42fbd commit 8f2e329

File tree

9 files changed

+146
-14
lines changed

9 files changed

+146
-14
lines changed

server/athenian/api/precompute/accounts.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ async def precompute_reposet(
185185
time_to: datetime,
186186
no_time_from: datetime,
187187
time_from: datetime,
188-
) -> None:
188+
) -> DataHealthMetrics:
189189
"""
190190
Execute precomputations for a single reposet.
191191
@@ -212,7 +212,7 @@ async def precompute_reposet(
212212
except Exception as e:
213213
log.error("prolog %d: %s: %s", reposet.owner_id, type(e).__name__, e)
214214
sentry_sdk.capture_exception(e)
215-
return
215+
return health_metrics
216216
deref_items, missing = prefixer.dereference_repositories(
217217
reposet_items_to_refs(reposet.items), return_missing=True, logger=log,
218218
)
@@ -446,6 +446,7 @@ async def report_precompute_success():
446446
)
447447
finally:
448448
await wait_deferred()
449+
return health_metrics
449450

450451

451452
async def ensure_teams(

server/athenian/api/precompute/store_external_health.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import argparse
2+
import asyncio
23
from datetime import datetime, timezone
34
import logging
45
from typing import Any
@@ -17,7 +18,7 @@
1718
async def _record_performance_metrics(
1819
prometheus_endpoint: str,
1920
rdb: Database,
20-
):
21+
) -> None:
2122
log = logging.getLogger(f"{metadata.__package__}._record_performance_metrics")
2223
for pct in ("50", "95"):
2324
inserted = []
@@ -62,13 +63,10 @@ async def _record_performance_metrics(
6263

6364
async def _record_inconsistency_metrics(
6465
prometheus_endpoint: str,
65-
sdb: Database,
66+
acc_id_map_task: asyncio.Task,
6667
rdb: Database,
67-
):
68+
) -> None:
6869
log = logging.getLogger(f"{metadata.__package__}._record_inconsistency_metrics")
69-
acc_id_map = dict(
70-
await sdb.fetch_all(select(AccountGitHubAccount.id, AccountGitHubAccount.account_id)),
71-
)
7270
inserted = []
7371
for attempt in range(3):
7472
async with aiohttp.ClientSession() as session:
@@ -87,7 +85,10 @@ async def _record_inconsistency_metrics(
8785
await response.text(),
8886
)
8987
continue
90-
for obj in (await response.json())["data"]["result"]:
88+
objs = (await response.json())["data"]["result"]
89+
await acc_id_map_task
90+
acc_id_map = acc_id_map_task.result()
91+
for obj in objs:
9192
try:
9293
account = acc_id_map[int(obj["metric"]["acc_id"])]
9394
except KeyError:
@@ -105,9 +106,30 @@ async def _record_inconsistency_metrics(
105106
await rdb.execute_many(insert(HealthMetric), inserted)
106107

107108

109+
async def _record_pending_fetch_metrics(
110+
acc_id_map_task: asyncio.Task,
111+
mdb: Database,
112+
rdb: Database,
113+
) -> None:
114+
log = logging.getLogger(f"{metadata.__package__}._record_pending_fetch_metrics")
115+
inserted = []
116+
# df_pending_prs, df_pending_commits = await gather()
117+
if inserted:
118+
log.info("inserting %d data inconsistency records", len(inserted))
119+
await rdb.execute_many(insert(HealthMetric), inserted)
120+
121+
122+
async def _fetch_acc_id_map(sdb: Database) -> dict[int, int]:
123+
return dict(
124+
await sdb.fetch_all(select(AccountGitHubAccount.id, AccountGitHubAccount.account_id)),
125+
)
126+
127+
108128
async def main(context: PrecomputeContext, args: argparse.Namespace) -> Any:
109129
"""Fill missing commit references in the deployed components."""
130+
acc_id_map_task = asyncio.create_task(_fetch_acc_id_map(context.sdb), name="_fetch_acc_id_map")
110131
await gather(
111132
_record_performance_metrics(args.prometheus, context.rdb),
112-
_record_inconsistency_metrics(args.prometheus, context.sdb, context.rdb),
133+
_record_inconsistency_metrics(args.prometheus, acc_id_map_task, context.rdb),
134+
_record_pending_fetch_metrics(acc_id_map_task, context.mdb, context.rdb),
113135
)

server/tests/controllers/miners/github/test_branches.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
from sqlalchemy import delete, insert, select, update
33

44
from athenian.api.defer import wait_deferred, with_defer
5+
from athenian.api.internal.miners.github.branches import BranchMinerMetrics
56
from athenian.api.models.metadata.github import Branch
67

78

89
async def test_extract_branches_zero(mdb, branch_miner, prefixer):
10+
metrics = BranchMinerMetrics.empty()
911
branches, defaults = await branch_miner.extract_branches(
10-
["src-d/gitbase"], prefixer, (6366825,), mdb, None,
12+
["src-d/gitbase"], prefixer, (6366825,), mdb, None, metrics=metrics,
1113
)
1214
assert branches.empty
15+
assert metrics.empty_count == 1
1316
assert defaults == {"src-d/gitbase": "master"}
1417
branches, defaults = await branch_miner.extract_branches(
1518
["src-d/gitbase"], prefixer, (6366825,), mdb, None,
@@ -48,6 +51,7 @@ async def test_extract_branches_cache(mdb, cache, branch_miner, prefixer):
4851

4952
async def test_extract_branches_main(mdb_rw, branch_miner, prefixer):
5053
mdb = mdb_rw
54+
metrics = BranchMinerMetrics.empty()
5155
await mdb.execute(
5256
update(Branch)
5357
.where(Branch.branch_name == "master")
@@ -60,7 +64,7 @@ async def test_extract_branches_main(mdb_rw, branch_miner, prefixer):
6064
)
6165
try:
6266
_, defaults = await branch_miner.extract_branches(
63-
["src-d/go-git"], prefixer, (6366825,), mdb, None,
67+
["src-d/go-git"], prefixer, (6366825,), mdb, None, metrics=metrics,
6468
)
6569
assert defaults == {"src-d/go-git": "main", "src-d/юникод": "вадим"}
6670
finally:
@@ -74,6 +78,8 @@ async def test_extract_branches_main(mdb_rw, branch_miner, prefixer):
7478
},
7579
),
7680
)
81+
assert metrics.count == 5
82+
assert metrics.no_default == 1
7783

7884

7985
async def test_extract_branches_max_date(mdb_rw, branch_miner, prefixer):

server/tests/controllers/miners/github/test_commit.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from athenian.api.defer import wait_deferred, with_defer
99
from athenian.api.internal.miners.github.branches import BranchMiner
1010
from athenian.api.internal.miners.github.commit import (
11+
CommitDAGMetrics,
1112
FilterCommitsProperty,
1213
_fetch_commit_history_dag,
1314
extract_commits,
@@ -253,6 +254,7 @@ async def test__fetch_commit_history_dag_inconsistent(mdb_rw, dag):
253254
},
254255
),
255256
)
257+
metrics = CommitDAGMetrics.empty()
256258
try:
257259
consistent, _, newhashes, newvertexes, newedges = await _fetch_commit_history_dag(
258260
True,
@@ -264,9 +266,12 @@ async def test__fetch_commit_history_dag_inconsistent(mdb_rw, dag):
264266
"src-d/go-git",
265267
(6366825,),
266268
mdb_rw,
269+
metrics=metrics,
267270
)
268271
finally:
269272
await mdb_rw.execute(delete(NodeCommitParent).where(NodeCommitParent.parent_id == 100500))
270273
assert not consistent
271274
assert len(newhashes) == len(hashes)
272275
assert b"1" * 40 not in newhashes
276+
assert metrics.corrupted == {"src-d/go-git"}
277+
assert metrics.orphaned == {"src-d/go-git"}

server/tests/controllers/miners/github/test_release.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from athenian.api.defer import wait_deferred, with_defer
1616
from athenian.api.internal.miners.filters import JIRAFilter, LabelFilter
1717
from athenian.api.internal.miners.github.commit import (
18+
CommitDAGMetrics,
1819
_empty_dag,
1920
_fetch_commit_history_dag,
2021
fetch_repository_commits,
@@ -26,7 +27,10 @@
2627
mark_dag_parents,
2728
partition_dag,
2829
)
29-
from athenian.api.internal.miners.github.release_load import group_repos_by_release_match
30+
from athenian.api.internal.miners.github.release_load import (
31+
MineReleaseMetrics,
32+
group_repos_by_release_match,
33+
)
3034
from athenian.api.internal.miners.github.release_mine import (
3135
mine_releases,
3236
mine_releases_by_name,
@@ -847,6 +851,7 @@ async def test_load_releases_events_unresolved(
847851
.explode(with_primary_keys=True),
848852
),
849853
)
854+
metrics = MineReleaseMetrics.empty()
850855
releases, matched_bys = await release_loader.load_releases(
851856
["src-d/go-git"],
852857
branches,
@@ -863,9 +868,12 @@ async def test_load_releases_events_unresolved(
863868
rdb,
864869
None,
865870
index=Release.node_id.name,
871+
metrics=metrics,
866872
)
867873
assert releases.empty
868874
assert matched_bys == {"src-d/go-git": ReleaseMatch.event}
875+
assert metrics.count_by_event == 0
876+
assert metrics.unresolved == 1
869877

870878

871879
@pytest.fixture(scope="module")
@@ -981,6 +989,7 @@ async def test__fetch_repository_commits_initial_commit(mdb, pdb, prune, heads_d
981989
@with_defer
982990
@freeze_time("2015-04-05")
983991
async def test__fetch_repository_commits_orphan_skip(mdb, pdb, prune, heads_df1):
992+
metrics = CommitDAGMetrics.empty()
984993
dags = await fetch_repository_commits(
985994
{
986995
"src-d/go-git": (
@@ -1000,7 +1009,10 @@ async def test__fetch_repository_commits_orphan_skip(mdb, pdb, prune, heads_df1)
10001009
mdb,
10011010
pdb,
10021011
None,
1012+
metrics=metrics,
10031013
)
1014+
if prune:
1015+
assert metrics.pristine == {"src-d/go-git"}
10041016
hashes, vertexes, edges = dags["src-d/go-git"][1]
10051017
if prune:
10061018
assert len(hashes) == 0
@@ -1553,6 +1565,7 @@ async def test_precomputed_smoke(
15531565
):
15541566
time_from = datetime(year=2015, month=1, day=1, tzinfo=timezone.utc)
15551567
time_to = datetime(year=2020, month=12, day=1, tzinfo=timezone.utc)
1568+
metrics = MineReleaseMetrics.empty()
15561569
kwargs = self._kwargs(
15571570
time_from=time_from,
15581571
time_to=time_to,
@@ -1561,9 +1574,11 @@ async def test_precomputed_smoke(
15611574
mdb=mdb,
15621575
pdb=pdb,
15631576
rdb=rdb,
1577+
metrics=metrics,
15641578
)
15651579
await mine_releases(**kwargs)
15661580
await wait_deferred()
1581+
assert metrics.count_by_tag == 53
15671582
kwargs["with_deployments"] = False
15681583
releases, avatars, _, _ = await mine_releases(**kwargs)
15691584
assert len(releases) == 53

server/tests/controllers/test_filter_controller.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2696,6 +2696,7 @@ async def test_filter_labels_nasty_input(client, headers, account, repos, status
26962696
assert response.status == status, response_body
26972697

26982698

2699+
@pytest.mark.flaky(reruns=3)
26992700
async def test_get_releases_smoke(client, headers):
27002701
body = {
27012702
"account": 1,

server/tests/internal/features/test_entries.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from athenian.api.internal.features.entries import (
1414
MetricEntriesCalculator,
1515
MetricsLineRequest,
16+
MinePullRequestMetrics,
1617
PRFactsCalculator,
1718
TeamSpecificFilters,
1819
make_calculator,
@@ -64,9 +65,14 @@ async def test_gaetano_bug(
6465
calculator = PRFactsCalculator(
6566
1, meta_ids, mdb, pdb, rdb, **self._init_kwargs(), cache=None,
6667
)
68+
metrics = MinePullRequestMetrics.empty()
6769

6870
base_kwargs = await self._kwargs(meta_ids, mdb, sdb)
71+
base_kwargs["metrics"] = metrics
6972
facts = await calculator(time_from=dt(2017, 8, 10), time_to=dt(2017, 9, 1), **base_kwargs)
73+
assert metrics == MinePullRequestMetrics(
74+
count=33, done_count=29, merged_count=3, open_count=1,
75+
)
7076
last_review = facts[facts.node_id == 163078].last_review.values[0]
7177

7278
await calculator(time_from=dt(2017, 8, 10), time_to=dt(2017, 8, 20), **base_kwargs)

server/tests/internal/miners/github/test_deployment.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from athenian.api.internal.jira import JIRAConfig
1616
from athenian.api.internal.miners.filters import JIRAFilter, LabelFilter
1717
from athenian.api.internal.miners.github.deployment import (
18+
MineDeploymentsMetrics,
1819
deployment_facts_extract_mentioned_people,
1920
hide_outlier_first_deployments,
2021
mine_deployments,
@@ -59,6 +60,7 @@ async def test_mine_deployments_from_scratch(
5960
):
6061
time_from = datetime(2015, 1, 1, tzinfo=timezone.utc)
6162
time_to = datetime(2020, 1, 1, tzinfo=timezone.utc)
63+
metrics = MineDeploymentsMetrics.empty()
6264
await mine_releases(
6365
["src-d/go-git"],
6466
{},
@@ -82,6 +84,7 @@ async def test_mine_deployments_from_scratch(
8284
with_deployments=False,
8385
)
8486
await wait_deferred()
87+
8588
deps = await mine_deployments(
8689
["src-d/go-git"],
8790
{},
@@ -105,12 +108,15 @@ async def test_mine_deployments_from_scratch(
105108
pdb,
106109
rdb,
107110
cache,
111+
metrics=metrics,
108112
)
109113
_validate_deployments(deps, 9, True)
110114
deployment_facts_extract_mentioned_people(deps)
111115
await wait_deferred()
112116
commits = await pdb.fetch_all(select([GitHubCommitDeployment]))
113117
assert len(commits) == 4684
118+
assert metrics.count == 18
119+
assert metrics.unresolved == 0
114120

115121
# test the cache
116122
deps = await mine_deployments(
@@ -140,6 +146,73 @@ async def test_mine_deployments_from_scratch(
140146
_validate_deployments(deps, 9, True)
141147

142148

149+
@with_defer
150+
async def test_mine_deployments_unresolved(
151+
sample_deployments,
152+
release_match_setting_tag_or_branch,
153+
branches,
154+
default_branches,
155+
prefixer,
156+
mdb,
157+
pdb,
158+
rdb,
159+
cache,
160+
):
161+
time_from = datetime(2015, 1, 1, tzinfo=timezone.utc)
162+
time_to = datetime(2020, 1, 1, tzinfo=timezone.utc)
163+
metrics = MineDeploymentsMetrics.empty()
164+
165+
await rdb.execute(
166+
insert(DeploymentNotification).values(
167+
account_id=1,
168+
name="whatever",
169+
conclusion="SUCCESS",
170+
environment="production",
171+
started_at=datetime(2019, 11, 2, tzinfo=timezone.utc),
172+
finished_at=datetime(2019, 11, 2, 0, 10, tzinfo=timezone.utc),
173+
created_at=datetime.now(timezone.utc),
174+
updated_at=datetime.now(timezone.utc),
175+
),
176+
)
177+
await rdb.execute(
178+
insert(DeployedComponent).values(
179+
account_id=1,
180+
deployment_name="whatever",
181+
repository_node_id=40550,
182+
reference="not exists",
183+
created_at=datetime.now(timezone.utc),
184+
),
185+
)
186+
187+
await mine_deployments(
188+
["src-d/go-git"],
189+
{},
190+
time_from,
191+
time_to,
192+
["production", "staging"],
193+
[],
194+
{},
195+
{},
196+
LabelFilter.empty(),
197+
JIRAFilter.empty(),
198+
release_match_setting_tag_or_branch,
199+
LogicalRepositorySettings.empty(),
200+
branches,
201+
default_branches,
202+
prefixer,
203+
1,
204+
None,
205+
(6366825,),
206+
mdb,
207+
pdb,
208+
rdb,
209+
cache,
210+
metrics=metrics,
211+
)
212+
assert metrics.count == 18
213+
assert metrics.unresolved == 1
214+
215+
143216
@with_defer
144217
async def test_mine_deployments_addons_cache(
145218
sample_deployments,

0 commit comments

Comments
 (0)