testing
**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
$ npx -y skills add notque/vexjoy-agent --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
Agent definition
testing.mdAnsible Testing Reference
> **Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns > **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ > **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
---
Overview
Ansible testing has three layers that catch different failure classes: ansible-lint catches code quality and style violations before execution, check mode (`--check`) previews changes without applying them, and Molecule provides full scenario-based integration testing with real containers. Most automation breakage happens because only manual testing was done — automating these three layers catches 90% of issues before production.
---
Pattern Table
| Tool | Version | Use When | What It Catches | |------|---------|----------|-----------------| | `ansible-lint` | 6.0+ | Pre-commit, CI/CD | Style, deprecated syntax, security patterns | | `--check` mode | all | Pre-production run | Which tasks would change (no side effects) | | `--diff` mode | all | With `--check` | Exact file/template changes | | Molecule `verify` | 6.0+ | Post-convergence | State assertions on converged instance | | `idempotency` scenario | 6.0+ | Full role testing | Tasks that always report `changed` |
---
Correct Patterns
Run ansible-lint in CI with Explicit Rules
# .github/workflows/lint.yml
- name: Run ansible-lint
uses: ansible/ansible-lint-action@v6
with:
args: "--profile production"
# .ansible-lint — project-level config
profile: production
exclude_paths:
- molecule/
warn_list:
- yaml[truthy]
skip_list:
- 'no-changed-when' # Only if you've deliberately handled changed_when**Why**: The `production` profile enforces a stricter rule set than default. Without explicit profile selection, CI may pass with rules that flag security issues.
---
Molecule Scenario Structure for Role Testing
roles/myrole/
└── molecule/
└── default/
├── molecule.yml # Platform/driver config
├── converge.yml # The playbook to test
├── verify.yml # State assertions
└── prepare.yml # Pre-test setup (optional)# molecule/default/molecule.yml
---
driver:
name: docker
platforms:
- name: instance
image: geerlingguy/docker-ubuntu2204-ansible:latest
pre_build_image: true
provisioner:
name: ansible
lint:
name: ansible-lint
verifier:
name: ansible# molecule/default/verify.yml — assert actual state, not just "it ran"
---
- name: Verify
hosts: all
tasks:
- name: Check nginx is running
service_facts:
- name: Assert nginx service is active
assert:
that:
- "'nginx' in services"
- "services['nginx'].state == 'running'"
fail_msg: "nginx service is not running"
- name: Check config file exists and has correct content
stat:
path: /etc/nginx/sites-enabled/default
register: nginx_conf
- name: Assert config file exists
assert:
that: nginx_conf.stat.exists**Why**: `verify.yml` that only checks "did the task run" provides false confidence. Assert actual state: service running, file exists and has correct permissions, port is listening.
---
Idempotency Test Pattern
# molecule/default/molecule.yml — add idempotency check
provisioner:
name: ansible
playbooks:
converge: converge.yml
lint:
name: ansible-lint
# Or run manually:
# molecule converge && molecule idempotency# Manual idempotency check — second run should report 0 changed tasks
ansible-playbook site.yml --check 2>&1 | grep -E "changed=|failed="
# Expected: changed=0 failed=0
**Why**: A role that reports `changed` on every run breaks pipeline assumptions (e.g., "if nothing changed, skip downstream steps"). Idempotency failures also indicate state corruption on repeated runs.
---
Pattern Catalog
Set changed_when on All command/shell Tasks
**Detection**:
# Find command/shell tasks missing changed_when
grep -rn -A5 "^\s*\(command\|shell\):" roles/ playbooks/ \
| grep -v "changed_when\|register\|when:"
# Or with ripgrep
rg -t yaml '^\s+(command|shell):' roles/ playbooks/ -A5 \
| grep -B3 -v "changed_when"
**Signal**:
- name: Check disk usage
command: df -h
register: disk_result
# Missing: changed_when: false
**Why this matters**: `command` and `shell` always report `changed` unless told otherwise. This breaks idempotency checks (idempotency scenario always fails), makes `--check` output noisy, and triggers handlers incorrectly.
**Preferred action**:
- name: Check disk usage
command: df -h
register: disk_result
changed_when: false # Read-only command, never changes state
- name: Run migration script
command: /app/bin/migrate up
register: migration_result
changed_when: "'No migrations' not in migration_result.stdout"
# Only reports changed when migrations actually ran
---
Write Meaningful Assertions in verify.yml
**Detection**:
# Find verify.yml files without assert tasks
grep -rL "assert:" molecule/*/verify.yml
# Find verify.yml files with only ping/gather_facts
grep -rn "tasks:" molecule/*/verify.yml -A5 \
| grep -v "assert\|stat\|uri\|service_facts\|command"
**Signal**:
# molecule/default/verify.yml — provides zero value
---
- name: Verify
hosts: all
tasks:
- name: Check connectivity
ping:**Why this matters**: Ping passing means the container is alive, not that the role worked. A role that fails to install nginx will still pass this verify.
**Preferred action**:
---
- name: Verify
hosts: all
gather_facts: false
tasks:
- name: Check nginx listening on port 80
wait_for:
port: 80
timeout: 5
- name: Check nginx config syntax
command: nginx -t
changed_when: false
- name: Verify nginx config file dRead more
Ansible Testing Reference
> **Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns > **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ > **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
---
Overview
Ansible testing has three layers that catch different failure classes: ansible-lint catches code quality and style violations before execution, check mode (`--check`) previews changes without applying them, and Molecule provides full scenario-based integration testing with real containers. Most automation breakage happens because only manual testing was done — automating these three layers catches 90% of issues before production.
---
Pattern Table
| Tool | Version | Use When | What It Catches | |------|---------|----------|-----------------| | `ansible-lint` | 6.0+ | Pre-commit, CI/CD | Style, deprecated syntax, security patterns | | `--check` mode | all | Pre-production run | Which tasks would change (no side effects) | | `--diff` mode | all | With `--check` | Exact file/template changes | | Molecule `verify` | 6.0+ | Post-convergence | State assertions on converged instance | | `idempotency` scenario | 6.0+ | Full role testing | Tasks that always report `changed` |
---
Correct Patterns
Run ansible-lint in CI with Explicit Rules
# .github/workflows/lint.yml
- name: Run ansible-lint
uses: ansible/ansible-lint-action@v6
with:
args: "--profile production"
# .ansible-lint — project-level config
profile: production
exclude_paths:
- molecule/
warn_list:
- yaml[truthy]
skip_list:
- 'no-changed-when' # Only if you've deliberately handled changed_when**Why**: The `production` profile enforces a stricter rule set than default. Without explicit profile selection, CI may pass with rules that flag security issues.
---
Molecule Scenario Structure for Role Testing
roles/myrole/
└── molecule/
└── default/
├── molecule.yml # Platform/driver config
├── converge.yml # The playbook to test
├── verify.yml # State assertions
└── prepare.yml # Pre-test setup (optional)# molecule/default/molecule.yml
---
driver:
name: docker
platforms:
- name: instance
image: geerlingguy/docker-ubuntu2204-ansible:latest
pre_build_image: true
provisioner:
name: ansible
lint:
name: ansible-lint
verifier:
name: ansible# molecule/default/verify.yml — assert actual state, not just "it ran"
---
- name: Verify
hosts: all
tasks:
- name: Check nginx is running
service_facts:
- name: Assert nginx service is active
assert:
that:
- "'nginx' in services"
- "services['nginx'].state == 'running'"
fail_msg: "nginx service is not running"
- name: Check config file exists and has correct content
stat:
path: /etc/nginx/sites-enabled/default
register: nginx_conf
- name: Assert config file exists
assert:
that: nginx_conf.stat.exists**Why**: `verify.yml` that only checks "did the task run" provides false confidence. Assert actual state: service running, file exists and has correct permissions, port is listening.
---
Idempotency Test Pattern
# molecule/default/molecule.yml — add idempotency check
provisioner:
name: ansible
playbooks:
converge: converge.yml
lint:
name: ansible-lint
# Or run manually:
# molecule converge && molecule idempotency# Manual idempotency check — second run should report 0 changed tasks ansible-playbook site.yml --check 2>&1 | grep -E "changed=|failed=" # Expected: changed=0 failed=0
**Why**: A role that reports `changed` on every run breaks pipeline assumptions (e.g., "if nothing changed, skip downstream steps"). Idempotency failures also indicate state corruption on repeated runs.
---
Pattern Catalog
Set changed_when on All command/shell Tasks
**Detection**:
# Find command/shell tasks missing changed_when grep -rn -A5 "^\s*\(command\|shell\):" roles/ playbooks/ \ | grep -v "changed_when\|register\|when:" # Or with ripgrep rg -t yaml '^\s+(command|shell):' roles/ playbooks/ -A5 \ | grep -B3 -v "changed_when"
**Signal**:
- name: Check disk usage command: df -h register: disk_result # Missing: changed_when: false
**Why this matters**: `command` and `shell` always report `changed` unless told otherwise. This breaks idempotency checks (idempotency scenario always fails), makes `--check` output noisy, and triggers handlers incorrectly.
**Preferred action**:
- name: Check disk usage command: df -h register: disk_result changed_when: false # Read-only command, never changes state - name: Run migration script command: /app/bin/migrate up register: migration_result changed_when: "'No migrations' not in migration_result.stdout" # Only reports changed when migrations actually ran
---
Write Meaningful Assertions in verify.yml
**Detection**:
# Find verify.yml files without assert tasks grep -rL "assert:" molecule/*/verify.yml # Find verify.yml files with only ping/gather_facts grep -rn "tasks:" molecule/*/verify.yml -A5 \ | grep -v "assert\|stat\|uri\|service_facts\|command"
**Signal**:
# molecule/default/verify.yml — provides zero value
---
- name: Verify
hosts: all
tasks:
- name: Check connectivity
ping:**Why this matters**: Ping passing means the container is alive, not that the role worked. A role that fails to install nginx will still pass this verify.
**Preferred action**:
---
- name: Verify
hosts: all
gather_facts: false
tasks:
- name: Check nginx listening on port 80
wait_for:
port: 80
timeout: 5
- name: Check nginx config syntax
command: nginx -t
changed_when: false
- name: Verify nginx config file dEssays and writing behind this toolkit live at vexjoy.com. AI agents skip steps. "Looks correct" replaces running tests. "Trivial change" replaces verification.
Repo: notque/vexjoy-agent
Other agents on vexjoy-agent.
- ansible-automation-engineer
Ansible automation: playbooks, roles, collections, Molecule testing, Vault security.
Open agent - modules
**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) **Generated**: 2026-04-04 — verify against current Ansible
Open agent - base-instructions
Universal operational rules injected by /do at agent dispatch. Domain-specific rules live in each agent's .md file.
Open agent - communication-patterns
**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what to detect and how to fix each. **Version range**: all versions **Generated**: 2026-05-11
Open agent - combat-effects-upgrade
Zero-dependency combat visual upgrades: CSS particle replacement, Framer Motion combat juice, CSS 3D card transforms.
Open agent - css-3d-card-transforms
<!-- Loaded by combat-effects-upgrade when task involves card tilt, backface-visibility, CSS perspective, or Framer Motion + CSS 3D integration -->
Open agent

