mirror of
https://github.com/trailofbits/algo.git
synced 2025-09-24 04:35:21 +02:00
* Fix Ansible 12.0.0 boolean type checking issue Ansible 12.0.0 enforces strict type checking for conditionals and no longer automatically converts string values "true"/"false" to booleans. This was causing deployment failures after the recent Ansible version bump. Changes: - Fix ipv6_support to use 'is defined' which returns boolean instead of string - Fix algo_* variables in input.yml to use {{ false }} instead of string "false" - Add comprehensive tests to prevent regression - Add mutation testing to verify tests catch the issue The fix uses native boolean expressions instead of string literals, ensuring compatibility with Ansible 12's strict type checking while maintaining backward compatibility. Fixes #14833 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix Python linting issues in test files - Fix import sorting and remove unused imports - Remove trailing whitespace and blank lines with whitespace - Use underscore prefix for unused loop variables - Remove unnecessary file open mode arguments - Add newlines at end of files - Remove unused variable assignments All ruff checks now pass. --------- Co-authored-by: Claude <noreply@anthropic.com>
121 lines
5.4 KiB
Python
121 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Test that Ansible variables produce proper boolean types, not strings.
|
|
This prevents issues with Ansible 12.0.0's strict type checking.
|
|
"""
|
|
|
|
import jinja2
|
|
|
|
|
|
def render_template(template_str, variables=None):
|
|
"""Render a Jinja2 template with given variables."""
|
|
env = jinja2.Environment()
|
|
template = env.from_string(template_str)
|
|
return template.render(variables or {})
|
|
|
|
|
|
class TestBooleanVariables:
|
|
"""Test that critical variables produce actual booleans."""
|
|
|
|
def test_ipv6_support_produces_boolean(self):
|
|
"""Ensure ipv6_support produces boolean, not string 'true'/'false'."""
|
|
# Test with gateway defined (should be boolean True)
|
|
template = "{{ ansible_default_ipv6['gateway'] is defined }}"
|
|
vars_with_gateway = {'ansible_default_ipv6': {'gateway': 'fe80::1'}}
|
|
result = render_template(template, vars_with_gateway)
|
|
assert result == "True" # Jinja2 renders boolean True as string "True"
|
|
|
|
# Test without gateway (should be boolean False)
|
|
vars_no_gateway = {'ansible_default_ipv6': {}}
|
|
result = render_template(template, vars_no_gateway)
|
|
assert result == "False" # Jinja2 renders boolean False as string "False"
|
|
|
|
# The key is that we're NOT producing string literals "true" or "false"
|
|
bad_template = "{% if ansible_default_ipv6['gateway'] is defined %}true{% else %}false{% endif %}"
|
|
result_bad = render_template(bad_template, vars_no_gateway)
|
|
assert result_bad == "false" # This is a string literal, not a boolean
|
|
|
|
# Verify our fix doesn't produce string literals
|
|
assert result != "false" # Our fix produces "False" (from boolean), not "false" (string literal)
|
|
|
|
def test_algo_variables_boolean_fallbacks(self):
|
|
"""Ensure algo_* variables produce booleans in their fallback cases."""
|
|
# Test the fixed template (produces boolean)
|
|
good_template = "{% if var is defined %}{{ var | bool }}{%- else %}{{ false }}{% endif %}"
|
|
result_good = render_template(good_template, {})
|
|
assert result_good == "False" # Boolean False renders as "False"
|
|
|
|
# Test the old broken template (produces string)
|
|
bad_template = "{% if var is defined %}{{ var | bool }}{%- else %}false{% endif %}"
|
|
result_bad = render_template(bad_template, {})
|
|
assert result_bad == "false" # String literal "false"
|
|
|
|
# Verify they're different
|
|
assert result_good != result_bad
|
|
assert result_good == "False" and result_bad == "false"
|
|
|
|
def test_boolean_filter_on_strings(self):
|
|
"""Test that the bool filter correctly converts string values."""
|
|
# Since we can't test Ansible's bool filter directly in Jinja2,
|
|
# we test the pattern we're using in our templates
|
|
|
|
# Test that our templates don't use raw string "true"/"false"
|
|
# which would fail in Ansible 12
|
|
bad_pattern = "{%- else %}false{% endif %}"
|
|
good_pattern = "{%- else %}{{ false }}{% endif %}"
|
|
|
|
# The bad pattern produces a string literal
|
|
result_bad = render_template("{% if var is defined %}something" + bad_pattern, {})
|
|
assert "false" in result_bad # String literal
|
|
|
|
# The good pattern produces a boolean value
|
|
result_good = render_template("{% if var is defined %}something" + good_pattern, {})
|
|
assert "False" in result_good # Boolean False rendered as "False"
|
|
|
|
def test_ansible_12_conditional_compatibility(self):
|
|
"""
|
|
Test that our fixes work with Ansible 12's strict type checking.
|
|
This simulates what Ansible 12 will do with our variables.
|
|
"""
|
|
# Our fixed template - produces actual boolean
|
|
fixed_ipv6 = "{{ ansible_default_ipv6['gateway'] is defined }}"
|
|
fixed_algo = "{% if var is defined %}{{ var | bool }}{%- else %}{{ false }}{% endif %}"
|
|
|
|
# Simulate the boolean value in a conditional context
|
|
# In Ansible 12, this would fail if it's a string "true"/"false"
|
|
vars_with_gateway = {'ansible_default_ipv6': {'gateway': 'fe80::1'}}
|
|
ipv6_result = render_template(fixed_ipv6, vars_with_gateway)
|
|
|
|
# The result should be "True" (boolean rendered), not "true" (string literal)
|
|
assert ipv6_result == "True"
|
|
assert ipv6_result != "true"
|
|
|
|
# Test algo variable fallback
|
|
algo_result = render_template(fixed_algo, {})
|
|
assert algo_result == "False"
|
|
assert algo_result != "false"
|
|
|
|
def test_regression_no_string_booleans(self):
|
|
"""
|
|
Regression test: ensure we never produce string literals 'true' or 'false'.
|
|
This is what breaks Ansible 12.0.0.
|
|
"""
|
|
# These patterns should NOT appear in our fixed code
|
|
bad_patterns = [
|
|
"{}true{}",
|
|
"{}false{}",
|
|
"{%- else %}true{% endif %}",
|
|
"{%- else %}false{% endif %}",
|
|
]
|
|
|
|
# Test that our fixed templates don't produce string boolean literals
|
|
fixed_template = "{{ ansible_default_ipv6['gateway'] is defined }}"
|
|
for _pattern in bad_patterns:
|
|
assert "true" not in fixed_template.replace(" ", "")
|
|
assert "false" not in fixed_template.replace(" ", "")
|
|
|
|
# Test algo variable fix
|
|
fixed_algo = "{% if var is defined %}{{ var | bool }}{%- else %}{{ false }}{% endif %}"
|
|
assert "{}false{}" not in fixed_algo.replace(" ", "")
|
|
assert "{{ false }}" in fixed_algo
|
|
|