-
Notifications
You must be signed in to change notification settings - Fork 1
Scenario Inheritance
Reuse and extend test scenarios with the extends: feature to eliminate duplication and create modular test suites.
Without inheritance, you duplicate scenarios:
❌ Without inheritance:
# dev-test.yml
vars:
environment: "dev"
bucket: "dev-data"
policy_template: "policy.json.tpl"
scp_paths: ["scp/org-policy.json"]
tests:
- name: "Test GetObject"
action: "s3:GetObject"
resource: "arn:aws:s3:::{{.bucket}}/*"
expect: "allowed"# prod-test.yml
vars:
environment: "prod"
bucket: "prod-data"
policy_template: "policy.json.tpl" # Duplicated!
scp_paths: ["scp/org-policy.json"] # Duplicated!
tests:
- name: "Test GetObject" # Duplicated!
action: "s3:GetObject"
resource: "arn:aws:s3:::{{.bucket}}/*"
expect: "allowed"✓ With inheritance:
# base.yml
policy_template: "policy.json.tpl"
scp_paths: ["scp/org-policy.json"]
tests:
- name: "Test GetObject"
action: "s3:GetObject"
resource: "arn:aws:s3:::{{.bucket}}/*"
expect: "allowed"# dev-test.yml
extends: "base.yml"
vars:
environment: "dev"
bucket: "dev-data"# prod-test.yml
extends: "base.yml"
vars:
environment: "prod"
bucket: "prod-data"From test/scenarios/14-base-with-variables.yml:
# Base scenario with common configuration
vars_file: "../vars/common-vars.yml"
policy_template: "../policies/s3-templated.json.tpl"
caller_arn: "arn:aws:iam::{{.account_id}}:user/alice"
tests:
- name: "GetObject allowed with correct department tag"
action: "s3:GetObject"
resource: "arn:aws:s3:::{{.bucket_name}}/data.txt"
context:
- ContextKeyName: "aws:PrincipalTag/Department"
ContextKeyType: "string"
ContextKeyValues: ["{{.department}}"]
expect: "allowed"
- name: "PutObject allowed with correct department tag"
action: "s3:PutObject"
resource: "arn:aws:s3:::{{.bucket_name}}/upload.txt"
context:
- ContextKeyName: "aws:PrincipalTag/Department"
ContextKeyType: "string"
ContextKeyValues: ["{{.department}}"]
expect: "allowed"From test/scenarios/15-extends-add-scp.yml:
# Child scenario: adds SCP constraints
extends: "14-base-with-variables.yml"
# Add organizational SCP that denies writes
scp_paths:
- "../scp/deny-s3-write.json"
# Override tests - writes now denied by SCP
tests:
- name: "GetObject still allowed (SCP allows reads)"
action: "s3:GetObject"
resource: "arn:aws:s3:::{{.bucket_name}}/data.txt"
context:
- ContextKeyName: "aws:PrincipalTag/Department"
ContextKeyType: "string"
ContextKeyValues: ["{{.department}}"]
expect: "allowed"
- name: "PutObject now denied by SCP"
action: "s3:PutObject"
resource: "arn:aws:s3:::{{.bucket_name}}/upload.txt"
context:
- ContextKeyName: "aws:PrincipalTag/Department"
ContextKeyType: "string"
ContextKeyValues: ["{{.department}}"]
expect: "explicitDeny" # Changed expectation!politest merges parent and child scenarios with specific rules:
vars and expect maps are deep-merged (child adds/overrides keys):
# parent.yml
vars:
bucket: "parent-bucket"
region: "us-east-1"
department: "Engineering"
# child.yml
extends: "parent.yml"
vars:
bucket: "child-bucket" # Overrides
environment: "prod" # Adds new
# Result:
# vars:
# bucket: "child-bucket" ← Overridden
# region: "us-east-1" ← Inherited
# department: "Engineering" ← Inherited
# environment: "prod" ← Addedactions, resources, scp_paths, tests arrays are replaced entirely:
# parent.yml
tests:
- name: "Test 1"
action: "s3:GetObject"
expect: "allowed"
- name: "Test 2"
action: "s3:PutObject"
expect: "allowed"
# child.yml
extends: "parent.yml"
tests:
- name: "Test 3"
action: "s3:DeleteObject"
expect: "implicitDeny"
# Result: Only Test 3 runs (parent tests replaced)String fields like policy_json, caller_arn, etc. are replaced:
# parent.yml
policy_json: "parent-policy.json"
caller_arn: "arn:aws:iam::111111111111:user/alice"
# child.yml
extends: "parent.yml"
caller_arn: "arn:aws:iam::111111111111:user/bob"
# Result:
# policy_json: "parent-policy.json" ← Inherited
# caller_arn: "...user/bob" ← OverriddenFrom test/scenarios/16-extends-override-vars.yml:
extends: "14-base-with-variables.yml"
# Override variables for different department
vars:
department: "Finance"
bucket_name: "finance-data"
# All tests from parent re-run with Finance departmentResult: Same tests, different variables - tests Finance department access instead of Engineering.
# base-developer.yml
policy_json: "developer-policy.json"
tests:
- name: "EC2 access"
action: "ec2:RunInstances"
resource: "*"
expect: "allowed"
# production-developer.yml
extends: "base-developer.yml"
# Add production SCP that restricts access
scp_paths:
- "scp/deny-production-resources.json"
# Override expectations
tests:
- name: "EC2 dev instances allowed"
action: "ec2:RunInstances"
resource: "arn:aws:ec2:*:*:instance/i-dev-*"
expect: "allowed"
- name: "EC2 prod instances denied by SCP"
action: "ec2:RunInstances"
resource: "arn:aws:ec2:*:*:instance/i-prod-*"
expect: "explicitDeny"politest supports recursive inheritance (child extends child extends base):
# base.yml - Foundation
policy_template: "policy.json.tpl"
vars_file: "vars/common.yml"
# dev.yml - Add dev-specific settings
extends: "base.yml"
vars:
environment: "dev"
# dev-alice.yml - Add user-specific settings
extends: "dev.yml"
caller_arn: "arn:aws:iam::111111111111:user/alice"Merge order: base.yml → dev.yml → dev-alice.yml
scenarios/
├── base.yml # Common policy and tests
├── dev.yml # extends base, dev vars
├── staging.yml # extends base, staging vars
└── prod.yml # extends base, prod vars + stricter SCPs
# base-policy.yml - Just the policy
policy_template: "policy.json.tpl"
# base-tests.yml - Common tests
extends: "base-policy.yml"
tests:
- name: "Standard test 1"
...
# with-scp.yml - Add organizational constraints
extends: "base-tests.yml"
scp_paths: ["scp/*.json"]Add comments to child scenarios:
# Inherits from: base.yml
# Adds: Production SCPs
# Overrides: Stricter expectations
extends: "base.yml"Always use paths relative to the scenario file:
# In scenarios/prod/test.yml
extends: "../base.yml" # Up to scenarios/, then base.yml
policy_json: "../../policies/policy.json" # Up two levelsProblem: extends: "parent.yml" fails
Solution: Use path relative to current scenario file:
# If current file is: scenarios/env/child.yml
# And parent is: scenarios/base.yml
extends: "../base.yml" # Not "base.yml"Problem: Child vars don't override parent vars
Solution: Check that vars are maps (key-value), not arrays:
# ✓ Correct (map)
vars:
bucket: "new-value"
# ✗ Wrong (won't work)
vars:
- bucket: "new-value"Problem: Expected child tests only, but parent tests also run
Solution: Remember that tests array is replaced, not merged. If you see parent tests, child scenario likely doesn't have a tests: field.
- Template Variables - Combine with variables for powerful reuse
- See Examples - Scenarios 14-16 demonstrate inheritance
- API Reference - Complete inheritance merge rules
Working examples: test/scenarios/15-extends-add-scp.yml →