import unittest from pathlib import Path import re import os import sys # Ensure your project root is on PYTHONPATH so filter_plugins can be imported ROOT = Path(__file__).parents[2] sys.path.insert(0, str(ROOT)) from filter_plugins.invokable_paths import get_invokable_paths STAGES_DIR = ROOT / "tasks" / "stages" GROUPS_DIR = ROOT / "tasks" / "groups" class TestMetaRolesIntegration(unittest.TestCase): @classmethod def setUpClass(cls): # Use the filter directly cls.role_files = get_invokable_paths(suffix="-roles.yml") cls.invokable_items = get_invokable_paths() # Read all playbook YAML contents under tasks/stages cls.playbook_contents = {} for path in STAGES_DIR.rglob("*.yml"): cls.playbook_contents[path] = path.read_text(encoding="utf-8") # Regex for include_tasks line with {{ item }}-roles.yml cls.include_pattern = re.compile( r'include_tasks:\s*["\']\./tasks/groups/\{\{\s*item\s*\}\}-roles\.yml["\']' ) def test_all_role_files_exist(self): """Each '-roles.yml' path returned by the filter must exist in the project root.""" missing = [] for fname in self.role_files: path = GROUPS_DIR / fname if not path.is_file(): missing.append(fname) self.assertFalse( missing, f"The following role files are missing at project root: {missing}" ) def test_each_invokable_item_referenced_in_playbooks(self): """ Each invokable item (without suffix) must be looped through in at least one playbook and include its corresponding include_tasks entry. """ not_referenced = [] for item in self.invokable_items: found = False loop_entry = re.compile(rf"-\s*{re.escape(item)}\b") for content in self.playbook_contents.values(): if self.include_pattern.search(content) and loop_entry.search(content): found = True break if not found: not_referenced.append(item) self.assertEqual( not_referenced, [], f"The following invokable items are not referenced in any playbook: {not_referenced}" ) if __name__ == "__main__": unittest.main()