Phase 1: Quick wins for code quality and test infrastructure (#14907)

- Replace ignore_errors: true with failed_when: false in 5 files
  (main.yml, users.yml, ubuntu.yml, umount.yml, test-wireguard-real-async.yml)
- Add pytest.ini configuration for test discovery
- Add tests/conftest.py with shared fixtures and mock helpers

The failed_when: false pattern is preferred by ansible-lint as it
explicitly indicates expected failure handling rather than silently
ignoring all errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Dan Guido
2025-11-28 17:02:17 -05:00
committed by GitHub
co-authored by Claude
parent ff4b853fa8
commit f4e2b8c9e7
7 changed files with 165 additions and 5 deletions
+1 -1
View File
@@ -18,7 +18,7 @@
- name: Ensure the requirements installed - name: Ensure the requirements installed
debug: debug:
msg: "{{ '192.168.1.1' | ansible.utils.ipaddr }}" msg: "{{ '192.168.1.1' | ansible.utils.ipaddr }}"
ignore_errors: true failed_when: false
no_log: true no_log: true
register: ipaddr register: ipaddr
+1 -1
View File
@@ -8,7 +8,7 @@
- block: - block:
- name: MacOS | check fs the ramdisk exists - name: MacOS | check fs the ramdisk exists
command: /usr/sbin/diskutil info "{{ facts.tmpfs_volume_name }}" command: /usr/sbin/diskutil info "{{ facts.tmpfs_volume_name }}"
ignore_errors: true failed_when: false
changed_when: false changed_when: false
register: diskutil_info register: diskutil_info
+9
View File
@@ -0,0 +1,9 @@
[pytest]
testpaths = tests/unit
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --tb=short
filterwarnings =
ignore::DeprecationWarning
ignore::PendingDeprecationWarning
+1 -1
View File
@@ -105,7 +105,7 @@
- name: Check apparmor support - name: Check apparmor support
command: apparmor_status command: apparmor_status
ignore_errors: true failed_when: false
changed_when: false changed_when: false
register: apparmor_status register: apparmor_status
+151
View File
@@ -0,0 +1,151 @@
"""Shared pytest fixtures for Algo VPN tests."""
import base64
import secrets
import sys
import tempfile
from pathlib import Path
import pytest
import yaml
# Add library directory to path for custom module imports
sys.path.insert(0, str(Path(__file__).parent.parent / "library"))
@pytest.fixture
def test_variables():
"""Load test variables from YAML fixture."""
fixture_path = Path(__file__).parent / "fixtures" / "test_variables.yml"
with open(fixture_path) as f:
return yaml.safe_load(f)
@pytest.fixture
def test_config(test_variables):
"""Get test configuration with common defaults."""
return test_variables.copy()
@pytest.fixture
def temp_directory():
"""Create a temporary directory for test files."""
with tempfile.TemporaryDirectory() as tmpdir:
yield Path(tmpdir)
@pytest.fixture
def wireguard_private_key():
"""Generate a random WireGuard-compatible private key."""
raw_key = secrets.token_bytes(32)
return base64.b64encode(raw_key).decode()
@pytest.fixture
def wireguard_key_pair(temp_directory):
"""Generate a WireGuard key pair and return paths and values."""
raw_key = secrets.token_bytes(32)
b64_key = base64.b64encode(raw_key).decode()
private_key_path = temp_directory / "private.key"
private_key_path.write_bytes(raw_key)
return {
"private_key_raw": raw_key,
"private_key_b64": b64_key,
"private_key_path": str(private_key_path),
}
class MockAnsibleModule:
"""Mock AnsibleModule for testing custom Ansible modules."""
def __init__(self, params):
"""Initialize with module parameters."""
self.params = params
self.result = {}
self.failed = False
self.fail_msg = None
def fail_json(self, **kwargs):
"""Record failure and raise exception."""
self.failed = True
self.fail_msg = kwargs.get("msg", "Unknown error")
raise Exception(f"Module failed: {self.fail_msg}")
def exit_json(self, **kwargs):
"""Record successful result."""
self.result = kwargs
@pytest.fixture
def mock_ansible_module():
"""Fixture providing MockAnsibleModule class."""
return MockAnsibleModule
# Jinja2 mock filters for template testing
def mock_to_uuid(value):
"""Mock the to_uuid filter."""
return "12345678-1234-5678-1234-567812345678"
def mock_bool(value):
"""Mock the bool filter."""
return str(value).lower() in ("true", "1", "yes", "on")
def mock_lookup(lookup_type, path):
"""Mock the lookup function."""
if lookup_type == "file":
if "private" in path:
return "MOCK_PRIVATE_KEY_BASE64=="
elif "public" in path:
return "MOCK_PUBLIC_KEY_BASE64=="
elif "preshared" in path:
return "MOCK_PRESHARED_KEY_BASE64=="
return "MOCK_LOOKUP_DATA"
@pytest.fixture
def jinja2_env():
"""Create a Jinja2 environment with mock Ansible filters."""
from jinja2 import Environment, FileSystemLoader, StrictUndefined
def create_env(template_dir):
env = Environment(loader=FileSystemLoader(template_dir), undefined=StrictUndefined)
env.globals["lookup"] = mock_lookup
env.filters["to_uuid"] = mock_to_uuid
env.filters["bool"] = mock_bool
return env
return create_env
@pytest.fixture
def project_root():
"""Return the project root directory."""
return Path(__file__).parent.parent
@pytest.fixture
def roles_dir(project_root):
"""Return the roles directory."""
return project_root / "roles"
# Skip markers for conditional tests
def pytest_configure(config):
"""Register custom markers."""
config.addinivalue_line("markers", "requires_wireguard: mark test as requiring WireGuard tools")
config.addinivalue_line("markers", "slow: mark test as slow running")
@pytest.fixture(autouse=True)
def skip_wireguard_tests(request):
"""Skip tests marked with requires_wireguard if WireGuard tools aren't available."""
if request.node.get_closest_marker("requires_wireguard"):
import shutil
if not shutil.which("wg"):
pytest.skip("WireGuard tools not available")
+1 -1
View File
@@ -55,7 +55,7 @@
mode: "0600" mode: "0600"
when: item.changed when: item.changed
loop: "{{ wg_genkey_results.results }}" loop: "{{ wg_genkey_results.results }}"
ignore_errors: true failed_when: false
- name: Cleanup - name: Cleanup
file: file:
+1 -1
View File
@@ -92,7 +92,7 @@
port: "{{ ansible_ssh_port | default(ssh_port) | int }}" port: "{{ ansible_ssh_port | default(ssh_port) | int }}"
timeout: 10 timeout: 10
register: ssh_check register: ssh_check
ignore_errors: true failed_when: false
when: algo_server != 'localhost' when: algo_server != 'localhost'
- name: Fail with helpful message if server unreachable - name: Fail with helpful message if server unreachable