From 51336a5b28cde775a8adaf2ce9b2266f0677665d Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Mon, 23 Mar 2026 14:44:45 -0400 Subject: [PATCH] WIP: Gemini Code progress on #1139 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automated salvage commit — agent session ended (exit 124). Work in progress, may need continuation. --- .../dashboard/services/test_agent_metrics.py | 21 ++ .../services/test_get_period_bounds.py | 23 ++ .../services/test_scorecard_service.py | 287 ++++++++++++++++++ .../services/test_scorecard_summary.py | 55 ++++ 4 files changed, 386 insertions(+) create mode 100644 tests/dashboard/services/test_agent_metrics.py create mode 100644 tests/dashboard/services/test_get_period_bounds.py create mode 100644 tests/dashboard/services/test_scorecard_service.py create mode 100644 tests/dashboard/services/test_scorecard_summary.py diff --git a/tests/dashboard/services/test_agent_metrics.py b/tests/dashboard/services/test_agent_metrics.py new file mode 100644 index 00000000..c5f55877 --- /dev/null +++ b/tests/dashboard/services/test_agent_metrics.py @@ -0,0 +1,21 @@ + +import unittest +from src.dashboard.services.scorecard_service import ( + AgentMetrics, +) + +class TestAgentMetrics(unittest.TestCase): + def test_agent_metrics_pr_merge_rate(self): + metrics = AgentMetrics(agent_id="test_agent") + self.assertEqual(metrics.pr_merge_rate, 0.0) + + metrics.prs_opened = {1, 2, 3, 4} + metrics.prs_merged = {1, 3} + self.assertEqual(metrics.pr_merge_rate, 0.5) + + metrics.prs_opened = {1, 2, 3, 4, 5} + metrics.prs_merged = {1, 2, 3, 4, 5} + self.assertEqual(metrics.pr_merge_rate, 1.0) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/dashboard/services/test_get_period_bounds.py b/tests/dashboard/services/test_get_period_bounds.py new file mode 100644 index 00000000..795359ea --- /dev/null +++ b/tests/dashboard/services/test_get_period_bounds.py @@ -0,0 +1,23 @@ + +import unittest +from datetime import datetime, UTC +from src.dashboard.services.scorecard_service import ( + PeriodType, + _get_period_bounds, +) + +class TestGetPeriodBounds(unittest.TestCase): + def test_get_period_bounds(self): + # Test daily period + reference_date = datetime(2024, 1, 10, 12, 0, 0, tzinfo=UTC) + start, end = _get_period_bounds(PeriodType.daily, reference_date) + self.assertEqual(start, datetime(2024, 1, 9, 0, 0, 0, tzinfo=UTC)) + self.assertEqual(end, datetime(2024, 1, 10, 0, 0, 0, tzinfo=UTC)) + + # Test weekly period + start, end = _get_period_bounds(PeriodType.weekly, reference_date) + self.assertEqual(start, datetime(2024, 1, 3, 0, 0, 0, tzinfo=UTC)) + self.assertEqual(end, datetime(2024, 1, 10, 0, 0, 0, tzinfo=UTC)) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/dashboard/services/test_scorecard_service.py b/tests/dashboard/services/test_scorecard_service.py new file mode 100644 index 00000000..da4f738f --- /dev/null +++ b/tests/dashboard/services/test_scorecard_service.py @@ -0,0 +1,287 @@ + +import unittest +from unittest.mock import MagicMock, patch +from datetime import datetime, UTC, timedelta + +from src.dashboard.services.scorecard_service import ( + AgentMetrics, + PeriodType, + ScorecardSummary, + _get_period_bounds, + _extract_actor_from_event, + _is_tracked_agent, + _aggregate_metrics, + _generate_narrative_bullets, + _detect_patterns, + generate_all_scorecards, + generate_scorecard, + get_tracked_agents, +) +from infrastructure.events.bus import Event + + +class TestScorecardService(unittest.TestCase): + def test_get_tracked_agents(self): + self.assertEqual( + get_tracked_agents(), ["claude", "gemini", "hermes", "kimi", "manus"] + ) + + def test_agent_metrics_pr_merge_rate(self): + metrics = AgentMetrics(agent_id="test_agent") + self.assertEqual(metrics.pr_merge_rate, 0.0) + + metrics.prs_opened = {1, 2, 3, 4} + metrics.prs_merged = {1, 3} + self.assertEqual(metrics.pr_merge_rate, 0.5) + + metrics.prs_opened = {1, 2, 3, 4, 5} + metrics.prs_merged = {1, 2, 3, 4, 5} + self.assertEqual(metrics.pr_merge_rate, 1.0) + + def test_scorecard_summary_to_dict(self): + metrics = AgentMetrics( + agent_id="test_agent", + issues_touched={1, 2}, + prs_opened={3, 4}, + prs_merged={3}, + tests_affected={"test1.py", "test2.py"}, + tokens_earned=100, + tokens_spent=50, + commits=10, + comments=5, + ) + summary = ScorecardSummary( + agent_id="test_agent", + period_type=PeriodType.daily, + period_start=datetime(2024, 1, 1, tzinfo=UTC), + period_end=datetime(2024, 1, 2, tzinfo=UTC), + metrics=metrics, + narrative_bullets=["test bullet"], + patterns=["test pattern"], + ) + expected_dict = { + "agent_id": "test_agent", + "period_type": "daily", + "period_start": "2024-01-01T00:00:00+00:00", + "period_end": "2024-01-02T00:00:00+00:00", + "metrics": { + "issues_touched": 2, + "prs_opened": 2, + "prs_merged": 1, + "pr_merge_rate": 0.5, + "tests_affected": 2, + "commits": 10, + "comments": 5, + "tokens_earned": 100, + "tokens_spent": 50, + "token_net": 50, + }, + "narrative_bullets": ["test bullet"], + "patterns": ["test pattern"], + } + self.assertEqual(summary.to_dict(), expected_dict) + + def test_get_period_bounds(self): + # Test daily period + reference_date = datetime(2024, 1, 10, 12, 0, 0, tzinfo=UTC) + start, end = _get_period_bounds(PeriodType.daily, reference_date) + self.assertEqual(start, datetime(2024, 1, 9, 0, 0, 0, tzinfo=UTC)) + self.assertEqual(end, datetime(2024, 1, 10, 0, 0, 0, tzinfo=UTC)) + + # Test weekly period + start, end = _get_period_bounds(PeriodType.weekly, reference_date) + self.assertEqual(start, datetime(2024, 1, 3, 0, 0, 0, tzinfo=UTC)) + self.assertEqual(end, datetime(2024, 1, 10, 0, 0, 0, tzinfo=UTC)) + + def test_extract_actor_from_event(self): + event_with_actor = Event( + id="1", + source="test", + timestamp="2024-01-01T00:00:00Z", + type="test.event", + data={"actor": "gemini"}, + ) + self.assertEqual(_extract_actor_from_event(event_with_actor), "gemini") + + event_with_agent_id = Event( + id="2", + source="test", + timestamp="2024-01-01T00:00:00Z", + type="test.event", + data={"agent_id": "claude"}, + ) + self.assertEqual(_extract_actor_from_event(event_with_agent_id), "claude") + + event_with_source = Event( + id="3", + source="hermes", + timestamp="2024-01-01T00:00:00Z", + type="test.event", + data={}, + ) + self.assertEqual(_extract_actor_from_event(event_with_source), "hermes") + + def test_is_tracked_agent(self): + self.assertTrue(_is_tracked_agent("gemini")) + self.assertTrue(_is_tracked_agent("GEMINI")) + self.assertFalse(_is_tracked_agent("untracked_agent")) + self.assertFalse(_is_tracked_agent("test_user")) + + def test_aggregate_metrics(self): + events = [ + Event( + id="1", + source="gemini", + timestamp="ts", + type="gitea.push", + data={"num_commits": 2}, + ), + Event( + id="2", + source="gemini", + timestamp="ts", + type="gitea.issue.opened", + data={"issue_number": 101}, + ), + Event( + id="3", + source="gemini", + timestamp="ts", + type="gitea.issue.comment", + data={"issue_number": 101}, + ), + Event( + id="4", + source="gemini", + timestamp="ts", + type="gitea.pull_request", + data={"pr_number": 202, "action": "opened"}, + ), + Event( + id="5", + source="gemini", + timestamp="ts", + type="gitea.pull_request", + data={"pr_number": 202, "action": "closed", "merged": True}, + ), + Event( + id="6", + source="gemini", + timestamp="ts", + type="agent.task.completed", + data={"tests_affected": ["test_a.py"], "token_reward": 50}, + ), + Event( + id="7", + source="claude", + timestamp="ts", + type="test.execution", + data={"test_files": ["test_b.py"]}, + ), + ] + + metrics_by_agent = _aggregate_metrics(events) + self.assertIn("gemini", metrics_by_agent) + self.assertIn("claude", metrics_by_agent) + + gemini_metrics = metrics_by_agent["gemini"] + self.assertEqual(gemini_metrics.commits, 2) + self.assertEqual(gemini_metrics.issues_touched, {101, 202}) + self.assertEqual(gemini_metrics.prs_opened, {202}) + self.assertEqual(gemini_metrics.prs_merged, {202}) + self.assertEqual(gemini_metrics.tests_affected, {"test_a.py"}) + self.assertEqual(gemini_metrics.tokens_earned, 50) + self.assertEqual(gemini_metrics.comments, 1) + + claude_metrics = metrics_by_agent["claude"] + self.assertEqual(claude_metrics.tests_affected, {"test_b.py"}) + + def test_generate_narrative_bullets(self): + metrics = AgentMetrics("gemini", commits=5, prs_opened={1}, prs_merged={1}) + bullets = _generate_narrative_bullets(metrics, PeriodType.daily) + self.assertIn("Active across 5 commits, 1 PR opened, 1 PR merged this day.", bullets) + + metrics = AgentMetrics("gemini", tokens_earned=100, tokens_spent=20) + bullets = _generate_narrative_bullets(metrics, PeriodType.weekly) + self.assertIn("Net earned 80 tokens (100 earned, 20 spent).", bullets) + + metrics = AgentMetrics("gemini") + bullets = _generate_narrative_bullets(metrics, PeriodType.daily) + self.assertEqual(bullets, ["No recorded activity this day."]) + + def test_detect_patterns(self): + # High merge rate + metrics = AgentMetrics("gemini", prs_opened={1, 2, 3}, prs_merged={1, 2, 3}) + patterns = _detect_patterns(metrics) + self.assertIn("High merge rate with few failures — code quality focus.", patterns) + + # High commit volume without PRs + metrics = AgentMetrics("gemini", commits=11) + patterns = _detect_patterns(metrics) + self.assertIn("High commit volume without PRs — working directly on main?", patterns) + + # Strong token accumulation + metrics = AgentMetrics("gemini", tokens_earned=150, tokens_spent=20) + patterns = _detect_patterns(metrics) + self.assertIn("Strong token accumulation — high value delivery.", patterns) + + @patch("src.dashboard.services.scorecard_service._collect_events_for_period") + @patch("src.dashboard.services.scorecard_service._query_token_transactions") + def test_generate_scorecard( + self, mock_query_tokens, mock_collect_events + ): + mock_collect_events.return_value = [ + Event( + id="1", + source="gemini", + timestamp="ts", + type="gitea.push", + data={"num_commits": 2}, + ) + ] + mock_query_tokens.return_value = (100, 50) + + scorecard = generate_scorecard("gemini", PeriodType.daily) + self.assertIsNotNone(scorecard) + self.assertEqual(scorecard.agent_id, "gemini") + self.assertEqual(scorecard.metrics.commits, 2) + self.assertEqual(scorecard.metrics.tokens_earned, 100) + self.assertEqual(scorecard.metrics.tokens_spent, 50) + + @patch("src.dashboard.services.scorecard_service._collect_events_for_period") + @patch("src.dashboard.services.scorecard_service._query_token_transactions") + def test_generate_all_scorecards( + self, mock_query_tokens, mock_collect_events + ): + mock_collect_events.return_value = [ + Event( + id="1", + source="gemini", + timestamp="ts", + type="gitea.push", + data={"num_commits": 2}, + ), + Event( + id="2", + source="claude", + timestamp="ts", + type="gitea.issue.comment", + data={"issue_number": 101}, + ), + ] + mock_query_tokens.side_effect = [(100, 50), (200, 75), (0, 0), (0, 0), (0,0)] + + scorecards = generate_all_scorecards(PeriodType.daily) + self.assertEqual(len(scorecards), 5) + + gemini_scorecard = next(s for s in scorecards if s.agent_id == "gemini") + self.assertEqual(gemini_scorecard.metrics.commits, 2) + self.assertEqual(gemini_scorecard.metrics.tokens_earned, 100) + + claude_scorecard = next(s for s in scorecards if s.agent_id == "claude") + self.assertEqual(claude_scorecard.metrics.comments, 1) + self.assertEqual(claude_scorecard.metrics.tokens_earned, 200) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/dashboard/services/test_scorecard_summary.py b/tests/dashboard/services/test_scorecard_summary.py new file mode 100644 index 00000000..d734b966 --- /dev/null +++ b/tests/dashboard/services/test_scorecard_summary.py @@ -0,0 +1,55 @@ + +import unittest +from datetime import datetime, UTC +from src.dashboard.services.scorecard_service import ( + AgentMetrics, + PeriodType, + ScorecardSummary, +) + +class TestScorecardSummary(unittest.TestCase): + def test_scorecard_summary_to_dict(self): + metrics = AgentMetrics( + agent_id="test_agent", + issues_touched={1, 2}, + prs_opened={3, 4}, + prs_merged={3}, + tests_affected={"test1.py", "test2.py"}, + tokens_earned=100, + tokens_spent=50, + commits=10, + comments=5, + ) + summary = ScorecardSummary( + agent_id="test_agent", + period_type=PeriodType.daily, + period_start=datetime(2024, 1, 1, tzinfo=UTC), + period_end=datetime(2024, 1, 2, tzinfo=UTC), + metrics=metrics, + narrative_bullets=["test bullet"], + patterns=["test pattern"], + ) + expected_dict = { + "agent_id": "test_agent", + "period_type": "daily", + "period_start": "2024-01-01T00:00:00+00:00", + "period_end": "2024-01-02T00:00:00+00:00", + "metrics": { + "issues_touched": 2, + "prs_opened": 2, + "prs_merged": 1, + "pr_merge_rate": 0.5, + "tests_affected": 2, + "commits": 10, + "comments": 5, + "tokens_earned": 100, + "tokens_spent": 50, + "token_net": 50, + }, + "narrative_bullets": ["test bullet"], + "patterns": ["test pattern"], + } + self.assertEqual(summary.to_dict(), expected_dict) + +if __name__ == "__main__": + unittest.main()