1
+ #!/usr/bin/env python3
2
+ """
3
+ Test that Ansible variables produce proper boolean types, not strings.
4
+ This prevents issues with Ansible 12.0.0's strict type checking.
5
+ """
6
+
7
+ import jinja2
8
+ import pytest
9
+
10
+
11
+ def render_template (template_str , variables = None ):
12
+ """Render a Jinja2 template with given variables."""
13
+ env = jinja2 .Environment ()
14
+ template = env .from_string (template_str )
15
+ return template .render (variables or {})
16
+
17
+
18
+ class TestBooleanVariables :
19
+ """Test that critical variables produce actual booleans."""
20
+
21
+ def test_ipv6_support_produces_boolean (self ):
22
+ """Ensure ipv6_support produces boolean, not string 'true'/'false'."""
23
+ # Test with gateway defined (should be boolean True)
24
+ template = "{{ ansible_default_ipv6['gateway'] is defined }}"
25
+ vars_with_gateway = {'ansible_default_ipv6' : {'gateway' : 'fe80::1' }}
26
+ result = render_template (template , vars_with_gateway )
27
+ assert result == "True" # Jinja2 renders boolean True as string "True"
28
+
29
+ # Test without gateway (should be boolean False)
30
+ vars_no_gateway = {'ansible_default_ipv6' : {}}
31
+ result = render_template (template , vars_no_gateway )
32
+ assert result == "False" # Jinja2 renders boolean False as string "False"
33
+
34
+ # The key is that we're NOT producing string literals "true" or "false"
35
+ bad_template = "{% if ansible_default_ipv6['gateway'] is defined %}true{% else %}false{% endif %}"
36
+ result_bad = render_template (bad_template , vars_no_gateway )
37
+ assert result_bad == "false" # This is a string literal, not a boolean
38
+
39
+ # Verify our fix doesn't produce string literals
40
+ assert result != "false" # Our fix produces "False" (from boolean), not "false" (string literal)
41
+
42
+ def test_algo_variables_boolean_fallbacks (self ):
43
+ """Ensure algo_* variables produce booleans in their fallback cases."""
44
+ # Test the fixed template (produces boolean)
45
+ good_template = "{% if var is defined %}{{ var | bool }}{%- else %}{{ false }}{% endif %}"
46
+ result_good = render_template (good_template , {})
47
+ assert result_good == "False" # Boolean False renders as "False"
48
+
49
+ # Test the old broken template (produces string)
50
+ bad_template = "{% if var is defined %}{{ var | bool }}{%- else %}false{% endif %}"
51
+ result_bad = render_template (bad_template , {})
52
+ assert result_bad == "false" # String literal "false"
53
+
54
+ # Verify they're different
55
+ assert result_good != result_bad
56
+ assert result_good == "False" and result_bad == "false"
57
+
58
+ def test_boolean_filter_on_strings (self ):
59
+ """Test that the bool filter correctly converts string values."""
60
+ # Since we can't test Ansible's bool filter directly in Jinja2,
61
+ # we test the pattern we're using in our templates
62
+
63
+ # Test that our templates don't use raw string "true"/"false"
64
+ # which would fail in Ansible 12
65
+ bad_pattern = "{%- else %}false{% endif %}"
66
+ good_pattern = "{%- else %}{{ false }}{% endif %}"
67
+
68
+ # The bad pattern produces a string literal
69
+ result_bad = render_template ("{% if var is defined %}something" + bad_pattern , {})
70
+ assert "false" in result_bad # String literal
71
+
72
+ # The good pattern produces a boolean value
73
+ result_good = render_template ("{% if var is defined %}something" + good_pattern , {})
74
+ assert "False" in result_good # Boolean False rendered as "False"
75
+
76
+ def test_ansible_12_conditional_compatibility (self ):
77
+ """
78
+ Test that our fixes work with Ansible 12's strict type checking.
79
+ This simulates what Ansible 12 will do with our variables.
80
+ """
81
+ # Our fixed template - produces actual boolean
82
+ fixed_ipv6 = "{{ ansible_default_ipv6['gateway'] is defined }}"
83
+ fixed_algo = "{% if var is defined %}{{ var | bool }}{%- else %}{{ false }}{% endif %}"
84
+
85
+ # Simulate the boolean value in a conditional context
86
+ # In Ansible 12, this would fail if it's a string "true"/"false"
87
+ vars_with_gateway = {'ansible_default_ipv6' : {'gateway' : 'fe80::1' }}
88
+ ipv6_result = render_template (fixed_ipv6 , vars_with_gateway )
89
+
90
+ # The result should be "True" (boolean rendered), not "true" (string literal)
91
+ assert ipv6_result == "True"
92
+ assert ipv6_result != "true"
93
+
94
+ # Test algo variable fallback
95
+ algo_result = render_template (fixed_algo , {})
96
+ assert algo_result == "False"
97
+ assert algo_result != "false"
98
+
99
+ def test_regression_no_string_booleans (self ):
100
+ """
101
+ Regression test: ensure we never produce string literals 'true' or 'false'.
102
+ This is what breaks Ansible 12.0.0.
103
+ """
104
+ # These patterns should NOT appear in our fixed code
105
+ bad_patterns = [
106
+ "{}true{}" ,
107
+ "{}false{}" ,
108
+ "{%- else %}true{% endif %}" ,
109
+ "{%- else %}false{% endif %}" ,
110
+ ]
111
+
112
+ # Our fixes should use these patterns instead
113
+ good_patterns = [
114
+ "{{ true }}" ,
115
+ "{{ false }}" ,
116
+ "is defined" ,
117
+ "| bool" ,
118
+ ]
119
+
120
+ # Test that our fixed templates don't produce string boolean literals
121
+ fixed_template = "{{ ansible_default_ipv6['gateway'] is defined }}"
122
+ for pattern in bad_patterns :
123
+ assert "true" not in fixed_template .replace (" " , "" )
124
+ assert "false" not in fixed_template .replace (" " , "" )
125
+
126
+ # Test algo variable fix
127
+ fixed_algo = "{% if var is defined %}{{ var | bool }}{%- else %}{{ false }}{% endif %}"
128
+ assert "{}false{}" not in fixed_algo .replace (" " , "" )
129
+ assert "{{ false }}" in fixed_algo
0 commit comments