Finished integration test for dependencies

This commit is contained in:
Kevin Veen-Birkenbach 2025-05-20 06:29:17 +02:00
parent a51a474cb3
commit 23ccaca3aa
No known key found for this signature in database
GPG Key ID: 44D8F11FD62F878E

View File

@ -1,7 +1,6 @@
import unittest import unittest
import os import os
import yaml import yaml
import warnings
def load_yaml_file(file_path): def load_yaml_file(file_path):
"""Load a YAML file and return its content.""" """Load a YAML file and return its content."""
@ -9,93 +8,60 @@ def load_yaml_file(file_path):
return yaml.safe_load(file) or {} return yaml.safe_load(file) or {}
def get_meta_info(role_path): def get_meta_info(role_path):
"""Extract dependencies and run orders (before/after) from the meta/main.yml of a role.""" """Extract dependencies from the meta/main.yml of a role."""
meta_file = os.path.join(role_path, 'meta', 'main.yml') meta_file = os.path.join(role_path, 'meta', 'main.yml')
if not os.path.isfile(meta_file): if not os.path.isfile(meta_file):
return [], [], [] return []
meta_data = load_yaml_file(meta_file) meta_data = load_yaml_file(meta_file)
dependencies = meta_data.get('dependencies', []) dependencies = meta_data.get('dependencies', [])
run_order = meta_data.get('applications_run_order', {}) return dependencies
return dependencies, run_order.get('before', []), run_order.get('after', [])
def resolve_dependencies(roles_dir): def resolve_dependencies(roles_dir):
"""Resolve all role dependencies and detect circular dependencies.""" """Resolve all role dependencies and detect circular dependencies."""
visited = set() visited = set() # Tracks roles that have been processed
stack = set()
def visit(role_path): def visit(role_path, stack):
role_name = os.path.basename(role_path) role_name = os.path.basename(role_path)
# Check for circular dependencies # Check for circular dependencies (if role is already in the current stack)
if role_name in stack: if role_name in stack:
warning_msg = f"Circular dependency detected with role: {role_name} in path: {' -> '.join(stack)}" raise ValueError(f"Circular dependency detected: {' -> '.join(stack)} -> {role_name}")
warnings.warn(warning_msg)
raise ValueError(f"Circular dependency detected with role: {role_name} in path: {' -> '.join(stack)}")
# Check if role is already processed # Check if role is already processed
if role_name in visited: if role_name in visited:
return [], [] return []
# Mark role as being processed # Mark role as visited and add to stack
stack.add(role_name)
visited.add(role_name) visited.add(role_name)
stack.append(role_name)
# Get dependencies and before/after orders # Get dependencies and resolve them
dependencies, before, after = get_meta_info(role_path) dependencies = get_meta_info(role_path)
# Resolve before dependencies
before_order, after_order = [], []
for dep in dependencies: for dep in dependencies:
# Add more detailed debug information for `dep` dep_path = os.path.join(roles_dir, dep)
try: visit(dep_path, stack) # Recurse into dependencies
print(f"Visiting dependency: {dep} for role {role_name}, role_path: {role_path}")
# Check if dep is a dictionary and extract the role name if necessary
if isinstance(dep, dict):
print(f"Dependency {dep} is a dictionary. Extracting role name...")
dep = list(dep.keys())[0] # Extract the key if it's a dictionary
dep_path = os.path.join(roles_dir, dep)
print(f"Resolved dependency path: {dep_path}")
dep_before, dep_after = visit(dep_path) # Recurse into dependencies stack.pop() # Remove the current role from the stack
before_order.extend(dep_before)
after_order.extend(dep_after)
except Exception as e:
# Print error message with role path to help with debugging
raise ValueError(f"Error in role {role_name} while processing dependency {dep} at path {role_path}: {str(e)}")
# Add the current role's own before/after orders
before_order.extend(before)
after_order.extend(after)
# Mark role as processed
stack.remove(role_name)
return before_order, after_order
all_run_orders = {}
for role_name in os.listdir(roles_dir): for role_name in os.listdir(roles_dir):
role_path = os.path.join(roles_dir, role_name) role_path = os.path.join(roles_dir, role_name)
if os.path.isdir(role_path): if os.path.isdir(role_path):
try: try:
all_run_orders[role_path] = visit(role_path) visit(role_path, []) # Start recursion from this role
except ValueError as e: except ValueError as e:
# Improved error reporting with role path information
raise ValueError(f"Error processing role '{role_name}' at path '{role_path}': {str(e)}") raise ValueError(f"Error processing role '{role_name}' at path '{role_path}': {str(e)}")
return all_run_orders
class TestRoleDependencies(unittest.TestCase): class TestRoleDependencies(unittest.TestCase):
def test_no_circular_dependencies(self): def test_no_circular_dependencies(self):
roles_dir = "roles" roles_dir = "roles" # Path to the roles directory
try: try:
run_orders = resolve_dependencies(roles_dir) resolve_dependencies(roles_dir)
except ValueError as e: except ValueError as e:
self.fail(f"Circular dependency detected: {e}") self.fail(f"Circular dependency detected: {e}")
# If no exception, the test passed # If no exception, the test passed
self.assertIsInstance(run_orders, dict) self.assertTrue(True)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()