Files
algo/tests/unit/test_boolean_variables.py
21e21747ea ci: modernize tooling with prek, ty, and security scanning (#14956)
* ci: modernize tooling with prek, ty, and security scanning

Migrate from pre-commit to prek (Rust-native, faster hooks) and add
comprehensive CI improvements for code quality and security.

Changes:
- Replace pre-commit with prek for git hooks
- Add ty type checker (Rust-based, replaces mypy)
- Expand ruff rules: security (S), simplify (SIM), commented code (ERA)
- Add pip-audit workflow for Python dependency CVE scanning
- Add actionlint and zizmor for GitHub Actions linting/security
- Add ruff format check to CI
- Enable stricter ansible-lint rules (no-changed-when, risky-file-permissions)
- Remove obsolete Claude workflow files
- Apply ruff formatting fixes to test files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): resolve actionlint install and ty type errors

- Use actionlint's official install script instead of broken URL pattern
- Exclude test mock modules from ty type checking
- Run workflows on push only for main/master to avoid duplicate PR runs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): use glob pattern for actionlint, exclude all tests from ty

- actionlint requires *.yml glob, not directory path
- Exclude all tests from ty type checking (test code has looser typing)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): quote shell variables to fix shellcheck warnings

Fix SC2046/SC2086 warnings in workflow scripts:
- Quote $(uname -r) in apt-get install
- Quote $(pwd) in docker volume mount
- Quote $existing in gh issue comment

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(ci): move key-order[task] to warn_list

Too many existing violations in the codebase to enable as error.
Move to warn_list for gradual fixes over time.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 00:10:05 -05:00

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