diff --git a/gittensor/classes.py b/gittensor/classes.py index 93bed5e1d..205a2b11f 100644 --- a/gittensor/classes.py +++ b/gittensor/classes.py @@ -708,6 +708,9 @@ def _build_cache_entry(evaluation: 'MinerEvaluation') -> 'MinerEvaluation': cached.merged_pull_requests = [_pr_for_cache(pr) for pr in evaluation.merged_pull_requests] cached.open_pull_requests = [_pr_for_cache(pr) for pr in evaluation.open_pull_requests] cached.closed_pull_requests = [_pr_for_cache(pr) for pr in evaluation.closed_pull_requests] + cached.mirror_merged_prs = [_scored_mirror_pr_for_cache(pr) for pr in evaluation.mirror_merged_prs] + cached.mirror_open_prs = [_scored_mirror_pr_for_cache(pr) for pr in evaluation.mirror_open_prs] + cached.mirror_closed_prs = [_scored_mirror_pr_for_cache(pr) for pr in evaluation.mirror_closed_prs] return cached @staticmethod @@ -735,3 +738,9 @@ def _pr_with_fresh_issues(pr: 'PullRequest') -> 'PullRequest': if pr.issues is not None: pr_copy.issues = [copy.copy(issue) for issue in pr.issues] return pr_copy + + +def _scored_mirror_pr_for_cache(scored: 'ScoredMirrorPR') -> 'ScoredMirrorPR': + scored_copy = copy.copy(scored) + scored_copy.files = None + return scored_copy diff --git a/tests/validator/test_validator_cache_fallback.py b/tests/validator/test_validator_cache_fallback.py index 89667d195..16e45ab08 100644 --- a/tests/validator/test_validator_cache_fallback.py +++ b/tests/validator/test_validator_cache_fallback.py @@ -4,6 +4,8 @@ from typing import cast from gittensor.classes import FileChange, Issue, MinerEvaluation, MinerEvaluationCache, PRState, PullRequest +from gittensor.utils.mirror.models import MirrorFile, MirrorPullRequest, MirrorReviewSummary +from gittensor.validator.oss_contributions.mirror.scored_pr import ScoredMirrorPR from neurons.validator import Validator @@ -175,3 +177,69 @@ def test_store_isolates_cache_from_source_issue_mutations(self): cached_issues = cached.merged_pull_requests[0].issues assert cached_issues is not None assert cached_issues[0].discovery_earned_score == 0.0 + + +def _make_mirror_pr_with_file_blob(blob: str, pr_number: int = 7) -> ScoredMirrorPR: + now = datetime.now(timezone.utc) + pr = MirrorPullRequest( + repo_full_name='owner/repo', + pr_number=pr_number, + title='cached mirror pr', + body=None, + state='MERGED', + author_github_id='12345', + author_login='miner', + author_association='CONTRIBUTOR', + created_at=now, + closed_at=None, + merged_at=now, + last_edited_at=None, + edited_after_merge=False, + hours_since_merge=0.0, + merged_by_login='maintainer', + base_ref='main', + head_ref='feature', + head_repo_full_name='owner/repo', + default_branch='main', + head_sha='abc', + base_sha='def', + merge_base_sha='def', + additions=1, + deletions=0, + commits_count=1, + scoring_data_stored=True, + review_summary=MirrorReviewSummary(), + ) + scored = ScoredMirrorPR(pr=pr, base_score=8.0, token_score=12.0) + scored.files = [ + MirrorFile( + filename='src/lib.py', + previous_filename=None, + status='modified', + additions=1, + deletions=0, + changes=1, + is_binary=False, + head_content=blob, + base_content=blob, + ) + ] + return scored + + +class TestMirrorCacheIsolation: + def test_cache_drops_mirror_files_without_mutating_source(self): + cache = MinerEvaluationCache() + source = MinerEvaluation(uid=1, hotkey='hotkey_1', github_id='12345', github_pat='secret') + source.mirror_merged_prs = [_make_mirror_pr_with_file_blob('A' * 10_000)] + cache.store(source) + + assert source.github_pat == 'secret' + source_files = source.mirror_merged_prs[0].files + assert source_files is not None + assert source_files[0].head_content == 'A' * 10_000 + + cached = cache.get(uid=1, hotkey='hotkey_1', github_id='12345') + assert cached is not None + assert cached.github_pat is None + assert cached.mirror_merged_prs[0].files is None