"""Tests unitaires pour le module utils.gitea. Ces tests vérifient les fonctions d'interaction avec l'API Gitea. """ import base64 import os from datetime import datetime, timezone from unittest.mock import MagicMock, 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") @patch("os.path.exists") @patch("os.path.getmtime") def test_telechargement_fichier_inexistant(self, mock_getmtime, mock_exists, mock_date_commit, mock_get): """Test le téléchargement quand le fichier local n'existe pas.""" # Fichier local n'existe pas mock_exists.return_value = False # 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 with patch("builtins.open", mock_open()) as mock_file: resultat = charger_instructions_depuis_gitea("Instructions.md") # Vérifie que le fichier a été écrit mock_file.assert_called_once_with("Instructions.md", "w", encoding="utf-8") assert resultat == contenu_md @patch("utils.gitea.requests.get") @patch("utils.gitea.recuperer_date_dernier_commit") @patch("os.path.exists") @patch("os.path.getmtime") @patch("builtins.open", new_callable=mock_open, read_data="# Local Instructions") def test_utilisation_cache_local_recent(self, mock_file, mock_getmtime, mock_exists, mock_date_commit, mock_get): """Test l'utilisation du cache local si plus récent.""" # Fichier local existe mock_exists.return_value = True # Date fichier local plus récent local_time = datetime(2025, 1, 20, tzinfo=timezone.utc) mock_getmtime.return_value = local_time.timestamp() # Date commit distant plus ancien mock_date_commit.return_value = datetime(2025, 1, 15, tzinfo=timezone.utc) 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("os.path.exists", return_value=True): with patch("builtins.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") @patch("os.path.exists") def test_erreur_reseau_sans_cache(self, mock_exists, mock_date_commit, mock_get): """Test le retour None si erreur et pas de cache.""" mock_exists.return_value = False mock_date_commit.side_effect = requests.RequestException("Network error") 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") @patch("os.path.exists") @patch("os.path.getmtime") def test_telechargement_schema_file(self, mock_getmtime, mock_exists, mock_date_commit, mock_get): """Test le téléchargement d'un fichier schema depuis Gitea.""" # Fichier local n'existe pas mock_exists.return_value = False # 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 with patch("builtins.open", mock_open()) as mock_file: resultat = charger_schema_depuis_gitea("test_schema.txt") # Vérifie que le fichier a été écrit assert mock_file.called assert resultat == "OK" @patch("utils.gitea.requests.get") @patch("utils.gitea.recuperer_date_dernier_commit") @patch("os.path.exists") @patch("os.path.getmtime") @patch("builtins.open", new_callable=mock_open) def test_cache_schema_file(self, mock_file, mock_getmtime, mock_exists, mock_date_commit, mock_get): """Test l'utilisation du cache pour le fichier schema.""" # Fichier local existe et plus récent mock_exists.return_value = True local_time = datetime(2025, 1, 20, tzinfo=timezone.utc) mock_getmtime.return_value = 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("digraph G { cached }".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 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 == {}