Files
computer-playbook/tests/integration/test_uppercase_constant_vars_unique.py
Kevin Veen-Birkenbach cb66fb2978 Refactor LDAP variable schema to use top-level constant LDAP and nested ALL-CAPS keys.
- Converted group_vars/all/13_ldap.yml from lower-case to ALL-CAPS nested keys.
- Updated all roles, tasks, templates, and filter_plugins to reference LDAP.* instead of ldap.*.
- Fixed Keycloak JSON templates to properly quote Jinja variables.
- Adjusted svc-db-openldap filter plugins and unit tests to handle new LDAP structure.
- Updated integration test to only check uniqueness of TOP-LEVEL ALL-CAPS constants, ignoring nested keys.

See: https://chatgpt.com/share/68b01017-efe0-800f-a508-7d7e2f1c8c8d
2025-08-28 10:15:48 +02:00

121 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""
Integration test: ensure every TOP-LEVEL ALL-CAPS variable is defined only once project-wide.
Scope (by design):
- group_vars/**/*.yml
- roles/*/vars/*.yml
- roles/*/defaults/*.yml
- roles/*/defauls/*.yml # included on purpose in case of folder typos
A variable is considered a “constant” if its KEY (at the top level of a YAML document)
matches: ^[A-Z0-9_]+$
Only TOP-LEVEL keys are checked for uniqueness. Nested keys are ignored to allow
namespacing like DICTIONARYA.ENTRY and DICTIONARYB.ENTRY without conflicts.
"""
import os
import glob
import unittest
import re
from collections import defaultdict
try:
import yaml
except Exception as e: # pragma: no cover
raise SystemExit(
"PyYAML is required for this test. Install with: pip install pyyaml"
) from e
UPPER_CONST_RE = re.compile(r"^[A-Z0-9_]+$")
def _iter_yaml_files():
"""Yield all YAML file paths in the intended scope."""
patterns = [
os.path.join("group_vars", "**", "*.yml"),
os.path.join("roles", "*", "vars", "*.yml"),
os.path.join("roles", "*", "defaults", "*.yml"),
os.path.join("roles", "*", "defauls", "*.yml"), # intentionally included
]
seen = set()
for pattern in patterns:
for path in glob.glob(pattern, recursive=True):
norm = os.path.normpath(path)
if norm not in seen and os.path.isfile(norm):
seen.add(norm)
yield norm
def _extract_top_level_uppercase_keys(docs):
"""
Return a set of TOP-LEVEL ALL-CAPS keys found across all mapping documents in a file.
Nested keys are intentionally ignored.
"""
found = set()
for doc in docs:
if isinstance(doc, dict):
for k in doc.keys():
if isinstance(k, str) and UPPER_CONST_RE.match(k):
found.add(k)
return found
class TestUppercaseConstantVarsUnique(unittest.TestCase):
def test_uppercase_constants_unique(self):
# Track where each TOP-LEVEL constant is defined
constant_to_files = defaultdict(set)
# Track YAML parse errors to fail with a helpful message
parse_errors = []
yaml_files = list(_iter_yaml_files())
for yml in yaml_files:
try:
with open(yml, "r", encoding="utf-8") as f:
docs = list(yaml.safe_load_all(f))
except Exception as e:
parse_errors.append(f"{yml}: {e}")
continue
if not docs:
continue
file_constants = _extract_top_level_uppercase_keys(docs)
for const in file_constants:
constant_to_files[const].add(yml)
if parse_errors:
self.fail(
"YAML parsing failed for one or more files:\n"
+ "\n".join(f"- {err}" for err in parse_errors)
)
# Duplicates are same TOP-LEVEL constant appearing in >1 files
duplicates = {
c: sorted(files)
for c, files in constant_to_files.items()
if len(files) > 1
}
if duplicates:
msg_lines = [
"Found TOP-LEVEL constants defined more than once. "
"ALL-CAPS top-level variables are treated as constants and must be defined only once project-wide.\n"
"Nested ALL-CAPS keys are allowed and ignored by this test.",
"",
]
for const, files in sorted(duplicates.items()):
msg_lines.append(f"* {const} defined in {len(files)} files:")
for f in files:
msg_lines.append(f" - {f}")
msg_lines.append("") # spacer
self.fail("\n".join(msg_lines))
if __name__ == "__main__":
unittest.main()