diff --git a/test/test_plan.base.md b/test/test_plan.base.md new file mode 100644 index 0000000..0c881d2 --- /dev/null +++ b/test/test_plan.base.md @@ -0,0 +1,541 @@ + + + + + + +# Test Plan — ERPLibre `./test/` + +## Overview + + +# Plan de test — ERPLibre `./test/` + +## Vue d'ensemble + + +| Metric / Métrique | Value / Valeur | +|---|---| +| Test files / Fichiers de test | 13 | +| Test classes / Classes de test | 56 | +| Test methods / Méthodes de test | 262 | +| Framework | `unittest` (Python stdlib) | +| Command / Commande | `python3 -m unittest discover -s test -v` | + +--- + + +## 1. `test_config_file.py` — Configuration and file merging + +**Type**: Unit test (pure logic, no real I/O) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestDeepMergeWithLists` | 11 | Recursive dict merging with list strategies (`replace` vs `extend`), source dict immutability | +| `TestGetConfig` | 6 | Loading and merging configs from base + override + private JSON, correct precedence | +| `TestGetConfigValue` | 2 | Navigating nested values by key path | +| `TestGetLogoAsciiFilePath` | 1 | Returns the logo file path | + +**Mocks**: `@patch` on CONFIG_FILE paths, temporary JSON files + + +## 1. `test_config_file.py` — Configuration et fusion de fichiers + +**Type** : Test unitaire (pure logique, aucun I/O réel) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestDeepMergeWithLists` | 11 | Fusion récursive de dicts avec stratégies de listes (`replace` vs `extend`), immutabilité du dict source | +| `TestGetConfig` | 6 | Chargement et fusion de configs depuis base + override + private JSON, précédence correcte | +| `TestGetConfigValue` | 2 | Navigation dans les valeurs imbriquées par chemin de clés | +| `TestGetLogoAsciiFilePath` | 1 | Retourne le chemin du fichier logo | + +**Mocks** : `@patch` sur les chemins CONFIG_FILE, fichiers temporaires JSON + + +--- + + +## 2. `test_execute.py` — Shell command execution + +**Type**: Integration test (executes real bash processes) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestExecuteInit` | 3 | Terminal detection (gnome-terminal, osascript), venv configuration | +| `TestExecCommandLive` | 15 | Command execution with various return modes (status, output, command), venv activation, custom environment variables | + +**Mocks**: `@patch("shutil.which")` for terminal. Bash commands are actually executed. + + +## 2. `test_execute.py` — Exécution de commandes shell + +**Type** : Test d'intégration (exécute de vrais processus bash) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestExecuteInit` | 3 | Détection du terminal (gnome-terminal, osascript), configuration du venv | +| `TestExecCommandLive` | 15 | Exécution de commandes avec différents modes de retour (status, output, commande), activation de venv, variables d'environnement custom | + +**Mocks** : `@patch("shutil.which")` pour le terminal. Les commandes bash sont exécutées réellement. + + +--- + + +## 3. `test_git_tool.py` — Git utilities and manifest parsing + +**Type**: Unit test + +| Class | # Tests | What is validated | +|---|---|---| +| `TestRepoAttrs` | 3 | Repo attributes data structure | +| `TestGetUrl` | 3 | URL format conversion HTTPS ↔ SSH | +| `TestGetTransformedRepoInfo` | 10 | Org, repo name, path extraction from URL, org forcing, revision, clone_depth | +| `TestDefaultProperties` | 5 | Default constants, Odoo version reading | +| `TestStrInsert` | 3 | Substring insertion at position | +| `TestGetProjectConfig` | 1 | Reading `EL_GITHUB_TOKEN` from `env_var.sh` | +| `TestGetRepoInfoSubmodule` | 2 | Parsing `.gitmodules` | +| `TestGetManifestXmlInfo` | 2 | Parsing Google Repo manifest XML | +| `TestConstants` | 3 | Verifying defined constants | + +**Mocks**: `@patch("builtins.open")`, temporary files for `.gitmodules` and XML + + +## 3. `test_git_tool.py` — Utilitaires Git et parsing de manifests + +**Type** : Test unitaire + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestRepoAttrs` | 3 | Structure de données des attributs de repo | +| `TestGetUrl` | 3 | Conversion d'URL HTTPS ↔ SSH | +| `TestGetTransformedRepoInfo` | 10 | Extraction d'org, nom de repo, chemin depuis URL, forçage d'org, revision, clone_depth | +| `TestDefaultProperties` | 5 | Constantes par défaut, lecture de la version Odoo | +| `TestStrInsert` | 3 | Insertion de sous-chaîne à une position | +| `TestGetProjectConfig` | 1 | Lecture de `EL_GITHUB_TOKEN` depuis `env_var.sh` | +| `TestGetRepoInfoSubmodule` | 2 | Parsing de `.gitmodules` | +| `TestGetManifestXmlInfo` | 2 | Parsing de manifests XML Google Repo | +| `TestConstants` | 3 | Vérification des constantes définies | + +**Mocks** : `@patch("builtins.open")`, fichiers temporaires pour `.gitmodules` et XML + + +--- + + +## 4. `test_kill_process_by_port.py` — Process management + +**Type**: Unit test (mocked processes, no real kill) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestProcDesc` | 3 | Process description formatting (pid, cmdline, username) | +| `TestGetAncestry` | 3 | Parent process chain walking (stops at pid 1, ppid 0) | +| `TestChooseTarget` | 3 | Target process selection (detection of `./run.sh`, `./odoo_bin.sh`) | +| `TestKillProcess` | 2 | Process termination (gentle terminate vs force kill) | +| `TestKillTree` | 3 | Recursive process tree killing (children first) | +| `TestFindListeners` | 5 | Network socket enumeration by port (filtering, deduplication) | +| `TestProtectedNames` | 2 | System process protection (systemd, gnome-shell, sshd) | +| `TestStopParentKill` | 2 | Wrapper script detection | + +**Mocks**: `_make_proc()` creates mocked `psutil.Process` objects, `@patch` on psutil functions + + +## 4. `test_kill_process_by_port.py` — Gestion de processus + +**Type** : Test unitaire (processus mockés, aucun kill réel) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestProcDesc` | 3 | Formatage de description de processus (pid, cmdline, username) | +| `TestGetAncestry` | 3 | Remontée de la chaîne de processus parents (arrêt à pid 1, ppid 0) | +| `TestChooseTarget` | 3 | Sélection du processus cible (détection de `./run.sh`, `./odoo_bin.sh`) | +| `TestKillProcess` | 2 | Terminaison de processus (gentle terminate vs force kill) | +| `TestKillTree` | 3 | Kill récursif de l'arbre de processus (enfants d'abord) | +| `TestFindListeners` | 5 | Énumération des sockets réseau par port (filtrage, dédoublonnage) | +| `TestProtectedNames` | 2 | Protection des processus système (systemd, gnome-shell, sshd) | +| `TestStopParentKill` | 2 | Détection des scripts wrapper | + +**Mocks** : `_make_proc()` crée des objets `psutil.Process` mockés, `@patch` sur les fonctions psutil + + +--- + + +## 5. `test_todo.py` — Interactive CLI TODO + +**Type**: Unit test + +| Class | # Tests | What is validated | +|---|---|---| +| `TestTODOInit` | 1 | TODO class initialization | +| `TestFillHelpInfo` | 3 | Help text generation, i18n key resolution | +| `TestGetOdooVersion` | 3 | Parsing `version.json`, active version detection | +| `TestOnDirSelected` | 1 | Directory selection | +| `TestExecuteFromConfiguration` | 5 | Executing commands/makefiles/callbacks from config dict | +| `TestConstants` | 7 | Path constants verification | +| `TestDeployGitServer` | 2 | Git daemon deployment logic | +| `TestProcessKillGitDaemon` | 1 | Git daemon process killing | +| `TestExecuteUnitTests` | 2 | Unit test execution | +| `TestKdbxGetExtraCommandUser` | 3 | KeePass integration | +| `TestSetupClaudeCommit` | 1 | Claude commit template setup | +| `TestSelectDatabase` | 2 | Database selection | +| `TestRestoreFromDatabase` | 2 | Database restoration | +| `TestCreateBackupFromDatabase` | 1 | Backup creation | + +**Mocks**: `MagicMock()` for Execute and database manager, temporary files + + +## 5. `test_todo.py` — CLI interactif TODO + +**Type** : Test unitaire + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestTODOInit` | 1 | Initialisation de la classe TODO | +| `TestFillHelpInfo` | 3 | Génération de texte d'aide, résolution des clés i18n | +| `TestGetOdooVersion` | 3 | Parsing de `version.json`, détection de la version active | +| `TestOnDirSelected` | 1 | Sélection de répertoire | +| `TestExecuteFromConfiguration` | 5 | Exécution de commandes/makefiles/callbacks depuis config dict | +| `TestConstants` | 7 | Vérification des chemins constants | +| `TestDeployGitServer` | 2 | Logique de déploiement du git daemon | +| `TestProcessKillGitDaemon` | 1 | Kill du processus git daemon | +| `TestExecuteUnitTests` | 2 | Exécution des tests unitaires | +| `TestKdbxGetExtraCommandUser` | 3 | Intégration KeePass | +| `TestSetupClaudeCommit` | 1 | Setup du template de commit Claude | +| `TestSelectDatabase` | 2 | Sélection de base de données | +| `TestRestoreFromDatabase` | 2 | Restauration de base de données | +| `TestCreateBackupFromDatabase` | 1 | Création de backup | + +**Mocks** : `MagicMock()` pour Execute et database manager, fichiers temporaires + + +--- + + +## 6. `test_todo_i18n.py` — Internationalization (FR/EN) + +**Type**: Unit test + +| Class | # Tests | What is validated | +|---|---|---| +| `TestTranslations` | 3 | TRANSLATIONS dictionary completeness (all keys have `fr` and `en`, no empty values) | +| `TestT` | 4 | `t()` function: returns correct language, falls back to key if unknown | +| `TestGetLang` | 7 | Language detection: memory cache, `env_var.sh` reading, environment variable, fallback to `"fr"` | +| `TestSetLang` | 4 | Language persistence in `env_var.sh`, missing file handling | +| `TestLangIsConfigured` | 3 | Configuration state detection | + +**Mocks**: `@patch.object` on `ENV_VAR_FILE`, `@patch.dict(os.environ)`, `setUp/tearDown` to reset `_current_lang` + + +## 6. `test_todo_i18n.py` — Internationalisation (FR/EN) + +**Type** : Test unitaire + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestTranslations` | 3 | Complétude du dictionnaire TRANSLATIONS (toutes les clés ont `fr` et `en`, aucune valeur vide) | +| `TestT` | 4 | Fonction `t()` : retourne la bonne langue, fallback sur la clé si inconnue | +| `TestGetLang` | 7 | Détection de langue : cache mémoire, lecture de `env_var.sh`, variable d'environnement, fallback à `"fr"` | +| `TestSetLang` | 4 | Persistance de la langue dans `env_var.sh`, gestion de fichier manquant | +| `TestLangIsConfigured` | 3 | Détection de l'état de configuration | + +**Mocks** : `@patch.object` sur `ENV_VAR_FILE`, `@patch.dict(os.environ)`, `setUp/tearDown` pour réinitialiser `_current_lang` + + +--- + + +## 7. `test_check_addons_exist.py` — Odoo addon validation + +**Type**: Unit test (temporary files, no Odoo required) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestMainModuleFound` | 3 | Module found with valid `__manifest__.py`, JSON and formatted JSON output | +| `TestMainModuleMissing` | 2 | Missing module returns code 1, JSON output for missing module | +| `TestMainDuplicateModule` | 1 | Duplicate module in multiple paths returns code 2 | +| `TestMainMissingManifest` | 1 | Directory without `__manifest__.py` returns code 1 | +| `TestMainBadConfig` | 2 | INI config without `[options]` section or without `addons_path` key returns -1 | + +**Mocks**: `@patch("sys.argv")`, temporary files for addons and INI config + + +## 7. `test_check_addons_exist.py` — Validation des addons Odoo + +**Type** : Test unitaire (fichiers temporaires, aucun Odoo requis) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestMainModuleFound` | 3 | Module trouvé avec `__manifest__.py` valide, sortie JSON et JSON formaté | +| `TestMainModuleMissing` | 2 | Module absent retourne code 1, sortie JSON pour module manquant | +| `TestMainDuplicateModule` | 1 | Module en double dans plusieurs chemins retourne code 2 | +| `TestMainMissingManifest` | 1 | Répertoire sans `__manifest__.py` retourne code 1 | +| `TestMainBadConfig` | 2 | Config INI sans section `[options]` ou sans clé `addons_path` retourne -1 | + +**Mocks** : `@patch("sys.argv")`, fichiers temporaires pour addons et config INI + + +--- + + +## 8. `test_iscompatible.py` — Pip version compatibility + +**Type**: Unit test (pure logic, no I/O) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestStringToTuple` | 4 | Version string to integer tuple conversion | +| `TestParseRequirements` | 7 | Parsing requirements.txt syntax (operators, multiple constraints, comments) | +| `TestIsCompatible` | 11 | Version compatibility checking with operators `==`, `>=`, `>`, `<`, `<=`, `!=` and ranges | + +**Mocks**: None — pure functions + + +## 8. `test_iscompatible.py` — Compatibilité de versions pip + +**Type** : Test unitaire (pure logique, aucun I/O) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestStringToTuple` | 4 | Conversion de chaîne de version en tuple d'entiers | +| `TestParseRequirements` | 7 | Parsing de la syntaxe requirements.txt (opérateurs, contraintes multiples, commentaires) | +| `TestIsCompatible` | 11 | Vérification de compatibilité de version avec opérateurs `==`, `>=`, `>`, `<`, `<=`, `!=` et plages | + +**Mocks** : Aucun — fonctions pures + + +--- + + +## 9. `test_format_file_to_commit.py` — Modified file detection for formatting + +**Type**: Unit test (mocked git) + Integration test (`execute_shell` runs real processes) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestExecuteShell` | 5 | Shell command execution: success, failure (return code), stderr capture, empty output, multiline output | +| `TestGetModifiedFiles` | 9 | Parsing `git status --porcelain`: modified, added, deleted (ignored), ZIP/tar.gz (ignored), untracked, empty status, multiple statuses, git error | + +**Mocks**: `@patch` on `subprocess.run`, `os.path.exists`, `os.path.isfile` + + +## 9. `test_format_file_to_commit.py` — Détection de fichiers modifiés pour formatage + +**Type** : Test unitaire (git mocké) + Test d'intégration (`execute_shell` exécute de vrais processus) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestExecuteShell` | 5 | Exécution de commandes shell : succès, échec (code retour), capture stderr, sortie vide, sortie multiligne | +| `TestGetModifiedFiles` | 9 | Parsing du `git status --porcelain` : fichier modifié, ajouté, supprimé (ignoré), ZIP/tar.gz (ignorés), non suivi, statut vide, statuts multiples, erreur git | + +**Mocks** : `@patch` sur `subprocess.run`, `os.path.exists`, `os.path.isfile` + + +--- + + +## 10. `test_version.py` — ERPLibre version management + +**Type**: Unit test + +| Class | # Tests | What is validated | +|---|---|---| +| `TestRemoveDotPath` | 6 | Removing `./` prefix from paths | +| `TestDie` | 3 | Conditional exit with configurable error code | +| `TestConstants` | 7 | Versioned file name templates (venv, manifest, pyproject, poetry lock, addons, odoo) | +| `TestUpdateValidateVersion` | 5 | Version resolution: explicit, by odoo_version, default fallback, detected version, expected paths | +| `TestUpdateDetectVersion` | 2 | Version detection: missing files, matching with `data_version` | +| `TestUpdatePrintLog` | 2 | Execution log display (empty and populated) | + +**Mocks**: `@patch("sys.argv")`, temporary files for version files, `@patch` on path constants + + +## 10. `test_version.py` — Gestion des versions ERPLibre + +**Type** : Test unitaire + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestRemoveDotPath` | 6 | Suppression du préfixe `./` des chemins | +| `TestDie` | 3 | Sortie conditionnelle avec code d'erreur configurable | +| `TestConstants` | 7 | Templates de noms de fichiers versionnés (venv, manifest, pyproject, poetry lock, addons, odoo) | +| `TestUpdateValidateVersion` | 5 | Résolution de version : explicite, par odoo_version, fallback par défaut, version détectée, chemins attendus | +| `TestUpdateDetectVersion` | 2 | Détection de version : fichiers absents, correspondance avec `data_version` | +| `TestUpdatePrintLog` | 2 | Affichage du log d'exécution vide et peuplé | + +**Mocks** : `@patch("sys.argv")`, fichiers temporaires pour les fichiers de version, `@patch` sur les constantes de chemin + + +--- + + +## 11. `test_docker_update_version.py` — Docker version update + +**Type**: Unit test (temporary files) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestEditText` | 2 | Image update in `docker-compose.yml` after ERPLibre service, other lines preserved | +| `TestEditDockerProd` | 3 | `FROM` line replacement in Dockerfile, RUN/COPY lines preserved, multi-FROM handling | + +**Mocks**: None — uses `tempfile.NamedTemporaryFile` for Docker files + + +## 11. `test_docker_update_version.py` — Mise à jour de version Docker + +**Type** : Test unitaire (fichiers temporaires) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestEditText` | 2 | Mise à jour de l'image dans `docker-compose.yml` après le service ERPLibre, préservation des autres lignes | +| `TestEditDockerProd` | 3 | Remplacement de la ligne `FROM` dans Dockerfile, préservation des lignes RUN/COPY, gestion multi-FROM | + +**Mocks** : Aucun — utilise `tempfile.NamedTemporaryFile` pour les fichiers Docker + + +--- + + +## 12. `test_code_generator_tools.py` — Code generator tools + +**Type**: Unit test (pure AST and string logic) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestCountSpaceTab` | 9 | Indentation counting: spaces, tabs, mixed, `\n`/`\r`, custom group_space | +| `TestExtractLambda` | 2 | Lambda expression extraction from AST node, outer parentheses removal | +| `TestFillSearchField` | 11 | Recursive AST value extraction: Constant (str, int, bool), UnaryOp (negative), Name, Attribute, List, Dict, Tuple, Lambda, unsupported type | +| `TestSearchAndReplace` | 4 | Value replacement in Python code: quoted value, empty model, missing keyword, custom keyword | +| `TestArgsTypeParam` | 6 | ARGS_TYPE_PARAM dictionary validation for Odoo field types | + +**Mocks**: None — pure functions on AST and strings + + +## 12. `test_code_generator_tools.py` — Outils du générateur de code + +**Type** : Test unitaire (pure logique AST et string) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestCountSpaceTab` | 9 | Comptage d'indentation : espaces, tabulations, mixte, `\n`/`\r`, group_space personnalisé | +| `TestExtractLambda` | 2 | Extraction d'expression lambda depuis un nœud AST, suppression des parenthèses externes | +| `TestFillSearchField` | 11 | Extraction récursive de valeurs AST : Constant (str, int, bool), UnaryOp (négatif), Name, Attribute, List, Dict, Tuple, Lambda, type non supporté | +| `TestSearchAndReplace` | 4 | Remplacement de valeurs dans du code Python : valeur entre guillemets, modèle vide, mot-clé absent, mot-clé personnalisé | +| `TestArgsTypeParam` | 6 | Validation du dictionnaire ARGS_TYPE_PARAM pour les types de champs Odoo | + +**Mocks** : Aucun — fonctions pures sur AST et chaînes de caractères + + +--- + + +## 13. `test_database_tools.py` — Database tools + +**Type**: Unit test (temporary files) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestProcessZip` | 4 | ZIP file processing: line removal by keyword, preservation when no match, total removal, non-targeted files intact | +| `TestCompareDatabaseApplicationLogic` | 4 | CSV set comparison: identical CSVs, different, empty, one empty | + +**Mocks**: None — uses `tempfile` and `zipfile` for temporary files + + +## 13. `test_database_tools.py` — Outils de base de données + +**Type** : Test unitaire (fichiers temporaires) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestProcessZip` | 4 | Traitement de fichiers ZIP : suppression de lignes par mot-clé, préservation quand pas de correspondance, suppression totale, fichiers non ciblés intacts | +| `TestCompareDatabaseApplicationLogic` | 4 | Comparaison de CSV par ensembles : CSVs identiques, différents, vides, un seul vide | + +**Mocks** : Aucun — utilise `tempfile` et `zipfile` pour les fichiers temporaires + + +--- + + +## Coverage matrix by component + +| Tested component | Source file | Test file | Type | +|---|---|---|---| +| `ConfigFile` | `script/config/config_file.py` | `test_config_file.py` | Unit | +| `Execute` | `script/execute/execute.py` | `test_execute.py` | Integration | +| `GitTool` | `script/git/git_tool.py` | `test_git_tool.py` | Unit | +| `kill_process_by_port` | `script/process/kill_process_by_port.py` | `test_kill_process_by_port.py` | Unit | +| `TODO` | `script/todo/todo.py` | `test_todo.py` | Unit | +| `todo_i18n` | `script/todo/todo_i18n.py` | `test_todo_i18n.py` | Unit | +| `check_addons_exist` | `script/addons/check_addons_exist.py` | `test_check_addons_exist.py` | Unit | +| `iscompatible` | `script/poetry/iscompatible.py` | `test_iscompatible.py` | Unit | +| `format_file_to_commit` | `script/maintenance/format_file_to_commit.py` | `test_format_file_to_commit.py` | Unit + Integration | +| `update_env_version` | `script/version/update_env_version.py` | `test_version.py` | Unit | +| `docker_update_version` | `script/docker/docker_update_version.py` | `test_docker_update_version.py` | Unit | +| Code Generator (tools) | `script/code_generator/search_class_model.py` | `test_code_generator_tools.py` | Unit | +| Database (tools) | `script/database/migrate/process_backup_file.py` | `test_database_tools.py` | Unit | + +## Partially covered components + +The following components have tests for their pure functions, but functions requiring infrastructure (Odoo, PostgreSQL, network) are not unit tested: + +- `script/database/` — `process_zip` and CSV logic tested; `db_restore`, `image_db`, `list_remote` not tested (require Odoo/PostgreSQL) +- `script/code_generator/` — `extract_lambda`, `fill_search_field`, `search_and_replace`, `count_space_tab` tested; `generate_module`, `main` not tested (require Odoo) +- `script/poetry/` — `iscompatible`, `parse_requirements`, `string_to_tuple` tested; `combine_requirements`, `poetry_update.main` not tested (require full filesystem) + + +## Matrice de couverture par composant + +| Composant testé | Fichier source | Fichier test | Type | +|---|---|---|---| +| `ConfigFile` | `script/config/config_file.py` | `test_config_file.py` | Unitaire | +| `Execute` | `script/execute/execute.py` | `test_execute.py` | Intégration | +| `GitTool` | `script/git/git_tool.py` | `test_git_tool.py` | Unitaire | +| `kill_process_by_port` | `script/process/kill_process_by_port.py` | `test_kill_process_by_port.py` | Unitaire | +| `TODO` | `script/todo/todo.py` | `test_todo.py` | Unitaire | +| `todo_i18n` | `script/todo/todo_i18n.py` | `test_todo_i18n.py` | Unitaire | +| `check_addons_exist` | `script/addons/check_addons_exist.py` | `test_check_addons_exist.py` | Unitaire | +| `iscompatible` | `script/poetry/iscompatible.py` | `test_iscompatible.py` | Unitaire | +| `format_file_to_commit` | `script/maintenance/format_file_to_commit.py` | `test_format_file_to_commit.py` | Unitaire + Intégration | +| `update_env_version` | `script/version/update_env_version.py` | `test_version.py` | Unitaire | +| `docker_update_version` | `script/docker/docker_update_version.py` | `test_docker_update_version.py` | Unitaire | +| Code Generator (tools) | `script/code_generator/search_class_model.py` | `test_code_generator_tools.py` | Unitaire | +| Database (tools) | `script/database/migrate/process_backup_file.py` | `test_database_tools.py` | Unitaire | + +## Composants partiellement couverts + +Les composants suivants ont des tests pour leurs fonctions pures, mais les fonctions nécessitant une infrastructure (Odoo, PostgreSQL, réseau) ne sont pas testées unitairement : + +- `script/database/` — `process_zip` et logique CSV testés ; `db_restore`, `image_db`, `list_remote` non testés (requièrent Odoo/PostgreSQL) +- `script/code_generator/` — `extract_lambda`, `fill_search_field`, `search_and_replace`, `count_space_tab` testés ; `generate_module`, `main` non testés (requièrent Odoo) +- `script/poetry/` — `iscompatible`, `parse_requirements`, `string_to_tuple` testés ; `combine_requirements`, `poetry_update.main` non testés (requièrent le filesystem complet) + + +--- + + +## Test patterns used + +| Pattern | Usage | +|---|---| +| **Mocking (`@patch`)** | System dependency isolation (psutil, shutil, subprocess, os.path, files) | +| **Temporary files** | `tempfile.mkdtemp()` and `tempfile.NamedTemporaryFile()` for side-effect-free I/O testing | +| **Helper factory** | `_make_proc()` in test_kill_process, `_mock_git()` in test_format, `_write_csv()` in test_database | +| **Isolated state** | `setUp/tearDown` to reset globals between tests | +| **Real execution** | `test_execute.py` and `TestExecuteShell` launch real bash processes | +| **SimpleNamespace** | Lightweight config creation without argparse in test_docker | +| **AST inline** | `ast.parse(code, mode="eval").body` to create test AST nodes | + + +## Patterns de test utilisés + +| Pattern | Usage | +|---|---| +| **Mocking (`@patch`)** | Isolation des dépendances système (psutil, shutil, subprocess, os.path, fichiers) | +| **Fichiers temporaires** | `tempfile.mkdtemp()` et `tempfile.NamedTemporaryFile()` pour tester l'I/O sans effets de bord | +| **Helper factory** | `_make_proc()` dans test_kill_process, `_mock_git()` dans test_format, `_write_csv()` dans test_database | +| **État isolé** | `setUp/tearDown` pour réinitialiser les globals entre tests | +| **Exécution réelle** | `test_execute.py` et `TestExecuteShell` lancent de vrais processus bash | +| **SimpleNamespace** | Création de configs légères sans argparse dans test_docker | +| **AST inline** | `ast.parse(code, mode="eval").body` pour créer des nœuds AST de test | diff --git a/test/test_plan.fr.md b/test/test_plan.fr.md new file mode 100644 index 0000000..110ac89 --- /dev/null +++ b/test/test_plan.fr.md @@ -0,0 +1,264 @@ + +# Plan de test — ERPLibre `./test/` + +## Vue d'ensemble + +| Metric / Métrique | Value / Valeur | +|---|---| +| Test files / Fichiers de test | 13 | +| Test classes / Classes de test | 56 | +| Test methods / Méthodes de test | 262 | +| Framework | `unittest` (Python stdlib) | +| Command / Commande | `python3 -m unittest discover -s test -v` | + +--- + +## 1. `test_config_file.py` — Configuration et fusion de fichiers + +**Type** : Test unitaire (pure logique, aucun I/O réel) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestDeepMergeWithLists` | 11 | Fusion récursive de dicts avec stratégies de listes (`replace` vs `extend`), immutabilité du dict source | +| `TestGetConfig` | 6 | Chargement et fusion de configs depuis base + override + private JSON, précédence correcte | +| `TestGetConfigValue` | 2 | Navigation dans les valeurs imbriquées par chemin de clés | +| `TestGetLogoAsciiFilePath` | 1 | Retourne le chemin du fichier logo | + +**Mocks** : `@patch` sur les chemins CONFIG_FILE, fichiers temporaires JSON + +--- + +## 2. `test_execute.py` — Exécution de commandes shell + +**Type** : Test d'intégration (exécute de vrais processus bash) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestExecuteInit` | 3 | Détection du terminal (gnome-terminal, osascript), configuration du venv | +| `TestExecCommandLive` | 15 | Exécution de commandes avec différents modes de retour (status, output, commande), activation de venv, variables d'environnement custom | + +**Mocks** : `@patch("shutil.which")` pour le terminal. Les commandes bash sont exécutées réellement. + +--- + +## 3. `test_git_tool.py` — Utilitaires Git et parsing de manifests + +**Type** : Test unitaire + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestRepoAttrs` | 3 | Structure de données des attributs de repo | +| `TestGetUrl` | 3 | Conversion d'URL HTTPS ↔ SSH | +| `TestGetTransformedRepoInfo` | 10 | Extraction d'org, nom de repo, chemin depuis URL, forçage d'org, revision, clone_depth | +| `TestDefaultProperties` | 5 | Constantes par défaut, lecture de la version Odoo | +| `TestStrInsert` | 3 | Insertion de sous-chaîne à une position | +| `TestGetProjectConfig` | 1 | Lecture de `EL_GITHUB_TOKEN` depuis `env_var.sh` | +| `TestGetRepoInfoSubmodule` | 2 | Parsing de `.gitmodules` | +| `TestGetManifestXmlInfo` | 2 | Parsing de manifests XML Google Repo | +| `TestConstants` | 3 | Vérification des constantes définies | + +**Mocks** : `@patch("builtins.open")`, fichiers temporaires pour `.gitmodules` et XML + +--- + +## 4. `test_kill_process_by_port.py` — Gestion de processus + +**Type** : Test unitaire (processus mockés, aucun kill réel) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestProcDesc` | 3 | Formatage de description de processus (pid, cmdline, username) | +| `TestGetAncestry` | 3 | Remontée de la chaîne de processus parents (arrêt à pid 1, ppid 0) | +| `TestChooseTarget` | 3 | Sélection du processus cible (détection de `./run.sh`, `./odoo_bin.sh`) | +| `TestKillProcess` | 2 | Terminaison de processus (gentle terminate vs force kill) | +| `TestKillTree` | 3 | Kill récursif de l'arbre de processus (enfants d'abord) | +| `TestFindListeners` | 5 | Énumération des sockets réseau par port (filtrage, dédoublonnage) | +| `TestProtectedNames` | 2 | Protection des processus système (systemd, gnome-shell, sshd) | +| `TestStopParentKill` | 2 | Détection des scripts wrapper | + +**Mocks** : `_make_proc()` crée des objets `psutil.Process` mockés, `@patch` sur les fonctions psutil + +--- + +## 5. `test_todo.py` — CLI interactif TODO + +**Type** : Test unitaire + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestTODOInit` | 1 | Initialisation de la classe TODO | +| `TestFillHelpInfo` | 3 | Génération de texte d'aide, résolution des clés i18n | +| `TestGetOdooVersion` | 3 | Parsing de `version.json`, détection de la version active | +| `TestOnDirSelected` | 1 | Sélection de répertoire | +| `TestExecuteFromConfiguration` | 5 | Exécution de commandes/makefiles/callbacks depuis config dict | +| `TestConstants` | 7 | Vérification des chemins constants | +| `TestDeployGitServer` | 2 | Logique de déploiement du git daemon | +| `TestProcessKillGitDaemon` | 1 | Kill du processus git daemon | +| `TestExecuteUnitTests` | 2 | Exécution des tests unitaires | +| `TestKdbxGetExtraCommandUser` | 3 | Intégration KeePass | +| `TestSetupClaudeCommit` | 1 | Setup du template de commit Claude | +| `TestSelectDatabase` | 2 | Sélection de base de données | +| `TestRestoreFromDatabase` | 2 | Restauration de base de données | +| `TestCreateBackupFromDatabase` | 1 | Création de backup | + +**Mocks** : `MagicMock()` pour Execute et database manager, fichiers temporaires + +--- + +## 6. `test_todo_i18n.py` — Internationalisation (FR/EN) + +**Type** : Test unitaire + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestTranslations` | 3 | Complétude du dictionnaire TRANSLATIONS (toutes les clés ont `fr` et `en`, aucune valeur vide) | +| `TestT` | 4 | Fonction `t()` : retourne la bonne langue, fallback sur la clé si inconnue | +| `TestGetLang` | 7 | Détection de langue : cache mémoire, lecture de `env_var.sh`, variable d'environnement, fallback à `"fr"` | +| `TestSetLang` | 4 | Persistance de la langue dans `env_var.sh`, gestion de fichier manquant | +| `TestLangIsConfigured` | 3 | Détection de l'état de configuration | + +**Mocks** : `@patch.object` sur `ENV_VAR_FILE`, `@patch.dict(os.environ)`, `setUp/tearDown` pour réinitialiser `_current_lang` + +--- + +## 7. `test_check_addons_exist.py` — Validation des addons Odoo + +**Type** : Test unitaire (fichiers temporaires, aucun Odoo requis) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestMainModuleFound` | 3 | Module trouvé avec `__manifest__.py` valide, sortie JSON et JSON formaté | +| `TestMainModuleMissing` | 2 | Module absent retourne code 1, sortie JSON pour module manquant | +| `TestMainDuplicateModule` | 1 | Module en double dans plusieurs chemins retourne code 2 | +| `TestMainMissingManifest` | 1 | Répertoire sans `__manifest__.py` retourne code 1 | +| `TestMainBadConfig` | 2 | Config INI sans section `[options]` ou sans clé `addons_path` retourne -1 | + +**Mocks** : `@patch("sys.argv")`, fichiers temporaires pour addons et config INI + +--- + +## 8. `test_iscompatible.py` — Compatibilité de versions pip + +**Type** : Test unitaire (pure logique, aucun I/O) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestStringToTuple` | 4 | Conversion de chaîne de version en tuple d'entiers | +| `TestParseRequirements` | 7 | Parsing de la syntaxe requirements.txt (opérateurs, contraintes multiples, commentaires) | +| `TestIsCompatible` | 11 | Vérification de compatibilité de version avec opérateurs `==`, `>=`, `>`, `<`, `<=`, `!=` et plages | + +**Mocks** : Aucun — fonctions pures + +--- + +## 9. `test_format_file_to_commit.py` — Détection de fichiers modifiés pour formatage + +**Type** : Test unitaire (git mocké) + Test d'intégration (`execute_shell` exécute de vrais processus) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestExecuteShell` | 5 | Exécution de commandes shell : succès, échec (code retour), capture stderr, sortie vide, sortie multiligne | +| `TestGetModifiedFiles` | 9 | Parsing du `git status --porcelain` : fichier modifié, ajouté, supprimé (ignoré), ZIP/tar.gz (ignorés), non suivi, statut vide, statuts multiples, erreur git | + +**Mocks** : `@patch` sur `subprocess.run`, `os.path.exists`, `os.path.isfile` + +--- + +## 10. `test_version.py` — Gestion des versions ERPLibre + +**Type** : Test unitaire + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestRemoveDotPath` | 6 | Suppression du préfixe `./` des chemins | +| `TestDie` | 3 | Sortie conditionnelle avec code d'erreur configurable | +| `TestConstants` | 7 | Templates de noms de fichiers versionnés (venv, manifest, pyproject, poetry lock, addons, odoo) | +| `TestUpdateValidateVersion` | 5 | Résolution de version : explicite, par odoo_version, fallback par défaut, version détectée, chemins attendus | +| `TestUpdateDetectVersion` | 2 | Détection de version : fichiers absents, correspondance avec `data_version` | +| `TestUpdatePrintLog` | 2 | Affichage du log d'exécution vide et peuplé | + +**Mocks** : `@patch("sys.argv")`, fichiers temporaires pour les fichiers de version, `@patch` sur les constantes de chemin + +--- + +## 11. `test_docker_update_version.py` — Mise à jour de version Docker + +**Type** : Test unitaire (fichiers temporaires) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestEditText` | 2 | Mise à jour de l'image dans `docker-compose.yml` après le service ERPLibre, préservation des autres lignes | +| `TestEditDockerProd` | 3 | Remplacement de la ligne `FROM` dans Dockerfile, préservation des lignes RUN/COPY, gestion multi-FROM | + +**Mocks** : Aucun — utilise `tempfile.NamedTemporaryFile` pour les fichiers Docker + +--- + +## 12. `test_code_generator_tools.py` — Outils du générateur de code + +**Type** : Test unitaire (pure logique AST et string) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestCountSpaceTab` | 9 | Comptage d'indentation : espaces, tabulations, mixte, `\n`/`\r`, group_space personnalisé | +| `TestExtractLambda` | 2 | Extraction d'expression lambda depuis un nœud AST, suppression des parenthèses externes | +| `TestFillSearchField` | 11 | Extraction récursive de valeurs AST : Constant (str, int, bool), UnaryOp (négatif), Name, Attribute, List, Dict, Tuple, Lambda, type non supporté | +| `TestSearchAndReplace` | 4 | Remplacement de valeurs dans du code Python : valeur entre guillemets, modèle vide, mot-clé absent, mot-clé personnalisé | +| `TestArgsTypeParam` | 6 | Validation du dictionnaire ARGS_TYPE_PARAM pour les types de champs Odoo | + +**Mocks** : Aucun — fonctions pures sur AST et chaînes de caractères + +--- + +## 13. `test_database_tools.py` — Outils de base de données + +**Type** : Test unitaire (fichiers temporaires) + +| Classe | # Tests | Ce qui est validé | +|---|---|---| +| `TestProcessZip` | 4 | Traitement de fichiers ZIP : suppression de lignes par mot-clé, préservation quand pas de correspondance, suppression totale, fichiers non ciblés intacts | +| `TestCompareDatabaseApplicationLogic` | 4 | Comparaison de CSV par ensembles : CSVs identiques, différents, vides, un seul vide | + +**Mocks** : Aucun — utilise `tempfile` et `zipfile` pour les fichiers temporaires + +--- + +## Matrice de couverture par composant + +| Composant testé | Fichier source | Fichier test | Type | +|---|---|---|---| +| `ConfigFile` | `script/config/config_file.py` | `test_config_file.py` | Unitaire | +| `Execute` | `script/execute/execute.py` | `test_execute.py` | Intégration | +| `GitTool` | `script/git/git_tool.py` | `test_git_tool.py` | Unitaire | +| `kill_process_by_port` | `script/process/kill_process_by_port.py` | `test_kill_process_by_port.py` | Unitaire | +| `TODO` | `script/todo/todo.py` | `test_todo.py` | Unitaire | +| `todo_i18n` | `script/todo/todo_i18n.py` | `test_todo_i18n.py` | Unitaire | +| `check_addons_exist` | `script/addons/check_addons_exist.py` | `test_check_addons_exist.py` | Unitaire | +| `iscompatible` | `script/poetry/iscompatible.py` | `test_iscompatible.py` | Unitaire | +| `format_file_to_commit` | `script/maintenance/format_file_to_commit.py` | `test_format_file_to_commit.py` | Unitaire + Intégration | +| `update_env_version` | `script/version/update_env_version.py` | `test_version.py` | Unitaire | +| `docker_update_version` | `script/docker/docker_update_version.py` | `test_docker_update_version.py` | Unitaire | +| Code Generator (tools) | `script/code_generator/search_class_model.py` | `test_code_generator_tools.py` | Unitaire | +| Database (tools) | `script/database/migrate/process_backup_file.py` | `test_database_tools.py` | Unitaire | + +## Composants partiellement couverts + +Les composants suivants ont des tests pour leurs fonctions pures, mais les fonctions nécessitant une infrastructure (Odoo, PostgreSQL, réseau) ne sont pas testées unitairement : + +- `script/database/` — `process_zip` et logique CSV testés ; `db_restore`, `image_db`, `list_remote` non testés (requièrent Odoo/PostgreSQL) +- `script/code_generator/` — `extract_lambda`, `fill_search_field`, `search_and_replace`, `count_space_tab` testés ; `generate_module`, `main` non testés (requièrent Odoo) +- `script/poetry/` — `iscompatible`, `parse_requirements`, `string_to_tuple` testés ; `combine_requirements`, `poetry_update.main` non testés (requièrent le filesystem complet) + +--- + +## Patterns de test utilisés + +| Pattern | Usage | +|---|---| +| **Mocking (`@patch`)** | Isolation des dépendances système (psutil, shutil, subprocess, os.path, fichiers) | +| **Fichiers temporaires** | `tempfile.mkdtemp()` et `tempfile.NamedTemporaryFile()` pour tester l'I/O sans effets de bord | +| **Helper factory** | `_make_proc()` dans test_kill_process, `_mock_git()` dans test_format, `_write_csv()` dans test_database | +| **État isolé** | `setUp/tearDown` pour réinitialiser les globals entre tests | +| **Exécution réelle** | `test_execute.py` et `TestExecuteShell` lancent de vrais processus bash | +| **SimpleNamespace** | Création de configs légères sans argparse dans test_docker | +| **AST inline** | `ast.parse(code, mode="eval").body` pour créer des nœuds AST de test | \ No newline at end of file diff --git a/test/test_plan.md b/test/test_plan.md new file mode 100644 index 0000000..4bb8f9c --- /dev/null +++ b/test/test_plan.md @@ -0,0 +1,264 @@ + +# Test Plan — ERPLibre `./test/` + +## Overview + +| Metric / Métrique | Value / Valeur | +|---|---| +| Test files / Fichiers de test | 13 | +| Test classes / Classes de test | 56 | +| Test methods / Méthodes de test | 262 | +| Framework | `unittest` (Python stdlib) | +| Command / Commande | `python3 -m unittest discover -s test -v` | + +--- + +## 1. `test_config_file.py` — Configuration and file merging + +**Type**: Unit test (pure logic, no real I/O) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestDeepMergeWithLists` | 11 | Recursive dict merging with list strategies (`replace` vs `extend`), source dict immutability | +| `TestGetConfig` | 6 | Loading and merging configs from base + override + private JSON, correct precedence | +| `TestGetConfigValue` | 2 | Navigating nested values by key path | +| `TestGetLogoAsciiFilePath` | 1 | Returns the logo file path | + +**Mocks**: `@patch` on CONFIG_FILE paths, temporary JSON files + +--- + +## 2. `test_execute.py` — Shell command execution + +**Type**: Integration test (executes real bash processes) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestExecuteInit` | 3 | Terminal detection (gnome-terminal, osascript), venv configuration | +| `TestExecCommandLive` | 15 | Command execution with various return modes (status, output, command), venv activation, custom environment variables | + +**Mocks**: `@patch("shutil.which")` for terminal. Bash commands are actually executed. + +--- + +## 3. `test_git_tool.py` — Git utilities and manifest parsing + +**Type**: Unit test + +| Class | # Tests | What is validated | +|---|---|---| +| `TestRepoAttrs` | 3 | Repo attributes data structure | +| `TestGetUrl` | 3 | URL format conversion HTTPS ↔ SSH | +| `TestGetTransformedRepoInfo` | 10 | Org, repo name, path extraction from URL, org forcing, revision, clone_depth | +| `TestDefaultProperties` | 5 | Default constants, Odoo version reading | +| `TestStrInsert` | 3 | Substring insertion at position | +| `TestGetProjectConfig` | 1 | Reading `EL_GITHUB_TOKEN` from `env_var.sh` | +| `TestGetRepoInfoSubmodule` | 2 | Parsing `.gitmodules` | +| `TestGetManifestXmlInfo` | 2 | Parsing Google Repo manifest XML | +| `TestConstants` | 3 | Verifying defined constants | + +**Mocks**: `@patch("builtins.open")`, temporary files for `.gitmodules` and XML + +--- + +## 4. `test_kill_process_by_port.py` — Process management + +**Type**: Unit test (mocked processes, no real kill) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestProcDesc` | 3 | Process description formatting (pid, cmdline, username) | +| `TestGetAncestry` | 3 | Parent process chain walking (stops at pid 1, ppid 0) | +| `TestChooseTarget` | 3 | Target process selection (detection of `./run.sh`, `./odoo_bin.sh`) | +| `TestKillProcess` | 2 | Process termination (gentle terminate vs force kill) | +| `TestKillTree` | 3 | Recursive process tree killing (children first) | +| `TestFindListeners` | 5 | Network socket enumeration by port (filtering, deduplication) | +| `TestProtectedNames` | 2 | System process protection (systemd, gnome-shell, sshd) | +| `TestStopParentKill` | 2 | Wrapper script detection | + +**Mocks**: `_make_proc()` creates mocked `psutil.Process` objects, `@patch` on psutil functions + +--- + +## 5. `test_todo.py` — Interactive CLI TODO + +**Type**: Unit test + +| Class | # Tests | What is validated | +|---|---|---| +| `TestTODOInit` | 1 | TODO class initialization | +| `TestFillHelpInfo` | 3 | Help text generation, i18n key resolution | +| `TestGetOdooVersion` | 3 | Parsing `version.json`, active version detection | +| `TestOnDirSelected` | 1 | Directory selection | +| `TestExecuteFromConfiguration` | 5 | Executing commands/makefiles/callbacks from config dict | +| `TestConstants` | 7 | Path constants verification | +| `TestDeployGitServer` | 2 | Git daemon deployment logic | +| `TestProcessKillGitDaemon` | 1 | Git daemon process killing | +| `TestExecuteUnitTests` | 2 | Unit test execution | +| `TestKdbxGetExtraCommandUser` | 3 | KeePass integration | +| `TestSetupClaudeCommit` | 1 | Claude commit template setup | +| `TestSelectDatabase` | 2 | Database selection | +| `TestRestoreFromDatabase` | 2 | Database restoration | +| `TestCreateBackupFromDatabase` | 1 | Backup creation | + +**Mocks**: `MagicMock()` for Execute and database manager, temporary files + +--- + +## 6. `test_todo_i18n.py` — Internationalization (FR/EN) + +**Type**: Unit test + +| Class | # Tests | What is validated | +|---|---|---| +| `TestTranslations` | 3 | TRANSLATIONS dictionary completeness (all keys have `fr` and `en`, no empty values) | +| `TestT` | 4 | `t()` function: returns correct language, falls back to key if unknown | +| `TestGetLang` | 7 | Language detection: memory cache, `env_var.sh` reading, environment variable, fallback to `"fr"` | +| `TestSetLang` | 4 | Language persistence in `env_var.sh`, missing file handling | +| `TestLangIsConfigured` | 3 | Configuration state detection | + +**Mocks**: `@patch.object` on `ENV_VAR_FILE`, `@patch.dict(os.environ)`, `setUp/tearDown` to reset `_current_lang` + +--- + +## 7. `test_check_addons_exist.py` — Odoo addon validation + +**Type**: Unit test (temporary files, no Odoo required) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestMainModuleFound` | 3 | Module found with valid `__manifest__.py`, JSON and formatted JSON output | +| `TestMainModuleMissing` | 2 | Missing module returns code 1, JSON output for missing module | +| `TestMainDuplicateModule` | 1 | Duplicate module in multiple paths returns code 2 | +| `TestMainMissingManifest` | 1 | Directory without `__manifest__.py` returns code 1 | +| `TestMainBadConfig` | 2 | INI config without `[options]` section or without `addons_path` key returns -1 | + +**Mocks**: `@patch("sys.argv")`, temporary files for addons and INI config + +--- + +## 8. `test_iscompatible.py` — Pip version compatibility + +**Type**: Unit test (pure logic, no I/O) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestStringToTuple` | 4 | Version string to integer tuple conversion | +| `TestParseRequirements` | 7 | Parsing requirements.txt syntax (operators, multiple constraints, comments) | +| `TestIsCompatible` | 11 | Version compatibility checking with operators `==`, `>=`, `>`, `<`, `<=`, `!=` and ranges | + +**Mocks**: None — pure functions + +--- + +## 9. `test_format_file_to_commit.py` — Modified file detection for formatting + +**Type**: Unit test (mocked git) + Integration test (`execute_shell` runs real processes) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestExecuteShell` | 5 | Shell command execution: success, failure (return code), stderr capture, empty output, multiline output | +| `TestGetModifiedFiles` | 9 | Parsing `git status --porcelain`: modified, added, deleted (ignored), ZIP/tar.gz (ignored), untracked, empty status, multiple statuses, git error | + +**Mocks**: `@patch` on `subprocess.run`, `os.path.exists`, `os.path.isfile` + +--- + +## 10. `test_version.py` — ERPLibre version management + +**Type**: Unit test + +| Class | # Tests | What is validated | +|---|---|---| +| `TestRemoveDotPath` | 6 | Removing `./` prefix from paths | +| `TestDie` | 3 | Conditional exit with configurable error code | +| `TestConstants` | 7 | Versioned file name templates (venv, manifest, pyproject, poetry lock, addons, odoo) | +| `TestUpdateValidateVersion` | 5 | Version resolution: explicit, by odoo_version, default fallback, detected version, expected paths | +| `TestUpdateDetectVersion` | 2 | Version detection: missing files, matching with `data_version` | +| `TestUpdatePrintLog` | 2 | Execution log display (empty and populated) | + +**Mocks**: `@patch("sys.argv")`, temporary files for version files, `@patch` on path constants + +--- + +## 11. `test_docker_update_version.py` — Docker version update + +**Type**: Unit test (temporary files) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestEditText` | 2 | Image update in `docker-compose.yml` after ERPLibre service, other lines preserved | +| `TestEditDockerProd` | 3 | `FROM` line replacement in Dockerfile, RUN/COPY lines preserved, multi-FROM handling | + +**Mocks**: None — uses `tempfile.NamedTemporaryFile` for Docker files + +--- + +## 12. `test_code_generator_tools.py` — Code generator tools + +**Type**: Unit test (pure AST and string logic) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestCountSpaceTab` | 9 | Indentation counting: spaces, tabs, mixed, `\n`/`\r`, custom group_space | +| `TestExtractLambda` | 2 | Lambda expression extraction from AST node, outer parentheses removal | +| `TestFillSearchField` | 11 | Recursive AST value extraction: Constant (str, int, bool), UnaryOp (negative), Name, Attribute, List, Dict, Tuple, Lambda, unsupported type | +| `TestSearchAndReplace` | 4 | Value replacement in Python code: quoted value, empty model, missing keyword, custom keyword | +| `TestArgsTypeParam` | 6 | ARGS_TYPE_PARAM dictionary validation for Odoo field types | + +**Mocks**: None — pure functions on AST and strings + +--- + +## 13. `test_database_tools.py` — Database tools + +**Type**: Unit test (temporary files) + +| Class | # Tests | What is validated | +|---|---|---| +| `TestProcessZip` | 4 | ZIP file processing: line removal by keyword, preservation when no match, total removal, non-targeted files intact | +| `TestCompareDatabaseApplicationLogic` | 4 | CSV set comparison: identical CSVs, different, empty, one empty | + +**Mocks**: None — uses `tempfile` and `zipfile` for temporary files + +--- + +## Coverage matrix by component + +| Tested component | Source file | Test file | Type | +|---|---|---|---| +| `ConfigFile` | `script/config/config_file.py` | `test_config_file.py` | Unit | +| `Execute` | `script/execute/execute.py` | `test_execute.py` | Integration | +| `GitTool` | `script/git/git_tool.py` | `test_git_tool.py` | Unit | +| `kill_process_by_port` | `script/process/kill_process_by_port.py` | `test_kill_process_by_port.py` | Unit | +| `TODO` | `script/todo/todo.py` | `test_todo.py` | Unit | +| `todo_i18n` | `script/todo/todo_i18n.py` | `test_todo_i18n.py` | Unit | +| `check_addons_exist` | `script/addons/check_addons_exist.py` | `test_check_addons_exist.py` | Unit | +| `iscompatible` | `script/poetry/iscompatible.py` | `test_iscompatible.py` | Unit | +| `format_file_to_commit` | `script/maintenance/format_file_to_commit.py` | `test_format_file_to_commit.py` | Unit + Integration | +| `update_env_version` | `script/version/update_env_version.py` | `test_version.py` | Unit | +| `docker_update_version` | `script/docker/docker_update_version.py` | `test_docker_update_version.py` | Unit | +| Code Generator (tools) | `script/code_generator/search_class_model.py` | `test_code_generator_tools.py` | Unit | +| Database (tools) | `script/database/migrate/process_backup_file.py` | `test_database_tools.py` | Unit | + +## Partially covered components + +The following components have tests for their pure functions, but functions requiring infrastructure (Odoo, PostgreSQL, network) are not unit tested: + +- `script/database/` — `process_zip` and CSV logic tested; `db_restore`, `image_db`, `list_remote` not tested (require Odoo/PostgreSQL) +- `script/code_generator/` — `extract_lambda`, `fill_search_field`, `search_and_replace`, `count_space_tab` tested; `generate_module`, `main` not tested (require Odoo) +- `script/poetry/` — `iscompatible`, `parse_requirements`, `string_to_tuple` tested; `combine_requirements`, `poetry_update.main` not tested (require full filesystem) + +--- + +## Test patterns used + +| Pattern | Usage | +|---|---| +| **Mocking (`@patch`)** | System dependency isolation (psutil, shutil, subprocess, os.path, files) | +| **Temporary files** | `tempfile.mkdtemp()` and `tempfile.NamedTemporaryFile()` for side-effect-free I/O testing | +| **Helper factory** | `_make_proc()` in test_kill_process, `_mock_git()` in test_format, `_write_csv()` in test_database | +| **Isolated state** | `setUp/tearDown` to reset globals between tests | +| **Real execution** | `test_execute.py` and `TestExecuteShell` launch real bash processes | +| **SimpleNamespace** | Lightweight config creation without argparse in test_docker | +| **AST inline** | `ast.parse(code, mode="eval").body` to create test AST nodes |