erplibre/test/test_format_file_to_commit.py
Mathieu Benoit cfbdd9406e [REF] format : passer l'outillage et les tests sous ruff
Le formateur de ce dépôt est ruff depuis qu'il remplace black, qui ne connaît
aucune cible au-delà de py313 ; ce passage applique sa norme à l'arbre entier,
d'un coup, pour qu'aucun commit de fond n'ait à porter du style. L'écart tient
presque entièrement aux chaînes coupées à la main que ruff recolle quand elles
tiennent sur une ligne, et aux « with » multiples qu'il regroupe : aucune
valeur ne change, et les clés de traduction non plus.
Vérifié : la suite unitaire reste verte après le passage, et le contrôle de
syntaxe ne signale rien.

--- EN ---

This repository's formatter is ruff since it replaced black, which knows no
target beyond py313; this pass applies its standard to the whole tree at once,
so that no substantive commit has to carry style. The difference is almost
entirely the hand-split strings ruff joins back when they fit on one line, and
the multiple "with" it merges: no value changes, nor do the translation keys.
Checked: the unit suite stays green after the pass, and the syntax check
reports nothing.

Assisted-by: Claude Opus 5
2026-09-24 14:40:38 -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 MagicMock, patch
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()