Files
computer-playbook/tests/integration/roles/applications/id/test_unique.py
kevinveenbirkenbachandClaude Opus 4.7 f6c8c85b1b test(lint): role-file-spot guard + 139-file mapping migration
Add tests/lint/repository/test_role_file_spot.py: AST-walks every .py
file under the production trees and tests/, flags any reference to a
role file (standalone literal, f-string segment, pathlib `/` chain,
`+` concat chain, os.path.join / Path.joinpath / posixpath.join /
ntpath.join call) whose value or joined trailing operands match a
ROLE_FILE_* constant in utils.roles.mapping. The flag set is
auto-discovered from the module so adding a new constant extends
coverage without further edits. ROLE_FILE_README is exempt as too
generic. Per-line `# nocheck: role-file-spot` (same-or-above) is the
documented opt-out, catalog row added in
docs/contributing/actions/testing/suppression.md.

Migrate 139 .py files (377 occurrences) to use the constants:

- 135 files via direct constant import (`from utils.roles.mapping
  import ROLE_FILE_*`), with the symbol used in pathlib chains, in
  os.path.join / Path.joinpath calls, in f-string interpolation, and
  in glob patterns.
- 4 files via `# nocheck: role-file-spot` where the literal lives in
  argparse help text or in an allowlist tuple meant to declare the
  literal path.

Adjust utils/roles/dependency_resolver.py to use lazy-formatting
positional args in the logger.exception call (G003-clean) instead of
string concatenation against the constant.

Drop a redundant `if cond: return True; return False` guard in the
new test for `return cond` (SIM103-clean).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 13:21:22 +02:00

42 lines
1.3 KiB
Python

import glob
import unittest
from utils.cache.yaml import load_yaml_any
from utils.roles.mapping import ROLE_FILE_VARS_MAIN
from . import PROJECT_ROOT
def find_application_ids():
"""
Scans all roles/*/vars/main.yml files and collects application_id values.
Returns a dict mapping application_id to list of file paths where it appears.
"""
ids = {}
pattern = str(PROJECT_ROOT / "roles" / "*" / ROLE_FILE_VARS_MAIN)
for file_path in glob.glob(pattern): # nocheck: project-walk
data = load_yaml_any(file_path) or {}
app_id = data.get("application_id")
if app_id is not None:
ids.setdefault(app_id, []).append(file_path)
return ids
class TestUniqueApplicationId(unittest.TestCase):
def test_application_ids_are_unique(self):
ids = find_application_ids()
duplicates = {app_id: paths for app_id, paths in ids.items() if len(paths) > 1}
if duplicates:
messages = []
for app_id, paths in duplicates.items():
file_list = "\n ".join(paths)
messages.append(
f"application_id '{app_id}' found in multiple files:\n {file_list}"
)
self.fail("\n\n".join(messages))
if __name__ == "__main__":
unittest.main(verbosity=2)