Skip to content

Commit 6b041f2

Browse files
committed
Added appstream logic test
1 parent 397bb80 commit 6b041f2

1 file changed

Lines changed: 288 additions & 0 deletions

File tree

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
import sys
2+
import os
3+
import pytest
4+
from unittest.mock import MagicMock, patch
5+
6+
# Add library path
7+
sys.path.insert(
8+
0, os.path.join(os.path.dirname(__file__), "..", "..", "libraries", "python")
9+
)
10+
# Add module path
11+
sys.path.insert(0, os.path.dirname(__file__))
12+
13+
# Mock dnf module before importing the promise module
14+
mock_dnf = MagicMock()
15+
mock_dnf.exceptions = MagicMock()
16+
sys.modules["dnf"] = mock_dnf
17+
sys.modules["dnf.exceptions"] = mock_dnf.exceptions
18+
19+
import dnf_appstream as dnf_appstream_module
20+
21+
dnf_appstream_module.dnf = mock_dnf
22+
23+
from dnf_appstream import DnfAppStreamPromiseTypeModule, ValidationError
24+
from cfengine_module_library import Result
25+
26+
27+
@pytest.fixture
28+
def module():
29+
# Reset mocks
30+
mock_dnf.reset_mock()
31+
if hasattr(mock_dnf.Base, "return_value"):
32+
mock_dnf.Base.return_value.reset_mock()
33+
34+
mod = DnfAppStreamPromiseTypeModule()
35+
mod._log_level = "info"
36+
mod._out = MagicMock()
37+
return mod
38+
39+
40+
@pytest.fixture
41+
def mock_mpc():
42+
# Setup the ModulePackageContainer mock
43+
mpc = MagicMock()
44+
# Setup constants
45+
mpc.ModuleState_ENABLED = 1
46+
mpc.ModuleState_DISABLED = 2
47+
mpc.ModuleState_INSTALLED = 3
48+
mpc.ModuleState_DEFAULT = 0
49+
return mpc
50+
51+
52+
@pytest.fixture
53+
def mock_base(mock_mpc):
54+
base = mock_dnf.Base.return_value
55+
base.sack._moduleContainer = mock_mpc
56+
return base
57+
58+
59+
def test_harness_setup(module):
60+
"""Verify the test harness is working"""
61+
assert module is not None
62+
assert module.name == "dnf_appstream_promise_module"
63+
64+
65+
def test_enable_module_already_enabled(module, mock_base, mock_mpc):
66+
"""Test enabling a module that is already enabled (KEPT)"""
67+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_ENABLED
68+
mock_mpc.getEnabledStream.return_value = "12"
69+
70+
result = module.evaluate_promise("nodejs", {"state": "enabled", "stream": "12"}, {})
71+
assert result == Result.KEPT
72+
73+
74+
def test_enable_module_repaired(module, mock_base, mock_mpc):
75+
"""Test enabling a module that is disabled (REPAIRED)"""
76+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_DISABLED
77+
mock_mpc.isEnabled.return_value = True # After enable() called
78+
79+
result = module.evaluate_promise("nodejs", {"state": "enabled", "stream": "12"}, {})
80+
81+
mock_mpc.enable.assert_called_with("nodejs", "12")
82+
assert result == Result.REPAIRED
83+
84+
85+
def test_disable_module_already_disabled(module, mock_base, mock_mpc):
86+
"""Test disabling a module that is already disabled (KEPT)"""
87+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_DISABLED
88+
89+
result = module.evaluate_promise(
90+
"nodejs", {"state": "disabled", "stream": "12"}, {}
91+
)
92+
assert result == Result.KEPT
93+
94+
95+
def test_disable_module_repaired(module, mock_base, mock_mpc):
96+
"""Test disabling a module that is enabled (REPAIRED)"""
97+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_ENABLED
98+
mock_mpc.isDisabled.return_value = True # After disable() called
99+
100+
result = module.evaluate_promise(
101+
"nodejs", {"state": "disabled", "stream": "12"}, {}
102+
)
103+
104+
mock_mpc.disable.assert_called_with("nodejs")
105+
assert result == Result.REPAIRED
106+
107+
108+
def test_install_profile_repaired(module, mock_base, mock_mpc):
109+
"""Test installing a specific profile using 'installed' state (REPAIRED)"""
110+
# Initial state: enabled but not fully installed with profile
111+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_ENABLED
112+
mock_mpc.getEnabledStream.return_value = "12"
113+
mock_mpc.getInstalledProfiles.return_value = []
114+
115+
# helper for _get_profile_packages
116+
# It queries module, gets stream, gets profiles, gets content
117+
mock_module_obj = MagicMock()
118+
mock_module_obj.getStream.return_value = "12"
119+
mock_profile_obj = MagicMock()
120+
mock_profile_obj.getName.return_value = "common"
121+
mock_profile_obj.getContent.return_value = ["pkg1"]
122+
mock_module_obj.getProfiles.return_value = [mock_profile_obj]
123+
mock_mpc.query.return_value = [mock_module_obj]
124+
125+
result = module.evaluate_promise(
126+
"nodejs", {"state": "installed", "stream": "12", "profile": "common"}, {}
127+
)
128+
129+
mock_mpc.install.assert_called_with("nodejs", "12", "common")
130+
assert result == Result.REPAIRED
131+
132+
133+
def test_remove_module_repaired(module, mock_base, mock_mpc):
134+
"""Test removing a module using 'removed' state (REPAIRED)"""
135+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_INSTALLED
136+
mock_mpc.getEnabledStream.return_value = "12"
137+
mock_mpc.getInstalledProfiles.return_value = ["common"]
138+
139+
# For removal, we also need package query to mock explicit package removal
140+
mock_module_obj = MagicMock()
141+
mock_module_obj.getStream.return_value = "12"
142+
mock_profile_obj = MagicMock()
143+
mock_profile_obj.getName.return_value = "common"
144+
mock_profile_obj.getContent.return_value = ["pkg1"]
145+
mock_module_obj.getProfiles.return_value = [mock_profile_obj]
146+
mock_mpc.query.return_value = [mock_module_obj]
147+
148+
result = module.evaluate_promise("nodejs", {"state": "removed", "stream": "12"}, {})
149+
150+
# Logic in _remove_module calls uninstall for each installed profile if no profile specified
151+
mock_mpc.uninstall.assert_called()
152+
assert result == Result.REPAIRED
153+
154+
155+
def test_install_profile_idempotency_success(module, mock_base, mock_mpc):
156+
"""Test installing a profile that is already present (KEPT)"""
157+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_INSTALLED
158+
mock_mpc.getEnabledStream.return_value = "12"
159+
mock_mpc.getInstalledProfiles.return_value = ["common"]
160+
161+
result = module.evaluate_promise(
162+
"nodejs", {"state": "installed", "stream": "12", "profile": "common"}, {}
163+
)
164+
165+
assert result == Result.KEPT
166+
167+
168+
def test_reset_module_repaired(module, mock_base, mock_mpc):
169+
"""Test resetting a module to default state (REPAIRED)"""
170+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_ENABLED
171+
172+
result = module.evaluate_promise("nodejs", {"state": "default", "stream": "12"}, {})
173+
174+
mock_mpc.reset.assert_called_with("nodejs")
175+
assert result == Result.REPAIRED
176+
177+
178+
def test_stream_default_resolution(module, mock_base, mock_mpc):
179+
"""Test resolving stream => 'default'"""
180+
mock_mpc.getDefaultStream.return_value = "12"
181+
182+
# State check uses resolved stream
183+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_ENABLED
184+
mock_mpc.getEnabledStream.return_value = "12"
185+
186+
result = module.evaluate_promise(
187+
"nodejs", {"state": "enabled", "stream": "default"}, {}
188+
)
189+
190+
assert result == Result.KEPT
191+
192+
193+
def test_profile_default_resolution(module, mock_base, mock_mpc):
194+
"""Test resolving profile => 'default'"""
195+
mock_mpc.getDefaultStream.return_value = "12"
196+
mock_mpc.getDefaultProfiles.return_value = ["default_prof"]
197+
198+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_INSTALLED
199+
mock_mpc.getEnabledStream.return_value = "12"
200+
mock_mpc.getInstalledProfiles.return_value = ["default_prof"]
201+
202+
result = module.evaluate_promise(
203+
"nodejs", {"state": "installed", "stream": "12", "profile": "default"}, {}
204+
)
205+
206+
assert result == Result.KEPT
207+
208+
209+
def test_invalid_aliases(module):
210+
"""Verify that aliases 'install' and 'remove' are invalid"""
211+
212+
# Test 'install'
213+
with pytest.raises(ValidationError):
214+
module._validate_state("install")
215+
216+
# Test 'remove'
217+
with pytest.raises(ValidationError):
218+
module._validate_state("remove")
219+
220+
221+
def test_get_module_state_logic(module, mock_mpc):
222+
"""Test the logic of _get_module_state"""
223+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_ENABLED
224+
assert module._get_module_state(mock_mpc, "nodejs") == "enabled"
225+
226+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_DISABLED
227+
assert module._get_module_state(mock_mpc, "nodejs") == "disabled"
228+
229+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_INSTALLED
230+
assert module._get_module_state(mock_mpc, "nodejs") == "installed"
231+
232+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_DEFAULT
233+
assert module._get_module_state(mock_mpc, "nodejs") == "removed"
234+
235+
mock_mpc.getModuleState.return_value = 999 # Unknown state
236+
assert module._get_module_state(mock_mpc, "nodejs") == "removed"
237+
238+
239+
def test_remove_already_removed(module, mock_base, mock_mpc):
240+
"""Test removing a module that is already removed (KEPT)"""
241+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_DEFAULT
242+
result = module.evaluate_promise("nodejs", {"state": "removed"}, {})
243+
assert result == Result.KEPT
244+
245+
246+
def test_remove_already_disabled(module, mock_base, mock_mpc):
247+
"""Test removing a module that is already disabled (KEPT)"""
248+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_DISABLED
249+
result = module.evaluate_promise("nodejs", {"state": "removed"}, {})
250+
assert result == Result.KEPT
251+
252+
253+
def test_enable_wrong_stream_repaired(module, mock_base, mock_mpc):
254+
"""Test enabling a module that is enabled but with wrong stream (REPAIRED)"""
255+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_ENABLED
256+
mock_mpc.getEnabledStream.return_value = "10"
257+
mock_mpc.isEnabled.return_value = True
258+
result = module.evaluate_promise("nodejs", {"state": "enabled", "stream": "12"}, {})
259+
mock_mpc.enable.assert_called_with("nodejs", "12")
260+
assert result == Result.REPAIRED
261+
262+
263+
def test_stream_default_not_found(module, mock_base, mock_mpc):
264+
"""Test stream => 'default' when no default stream exists (NOT_KEPT)"""
265+
mock_mpc.getDefaultStream.return_value = None
266+
result = module.evaluate_promise(
267+
"nodejs", {"state": "enabled", "stream": "default"}, {}
268+
)
269+
assert result == Result.NOT_KEPT
270+
271+
272+
def test_profile_default_not_found(module, mock_base, mock_mpc):
273+
"""Test profile => 'default' when no default profile exists (NOT_KEPT)"""
274+
mock_mpc.getDefaultStream.return_value = "12"
275+
mock_mpc.getDefaultProfiles.return_value = []
276+
result = module.evaluate_promise(
277+
"nodejs", {"state": "installed", "stream": "12", "profile": "default"}, {}
278+
)
279+
assert result == Result.NOT_KEPT
280+
281+
282+
def test_remove_unknown_module_runtime_error(module, mock_base, mock_mpc):
283+
"""Test removing a module when getEnabledStream raises RuntimeError"""
284+
mock_mpc.getModuleState.return_value = mock_mpc.ModuleState_ENABLED
285+
mock_mpc.getEnabledStream.side_effect = RuntimeError("No such module")
286+
result = module.evaluate_promise("unknown_mod", {"state": "removed"}, {})
287+
# With no stream and getEnabledStream failing, target_stream is None, so KEPT
288+
assert result == Result.KEPT

0 commit comments

Comments
 (0)