Files
computer-playbook/docs/agents/action/testing.md
T
kevinveenbirkenbachandClaude Opus 4.7 b3de2a4d7c refactor(meta): land req-011 meta/info.yml + utils/cache cleanup
req-011 — descriptive role-level metadata
  * Move logo / homepage / video / display out of
    meta/main.yml.galaxy_info into a new project-owned
    roles/<role>/meta/info.yml (file-root convention, no `info:` wrapper).
  * 78 role meta/main.yml files migrated via ruamel round-trip
    (key-order / quoting / inline-comments preserved). Roles with none
    of the four fields are left untouched (no empty info.yml created).
  * web-app-dashboard/lookup_plugins/docker_cards.py reads logo +
    display from meta/info.yml; description / galaxy_tags continue to
    come from galaxy_info (Galaxy-spec fields).
  * docker_cards.py now routes ALL its file reads through the
    process-wide caches: load_yaml_any for vars/main.yml,
    meta/main.yml, meta/info.yml; read_text from utils.cache.files for
    README.md. The orphan `import yaml` is gone.

New utils/cache/files (renamed from tests/utils/fs)
  * The process-wide filesystem cache (read_text, iter_project_files,
    iter_project_files_with_content, _all_project_files) is not
    test-specific; it now lives under utils/cache/files.py next to the
    other process-wide caches. 34 consumer imports rewritten across
    tests/ and docs/. The empty tests/utils/ package is removed.
  * New unit suite tests/unit/utils/cache/test_files.py — 12 cases
    covering read_text, iter_project_files (skip-dirs, extensions,
    exclude_tests, exclude_dirs, walk-cache invariants), the combined
    iterator, non-UTF-8 silent skip, and _reset.

utils/cache redundancy cleanup
  * applications.py + users.py + cli/create/inventory/services_disabler.py
    drop their direct yaml.safe_load calls and their private
    _load_yaml_mapping/_load_yaml_variant_list copies; everything routes
    through utils.cache.yaml.load_yaml / load_yaml_any. The dead
    utils.cache.base._load_yaml_mapping is removed.
  * users.py:_load_store_users (tokens.yml read) likewise migrated.
  * _load_yaml_variant_list moves out of base.py to applications.py
    (its only consumer) and is renamed _load_variants_overrides — also
    routes through the cached loader now.
  * utils.cache.yaml._reset_cache_for_tests is renamed _reset (kept as
    alias) for parity with the per-domain reset helpers.
  * utils.cache.files grows a _reset() that clears its two LRU caches.
  * utils.cache.__init__._reset_cache_for_tests now orchestrates ALL
    six caches (applications, users, domains, base, yaml, files) so
    tests get clean state across cache layers — needed because
    applications/users now share the yaml cache and would otherwise
    serve stale parses across reset boundaries.
  * utils.cache.files imports PROJECT_ROOT from base instead of
    redefining it.
  * tests/unit/utils/cache/test_data.py + test_base.py: obsolete
    TestLoadYamlMapping / TestLoadYamlVariantList classes deleted
    (covered by test_yaml.py + test_variants.py).
  * README.md updated for the new module list (files, yaml) and the
    six-module orchestrator.

Galaxy schema lint
  * tests/lint/repository/test_role_meta_main_galaxy_schema.py: split
    galaxy_info field requirements into hard-required {author,
    description, license} and soft-recommended {min_ansible_version,
    platforms}. Soft-recommended -> emit a GitHub Actions ::warning::
    annotation via utils.annotations.message.warning (titled
    "meta/main.yml galaxy_info" with file= pointing at the offender),
    no test failure. Rationale: min_ansible_version + platforms are
    only enforced by Galaxy when *publishing*, which Infinito.Nexus
    does not do.
  * Galaxy lint goes green (was 226 offenders before req-011 +
    license_url/repository/documentation strip; was 62 before this
    relaxation; now 0 hard offenders, 62 soft warnings).
  * roles/svc-opt-keyboard-color/meta/main.yml: backfill `license`
    (only role missing the hard-required field).

New lint
  * tests/lint/repository/test_role_meta_info.py — enforces meta/info.yml
    file-root convention, the four-field allowlist (logo, homepage,
    video, display), and shape (logo.class string, homepage/video
    non-empty strings, display bool). Also fails if any
    meta/main.yml.galaxy_info still carries one of the four migrated
    fields.

Docs
  * docs/contributing/design/services/layout.md: new section
    "Descriptive Role-Level Metadata — meta/info.yml" with allowed-
    fields table, optionality rule, materialised path, and consumer
    note. File-Layout table grows the meta/info.yml row;
    meta/main.yml row gets the explicit "no logo/homepage/video/display"
    note. req-011 added to header and Related Pages.
  * utils/cache/README.md: files.py + yaml.py rows added; six-module
    reset orchestrator documented.

Validation
  * Unit:        1573 passed.
  * Lint:        55 passed.
  * Integration: 161 passed (1 skipped) via pre-commit hook.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 12:27:17 +02:00

3.0 KiB

Testing

Filesystem Scans — Use the Shared Cache

  • When writing or modifying a test that scans the project tree, you MUST route the walk and file reads through utils.cache.files — specifically iter_project_files(...), iter_project_files_with_content(...), or read_text(...). You MUST NOT introduce raw os.walk(<project-root>), Path(<project-root>).rglob("*"), or broad un-scoped globs on the repo tree in test code.
  • Reason: pytest runs every test in one process. Without a shared cache each test re-walks the same tree and re-reads the same files; the walk cost multiplies by the number of tests that use it. Migrating a single test from a raw walk to iter_project_files_with_content dropped it from ~26 s to ~1.3 s because every subsequent test served the walk and the file contents from the process-level cache.
  • When to skip the rule: narrow globs that hit a fixed small set of paths (e.g. roles_dir.glob("*/meta/services.yml")) do not benefit meaningfully; walks inside an isolated tempfile.TemporaryDirectory() fixture are out of scope because the cache is read-only by design. Even in those cases, use utils.cache.files.read_text for individual file contents so other tests reading the same path get a cache hit.
  • API reference: iter_project_files(extensions=…, exclude_tests=…, exclude_dirs=…) yields absolute paths, the walk is cached once per process. iter_project_files_with_content(…) yields (path, content) with both layers cached. read_text(path) returns cached UTF-8 content. The walker automatically prunes .git, .cache, .pytest_cache, .ruff_cache, .venv, venv, __pycache__, and node_modules.

Test Changes

  • After every change to a test file, you MUST run the corresponding validation command before continuing with any further action. Use the suite-selection rules in Testing and Validation.
  • You MUST NOT run more than one unit test command at the same time. Unit tests MUST be executed serially, never in parallel.
  • If a new test is created or an existing test has been changed since the last test run, you MUST rerun it after every subsequent action until it passes.
  • If the last test run for a test failed, you MUST rerun it after every change until it succeeds.

Commits

  • make test is enforced automatically by the pre-commit hook before every commit for changes that include at least one file that is not .md, .rst, or .txt. You do NOT need to run it manually.
  • For markdown/reStructuredText/text-only changes, the hook skips make test automatically.
  • If the pre-commit hook warns about a staged file or its role, you MUST ask whether to fix that warning before you continue.
  • Keep the follow-up limited to the roles touched by staged files so the change stays focused.

On Failure

  • If that validation fails, you MUST run make clean and rerun it.
  • If the failure says service "infinito" is not running, restart the stack with Development Environment Setup and retry the validation.