use cel_engine::CelEngine;
use diagnostics::Diagnostic;
use schema_validator::SchemaValidator;
use std::sync::LazyLock;
use validation_engine::{EngineConfig, ValidateConfig, validate_bytes};
static SV: LazyLock<SchemaValidator> = LazyLock::new(SchemaValidator::default);
fn f6101_diagnostics(template: &str) -> Vec<Diagnostic> {
let engine = CelEngine::new(EngineConfig::default()).unwrap();
let report = validate_bytes(&engine, &SV, template.as_bytes(), ValidateConfig::default()).unwrap();
report.diagnostics.into_iter().filter(|d| d.rule_id == "F6101").collect()
}
fn f6101_getatt_diagnostics(template: &str) -> Vec<Diagnostic> {
f6101_diagnostics(template).into_iter().filter(|d| d.message.contains("GetAtt")).collect()
}
fn f6101_paths(template: &str) -> Vec<String> {
let mut paths: Vec<String> =
f6101_getatt_diagnostics(template).into_iter().map(|d| d.property_path.unwrap_or_default()).collect();
paths.sort();
paths
}
#[test]
fn direct_dotted_string_getatt() {
let template = r#"
AWSTemplateFormatVersion: "2010-09-09"
Resources:
CapacityReservation:
Type: AWS::EC2::CapacityReservation
Properties:
AvailabilityZone: us-east-1a
InstanceCount: 1
InstanceType: t2.micro
InstancePlatform: Linux/UNIX
Outputs:
DottedString:
Value: !GetAtt CapacityReservation.InstanceCount
"#;
let paths = f6101_paths(template);
assert_eq!(paths, vec!["Outputs/DottedString/Value.Fn::GetAtt"]);
let diags = f6101_getatt_diagnostics(template);
assert_eq!(diags.len(), 1);
assert!(diags[0].message.contains("CapacityReservation.InstanceCount"));
assert!(diags[0].message.contains("integer"));
}
#[test]
fn list_form_getatt() {
let template = r#"
AWSTemplateFormatVersion: "2010-09-09"
Resources:
CapacityReservation:
Type: AWS::EC2::CapacityReservation
Properties:
AvailabilityZone: us-east-1a
InstanceCount: 1
InstanceType: t2.micro
InstancePlatform: Linux/UNIX
Outputs:
ListForm:
Value:
Fn::GetAtt:
- CapacityReservation
- InstanceCount
"#;
let paths = f6101_paths(template);
assert_eq!(paths, vec!["Outputs/ListForm/Value.Fn::GetAtt"]);
}
#[test]
fn nested_in_join() {
let template = r#"
AWSTemplateFormatVersion: "2010-09-09"
Resources:
CapacityReservation:
Type: AWS::EC2::CapacityReservation
Properties:
AvailabilityZone: us-east-1a
InstanceCount: 1
InstanceType: t2.micro
InstancePlatform: Linux/UNIX
Outputs:
JoinOutput:
Value:
Fn::Join:
- ""
- - !GetAtt CapacityReservation.InstanceCount
"#;
let paths = f6101_paths(template);
assert_eq!(paths, vec!["Outputs/JoinOutput/Value.Fn::Join.1.0.Fn::GetAtt"]);
}
#[test]
fn fn_sub_implicit_getatt() {
let template = r#"
AWSTemplateFormatVersion: "2010-09-09"
Resources:
CapacityReservation:
Type: AWS::EC2::CapacityReservation
Properties:
AvailabilityZone: us-east-1a
InstanceCount: 1
InstanceType: t2.micro
InstancePlatform: Linux/UNIX
Outputs:
SubOutput:
Value:
Fn::Sub: "Count is ${CapacityReservation.InstanceCount}"
"#;
let paths = f6101_paths(template);
assert_eq!(paths, vec!["Outputs/SubOutput/Value.Fn::Sub"]);
}
#[test]
fn no_duplicate_container_and_literal() {
let template = r#"
AWSTemplateFormatVersion: "2010-09-09"
Resources:
CapacityReservation:
Type: AWS::EC2::CapacityReservation
Properties:
AvailabilityZone: us-east-1a
InstanceCount: 1
InstanceType: t2.micro
InstancePlatform: Linux/UNIX
Outputs:
ListOutput:
Value:
- !GetAtt CapacityReservation.InstanceCount
- "other"
"#;
let all_f6101 = f6101_diagnostics(template);
assert_eq!(all_f6101.len(), 1);
assert!(all_f6101[0].message.contains("not a list"));
let getatt_f6101 = f6101_getatt_diagnostics(template);
assert_eq!(getatt_f6101.len(), 0);
}
#[test]
fn combined_all_forms() {
let template = r#"
AWSTemplateFormatVersion: "2010-09-09"
Resources:
CapacityReservation:
Type: AWS::EC2::CapacityReservation
Properties:
AvailabilityZone: us-east-1a
InstanceCount: 1
InstanceType: t2.micro
InstancePlatform: Linux/UNIX
Outputs:
DottedString:
Value: !GetAtt CapacityReservation.InstanceCount
ListForm:
Value:
Fn::GetAtt:
- CapacityReservation
- InstanceCount
NestedJoin:
Value:
Fn::Join:
- ""
- - !GetAtt CapacityReservation.InstanceCount
SubImplicit:
Value:
Fn::Sub: "Count is ${CapacityReservation.InstanceCount}"
"#;
let diags = f6101_getatt_diagnostics(template);
assert_eq!(diags.len(), 4);
let paths = f6101_paths(template);
assert_eq!(
paths,
vec![
"Outputs/DottedString/Value.Fn::GetAtt",
"Outputs/ListForm/Value.Fn::GetAtt",
"Outputs/NestedJoin/Value.Fn::Join.1.0.Fn::GetAtt",
"Outputs/SubImplicit/Value.Fn::Sub",
]
);
}
#[test]
fn value_prefixed_output_name_join_getatt() {
let template = r#"
AWSTemplateFormatVersion: "2010-09-09"
Resources:
CapacityReservation:
Type: AWS::EC2::CapacityReservation
Properties:
AvailabilityZone: us-east-1a
InstanceCount: 1
InstanceType: t2.micro
InstancePlatform: Linux/UNIX
Outputs:
ValueFoo:
Value:
Fn::Join:
- ""
- - !GetAtt CapacityReservation.InstanceCount
"#;
let paths = f6101_paths(template);
assert_eq!(paths, vec!["Outputs/ValueFoo/Value.Fn::Join.1.0.Fn::GetAtt"]);
}