add test coverage for core filters (#68518)
Also remove now-useless exception handling.
This commit is contained in:
parent
2068131589
commit
835ad75a0a
4 changed files with 302 additions and 9 deletions
2
changelogs/fragments/68518-to_nice_json-cleanup.yaml
Normal file
2
changelogs/fragments/68518-to_nice_json-cleanup.yaml
Normal file
|
@ -0,0 +1,2 @@
|
|||
minor_changes:
|
||||
- to_nice_json filter - Removed now-useless exception handler
|
|
@ -80,12 +80,7 @@ def to_json(a, *args, **kw):
|
|||
|
||||
def to_nice_json(a, indent=4, sort_keys=True, *args, **kw):
|
||||
'''Make verbose, human readable JSON'''
|
||||
try:
|
||||
return json.dumps(a, indent=indent, sort_keys=sort_keys, separators=(',', ': '), cls=AnsibleJSONEncoder, *args, **kw)
|
||||
except Exception as e:
|
||||
# Fallback to the to_json filter
|
||||
display.warning(u'Unable to convert data using to_nice_json, falling back to to_json: %s' % to_text(e))
|
||||
return to_json(a, *args, **kw)
|
||||
return to_json(a, indent=indent, sort_keys=sort_keys, separators=(',', ': '), *args, **kw)
|
||||
|
||||
|
||||
def to_bool(a):
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
# Copyright: (c) 2019, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Note: |groupby is already tested by the `groupby_filter` target.
|
||||
|
||||
- set_fact:
|
||||
output_dir: "{{ lookup('env', 'OUTPUT_DIR') }}"
|
||||
|
||||
|
@ -108,6 +110,7 @@
|
|||
that:
|
||||
- '"{{ "hash" | hash("sha1") }}" == "2346ad27d7568ba9896f1b7da6b5991251debdf2"'
|
||||
- '"{{ "café" | hash("sha1") }}" == "f424452a9673918c6f09b0cdd35b20be8e6ae7d7"'
|
||||
- '"corned beef"|hash("haha, get it?") == None'
|
||||
|
||||
- name: Flatten tests
|
||||
block:
|
||||
|
@ -190,6 +193,12 @@
|
|||
- 4
|
||||
- key: value
|
||||
|
||||
- name: Verify combine fails with extra kwargs
|
||||
set_fact:
|
||||
foo: "{{[1] | combine(foo='bar')}}"
|
||||
ignore_errors: yes
|
||||
register: combine_fail
|
||||
|
||||
- name: Verify combine filter
|
||||
assert:
|
||||
that:
|
||||
|
@ -198,9 +207,12 @@
|
|||
- "(x | combine(y, z)) == {'x': 'x', 'y': 'y', 'z': 'z', 'key': 'z'}"
|
||||
- "([x, y, z] | combine) == {'x': 'x', 'y': 'y', 'z': 'z', 'key': 'z'}"
|
||||
- "([x, y] | combine(z)) == {'x': 'x', 'y': 'y', 'z': 'z', 'key': 'z'}"
|
||||
# more advance dicts combination tests are done in "merge_hash" function unit tests
|
||||
# but even it's redundant with those unit tests, we do at least the most complicated exemple of the documentation here
|
||||
- "None|combine == {}"
|
||||
# more advanced dict combination tests are done in the "merge_hash" function unit tests
|
||||
# but even though it's redundant with those unit tests, we do at least the most complicated example of the documentation here
|
||||
- "(default | combine(patch, recursive=True, list_merge='append_rp')) == result"
|
||||
- combine_fail is failed
|
||||
- "combine_fail.msg == \"'recursive' and 'list_merge' are the only valid keyword arguments\""
|
||||
|
||||
- set_fact:
|
||||
combine: "{{[x, [y]] | combine(z)}}"
|
||||
|
@ -261,3 +273,287 @@
|
|||
- named_groups == ['bye', 'good']
|
||||
- numbered_groups == ['bye', 'good']
|
||||
- failure is failed
|
||||
|
||||
- name: Verify to_bool
|
||||
assert:
|
||||
that:
|
||||
- 'None|bool == None'
|
||||
- 'False|bool == False'
|
||||
- '"TrUe"|bool == True'
|
||||
- '"FalSe"|bool == False'
|
||||
- '7|bool == False'
|
||||
|
||||
- name: Verify to_datetime
|
||||
assert:
|
||||
that:
|
||||
- '"1993-03-26 01:23:45"|to_datetime < "1994-03-26 01:23:45"|to_datetime'
|
||||
|
||||
- name: strftime invalid argument (failure expected)
|
||||
set_fact:
|
||||
foo: "{{ '%Y' | strftime('foo') }}"
|
||||
ignore_errors: yes
|
||||
register: strftime_fail
|
||||
|
||||
- name: Verify strftime
|
||||
assert:
|
||||
that:
|
||||
- '"%Y-%m-%d"|strftime(1585247522) == "2020-03-26"'
|
||||
- '("%Y"|strftime(None)).startswith("20")' # Current date, can't check much there.
|
||||
- strftime_fail is failed
|
||||
- '"Invalid value for epoch value" in strftime_fail.msg'
|
||||
|
||||
- name: Verify case-insensitive regex_replace
|
||||
assert:
|
||||
that:
|
||||
- '"hElLo there"|regex_replace("hello", "hi", ignorecase=True) == "hi there"'
|
||||
|
||||
- name: Verify case-insensitive regex_findall
|
||||
assert:
|
||||
that:
|
||||
- '"hEllo there heLlo haha HELLO there"|regex_findall("h.... ", ignorecase=True)|length == 3'
|
||||
|
||||
- name: Verify ternary
|
||||
assert:
|
||||
that:
|
||||
- 'True|ternary("seven", "eight") == "seven"'
|
||||
- 'None|ternary("seven", "eight") == "eight"'
|
||||
- 'None|ternary("seven", "eight", "nine") == "nine"'
|
||||
- 'False|ternary("seven", "eight") == "eight"'
|
||||
- '123|ternary("seven", "eight") == "seven"'
|
||||
- '"haha"|ternary("seven", "eight") == "seven"'
|
||||
|
||||
- name: Verify regex_escape raises on posix_extended (failure expected)
|
||||
set_fact:
|
||||
foo: '{{"]]^"|regex_escape(re_type="posix_extended")}}'
|
||||
ignore_errors: yes
|
||||
register: regex_escape_fail_1
|
||||
|
||||
- name: Verify regex_escape raises on other re_type (failure expected)
|
||||
set_fact:
|
||||
foo: '{{"]]^"|regex_escape(re_type="haha")}}'
|
||||
ignore_errors: yes
|
||||
register: regex_escape_fail_2
|
||||
|
||||
- name: Verify regex_escape with re_type other than 'python'
|
||||
assert:
|
||||
that:
|
||||
- '"]]^"|regex_escape(re_type="posix_basic") == "\\]\\]\\^"'
|
||||
- regex_escape_fail_1 is failed
|
||||
- 'regex_escape_fail_1.msg == "Regex type (posix_extended) not yet implemented"'
|
||||
- regex_escape_fail_2 is failed
|
||||
- 'regex_escape_fail_2.msg == "Invalid regex type (haha)"'
|
||||
|
||||
- name: Verify from_yaml and from_yaml_all
|
||||
assert:
|
||||
that:
|
||||
- "'---\nbananas: yellow\napples: red'|from_yaml == {'bananas': 'yellow', 'apples': 'red'}"
|
||||
- "2|from_yaml == 2"
|
||||
- "'---\nbananas: yellow\n---\napples: red'|from_yaml_all|list == [{'bananas': 'yellow'}, {'apples': 'red'}]"
|
||||
- "2|from_yaml_all == 2"
|
||||
|
||||
- name: Verify random raises on non-iterable input (failure expected)
|
||||
set_fact:
|
||||
foo: '{{None|random}}'
|
||||
ignore_errors: yes
|
||||
register: random_fail_1
|
||||
|
||||
- name: Verify random raises on iterable input with start (failure expected)
|
||||
set_fact:
|
||||
foo: '{{[1,2,3]|random(start=2)}}'
|
||||
ignore_errors: yes
|
||||
register: random_fail_2
|
||||
|
||||
- name: Verify random raises on iterable input with step (failure expected)
|
||||
set_fact:
|
||||
foo: '{{[1,2,3]|random(step=2)}}'
|
||||
ignore_errors: yes
|
||||
register: random_fail_3
|
||||
|
||||
- name: Verify random
|
||||
assert:
|
||||
that:
|
||||
- '2|random in [0,1]'
|
||||
- '2|random(seed=1337) in [0,1]'
|
||||
- '["a", "b"]|random in ["a", "b"]'
|
||||
- '20|random(start=10) in range(10, 20)'
|
||||
- '20|random(start=10, step=2) % 2 == 0'
|
||||
- random_fail_1 is failure
|
||||
- '"random can only be used on" in random_fail_1.msg'
|
||||
- random_fail_2 is failure
|
||||
- '"start and step can only be used" in random_fail_2.msg'
|
||||
- random_fail_3 is failure
|
||||
- '"start and step can only be used" in random_fail_3.msg'
|
||||
|
||||
# It's hard to actually verify much here since the result is, well, random.
|
||||
- name: Verify randomize_list
|
||||
assert:
|
||||
that:
|
||||
- '[1,3,5,7,9]|shuffle|length == 5'
|
||||
- '[1,3,5,7,9]|shuffle(seed=1337)|length == 5'
|
||||
- '22|shuffle == 22'
|
||||
|
||||
- name: Verify password_hash throws on weird salt_size type
|
||||
set_fact:
|
||||
foo: '{{"hey"|password_hash(salt_size=[999])}}'
|
||||
ignore_errors: yes
|
||||
register: password_hash_1
|
||||
|
||||
- name: Verify password_hash throws on weird hashtype
|
||||
set_fact:
|
||||
foo: '{{"hey"|password_hash(hashtype="supersecurehashtype")}}'
|
||||
ignore_errors: yes
|
||||
register: password_hash_2
|
||||
|
||||
- name: Verify password_hash
|
||||
assert:
|
||||
that:
|
||||
- "'what in the WORLD is up?'|password_hash|length == 106"
|
||||
# This throws a vastly different error on py2 vs py3, so we just check
|
||||
# that it's a failure, not a substring of the exception.
|
||||
- password_hash_1 is failed
|
||||
- password_hash_2 is failed
|
||||
- "'not support' in password_hash_2.msg"
|
||||
|
||||
- name: Verify to_uuid throws on weird namespace
|
||||
set_fact:
|
||||
foo: '{{"hey"|to_uuid(namespace=22)}}'
|
||||
ignore_errors: yes
|
||||
register: to_uuid_1
|
||||
|
||||
- name: Verify to_uuid
|
||||
assert:
|
||||
that:
|
||||
- '"monkeys"|to_uuid == "0d03a178-da0f-5b51-934e-cda9c76578c3"'
|
||||
- to_uuid_1 is failed
|
||||
- '"Invalid value" in to_uuid_1.msg'
|
||||
|
||||
- name: Verify mandatory throws on undefined variable
|
||||
set_fact:
|
||||
foo: '{{hey|mandatory}}'
|
||||
ignore_errors: yes
|
||||
register: mandatory_1
|
||||
|
||||
- name: Verify mandatory throws on undefined variable with custom message
|
||||
set_fact:
|
||||
foo: '{{hey|mandatory("You did not give me a variable. I am a sad wolf.")}}'
|
||||
ignore_errors: yes
|
||||
register: mandatory_2
|
||||
|
||||
- name: Set a variable
|
||||
set_fact:
|
||||
mandatory_demo: 123
|
||||
|
||||
- name: Verify mandatory
|
||||
assert:
|
||||
that:
|
||||
- '{{mandatory_demo|mandatory}} == 123'
|
||||
- mandatory_1 is failed
|
||||
- "mandatory_1.msg == \"Mandatory variable 'hey' not defined.\""
|
||||
- mandatory_2 is failed
|
||||
- "mandatory_2.msg == 'You did not give me a variable. I am a sad wolf.'"
|
||||
|
||||
- name: Verify comment
|
||||
assert:
|
||||
that:
|
||||
- '"boo!"|comment == "#\n# boo!\n#"'
|
||||
- '"boo!"|comment(decoration="-- ") == "--\n-- boo!\n--"'
|
||||
- '"boo!"|comment(style="cblock") == "/*\n *\n * boo!\n *\n */"'
|
||||
- '"boo!"|comment(decoration="") == "boo!\n"'
|
||||
- '"boo!"|comment(prefix="\n", prefix_count=20) == "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# boo!\n#"'
|
||||
|
||||
- name: Verify subelements throws on invalid obj
|
||||
set_fact:
|
||||
foo: '{{True|subelements("foo")}}'
|
||||
ignore_errors: yes
|
||||
register: subelements_1
|
||||
|
||||
- name: Verify subelements throws on invalid subelements arg
|
||||
set_fact:
|
||||
foo: '{{{}|subelements(17)}}'
|
||||
ignore_errors: yes
|
||||
register: subelements_2
|
||||
|
||||
- name: Set demo data for subelements
|
||||
set_fact:
|
||||
subelements_demo: '{{ [{"name": "alice", "groups": ["wheel"], "authorized": ["/tmp/alice/onekey.pub"]}] }}'
|
||||
|
||||
- name: Verify subelements throws on bad key
|
||||
set_fact:
|
||||
foo: '{{subelements_demo | subelements("does not compute")}}'
|
||||
ignore_errors: yes
|
||||
register: subelements_3
|
||||
|
||||
- name: Verify subelements throws on key pointing to bad value
|
||||
set_fact:
|
||||
foo: '{{subelements_demo | subelements("name")}}'
|
||||
ignore_errors: yes
|
||||
register: subelements_4
|
||||
|
||||
- name: Verify subelements throws on list of keys ultimately pointing to bad value
|
||||
set_fact:
|
||||
foo: '{{subelements_demo | subelements(["groups", "authorized"])}}'
|
||||
ignore_errors: yes
|
||||
register: subelements_5
|
||||
|
||||
- name: Verify subelements
|
||||
assert:
|
||||
that:
|
||||
- subelements_1 is failed
|
||||
- 'subelements_1.msg == "obj must be a list of dicts or a nested dict"'
|
||||
- subelements_2 is failed
|
||||
- 'subelements_2.msg == "subelements must be a list or a string"'
|
||||
- 'subelements_demo|subelements("does not compute", skip_missing=True) == []'
|
||||
- subelements_3 is failed
|
||||
- '"could not find" in subelements_3.msg'
|
||||
- subelements_4 is failed
|
||||
- '"should point to a list" in subelements_4.msg'
|
||||
- subelements_5 is failed
|
||||
- '"should point to a dictionary" in subelements_5.msg'
|
||||
- 'subelements_demo|subelements("groups") == [({"name": "alice", "groups": ["wheel"], "authorized": ["/tmp/alice/onekey.pub"]}, "wheel")]'
|
||||
- 'subelements_demo|subelements(["groups"]) == [({"name": "alice", "groups": ["wheel"], "authorized": ["/tmp/alice/onekey.pub"]}, "wheel")]'
|
||||
|
||||
|
||||
- name: Verify dict2items throws on non-Mapping
|
||||
set_fact:
|
||||
foo: '{{True|dict2items}}'
|
||||
ignore_errors: yes
|
||||
register: dict2items_fail
|
||||
|
||||
- name: Verify dict2items
|
||||
assert:
|
||||
that:
|
||||
- '{"foo": "bar", "banana": "fruit"}|dict2items == [{"key": "foo", "value": "bar"}, {"key": "banana", "value": "fruit"}]'
|
||||
- dict2items_fail is failed
|
||||
- '"dict2items requires a dictionary" in dict2items_fail.msg'
|
||||
|
||||
- name: Verify items2dict throws on non-Mapping
|
||||
set_fact:
|
||||
foo: '{{True|items2dict}}'
|
||||
ignore_errors: yes
|
||||
register: items2dict_fail
|
||||
|
||||
- name: Verify items2dict
|
||||
assert:
|
||||
that:
|
||||
- '[{"key": "foo", "value": "bar"}, {"key": "banana", "value": "fruit"}]|items2dict == {"foo": "bar", "banana": "fruit"}'
|
||||
- items2dict_fail is failed
|
||||
- '"items2dict requires a list" in items2dict_fail.msg'
|
||||
|
||||
- name: Verify path_join throws on non-string and non-sequence
|
||||
set_fact:
|
||||
foo: '{{True|path_join}}'
|
||||
ignore_errors: yes
|
||||
register: path_join_fail
|
||||
|
||||
- name: Verify path_join
|
||||
assert:
|
||||
that:
|
||||
- '"foo"|path_join == "foo"'
|
||||
- '["foo", "bar"]|path_join in ["foo/bar", "foo\bar"]'
|
||||
- path_join_fail is failed
|
||||
- '"expects string or sequence" in path_join_fail.msg'
|
||||
|
||||
- name: Verify type_debug
|
||||
assert:
|
||||
that:
|
||||
- '"foo"|type_debug == "str"'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
This is a test of various filter plugins found in Ansible (ex: core.py), and
|
||||
This is a test of various filter plugins found in Ansible (ex: core.py), and
|
||||
not so much a test of the core filters in Jinja2.
|
||||
|
||||
Dumping the same structure to YAML
|
||||
|
|
Loading…
Reference in a new issue