teleo-infrastructure/tests/test_contributor.py
2026-05-29 15:08:09 +02:00

313 lines
12 KiB
Python

"""Tests for lib/contributor.py — contributor attribution functions."""
# ruff: noqa: E402,I001
import asyncio
import os
import sqlite3
import sys
from unittest.mock import AsyncMock, MagicMock, patch
sys.modules.setdefault("aiohttp", MagicMock())
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from lib.contributor import (
is_knowledge_pr,
refine_commit_type,
record_contributor_attribution,
upsert_contributor,
recalculate_tier,
)
# --- is_knowledge_pr ---
def test_knowledge_pr_domains():
diff = "+++ b/domains/crypto/some-claim.md\n"
assert is_knowledge_pr(diff) is True
def test_knowledge_pr_core():
diff = "+++ b/core/epistemology.md\n"
assert is_knowledge_pr(diff) is True
def test_knowledge_pr_foundations():
diff = "--- a/foundations/overview.md\n"
assert is_knowledge_pr(diff) is True
def test_knowledge_pr_decisions():
diff = "+++ b/decisions/some-decision.md\n"
assert is_knowledge_pr(diff) is True
def test_pipeline_only_pr():
diff = "+++ b/inbox/source.md\n+++ b/entities/metadao.md\n"
assert is_knowledge_pr(diff) is False
def test_mixed_pr_counts_as_knowledge():
diff = "+++ b/inbox/source.md\n+++ b/domains/crypto/claim.md\n"
assert is_knowledge_pr(diff) is True
def test_empty_diff():
assert is_knowledge_pr("") is False
# --- refine_commit_type ---
def test_refine_non_extract_unchanged():
assert refine_commit_type("anything", "research") == "research"
assert refine_commit_type("anything", "entity") == "entity"
def test_refine_extract_new_files():
diff = "diff --git a/x b/y\nnew file\n+++ b/domains/crypto/claim.md\n"
assert refine_commit_type(diff, "extract") == "extract"
def test_refine_extract_challenge():
diff = "diff --git a/x b/y\n+++ b/domains/crypto/claim.md\n+challenged_by: other\n"
assert refine_commit_type(diff, "extract") == "challenge"
def test_refine_extract_enrich():
diff = "diff --git a/x b/y\n+++ b/domains/crypto/claim.md\n+confidence: 0.8\n"
assert refine_commit_type(diff, "extract") == "enrich"
def test_refine_extract_mixed_new_and_modified():
diff = (
"diff --git a/x b/y\nnew file\n+++ b/domains/crypto/new.md\n"
"diff --git a/x b/z\n+++ b/domains/crypto/existing.md\n+foo\n"
)
assert refine_commit_type(diff, "extract") == "extract"
# --- upsert_contributor + recalculate_tier ---
def _make_db():
conn = sqlite3.connect(":memory:")
conn.row_factory = sqlite3.Row
conn.execute("""CREATE TABLE contributors (
handle TEXT PRIMARY KEY,
agent_id TEXT,
tier TEXT DEFAULT 'new',
first_contribution TEXT,
last_contribution TEXT,
claims_merged INTEGER DEFAULT 0,
challenges_survived INTEGER DEFAULT 0,
sourcer_count INTEGER DEFAULT 0,
extractor_count INTEGER DEFAULT 0,
challenger_count INTEGER DEFAULT 0,
synthesizer_count INTEGER DEFAULT 0,
reviewer_count INTEGER DEFAULT 0,
updated_at TEXT
)""")
conn.execute("""CREATE TABLE audit_log (
id INTEGER PRIMARY KEY,
ts TEXT DEFAULT (datetime('now')),
stage TEXT,
event TEXT,
detail TEXT
)""")
return conn
def test_upsert_new_contributor():
conn = _make_db()
with patch("lib.contributor.config") as mock_config:
mock_config.CONTRIBUTOR_TIER_RULES = {
"veteran": {"claims_merged": 50, "min_days_since_first": 90, "challenges_survived": 5},
"contributor": {"claims_merged": 10},
}
upsert_contributor(conn, "rio", "uuid-123", "extractor", "2026-04-16")
row = conn.execute("SELECT * FROM contributors WHERE handle = 'rio'").fetchone()
assert row["extractor_count"] == 1
assert row["claims_merged"] == 1
assert row["tier"] == "new"
def test_upsert_increment():
conn = _make_db()
upsert_contributor(conn, "rio", "uuid-123", "extractor", "2026-04-16")
upsert_contributor(conn, "rio", "uuid-123", "extractor", "2026-04-17")
row = conn.execute("SELECT * FROM contributors WHERE handle = 'rio'").fetchone()
assert row["extractor_count"] == 2
assert row["claims_merged"] == 2
def test_upsert_reviewer_no_claim_increment():
conn = _make_db()
upsert_contributor(conn, "leo", None, "reviewer", "2026-04-16")
row = conn.execute("SELECT * FROM contributors WHERE handle = 'leo'").fetchone()
assert row["reviewer_count"] == 1
assert row["claims_merged"] == 0
def test_upsert_unknown_role():
conn = _make_db()
upsert_contributor(conn, "rio", None, "wizard", "2026-04-16")
row = conn.execute("SELECT * FROM contributors WHERE handle = 'rio'").fetchone()
assert row is None # Should not insert
def test_recalculate_tier_contributor():
conn = _make_db()
conn.execute(
"""INSERT INTO contributors (handle, claims_merged, challenges_survived, first_contribution, tier)
VALUES ('rio', 15, 0, '2026-01-01', 'new')"""
)
with patch("lib.contributor.config") as mock_config:
mock_config.CONTRIBUTOR_TIER_RULES = {
"veteran": {"claims_merged": 50, "min_days_since_first": 90, "challenges_survived": 5},
"contributor": {"claims_merged": 10},
}
recalculate_tier(conn, "rio")
row = conn.execute("SELECT tier FROM contributors WHERE handle = 'rio'").fetchone()
assert row["tier"] == "contributor"
def test_recalculate_tier_veteran():
conn = _make_db()
conn.execute(
"""INSERT INTO contributors (handle, claims_merged, challenges_survived, first_contribution, tier)
VALUES ('rio', 60, 10, '2025-01-01', 'contributor')"""
)
with patch("lib.contributor.config") as mock_config:
mock_config.CONTRIBUTOR_TIER_RULES = {
"veteran": {"claims_merged": 50, "min_days_since_first": 90, "challenges_survived": 5},
"contributor": {"claims_merged": 10},
}
recalculate_tier(conn, "rio")
row = conn.execute("SELECT tier FROM contributors WHERE handle = 'rio'").fetchone()
assert row["tier"] == "veteran"
# --- record_contributor_attribution ---
def _make_attribution_db():
conn = _make_db()
conn.execute("""CREATE TABLE prs (
number INTEGER PRIMARY KEY,
commit_type TEXT,
agent TEXT,
submitted_by TEXT,
domain TEXT,
source_channel TEXT,
leo_verdict TEXT,
domain_verdict TEXT,
domain_agent TEXT,
merged_at TEXT
)""")
conn.execute("INSERT INTO prs (number, commit_type, agent) VALUES (100, 'extract', 'rio')")
return conn
def test_record_skips_pipeline_only():
conn = _make_attribution_db()
mock_diff = "+++ b/inbox/source.md\n"
async def run():
with patch("lib.contributor.get_pr_diff", new_callable=AsyncMock, return_value=mock_diff):
git_fn = AsyncMock(return_value=(0, ""))
await record_contributor_attribution(conn, 100, "extract/test", git_fn)
asyncio.run(run())
row = conn.execute("SELECT * FROM contributors").fetchone()
assert row is None # No attribution for pipeline-only
def test_record_fallback_to_pr_agent():
conn = _make_attribution_db()
mock_diff = "diff --git a/x b/domains/crypto/claim.md\nnew file\n+++ b/domains/crypto/claim.md\n+some content\n"
async def run():
with patch("lib.contributor.get_pr_diff", new_callable=AsyncMock, return_value=mock_diff):
# First call: trailer log (no trailers), Second call: author log (bot name → skipped)
git_fn = AsyncMock(
side_effect=[
(0, "no trailers here"),
(0, "domains/crypto/claim.md"),
(0, ""),
(0, "m3taversal"),
]
)
with patch("lib.contributor.config") as mock_config:
mock_config.CONTRIBUTOR_TIER_RULES = {
"veteran": {"claims_merged": 50, "min_days_since_first": 90, "challenges_survived": 5},
"contributor": {"claims_merged": 10},
}
await record_contributor_attribution(conn, 100, "extract/test", git_fn)
asyncio.run(run())
row = conn.execute("SELECT * FROM contributors WHERE handle = 'rio'").fetchone()
assert row is not None
assert row["extractor_count"] == 1
def test_record_fallback_to_git_author():
"""External contributors get credited via git commit author."""
conn = _make_attribution_db()
conn.execute("INSERT INTO prs (number, commit_type, agent) VALUES (200, 'contrib', 'external')")
mock_diff = (
"diff --git a/x b/domains/ai-alignment/claim.md\nnew file\n"
"+++ b/domains/ai-alignment/claim.md\n+new content\n"
)
async def run():
with patch("lib.contributor.get_pr_diff", new_callable=AsyncMock, return_value=mock_diff):
# First call: trailer log (no trailers), Second call: author log (external name)
git_fn = AsyncMock(
side_effect=[
(0, "no trailers"),
(0, "domains/ai-alignment/claim.md"),
(0, ""),
(0, "Cameron-S1"),
]
)
with patch("lib.contributor.config") as mock_config:
mock_config.CONTRIBUTOR_TIER_RULES = {
"veteran": {"claims_merged": 50, "min_days_since_first": 90, "challenges_survived": 5},
"contributor": {"claims_merged": 10},
}
await record_contributor_attribution(conn, 200, "contrib/cameron/challenge", git_fn)
asyncio.run(run())
row = conn.execute("SELECT * FROM contributors WHERE handle = 'cameron-s1'").fetchone()
assert row is not None
assert row["extractor_count"] == 1
def test_record_parses_pentagon_trailer():
conn = _make_attribution_db()
mock_diff = "+++ b/domains/crypto/claim.md\n+new file content\n"
trailer = "Pentagon-Agent: Theseus <uuid-456>"
async def run():
with patch("lib.contributor.get_pr_diff", new_callable=AsyncMock, return_value=mock_diff):
git_fn = AsyncMock(return_value=(0, trailer))
with patch("lib.contributor.config") as mock_config:
mock_config.CONTRIBUTOR_TIER_RULES = {
"veteran": {"claims_merged": 50, "min_days_since_first": 90, "challenges_survived": 5},
"contributor": {"claims_merged": 10},
}
await record_contributor_attribution(conn, 100, "extract/test", git_fn)
asyncio.run(run())
row = conn.execute("SELECT * FROM contributors WHERE handle = 'theseus'").fetchone()
assert row is not None
assert row["agent_id"] == "uuid-456"
def test_record_refines_commit_type():
conn = _make_attribution_db()
mock_diff = "diff --git a/x b/y\n+++ b/domains/crypto/claim.md\n+challenged_by: foo\n"
async def run():
with patch("lib.contributor.get_pr_diff", new_callable=AsyncMock, return_value=mock_diff):
git_fn = AsyncMock(return_value=(0, ""))
with patch("lib.contributor.config") as mock_config:
mock_config.CONTRIBUTOR_TIER_RULES = {
"veteran": {"claims_merged": 50, "min_days_since_first": 90, "challenges_survived": 5},
"contributor": {"claims_merged": 10},
}
await record_contributor_attribution(conn, 100, "extract/test", git_fn)
asyncio.run(run())
row = conn.execute("SELECT commit_type FROM prs WHERE number = 100").fetchone()
assert row["commit_type"] == "challenge"
def test_record_no_diff_returns_early():
conn = _make_attribution_db()
async def run():
with patch("lib.contributor.get_pr_diff", new_callable=AsyncMock, return_value=None):
git_fn = AsyncMock()
await record_contributor_attribution(conn, 100, "extract/test", git_fn)
git_fn.assert_not_called()
asyncio.run(run())