erplibre/test/test_format_file_to_commit.py
Mathieu Benoit 4a90f9face [FIX] tests : balayer test/test_*.py et refuser un fichier muet
Le lanceur ne prenait que sept préfixes de noms, « le reste demandant une
base de données » : les 3703 tests du répertoire passent avec PostgreSQL
injoignable, et la suite n'en exécutait que 1131. Il balaie test/test_*.py.

Deux formes rendent un fichier muet sans erreur : unittest.main() posé en
plein milieu, qui sort avant que la seconde moitié soit définie (quatre
fichiers, 87 tests), et l'absence de bloc __main__, qui compte zéro (huit
fichiers, 174 tests). Une garde refuse ces deux formes et le retour d'une
liste de préfixes.

--- EN ---

The runner took seven filename prefixes only, "the rest needing a
database": all 3703 tests in the directory pass with PostgreSQL
unreachable, and the suite ran 1131 of them. It now globs test/test_*.py.

Two shapes make a file silent without error: unittest.main() placed
mid-file, which exits before the second half is defined (four files, 87
tests), and no __main__ block at all, which counts zero (eight files, 174
tests). A guard now refuses both shapes and the return of a prefix list.

Assisted-by: Claude Opus 5
(cherry picked from commit db71b333cc08ae0fce4ddaaf037357648b10236b)
2026-08-29 02:11:04 -04:00

144 lines
5.2 KiB
Python

#!/usr/bin/env python3
# © 2026 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
import unittest
from unittest.mock import patch, MagicMock
from script.maintenance.format_file_to_commit import (
execute_shell,
get_modified_files,
)
MOD = "script.maintenance.format_file_to_commit"
class TestExecuteShell(unittest.TestCase):
def test_successful_command(self):
code, output = execute_shell("echo hello")
self.assertEqual(code, 0)
self.assertEqual(output, "hello")
def test_failed_command(self):
code, output = execute_shell("exit 42")
self.assertEqual(code, 42)
def test_stderr_captured(self):
code, output = execute_shell("echo err >&2")
self.assertEqual(code, 0)
self.assertIn("err", output)
def test_empty_output(self):
code, output = execute_shell("true")
self.assertEqual(code, 0)
self.assertEqual(output, "")
def test_multiline_output(self):
code, output = execute_shell("echo 'a\nb'")
self.assertEqual(code, 0)
self.assertIn("a", output)
self.assertIn("b", output)
class TestGetModifiedFiles(unittest.TestCase):
def _mock_git(self, git_output):
"""Helper to mock subprocess.run for git status."""
mock_result = MagicMock()
mock_result.stdout = git_output
mock_result.returncode = 0
return mock_result
def _no_repo_no_odoo(self, path):
"""Return False for repo bin and .odoo-version, True otherwise."""
if path in (".venv.erplibre/bin/repo",):
return False
return True
@patch(f"{MOD}.os.path.isfile", return_value=False)
@patch(f"{MOD}.os.path.exists")
@patch(f"{MOD}.subprocess.run")
def test_single_modified_file(self, mock_run, mock_exists, mock_isfile):
mock_exists.return_value = False
mock_run.return_value = self._mock_git(" M file.py")
files = get_modified_files()
self.assertIsNotNone(files)
self.assertEqual(len(files), 1)
self.assertEqual(files[0][0], "M")
@patch(f"{MOD}.os.path.isfile", return_value=False)
@patch(f"{MOD}.os.path.exists", return_value=False)
@patch(f"{MOD}.subprocess.run")
def test_added_file(self, mock_run, mock_exists, mock_isfile):
mock_run.return_value = self._mock_git("A new_file.py")
files = get_modified_files()
self.assertIsNotNone(files)
self.assertEqual(files[0][0], "A")
@patch(f"{MOD}.os.path.isfile", return_value=False)
@patch(f"{MOD}.os.path.exists", return_value=False)
@patch(f"{MOD}.subprocess.run")
def test_deleted_file_ignored(self, mock_run, mock_exists, mock_isfile):
mock_run.return_value = self._mock_git("D deleted.py")
files = get_modified_files()
self.assertIsNotNone(files)
self.assertEqual(len(files), 0)
@patch(f"{MOD}.os.path.isfile", return_value=False)
@patch(f"{MOD}.os.path.exists", return_value=False)
@patch(f"{MOD}.subprocess.run")
def test_zip_file_ignored(self, mock_run, mock_exists, mock_isfile):
mock_run.return_value = self._mock_git("M archive.zip")
files = get_modified_files()
self.assertIsNotNone(files)
self.assertEqual(len(files), 0)
@patch(f"{MOD}.os.path.isfile", return_value=False)
@patch(f"{MOD}.os.path.exists", return_value=False)
@patch(f"{MOD}.subprocess.run")
def test_tar_gz_file_ignored(self, mock_run, mock_exists, mock_isfile):
mock_run.return_value = self._mock_git("M archive.tar.gz")
files = get_modified_files()
self.assertIsNotNone(files)
self.assertEqual(len(files), 0)
@patch(f"{MOD}.os.path.isfile", return_value=False)
@patch(f"{MOD}.os.path.exists", return_value=False)
@patch(f"{MOD}.subprocess.run")
def test_untracked_file(self, mock_run, mock_exists, mock_isfile):
mock_run.return_value = self._mock_git("?? new_file.txt")
files = get_modified_files()
self.assertIsNotNone(files)
self.assertEqual(len(files), 1)
self.assertEqual(files[0][0], "??")
@patch(f"{MOD}.os.path.isfile", return_value=False)
@patch(f"{MOD}.os.path.exists", return_value=False)
@patch(f"{MOD}.subprocess.run")
def test_empty_git_status(self, mock_run, mock_exists, mock_isfile):
mock_run.return_value = self._mock_git("")
files = get_modified_files()
self.assertIsNotNone(files)
self.assertEqual(len(files), 0)
@patch(f"{MOD}.os.path.isfile", return_value=False)
@patch(f"{MOD}.os.path.exists", return_value=False)
@patch(f"{MOD}.subprocess.run")
def test_multiple_statuses(self, mock_run, mock_exists, mock_isfile):
mock_run.return_value = self._mock_git(
" M modified.py\nA added.py\n?? untracked.py"
)
files = get_modified_files()
self.assertIsNotNone(files)
self.assertEqual(len(files), 3)
@patch(f"{MOD}.subprocess.run")
def test_git_error_returns_none(self, mock_run):
import subprocess
mock_run.side_effect = subprocess.CalledProcessError(1, "git")
files = get_modified_files()
self.assertIsNone(files)
if __name__ == "__main__":
unittest.main()