Skip to content

fix: test suites that never ran, and thirty expectations that assert nothing - #286

Open
awsmadi wants to merge 4 commits into
aws-cloudformation:mainfrom
awsmadi:pr/dead-expectations-and-orphaned-suites
Open

fix: test suites that never ran, and thirty expectations that assert nothing#286
awsmadi wants to merge 4 commits into
aws-cloudformation:mainfrom
awsmadi:pr/dead-expectations-and-orphaned-suites

Conversation

@awsmadi

@awsmadi awsmadi commented Aug 26, 2026

Copy link
Copy Markdown

Four test-suite fixes in this repository. Three suites were never run at all, thirty expectations asserted nothing, and two case titles state the opposite of what their case tests. Nothing here changes a rule, and no .guard file is touched.

Thirty expectations that assert nothing

rules/aws/aws_cloudformation/tests/cfn_no_explicit_resource_names_tests.yml carries 63 expectation entries across 33 cases. Thirty of them name rules that do not exist, spread over 11 distinct names:

no_static_names, static_alarmname, static_dbinstanceidentifier, static_deploymentconfigname, static_deploymentgroupname, static_domainname, static_groupname, static_managedpolicyname, static_name, static_repositoryname, static_rolename

cfn_no_explicit_resource_names.guard defines exactly one rule, CFN_NO_EXPLICIT_RESOURCE_NAMES. For each of the thirty, cfn-guard test prints

No rule named static_rolename is in this file, so its expectation was not checked

and exits 0. So the suite has always reported clean while those thirty assertions ran nothing.

They are not a stale rename. Both files were added in the same commit, 5f4a972 (#265), and no commit since renames a rule. The eleven names are the intended decomposition of the rule into per-type checks, written into the test file although the decomposition was never written into the rules file. Each maps onto the property one type block checks — static_name to Name, static_alarmname to AlarmName, and so on.

Dropped rather than repointed, because every affected case already carries a CFN_NO_EXPLICIT_RESOURCE_NAMES expectation of its own. Renaming a dead entry onto the rule that exists produces a second copy of an assertion the case already makes, so the two operations are equivalent here and deletion is the smaller diff. No test input is removed: all 33 cases still run, against the same templates, asserting the same aggregate verdict.

What is lost is the granularity those eleven names wanted, and it is worth having — a per-type verdict says which resource type caused a failure, and lets one type be suppressed without suppressing the whole rule. Restoring it means adding per-type rules to the ruleset, which changes the public rule set and needs a cfn-guard that tolerates a rule reference to a rule that did not apply. That is a follow-up, not this change.

No case covered more than one resource type

Every one of the 33 original cases declares exactly one resource of exactly one covered type. So nothing in the suite ever distinguished the rule's fourteen type checks from each other, and nothing exercised the rule on a template carrying several of them — which is what a real template looks like.

Three cases are added for that: several covered types all clean, several with one violating, and several with more than one violating. The middle one is the shape that matters, because it is the only case in the suite where the aggregate must fail on account of one specific type while the others are fine.

Three suites that never ran

cfn-guard test pairs a test file to a rules file by name. Three test files were named so that no rules file claimed them, so they were silently skipped:

  • cloudfront_accesslogs_enabled_tests.yml — renamed to match its rule
  • iam_role_no_full_access_on_trust_policy_tests.yml — renamed to match its rule
  • s3_bucket_default_lock_enabled_tests.yml — renamed to match its rule

rds_instance_logging_enabled_tests.yml is removed: it duplicates rds_logging_enabled_tests.yml, which is paired and does run.

Two titles that describe the wrong outcome

Two cases in cfn_no_explicit_resource_names_tests.yml are titled as the opposite of what they assert. Titles only, no expectation changed.

Verification

cfn-guard test -d ./rules/ — the invocation .github/workflows/ci.yml uses — before and after, on the released cfn-guard and on a build from main:

expectation entries entries naming a rule that does not exist
before 63 30
after 36 0

Exit code is 0 before and after on every cfn-guard version tested, so this does not depend on a tool release. The reduction from 63 to 36 is the thirty dead entries plus the deduplication described above; case count goes from 33 to 36 with the three new ones.

Every .guard file in the repository parses to a byte-identical tree before and after, because none is modified.

Four test files under rules/aws/**/tests/ matched no rules file stem, so
cfn-guard never paired them and not one of them had ever run. Each failed
to pair for a different reason.

s3_bucket_default_lock_enable_tests.yml dropped the trailing d from
S3_BUCKET_DEFAULT_LOCK_ENABLED. No correctly named file existed beside it,
so s3_bucket_default_lock_enabled.guard had no test coverage at all. The
truncated spelling appeared eight times: once in the header comment, once
in each of the six expectations, and once as a Metadata.guard
SuppressedRules value inside the sixth case's template. Correcting only
the filename and the expectation names leaves that sixth case failing, and
the failure is the test data rather than the rule. The rule's filter
excludes a bucket that suppresses S3_BUCKET_DEFAULT_LOCK_ENABLED, the
template suppressed S3_BUCKET_DEFAULT_LOCK_ENABLE, and a suppression
naming a different rule must not suppress this one. All eight occurrences
are corrected here and the suite passes six of six.

The cloudfront access logs test filename began with a space. Nothing
inside it was wrong and its seven cases pass as written, so this is a
rename and nothing more. cloudfront_accesslogs_enabled.guard had gone
untested for that reason alone.

iam_role_no_full_access_on_trust_policy.yml lacked the _tests suffix
altogether. Its eight cases also pass unchanged, so it is likewise a
rename only, and iam_role_no_full_access_on_trust_policy.guard had gone
untested.

rds_instance_logging_enabled_tests.yml was named after the rule it
exercises, RDS_INSTANCE_LOGGING_ENABLED, rather than after its rules file,
rds_logging_enabled.guard, which is why it did not pair. It duplicated
rds_logging_enabled_tests.yml, which already pairs and already runs. Both
held five cases over the same five scenarios with the same expectations:
an empty template, no resources, EnableCloudwatchLogsExports present, that
property absent, and that property absent with the rule suppressed. No
scenario existed only in the orphan, so deleting it costs no coverage.

One case name in the surviving file described the wrong property. It read
"RDS Multi AZ property missing but rule is suppressed" while the rule
tests only whether EnableCloudwatchLogsExports exists, and the case's
template sets no MultiAZ property in either direction. It now says what it
tests.

Rule and test pairs go from 190 to 193, and all 193 exit 0. Test files
living under a tests/ directory go from 194 to 193 once the duplicate is
removed, and none of them is left unpaired.
"CodeDeploy DeploymentConfig without static deploymentConfigName, FAIL" and "IAM
Group without static groupName, FAIL" both set a static name and both expect
FAIL. Each is the with-a-static-name half of a pair whose other half already
reads "without", so as written the titles duplicate the PASS cases. Titles only;
no input or expectation changes.
Every one of the 33 existing cases declares exactly one resource of exactly one
covered type, so nothing in the suite exercised the rule's behaviour when several
covered types appear together. Three cases are added: all clean; only the IAM
Role statically named; the DynamoDB table and the IAM Role both statically named.

The middle one is the case worth having. It is the only shape in the suite where
one covered resource violates while another is clean, so it is what pins down
that a clean resource of one type does not mask a violation on another. Each
verdict was read off cfn-guard validate against the rules file before being
written here.

Cases go from 33 to 36.
cfn_no_explicit_resource_names_tests.yml carried 63 expectation entries across
33 cases. Thirty of them, spread over 11 distinct names -- no_static_names,
static_alarmname, static_dbinstanceidentifier and eight more in the same shape
-- name rules that do not exist. cfn_no_explicit_resource_names.guard defines
exactly one rule, CFN_NO_EXPLICIT_RESOURCE_NAMES.

cfn-guard prints "No rule named X is in this file, so its expectation was not
checked" for each and exits 0, so the suite has always reported clean while
those thirty assertions ran nothing.

They are not a stale rename. Both files were added in the same commit, 5f4a972
(aws-cloudformation#265), and no commit since has renamed a rule. The eleven names are the
author's intended decomposition of the rule into per-type checks, written into
the test file although the decomposition itself was never written into the
rules file. Each name maps onto the property one type block checks:
static_name to Name, static_alarmname to AlarmName, and so on.

Dropped rather than repointed. Every affected case already carries a
CFN_NO_EXPLICIT_RESOURCE_NAMES expectation of its own, so renaming a dead entry
onto the rule that exists yields a second copy of an assertion the case already
makes. The two operations are equivalent here and deletion is the smaller diff.
No test input is removed: all 33 cases still run, against the same templates,
asserting the same aggregate verdict as before.

What is lost is the granularity the eleven names wanted, and that is worth
having -- a per-type verdict says which resource type caused a failure, and
lets one type be suppressed without suppressing the rule. Restoring it means
adding the per-type rules to the ruleset, which changes the public rule set and
needs a cfn-guard that tolerates a rule reference to a rule that did not apply.
It is a follow-up, not this change.
@awsmadi

awsmadi commented Sep 1, 2026

Copy link
Copy Markdown
Author

CI has never executed here — the workflow runs are held at action_required pending maintainer approval, so there are no results to read either way.

That matters more than usual for this one, since the change is specifically about test suites that never ran and expectations that assert nothing. Without a CI run there is no evidence the newly-armed expectations actually fire. Could a maintainer enable the workflow run?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant