tests/installer/test_gitauth.py
Rama main · réplica de lectura del repositorio original.
"""Regression : les clones de repos MCP prives echouaient avec 'Host key
verification failed' car mcps.py ne beneficiait pas du fallback SSH->PAT
deja present pour le clone du repo Lyra principal. gitauth.resolve_repo_url()
centralise cette logique pour les deux (et la cache pour ne demander le
token qu'une seule fois par run, meme avec plusieurs MCPs prives).
Deuxieme regression : un token laisse vide etait auparavant accepte
silencieusement (le repo echouait sans que l'utilisateur ait fait un choix
delibere). Desormais un vide ne fait jamais avancer silencieusement : soit
l'utilisateur retape un vrai token, soit il confirme explicitement vouloir
continuer sans (et sait que ca ne marchera pas)."""
from unittest.mock import patch
import pytest
from installer.core import gitauth
class _FakeBroker:
def __init__(self, tokens=(), confirms=()):
self._tokens = list(tokens)
self._confirms = list(confirms)
self.prompts = []
self.confirm_prompts = []
def input(self, prompt, default=""):
self.prompts.append(prompt)
return self._tokens.pop(0) if self._tokens else ""
def confirm(self, prompt, default=True):
self.confirm_prompts.append(prompt)
return self._confirms.pop(0) if self._confirms else default
class _FakeCtx:
def __init__(self, broker):
self.broker = broker
self.events = []
def emit(self, event):
self.events.append(event)
@pytest.fixture(autouse=True)
def _clear_pat_cache():
gitauth._pat_cache.clear()
yield
gitauth._pat_cache.clear()
def test_https_url_passthrough_sans_verification():
ctx = _FakeCtx(_FakeBroker())
with patch("installer.core.gitauth.ssh_github_ok") as mock_ssh:
url = gitauth.resolve_repo_url(ctx, "https://github.com/marouabah/lyra.git")
assert url == "https://github.com/marouabah/lyra.git"
assert not mock_ssh.called
def test_ssh_ok_garde_url_ssh():
ctx = _FakeCtx(_FakeBroker())
with patch("installer.core.gitauth.ssh_github_ok", return_value=True):
url = gitauth.resolve_repo_url(ctx, "git@github.com:marouabah/pylips-mcp.git")
assert url == "git@github.com:marouabah/pylips-mcp.git"
assert not ctx.events
def test_ssh_ko_bascule_en_https_pat():
ctx = _FakeCtx(_FakeBroker(tokens=["ghp_test123"]))
with patch("installer.core.gitauth.ssh_github_ok", return_value=False):
url = gitauth.resolve_repo_url(ctx, "git@github.com:marouabah/pylips-mcp.git")
assert url == "https://ghp_test123@github.com/marouabah/pylips-mcp.git"
assert ctx.broker.prompts, "le PAT aurait du etre demande"
assert any("github.com/settings/tokens" in e.line for e in ctx.events), \
"l'explication de comment recuperer le token doit etre affichee"
def test_pat_reutilise_pour_plusieurs_repos_prives():
"""Le bug reel : avant le fix, chaque MCP prive (pylips-mcp,
fedora-agents...) declenchait sa propre course a l'echec SSH. Avec le
cache, un seul prompt PAT pour tout le run."""
ctx = _FakeCtx(_FakeBroker(tokens=["ghp_test123"]))
with patch("installer.core.gitauth.ssh_github_ok", return_value=False):
url1 = gitauth.resolve_repo_url(ctx, "git@github.com:marouabah/pylips-mcp.git")
url2 = gitauth.resolve_repo_url(ctx, "git@github.com:marouabah/fedora-agents.git")
assert url1 == "https://ghp_test123@github.com/marouabah/pylips-mcp.git"
assert url2 == "https://ghp_test123@github.com/marouabah/fedora-agents.git"
assert len(ctx.broker.prompts) == 1, "le PAT ne doit etre demande qu'une fois"
def test_token_vide_ne_passe_jamais_silencieusement():
"""Regression : laisser le champ vide ne doit plus faire echouer le
clone sans que l'utilisateur ait confirme explicitement. Ici il refuse
de continuer sans (confirm=False) -> on redemande le token."""
ctx = _FakeCtx(_FakeBroker(tokens=["", "ghp_apres_relance"],
confirms=[False]))
with patch("installer.core.gitauth.ssh_github_ok", return_value=False):
url = gitauth.resolve_repo_url(ctx, "git@github.com:marouabah/pylips-mcp.git")
assert url == "https://ghp_apres_relance@github.com/marouabah/pylips-mcp.git"
assert len(ctx.broker.prompts) == 2, "le token vide doit redemander, pas abandonner"
assert ctx.broker.confirm_prompts, "une confirmation explicite doit etre demandee"
def test_token_vide_avec_confirmation_explicite_leve_erreur():
"""Si l'utilisateur confirme explicitement vouloir continuer sans
token, LA on abandonne ce depot precis (gere en amont : fatal pour le
clone principal, optionnel pour un MCP)."""
ctx = _FakeCtx(_FakeBroker(tokens=[""], confirms=[True]))
with patch("installer.core.gitauth.ssh_github_ok", return_value=False):
with pytest.raises(RuntimeError, match="acces au repo prive impossible"):
gitauth.resolve_repo_url(ctx, "git@github.com:marouabah/pylips-mcp.git")