Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions gittensor/validator/oss_contributions/mirror/scoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ async def score_pr(
# load_miner_prs → _should_skip_merged_mirror_pr). By this point
# eval_.merged_prs contains only eligibility-passed PRs.

# Attribute the repo for a MERGED PR up front: the PR already counts toward
# eligibility (merged_prs / total_merged_prs), so unique-repo attribution must
# not depend on this round's mirror file-fetch outcome (transient early
# returns below). Regression of #65/#71 when scoring moved to the mirror path.
if pr.state == 'MERGED':
eval_.unique_repos_contributed_to.add(pr.repo_full_name)

has_fixed_base = repo_config.fixed_base_score is not None
scoring_cfg = resolve_scoring(repo_config.scoring)

Expand Down Expand Up @@ -181,11 +188,8 @@ async def score_pr(
scored.base_score = repo_config.fixed_base_score

_calculate_pr_multipliers(scored, repo_config, scoring_cfg)

if pr.state == 'MERGED':
eval_.unique_repos_contributed_to.add(pr.repo_full_name)
# Token totals are aggregated later in finalize_miner_scores; this
# function only sets per-PR state.
# Token totals are aggregated later in finalize_miner_scores; this
# function only sets per-PR state.


# ============================================================================
Expand Down
88 changes: 88 additions & 0 deletions tests/validator/oss_contributions/mirror/test_scoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,94 @@ def test_fixed_small_diff_outscores_nonfixed_large_diff_with_same_label(self, mo
assert fixed.calculate_final_earned_score() > plain.calculate_final_earned_score()


# ============================================================================
# Unique-repo attribution (#1616)
# ============================================================================


class TestUniqueRepoAttribution:
def _eval(self):
eval_ = Mock()
eval_.unique_repos_contributed_to = set()
return eval_

def test_merged_pr_attributes_repo_when_files_pending(self):
scored = ScoredPR(pr=_pr())
scored.pr.scoring_data_stored = False
eval_ = self._eval()
client = Mock()

asyncio.run(
score_pr(
scored,
eval_=eval_,
master_repositories={scored.pr.repo_full_name: _config()},
programming_languages={},
token_config=Mock(),
client=client,
)
)

client.get_pr_files.assert_not_called()
assert scored.pr.repo_full_name in eval_.unique_repos_contributed_to

def test_merged_pr_attributes_repo_when_fetch_raises(self):
scored = ScoredPR(pr=_pr())
eval_ = self._eval()
client = Mock()
client.get_pr_files.side_effect = scoring_module.MirrorRequestError('boom')

asyncio.run(
score_pr(
scored,
eval_=eval_,
master_repositories={scored.pr.repo_full_name: _config()},
programming_languages={},
token_config=Mock(),
client=client,
)
)

assert scored.pr.repo_full_name in eval_.unique_repos_contributed_to

def test_merged_pr_attributes_repo_when_no_files_returned(self):
scored = ScoredPR(pr=_pr())
eval_ = self._eval()
client = Mock()
client.get_pr_files.return_value.files = []

asyncio.run(
score_pr(
scored,
eval_=eval_,
master_repositories={scored.pr.repo_full_name: _config()},
programming_languages={},
token_config=Mock(),
client=client,
)
)

assert scored.pr.repo_full_name in eval_.unique_repos_contributed_to

def test_open_pr_does_not_attribute_repo(self):
scored = ScoredPR(pr=_pr(state='OPEN'))
scored.pr.scoring_data_stored = False
eval_ = self._eval()

asyncio.run(
score_pr(
scored,
eval_=eval_,
master_repositories={scored.pr.repo_full_name: _config()},
programming_languages={},
token_config=Mock(),
client=Mock(),
)
)

assert scored.pr.repo_full_name not in eval_.unique_repos_contributed_to


# ============================================================================
# File adapter
# ============================================================================
Expand Down