Skip to content

Commit 087d7a6

Browse files
committed
fix(module): ensure exit_json and fail_json return
Refactored the module to use `return` with `exit_json` and `fail_json` for better control flow. Added comprehensive tests for check mode behavior, covering scenarios where components are present or absent in the repository.
1 parent 7849f75 commit 087d7a6

File tree

2 files changed

+112
-8
lines changed

2 files changed

+112
-8
lines changed

plugins/modules/raw_component.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -299,14 +299,14 @@ def main():
299299
'changed': False,
300300
'msg': "Component already exists in repository"
301301
})
302-
module.exit_json(**result)
302+
return module.exit_json(**result)
303303

304304
if module.check_mode:
305305
result.update({
306306
'changed': True,
307307
'msg': "Component would be uploaded (check mode)"
308308
})
309-
module.exit_json(**result)
309+
return module.exit_json(**result)
310310

311311
# Proceed with upload
312312
upload_url = build_upload_url(base_url, repo_name)
@@ -335,21 +335,22 @@ def main():
335335
})
336336

337337
if success:
338-
module.exit_json(**result)
338+
return module.exit_json(**result)
339339
else:
340340
if not exists:
341341
result.update({
342342
'changed': False,
343343
'msg': "Component does not exist in repository"
344344
})
345-
module.exit_json(**result)
345+
return module.exit_json(**result)
346346

347347
if module.check_mode:
348348
result.update({
349349
'changed': True,
350350
'msg': "Component would have been deleted (if not in check mode)"
351351
})
352-
module.exit_json(**result)
352+
return module.exit_json(**result)
353+
353354

354355
# Proceed with deletion
355356
if delete_component_by_id(
@@ -363,7 +364,7 @@ def main():
363364
'changed': True,
364365
'msg': "Component deleted successfully"
365366
})
366-
module.exit_json(**result)
367+
return module.exit_json(**result)
367368
else:
368369
result.update({
369370
'changed': False,
@@ -376,13 +377,13 @@ def main():
376377
'msg': str(e),
377378
'error': {'type': 'component', 'details': str(e)}
378379
})
379-
module.fail_json(**result)
380+
return module.fail_json(**result)
380381
except Exception as e:
381382
result.update({
382383
'msg': f"An unexpected error occurred: {str(e)}",
383384
'error': {'type': 'unexpected', 'details': str(e)}
384385
})
385-
module.fail_json(**result)
386+
return module.fail_json(**result)
386387

387388

388389
if __name__ == '__main__':

tests/unit/plugins/modules/test_raw_component.py

+103
Original file line numberDiff line numberDiff line change
@@ -358,3 +358,106 @@ def _setup_mock_module(self, tmp_path):
358358
mock_module.exit_json = MagicMock()
359359

360360
return mock_module, test_file
361+
362+
def test_check_mode(self, tmp_path):
363+
"""Test check mode behavior for both present and absent states"""
364+
from ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component import main
365+
366+
# Create a test file
367+
test_file = tmp_path / "test-component.txt"
368+
test_file.write_text("test content")
369+
370+
# Basic module parameters
371+
module_params = {
372+
'state': 'present',
373+
'repository': 'https://nexus.example.com/repository/raw-hosted',
374+
'name': 'test-component.txt',
375+
'src': str(test_file),
376+
'dest': '/upload/path',
377+
'validate_certs': False,
378+
'timeout': 30,
379+
'username': 'testuser',
380+
'password': 'testpass'
381+
}
382+
383+
# Test check mode for state=present, component doesn't exist
384+
mock_module = MagicMock()
385+
mock_module.params = module_params.copy()
386+
mock_module.check_mode = True
387+
388+
with patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.AnsibleModule') as mock_ansible_module, \
389+
patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.get_repository_details') as mock_repo_details, \
390+
patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.check_component_exists') as mock_check_exists:
391+
392+
mock_ansible_module.return_value = mock_module
393+
mock_repo_details.return_value = ('raw', 'hosted')
394+
mock_check_exists.return_value = (False, None)
395+
396+
main()
397+
398+
# Verify check mode behavior for upload
399+
mock_module.exit_json.assert_called_once()
400+
call_args = mock_module.exit_json.call_args[1]
401+
assert call_args['changed'] is True
402+
assert call_args['msg'] == "Component would be uploaded (check mode)"
403+
assert not call_args['exists']
404+
405+
# Test check mode for state=present, component exists
406+
mock_module.exit_json.reset_mock()
407+
with patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.AnsibleModule') as mock_ansible_module, \
408+
patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.get_repository_details') as mock_repo_details, \
409+
patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.check_component_exists') as mock_check_exists:
410+
411+
mock_ansible_module.return_value = mock_module
412+
mock_repo_details.return_value = ('raw', 'hosted')
413+
mock_check_exists.return_value = (True, 'test-component-id')
414+
415+
main()
416+
417+
# Verify check mode behavior for existing component
418+
mock_module.exit_json.assert_called_once()
419+
call_args = mock_module.exit_json.call_args[1]
420+
assert call_args['changed'] is False
421+
assert call_args['msg'] == "Component already exists in repository"
422+
assert call_args['exists'] is True
423+
assert call_args['details']['component_id'] == 'test-component-id'
424+
425+
# Test check mode for state=absent, component exists
426+
mock_module.params['state'] = 'absent'
427+
mock_module.exit_json.reset_mock()
428+
with patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.AnsibleModule') as mock_ansible_module, \
429+
patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.get_repository_details') as mock_repo_details, \
430+
patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.check_component_exists') as mock_check_exists:
431+
432+
mock_ansible_module.return_value = mock_module
433+
mock_repo_details.return_value = ('raw', 'hosted')
434+
mock_check_exists.return_value = (True, 'test-component-id')
435+
436+
main()
437+
438+
# Verify check mode behavior for deletion
439+
mock_module.exit_json.assert_called_once()
440+
call_args = mock_module.exit_json.call_args[1]
441+
assert call_args['changed'] is True
442+
assert call_args['msg'] == "Component would have been deleted (if not in check mode)"
443+
assert call_args['exists'] is True
444+
assert call_args['details']['component_id'] == 'test-component-id'
445+
446+
# Test check mode for state=absent, component doesn't exist
447+
mock_module.exit_json.reset_mock()
448+
with patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.AnsibleModule') as mock_ansible_module, \
449+
patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.get_repository_details') as mock_repo_details, \
450+
patch('ansible_collections.cloudkrafter.nexus.plugins.modules.raw_component.check_component_exists') as mock_check_exists:
451+
452+
mock_ansible_module.return_value = mock_module
453+
mock_repo_details.return_value = ('raw', 'hosted')
454+
mock_check_exists.return_value = (False, None)
455+
456+
main()
457+
458+
# Verify check mode behavior for non-existent component
459+
mock_module.exit_json.assert_called_once()
460+
call_args = mock_module.exit_json.call_args[1]
461+
assert call_args['changed'] is False
462+
assert call_args['msg'] == "Component does not exist in repository"
463+
assert not call_args['exists']

0 commit comments

Comments
 (0)