"""Tests unitaires pour le module utils.gitea. Ces tests vérifient les fonctions d'interaction avec l'API Gitea. """ import base64 from datetime import datetime, timezone from unittest.mock import Mock, mock_open, patch import pytest import requests from utils.gitea import ( charger_arborescence_fiches, charger_instructions_depuis_gitea, charger_schema_depuis_gitea, lire_fichier_local, recuperer_date_dernier_commit, ) class TestLireFichierLocal: """Tests pour la fonction lire_fichier_local.""" def test_lecture_fichier_utf8(self, tmp_path): """Test la lecture d'un fichier UTF-8 standard.""" fichier = tmp_path / "test.txt" contenu_attendu = "Contenu de test avec caractères spéciaux: éàç" fichier.write_text(contenu_attendu, encoding="utf-8") resultat = lire_fichier_local(str(fichier)) assert resultat == contenu_attendu def test_lecture_fichier_avec_accents(self, tmp_path): """Test la lecture d'un fichier avec caractères accentués.""" fichier = tmp_path / "accents.txt" contenu = "Voici des accents: é, è, à, ç, ù" fichier.write_text(contenu, encoding="utf-8") resultat = lire_fichier_local(str(fichier)) assert resultat == contenu def test_lecture_fichier_vide(self, tmp_path): """Test la lecture d'un fichier vide.""" fichier = tmp_path / "vide.txt" fichier.write_text("", encoding="utf-8") resultat = lire_fichier_local(str(fichier)) assert resultat == "" def test_fichier_inexistant(self): """Test la gestion d'un fichier inexistant.""" with pytest.raises(FileNotFoundError): lire_fichier_local("fichier_inexistant.txt") class TestRecupererDateDernierCommit: """Tests pour la fonction recuperer_date_dernier_commit.""" @patch("utils.gitea.requests.get") def test_recuperation_date_commit(self, mock_get): """Test la récupération de la date du dernier commit.""" # Mock de la réponse Gitea mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = [ { "commit": { "author": { "date": "2025-01-15T10:30:00Z" } } } ] mock_get.return_value = mock_response resultat = recuperer_date_dernier_commit("https://gitea.example.com/api/v1/repos/org/repo/commits") assert resultat is not None assert isinstance(resultat, datetime) assert resultat.year == 2025 assert resultat.month == 1 assert resultat.day == 15 @patch("utils.gitea.requests.get") def test_aucun_commit(self, mock_get): """Test avec un dépôt sans commits.""" mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = [] mock_get.return_value = mock_response resultat = recuperer_date_dernier_commit("https://gitea.example.com/api/v1/repos/org/repo/commits") assert resultat is None @patch("utils.gitea.requests.get") def test_erreur_requete(self, mock_get): """Test la gestion d'une erreur de requête.""" mock_get.side_effect = requests.RequestException("Network error") resultat = recuperer_date_dernier_commit("https://gitea.example.com/api/v1/repos/org/repo/commits") assert resultat is None class TestChargerInstructionsDepuisGitea: """Tests pour la fonction charger_instructions_depuis_gitea.""" @patch("utils.gitea.requests.get") @patch("utils.gitea.recuperer_date_dernier_commit") def test_telechargement_fichier_inexistant(self, mock_date_commit, mock_get): """Test le téléchargement quand le fichier local n'existe pas.""" # Commit distant disponible mock_date_commit.return_value = datetime(2025, 1, 15, tzinfo=timezone.utc) # Contenu encodé en base64 contenu_md = "# Instructions\nCeci est un test" contenu_base64 = base64.b64encode(contenu_md.encode("utf-8")).decode("utf-8") mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"content": contenu_base64} mock_get.return_value = mock_response m_open = mock_open() with patch("pathlib.Path.exists", return_value=False), \ patch("pathlib.Path.open", m_open): resultat = charger_instructions_depuis_gitea("Instructions.md") # Vérifie que le fichier a été écrit m_open.assert_called_once_with("w", encoding="utf-8") assert resultat == contenu_md @patch("utils.gitea.requests.get") @patch("utils.gitea.recuperer_date_dernier_commit") def test_utilisation_cache_local_recent(self, mock_date_commit, mock_get): """Test l'utilisation du cache local si plus récent.""" # Date fichier local plus récent local_time = datetime(2025, 1, 20, tzinfo=timezone.utc) mock_stat = Mock() mock_stat.st_mtime = local_time.timestamp() # Date commit distant plus ancien mock_date_commit.return_value = datetime(2025, 1, 15, tzinfo=timezone.utc) with patch("pathlib.Path.exists", return_value=True), \ patch("pathlib.Path.stat", return_value=mock_stat), \ patch("pathlib.Path.open", mock_open(read_data="# Local Instructions")): resultat = charger_instructions_depuis_gitea("Instructions.md") # Doit lire le fichier local sans appeler l'API assert mock_get.call_count == 0 assert resultat == "# Local Instructions" @patch("utils.gitea.requests.get") @patch("utils.gitea.recuperer_date_dernier_commit") def test_erreur_reseau_avec_cache(self, mock_date_commit, _mock_get): """Test le fallback sur le cache en cas d'erreur réseau.""" mock_date_commit.side_effect = requests.RequestException("Network error") with patch("pathlib.Path.exists", return_value=True), \ patch("pathlib.Path.open", mock_open(read_data="# Cached content")): resultat = charger_instructions_depuis_gitea("Instructions.md") assert resultat == "# Cached content" @patch("utils.gitea.requests.get") @patch("utils.gitea.recuperer_date_dernier_commit") def test_erreur_reseau_sans_cache(self, mock_date_commit, _mock_get): """Test le retour None si erreur et pas de cache.""" mock_date_commit.side_effect = requests.RequestException("Network error") with patch("pathlib.Path.exists", return_value=False): resultat = charger_instructions_depuis_gitea("Instructions.md") assert resultat is None class TestChargerSchemaDepuisGitea: """Tests pour la fonction charger_schema_depuis_gitea.""" @patch("utils.gitea.requests.get") @patch("utils.gitea.recuperer_date_dernier_commit") def test_telechargement_schema_file(self, mock_date_commit, mock_get): """Test le téléchargement d'un fichier schema depuis Gitea.""" # Commit distant disponible mock_date_commit.return_value = datetime(2025, 1, 15, tzinfo=timezone.utc) # Contenu DOT encodé en base64 contenu_dot = "digraph G { A -> B; }" contenu_base64 = base64.b64encode(contenu_dot.encode("utf-8")).decode("utf-8") mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"content": contenu_base64} mock_get.return_value = mock_response m_open = mock_open() with patch("pathlib.Path.exists", return_value=False), \ patch("pathlib.Path.open", m_open): resultat = charger_schema_depuis_gitea("test_schema.txt") # Vérifie que le fichier a été écrit assert m_open.called assert resultat == "OK" @patch("utils.gitea.requests.get") @patch("utils.gitea.recuperer_date_dernier_commit") def test_cache_schema_file(self, mock_date_commit, mock_get): """Test l'utilisation du cache pour le fichier schema.""" # Date fichier local plus récent local_time = datetime(2025, 1, 20, tzinfo=timezone.utc) mock_stat = Mock() mock_stat.st_mtime = local_time.timestamp() # Date commit distant plus ancien mock_date_commit.return_value = datetime(2025, 1, 15, tzinfo=timezone.utc) # Mock response pour le premier appel (get file info) contenu_base64 = base64.b64encode(b"digraph G { cached }").decode("utf-8") mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"content": contenu_base64} mock_get.return_value = mock_response with patch("pathlib.Path.exists", return_value=True), \ patch("pathlib.Path.stat", return_value=mock_stat): resultat = charger_schema_depuis_gitea("test_schema.txt") # Doit retourner OK sans réécrire (fichier déjà à jour) assert resultat == "OK" @patch("utils.gitea.requests.get") def test_erreur_chargement_schema(self, mock_get): """Test la gestion d'erreur lors du chargement du schema.""" mock_get.side_effect = requests.RequestException("Network error") resultat = charger_schema_depuis_gitea("test_schema.txt") assert resultat is None class TestChargerArborescenceFiches: """Tests pour la fonction charger_arborescence_fiches.""" @patch("utils.gitea.requests.get") def test_arborescence_vide(self, mock_get): """Test avec un dépôt sans dossiers.""" mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = [] mock_get.return_value = mock_response resultat = charger_arborescence_fiches() assert resultat == {} @patch("utils.gitea.requests.get") def test_arborescence_avec_dossiers(self, mock_get): """Test avec des dossiers contenant des fiches.""" # Mock réponse pour la liste des dossiers mock_response_dossiers = Mock() mock_response_dossiers.status_code = 200 mock_response_dossiers.json.return_value = [ {"name": "Composants", "type": "dir", "url": "https://gitea.example.com/api/v1/repos/org/repo/contents/Documents/Composants"} ] # Mock réponse pour le contenu du dossier mock_response_fichiers = Mock() mock_response_fichiers.status_code = 200 mock_response_fichiers.json.return_value = [ {"name": "Processeur.md", "download_url": "https://gitea.example.com/download/Processeur.md"}, {"name": "Memoire.md", "download_url": "https://gitea.example.com/download/Memoire.md"} ] # Configure les appels successifs mock_get.side_effect = [mock_response_dossiers, mock_response_fichiers] resultat = charger_arborescence_fiches() assert "Composants" in resultat assert len(resultat["Composants"]) == 2 assert resultat["Composants"][0]["nom"] == "Memoire.md" # Trié alphabétiquement assert resultat["Composants"][1]["nom"] == "Processeur.md" @patch("utils.gitea.requests.get") def test_erreur_requete(self, mock_get): """Test la gestion d'erreur lors de la requête.""" mock_get.side_effect = requests.RequestException("Network error") resultat = charger_arborescence_fiches() assert resultat == {}