Skip to content

Commit 19f3a53

Browse files
committed
test(module): Add test coverage for version-aware language installer
Added unit tests to cover the new, version-specific logic for Odoo 15+ and Odoo 18+ in the language installer. This resolves the low test coverage reported by Codecov. - Created `tests/test_odoo_lib.py` to test the generic `get_odoo_version` function, including success and failure cases. - Added new tests in `tests/test_language_installer.py` to validate the installation paths for Odoo 18+ (direct activation) and Odoo 15-17 (wizard-based). - Added tests for warning conditions, such as when languages are already active.
1 parent 88e581a commit 19f3a53

File tree

3 files changed

+148
-2
lines changed

3 files changed

+148
-2
lines changed

src/odoo_data_flow/lib/actions/language_installer.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,9 @@ def _install_languages_legacy(
4949
try:
5050
log.info(f"Installing language: {lang_code}")
5151
wizard_id = wizard_obj.create({"lang": lang_code})
52-
# In legacy versions, the method is called on the model proxy with the ID.
5352
wizard_obj.lang_install([wizard_id])
5453
log.info(f"Triggered installation for '{lang_code}'.")
5554
except Exception as e:
56-
# Log the error for the specific language but continue with others.
5755
log.error(f"Failed to install language '{lang_code}': {e}")
5856

5957

@@ -71,6 +69,9 @@ def run_language_installation(config: str, languages: list[str]) -> None:
7169
# New logic for Odoo 18 and newer
7270
if odoo_version >= 18:
7371
_install_languages_v18_plus(connection, languages)
72+
log.info("Language installation process triggered successfully.")
73+
log.info("--- Language Installation Finished ---")
74+
return
7475

7576
# Logic for Odoo 15, 16, 17
7677
elif odoo_version >= 15:

tests/test_language_installer.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from unittest.mock import MagicMock, call, patch
44

5+
import pytest
6+
57
from odoo_data_flow.lib.actions.language_installer import (
68
run_language_installation,
79
)
@@ -100,3 +102,107 @@ def test_run_language_installation_api_error(
100102
logged_message = mock_log_error.call_args[0][0]
101103
assert "Failed to install language 'en_US'" in logged_message
102104
assert "Odoo API Error" in logged_message
105+
106+
107+
@patch("odoo_data_flow.lib.actions.language_installer.odoo_lib.get_odoo_version")
108+
@patch(
109+
"odoo_data_flow.lib.actions.language_installer.conf_lib.get_connection_from_config"
110+
)
111+
def test_run_installation_v18_success(
112+
mock_get_connection: MagicMock, mock_get_odoo_version: MagicMock
113+
) -> None:
114+
"""Tests the successful installation path for Odoo 18+."""
115+
# 1. Setup
116+
mock_get_odoo_version.return_value = 18
117+
mock_lang_model = MagicMock()
118+
mock_lang_model.search.return_value = [101, 102] # Dummy DB IDs
119+
120+
mock_connection = MagicMock()
121+
mock_connection.get_model.return_value = mock_lang_model
122+
mock_get_connection.return_value = mock_connection
123+
124+
# 2. Action
125+
run_language_installation(config="dummy.conf", languages=["de_DE", "es_ES"])
126+
127+
# 3. Assert
128+
mock_connection.get_model.assert_called_once_with("res.lang")
129+
mock_lang_model.search.assert_called_once_with(
130+
[("code", "in", ["de_DE", "es_ES"]), ("active", "=", False)]
131+
)
132+
mock_lang_model.write.assert_called_once_with([101, 102], {"active": True})
133+
134+
135+
@pytest.mark.parametrize("version", [15, 16, 17])
136+
@patch("odoo_data_flow.lib.actions.language_installer.odoo_lib.get_odoo_version")
137+
@patch(
138+
"odoo_data_flow.lib.actions.language_installer.conf_lib.get_connection_from_config"
139+
)
140+
def test_run_installation_v15_to_v17_success(
141+
mock_get_connection: MagicMock,
142+
mock_get_odoo_version: MagicMock,
143+
version: int,
144+
) -> None:
145+
"""Tests the successful installation path for Odoo 15, 16, and 17."""
146+
# 1. Setup
147+
mock_get_odoo_version.return_value = version
148+
149+
# Define separate mocks for each model we'll interact with
150+
mock_lang_model = MagicMock()
151+
mock_lang_model.search.return_value = [101, 102]
152+
153+
mock_wizard_obj = MagicMock()
154+
mock_wizard_obj.create.return_value = 42
155+
156+
mock_connection = MagicMock()
157+
158+
def get_model_side_effect(model_name: str) -> MagicMock:
159+
if model_name == "res.lang":
160+
return mock_lang_model
161+
if model_name == "base.language.install":
162+
return mock_wizard_obj
163+
# Return a default mock for any other unexpected calls
164+
return MagicMock()
165+
166+
mock_connection.get_model.side_effect = get_model_side_effect
167+
mock_get_connection.return_value = mock_connection
168+
169+
# 2. Action
170+
run_language_installation(config="dummy.conf", languages=["de_DE", "es_ES"])
171+
172+
# 3. Assertions
173+
# Check that the correct search was performed on the lang model
174+
mock_lang_model.search.assert_called_once_with([("code", "in", ["de_DE", "es_ES"])])
175+
176+
# Check that the wizard was created with the correct IDs from the search
177+
mock_wizard_obj.create.assert_called_once_with({"langs": [(6, 0, [101, 102])]})
178+
179+
# Check that the installation method was executed on the created wizard
180+
mock_wizard_obj.browse(42).lang_install.assert_called_once()
181+
182+
183+
@patch("odoo_data_flow.lib.actions.language_installer.log.warning")
184+
@patch("odoo_data_flow.lib.actions.language_installer.odoo_lib.get_odoo_version")
185+
@patch(
186+
"odoo_data_flow.lib.actions.language_installer.conf_lib.get_connection_from_config"
187+
)
188+
def test_run_installation_v18_already_active(
189+
mock_get_connection: MagicMock,
190+
mock_get_odoo_version: MagicMock,
191+
mock_log_warning: MagicMock,
192+
) -> None:
193+
"""Tests the warning log when languages are already active in Odoo 18."""
194+
# 1. Setup
195+
mock_get_odoo_version.return_value = 18
196+
mock_lang_model = MagicMock()
197+
mock_lang_model.search.return_value = [] # Simulate no inactive languages found
198+
199+
mock_connection = MagicMock()
200+
mock_connection.get_model.return_value = mock_lang_model
201+
mock_get_connection.return_value = mock_connection
202+
203+
# 2. Action
204+
run_language_installation(config="dummy.conf", languages=["de_DE"])
205+
206+
# 3. Assert
207+
mock_log_warning.assert_called_once()
208+
assert "already active or do not exist" in mock_log_warning.call_args[0][0]

tests/test_odoo_lib.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""This tests the common, reusable functions for interacting with Odoo."""
2+
3+
from unittest.mock import MagicMock, patch
4+
5+
from odoo_data_flow.lib.odoo_lib import get_odoo_version
6+
7+
8+
def test_get_odoo_version_success() -> None:
9+
"""Tests successful detection of an Odoo version."""
10+
# 1. Setup
11+
mock_connection = MagicMock()
12+
mock_ir_module = MagicMock()
13+
mock_ir_module.search_read.return_value = [{"latest_version": "17.0.1.0.0"}]
14+
mock_connection.get_model.return_value = mock_ir_module
15+
16+
# 2. Action
17+
version = get_odoo_version(mock_connection)
18+
19+
# 3. Assert
20+
assert version == 17
21+
mock_connection.get_model.assert_called_once_with("ir.module.module")
22+
23+
24+
@patch("odoo_data_flow.lib.odoo_lib.log.warning")
25+
def test_get_odoo_version_failure_on_exception(
26+
mock_log_warning: MagicMock,
27+
) -> None:
28+
"""Tests fallback behavior when version detection raises an exception."""
29+
# 1. Setup
30+
mock_connection = MagicMock()
31+
mock_connection.get_model.side_effect = Exception("Connection Error")
32+
33+
# 2. Action
34+
version = get_odoo_version(mock_connection)
35+
36+
# 3. Assert
37+
assert version == 14 # Should return the fallback value
38+
mock_log_warning.assert_called_once()
39+
assert "Could not detect Odoo version" in mock_log_warning.call_args[0][0]

0 commit comments

Comments
 (0)