feat(v1.2.0): retry API, dernier commit, tri, coloration, export JSON
- client.py: _get_with_retry (max 2 retries, backoff lineaire), get_latest_commit - collector.py: champ last_commit_date dans RepoData - display.py: colonne "Dernier commit", _sort_repos (name/issues/release/activity), _colorize_milestone_due (rouge/jaune/vert selon echeance) - cli.py: options --sort/-s et --format/-f (table/json) - exporter.py: nouveau module, repos_to_dicts + export_json - 88 tests (35 nouveaux), ruff clean fixes #8, fixes #7, fixes #10, fixes #9, fixes #6 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,9 @@ from io import StringIO
|
||||
from rich.console import Console
|
||||
|
||||
from gitea_dashboard.collector import RepoData
|
||||
from gitea_dashboard.display import render_dashboard
|
||||
from gitea_dashboard.display import (
|
||||
render_dashboard,
|
||||
)
|
||||
|
||||
|
||||
def _make_console():
|
||||
@@ -28,6 +30,7 @@ def _make_repo(
|
||||
is_mirror=False,
|
||||
latest_release=None,
|
||||
milestones=None,
|
||||
last_commit_date=None,
|
||||
):
|
||||
"""Build a RepoData for testing."""
|
||||
return RepoData(
|
||||
@@ -40,6 +43,7 @@ def _make_repo(
|
||||
is_mirror=is_mirror,
|
||||
latest_release=latest_release,
|
||||
milestones=milestones if milestones is not None else [],
|
||||
last_commit_date=last_commit_date,
|
||||
)
|
||||
|
||||
|
||||
@@ -142,6 +146,41 @@ class TestRenderDashboardTable:
|
||||
assert "repo-beta" in output
|
||||
|
||||
|
||||
class TestRenderDashboardLastCommit:
|
||||
"""Test the last commit column rendering."""
|
||||
|
||||
def test_last_commit_column_displayed(self):
|
||||
"""Column 'Dernier commit' appears in the table."""
|
||||
console, buf = _make_console()
|
||||
repos = [_make_repo(name="projet", last_commit_date="2026-03-10T14:30:00Z")]
|
||||
|
||||
render_dashboard(repos, console=console)
|
||||
output = buf.getvalue()
|
||||
|
||||
assert "Dernier commit" in output
|
||||
|
||||
def test_last_commit_shows_relative_date(self):
|
||||
"""Last commit date is shown as relative date."""
|
||||
console, buf = _make_console()
|
||||
repos = [_make_repo(name="projet", last_commit_date="2026-03-10T14:30:00Z")]
|
||||
|
||||
render_dashboard(repos, console=console)
|
||||
output = buf.getvalue()
|
||||
|
||||
# Should show some relative date (il y a Xj, etc.)
|
||||
assert "il y a" in output or "aujourd'hui" in output
|
||||
|
||||
def test_last_commit_none_shows_dash(self):
|
||||
"""Repo without commit shows dash."""
|
||||
console, buf = _make_console()
|
||||
repos = [_make_repo(name="vide", last_commit_date=None)]
|
||||
|
||||
render_dashboard(repos, console=console)
|
||||
output = buf.getvalue()
|
||||
|
||||
assert "\u2014" in output or "—" in output
|
||||
|
||||
|
||||
class TestRenderDashboardMilestones:
|
||||
"""Test the milestones section rendering."""
|
||||
|
||||
@@ -214,3 +253,95 @@ class TestRenderDashboardEmpty:
|
||||
output = buf.getvalue()
|
||||
|
||||
assert "Aucun repo" in output
|
||||
|
||||
|
||||
class TestColorizeMilestoneDue:
|
||||
"""Test _colorize_milestone_due function."""
|
||||
|
||||
def test_overdue(self):
|
||||
"""Past due date returns 'red'."""
|
||||
from gitea_dashboard.display import _colorize_milestone_due
|
||||
|
||||
assert _colorize_milestone_due("2020-01-01T00:00:00Z") == "red"
|
||||
|
||||
def test_soon(self):
|
||||
"""Due date within 7 days returns 'yellow'."""
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from gitea_dashboard.display import _colorize_milestone_due
|
||||
|
||||
soon = datetime.now(timezone.utc) + timedelta(days=3)
|
||||
assert _colorize_milestone_due(soon.isoformat()) == "yellow"
|
||||
|
||||
def test_ok(self):
|
||||
"""Due date more than 7 days away returns 'green'."""
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from gitea_dashboard.display import _colorize_milestone_due
|
||||
|
||||
future = datetime.now(timezone.utc) + timedelta(days=15)
|
||||
assert _colorize_milestone_due(future.isoformat()) == "green"
|
||||
|
||||
def test_no_due(self):
|
||||
"""No due date returns empty string."""
|
||||
from gitea_dashboard.display import _colorize_milestone_due
|
||||
|
||||
assert _colorize_milestone_due(None) == ""
|
||||
|
||||
|
||||
class TestSortRepos:
|
||||
"""Test _sort_repos function."""
|
||||
|
||||
def test_sort_by_name(self):
|
||||
"""Sorts alphabetically by name (case-insensitive)."""
|
||||
from gitea_dashboard.display import _sort_repos
|
||||
|
||||
repos = [
|
||||
_make_repo(name="Charlie"),
|
||||
_make_repo(name="alpha"),
|
||||
_make_repo(name="Bravo"),
|
||||
]
|
||||
result = _sort_repos(repos, "name")
|
||||
assert [r.name for r in result] == ["alpha", "Bravo", "Charlie"]
|
||||
|
||||
def test_sort_by_issues(self):
|
||||
"""Sorts by issues count descending."""
|
||||
from gitea_dashboard.display import _sort_repos
|
||||
|
||||
repos = [
|
||||
_make_repo(name="low", open_issues=1),
|
||||
_make_repo(name="high", open_issues=10),
|
||||
_make_repo(name="mid", open_issues=5),
|
||||
]
|
||||
result = _sort_repos(repos, "issues")
|
||||
assert [r.name for r in result] == ["high", "mid", "low"]
|
||||
|
||||
def test_sort_by_release(self):
|
||||
"""Sorts by release date descending; repos without release last."""
|
||||
from gitea_dashboard.display import _sort_repos
|
||||
|
||||
repos = [
|
||||
_make_repo(name="no-rel", latest_release=None),
|
||||
_make_repo(
|
||||
name="old",
|
||||
latest_release={"tag_name": "v1.0", "published_at": "2025-01-01T00:00:00Z"},
|
||||
),
|
||||
_make_repo(
|
||||
name="new",
|
||||
latest_release={"tag_name": "v2.0", "published_at": "2026-03-01T00:00:00Z"},
|
||||
),
|
||||
]
|
||||
result = _sort_repos(repos, "release")
|
||||
assert [r.name for r in result] == ["new", "old", "no-rel"]
|
||||
|
||||
def test_sort_by_activity(self):
|
||||
"""Sorts by last commit date descending; repos without commit last."""
|
||||
from gitea_dashboard.display import _sort_repos
|
||||
|
||||
repos = [
|
||||
_make_repo(name="inactive", last_commit_date=None),
|
||||
_make_repo(name="old-commit", last_commit_date="2025-06-01T00:00:00Z"),
|
||||
_make_repo(name="recent", last_commit_date="2026-03-10T00:00:00Z"),
|
||||
]
|
||||
result = _sort_repos(repos, "activity")
|
||||
assert [r.name for r in result] == ["recent", "old-commit", "inactive"]
|
||||
|
||||
Reference in New Issue
Block a user