#!/usr/bin/env python3 import os import re import unittest # Base directory for roles (adjust if needed) BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../roles')) class TestModeResetIntegration(unittest.TestCase): """ Verify that a role either mentioning 'MODE_RESET' under tasks/ OR containing a reset file: - provides a *_reset.yml (or reset.yml) in tasks/, - includes it exactly once across tasks/*.yml, - and the include is guarded in the SAME task block by a non-commented `when` that contains `MODE_RESET | bool` (inline, list, or array). Additional conditions (e.g., `and something`) are allowed. Commented-out conditions (e.g., `#when: ...` or `# include_tasks: ...`) do NOT count. """ def test_mode_reset_tasks(self): for role_name in os.listdir(BASE_DIR): with self.subTest(role=role_name): role_path = os.path.join(BASE_DIR, role_name) tasks_dir = os.path.join(role_path, 'tasks') if not os.path.isdir(tasks_dir): self.skipTest(f"Role '{role_name}' has no tasks directory.") # Gather all task files task_files = [] for root, _, files in os.walk(tasks_dir): for fname in files: if fname.lower().endswith(('.yml', '.yaml')): task_files.append(os.path.join(root, fname)) # Detect any *positive* 'MODE_RESET | bool' usage. # Purely negated conditions like `not MODE_RESET | bool` # should NOT force a reset.yml, because the role only # disables behaviour during reset but has no reset logic. mode_reset_found = False mode_reset_pat = re.compile(r'(? bool: # new task begins with "- " at current indentation return re.match(r'^\s*-\s', line) is not None # Expand upwards to task start start_idx = include_line_idx while start_idx > 0 and not is_task_start(lines[start_idx]): start_idx -= 1 # Expand downwards to next task start or EOF end_idx = include_line_idx while end_idx + 1 < len(lines) and not is_task_start(lines[end_idx + 1]): end_idx += 1 task_block = "\n".join(lines[start_idx:end_idx + 1]) # Build regexes that: # - DO NOT match commented lines (require ^\s*when: not preceded by '#') # - Allow additional conditions inline (and/or/parentheses/etc.) # - Support list form and yaml array form when_inline = re.search( r'(?m)^(?