1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
# -*- coding: utf-8 -*-
# Copyright (c) 2015-2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import annotations
import json
import sys
import datetime
import typing as t
import pytest
EMPTY_INVOCATION: dict[str, dict[str, t.Any]] = {u'module_args': {}}
DATETIME = datetime.datetime.strptime('2020-07-13 12:50:00', '%Y-%m-%d %H:%M:%S')
pytestmark = pytest.mark.usefixtures("module_env_mocker")
class TestAnsibleModuleExitJson:
"""
Test that various means of calling exitJson and FailJson return the messages they've been given
"""
DATA: tuple[tuple[dict[str, t.Any]], ...] = (
({}, {'invocation': EMPTY_INVOCATION}),
({'msg': 'message'}, {'msg': 'message', 'invocation': EMPTY_INVOCATION}),
({'msg': 'success', 'changed': True},
{'msg': 'success', 'changed': True, 'invocation': EMPTY_INVOCATION}),
({'msg': 'nochange', 'changed': False},
{'msg': 'nochange', 'changed': False, 'invocation': EMPTY_INVOCATION}),
({'msg': 'message', 'date': DATETIME.date()},
{'msg': 'message', 'date': DATETIME.date().isoformat(), 'invocation': EMPTY_INVOCATION}),
({'msg': 'message', 'datetime': DATETIME},
{'msg': 'message', 'datetime': DATETIME.isoformat(), 'invocation': EMPTY_INVOCATION}),
)
@pytest.mark.parametrize('args, expected, stdin', ((a, e, {}) for a, e in DATA), indirect=['stdin'])
def test_exit_json_exits(self, am, capfd, args, expected):
with pytest.raises(SystemExit) as ctx:
am.exit_json(**args)
assert ctx.value.code == 0
out, err = capfd.readouterr()
return_val = json.loads(out)
assert return_val == expected
@pytest.mark.parametrize('args, expected, stdin',
((a, e, {}) for a, e in DATA if 'msg' in a),
indirect=['stdin'])
def test_fail_json_exits(self, am, capfd, args, expected):
with pytest.raises(SystemExit) as ctx:
am.fail_json(**args)
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)
# Fail_json should add failed=True
expected['failed'] = True
assert return_val == expected
@pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
def test_fail_json_msg_positional(self, am, capfd):
with pytest.raises(SystemExit) as ctx:
am.fail_json('This is the msg')
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)
# Fail_json should add failed=True
assert return_val == {'msg': 'This is the msg', 'failed': True,
'invocation': EMPTY_INVOCATION}
@pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
def test_fail_json_msg_as_kwarg_after(self, am, capfd):
"""Test that msg as a kwarg after other kwargs works"""
with pytest.raises(SystemExit) as ctx:
am.fail_json(arbitrary=42, msg='This is the msg')
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)
# Fail_json should add failed=True
assert return_val == {'msg': 'This is the msg', 'failed': True,
'arbitrary': 42,
'invocation': EMPTY_INVOCATION}
@pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
def test_fail_json_no_msg(self, am):
with pytest.raises(TypeError) as ctx:
am.fail_json()
if sys.version_info >= (3, 10):
error_msg = "AnsibleModule.fail_json() missing 1 required positional argument: 'msg'"
else:
error_msg = "fail_json() missing 1 required positional argument: 'msg'"
assert ctx.value.args[0] == error_msg
class TestAnsibleModuleExitValuesRemoved:
"""
Test that ExitJson and FailJson remove password-like values
"""
OMIT = 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
DATA = (
(
dict(username='person', password='$ecret k3y'),
dict(one=1, pwd='$ecret k3y', url='https://username:password12345@foo.com/login/',
not_secret='following the leader', msg='here'),
dict(one=1, pwd=OMIT, url='https://username:password12345@foo.com/login/',
not_secret='following the leader', msg='here',
invocation=dict(module_args=dict(password=OMIT, token=None, username='person'))),
),
(
dict(username='person', password='password12345'),
dict(one=1, pwd='$ecret k3y', url='https://username:password12345@foo.com/login/',
not_secret='following the leader', msg='here'),
dict(one=1, pwd='$ecret k3y', url='https://username:********@foo.com/login/',
not_secret='following the leader', msg='here',
invocation=dict(module_args=dict(password=OMIT, token=None, username='person'))),
),
(
dict(username='person', password='$ecret k3y'),
dict(one=1, pwd='$ecret k3y', url='https://username:$ecret k3y@foo.com/login/',
not_secret='following the leader', msg='here'),
dict(one=1, pwd=OMIT, url='https://username:********@foo.com/login/',
not_secret='following the leader', msg='here',
invocation=dict(module_args=dict(password=OMIT, token=None, username='person'))),
),
)
@pytest.mark.parametrize('am, stdin, return_val, expected',
(({'username': {}, 'password': {'no_log': True}, 'token': {'no_log': True}}, s, r, e)
for s, r, e in DATA),
indirect=['am', 'stdin'])
def test_exit_json_removes_values(self, am, capfd, return_val, expected):
with pytest.raises(SystemExit):
am.exit_json(**return_val)
out, err = capfd.readouterr()
assert json.loads(out) == expected
@pytest.mark.parametrize('am, stdin, return_val, expected',
(({'username': {}, 'password': {'no_log': True}, 'token': {'no_log': True}}, s, r, e)
for s, r, e in DATA),
indirect=['am', 'stdin'])
def test_fail_json_removes_values(self, am, capfd, return_val, expected):
expected['failed'] = True
with pytest.raises(SystemExit):
am.fail_json(**return_val)
out, err = capfd.readouterr()
assert json.loads(out) == expected
|