|
| 1 | +""" |
| 2 | +Module des tests unitaires pour le parseur de fichier de log Apache. |
| 3 | +""" |
| 4 | + |
| 5 | +import pytest |
| 6 | +from re import match |
| 7 | +from datetime import datetime, timezone, timedelta |
| 8 | +from parse.parseur_log_apache import ParseurLogApache, FormatLogApacheInvalideException |
| 9 | + |
| 10 | +# Données utilisées pour les tests unitaires |
| 11 | + |
| 12 | +# Liste d'entrées valides |
| 13 | +lignes_log_apache = [ |
| 14 | + # Première entrée |
| 15 | + '192.168.1.1 - - [12/Jan/2025:10:15:32 +0000] "GET /index.html HTTP/1.1" 200 532', |
| 16 | + # Deuxième entrée |
| 17 | + '::1 - - [05/Mar/2025:16:59:43 +0100] "POST /backend/getConnexion.php HTTP/1.1" 200 20' |
| 18 | + '"http://localhost/backend/connexion.php?titre=Connexion" "Mozilla/5.0 (Windows NT 10.0;' |
| 19 | + ' Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"', |
| 20 | + # Troisième entrée |
| 21 | + '::1 - - [05/Mar/2025:16:59:43 +0100] "DELETE / HTTP/2.1" 200 20' |
| 22 | +] |
| 23 | + |
| 24 | +# Liste d'entrées invalides |
| 25 | +lignes_log_apache_invalides = [ |
| 26 | + '', |
| 27 | + 'Une ligne avec un format invalide !', |
| 28 | + '::1 - - [05/Mar/2025:16:59:43] "DELETE / HTTP/2.1" 200 20', |
| 29 | + '192.168.1.1 - [12/Jan/2025:10:15:32 +0000] "GET /index.html HTTP/1.1" test 532' |
| 30 | +] |
| 31 | + |
| 32 | +@pytest.fixture() |
| 33 | +def log_apache(tmp_path): |
| 34 | + """ |
| 35 | + Fixture pour créer et récupérer un fichier de log Apache temporaire. |
| 36 | + Cette fixture permet de générer un fichier de log Apache temporaire contenant |
| 37 | + soit des lignes valides, soit des lignes invalides selon le paramètre fourni. |
| 38 | + Args: |
| 39 | + tmp_path (Path): Chemin temporaire fourni par pytest. |
| 40 | + Returns: |
| 41 | + Callable[[bool], Path]: Une fonction qui crée et retourne le chemin |
| 42 | + du fichier de log temporaire. |
| 43 | + """ |
| 44 | + def _creer_log(valide): |
| 45 | + """ |
| 46 | + Crée un fichier de log Apache temporaire. |
| 47 | + Args: |
| 48 | + valide (bool): Si True, le fichier contient des lignes de log valides. |
| 49 | + Sinon, il contient des lignes invalides. |
| 50 | + Returns: |
| 51 | + Path: Le chemin du fichier de log temporaire créé. |
| 52 | + """ |
| 53 | + contenu = ( |
| 54 | + "\n".join(lignes_log_apache) |
| 55 | + if valide == True |
| 56 | + else "\n".join(lignes_log_apache_invalides) |
| 57 | + ) |
| 58 | + fichier_temp = tmp_path / "access.log" |
| 59 | + fichier_temp.write_text(contenu) |
| 60 | + return fichier_temp |
| 61 | + return _creer_log |
| 62 | + |
| 63 | +@pytest.fixture |
| 64 | +def parseur_log_apache(log_apache, request): |
| 65 | + """ |
| 66 | + Fixture pour initialiser le parseur de log Apache. |
| 67 | + Retourne une instance de la classe ParseurLogApache pour être utilisée dans les tests. |
| 68 | + Returns: |
| 69 | + ParseurLogApache: Une instance de la classe ParseurArgumentsCLI. |
| 70 | + """ |
| 71 | + if hasattr(request, "param") and request.param == False: |
| 72 | + return ParseurLogApache(log_apache(False)) |
| 73 | + return ParseurLogApache(log_apache(True)) |
| 74 | + |
| 75 | + |
| 76 | +# Tests unitaires |
| 77 | + |
| 78 | +def test_exception_fichier_invalide(): |
| 79 | + """ |
| 80 | + Vérifie qu'une exception est bien levée lorsque le fichier n'existe pas. |
| 81 | + Returns: |
| 82 | + None |
| 83 | + """ |
| 84 | + with pytest.raises(FileNotFoundError): |
| 85 | + parseur = ParseurLogApache("fichier/existe/pas.txt") |
| 86 | + |
| 87 | +@pytest.mark.parametrize("parseur_log_apache", [False], indirect=["parseur_log_apache"]) |
| 88 | +def test_exception_fichier_invalide(parseur_log_apache): |
| 89 | + """ |
| 90 | + Vérifie qu'une exception est bien levée lorsque le format d'un fichier n'est |
| 91 | + pas valide (une entrée invalide). |
| 92 | + Args: |
| 93 | + parseur_arguments_cli (ParseurArgumentsCLI): Fixture pour l'instance |
| 94 | + de la classe ParseurLogApache. |
| 95 | + Returns: |
| 96 | + None |
| 97 | + """ |
| 98 | + with pytest.raises(FormatLogApacheInvalideException): |
| 99 | + fichier = parseur_log_apache.parse_fichier() |
| 100 | + |
| 101 | +@pytest.mark.parametrize("ligne_log", lignes_log_apache_invalides) |
| 102 | +def test_exception_entree_invalide(parseur_log_apache, ligne_log): |
| 103 | + """ |
| 104 | + Vérifie qu'une exception est bien levée lorsque le format d'au moins une |
| 105 | + entrée est invalide dans un fichier de log Apache. |
| 106 | + Args: |
| 107 | + parseur_arguments_cli (ParseurArgumentsCLI): Fixture pour l'instance |
| 108 | + de la classe ParseurLogApache. |
| 109 | + ligne_log (str): L'entrée à analyser. |
| 110 | + Returns: |
| 111 | + None |
| 112 | + """ |
| 113 | + with pytest.raises(FormatLogApacheInvalideException): |
| 114 | + parseur_log_apache.parse_entree(ligne_log) |
| 115 | + |
| 116 | +def test_nombre_entrees_valide(parseur_log_apache): |
| 117 | + """ |
| 118 | + Vérifie que le nombre d'entrées trouvé correspond au nombre de ligne dans le log. |
| 119 | + Args: |
| 120 | + parseur_arguments_cli (ParseurArgumentsCLI): Fixture pour l'instance |
| 121 | + de la classe ParseurLogApache. |
| 122 | + Returns: |
| 123 | + None |
| 124 | + """ |
| 125 | + fichier_log = parseur_log_apache.parse_fichier() |
| 126 | + assert len(fichier_log.entrees) == len(lignes_log_apache) |
| 127 | + |
| 128 | +@pytest.mark.parametrize("nom_information, retour_attendu", [ |
| 129 | + ("ip", "192.168.1.1"), |
| 130 | + ("rfc", None), |
| 131 | + ("horodatage", "12/Jan/2025:10:15:32 +0000"), |
| 132 | + ("Existe pas !", None) |
| 133 | +]) |
| 134 | +def test_regex_recuperation_information_entree(parseur_log_apache, nom_information, retour_attendu): |
| 135 | + """ |
| 136 | + Vérifie que la récupération des informations à partir d'un résultat de regex fonctionne |
| 137 | + correctement et que toutes valeurs introuvables ou égales à - renvoient None. |
| 138 | + Args: |
| 139 | + parseur_arguments_cli (ParseurArgumentsCLI): Fixture pour l'instance |
| 140 | + de la classe ParseurLogApache. |
| 141 | + nom_information (str): Nom de l'information à récupérer. |
| 142 | + retour_attendu (Union[None, str]): La valeur attendue de l'information. |
| 143 | + Returns: |
| 144 | + None |
| 145 | + """ |
| 146 | + ligne = '192.168.1.1 - - [12/Jan/2025:10:15:32 +0000] "GET /index.html HTTP/1.1" 200 532' |
| 147 | + analyse = match(parseur_log_apache.PATTERN_ENTREE_LOG_APACHE, ligne) |
| 148 | + resultat_analyse = analyse.groupdict() |
| 149 | + assert parseur_log_apache.get_information_entree(resultat_analyse, nom_information) == retour_attendu |
| 150 | + |
| 151 | +def test_parsage_entree_valide(parseur_log_apache): |
| 152 | + """ |
| 153 | + Vérifie qu'une entrée est correctement analysée et que les informations partent |
| 154 | + au bon endroit avec le bon typage. |
| 155 | + Args: |
| 156 | + parseur_arguments_cli (ParseurArgumentsCLI): Fixture pour l'instance |
| 157 | + de la classe ParseurLogApache. |
| 158 | + Returns: |
| 159 | + None |
| 160 | + """ |
| 161 | + ligne = '192.168.1.1 - - [12/Jan/2025:10:15:32 +0000] "GET /index.html HTTP/1.1" ' \ |
| 162 | + '200 532 "/home" "Chrome/133.0.0.0"' |
| 163 | + entree = parseur_log_apache.parse_entree(ligne) |
| 164 | + assert entree.client.adresse_ip == "192.168.1.1" |
| 165 | + assert entree.client.identifiant_rfc == None |
| 166 | + assert entree.client.nom_utilisateur == None |
| 167 | + assert entree.client.agent_utilisateur == "Chrome/133.0.0.0" |
| 168 | + assert entree.requete.horodatage == datetime(2025, 1, 12, 10, 15, 32, |
| 169 | + tzinfo=timezone(timedelta(hours=0))) |
| 170 | + assert entree.requete.methode_http == "GET" |
| 171 | + assert entree.requete.url == "/index.html" |
| 172 | + assert entree.requete.protocole_http == "HTTP/1.1" |
| 173 | + assert entree.requete.ancienne_url == "/home" |
| 174 | + assert entree.reponse.code_status_http == 200 |
| 175 | + assert entree.reponse.taille_octets == 532 |
0 commit comments