From 1a1447b93f3f5ba22e84d00c89fd02e79cd0651d Mon Sep 17 00:00:00 2001 From: Kevin DeJong Date: Wed, 28 Aug 2024 08:59:13 -0700 Subject: [PATCH] Allow Fn::Length in Fn::Select (#3633) --- src/cfnlint/rules/functions/Select.py | 7 ++- test/unit/rules/functions/test_select.py | 6 +-- .../test_select_language_extension.py | 53 +++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 test/unit/rules/functions/test_select_language_extension.py diff --git a/src/cfnlint/rules/functions/Select.py b/src/cfnlint/rules/functions/Select.py index f5456d7560..12e47007f4 100644 --- a/src/cfnlint/rules/functions/Select.py +++ b/src/cfnlint/rules/functions/Select.py @@ -25,7 +25,7 @@ def __init__(self) -> None: self.fn_select = self.validate def schema(self, validator: Validator, instance: Any) -> dict[str, Any]: - return { + schema = { "type": "array", "maxItems": 2, "minItems": 2, @@ -63,3 +63,8 @@ def schema(self, validator: Validator, instance: Any) -> dict[str, Any]: }, ], } + + if validator.context.transforms.has_language_extensions_transform(): + schema["fn_items"][0]["functions"].append("Fn::Length") # type: ignore + + return schema diff --git a/test/unit/rules/functions/test_select.py b/test/unit/rules/functions/test_select.py index 88b0d06628..c700149696 100644 --- a/test/unit/rules/functions/test_select.py +++ b/test/unit/rules/functions/test_select.py @@ -55,12 +55,12 @@ def template(): ], ), ( - "Invalid Fn::Select using an invalid function for index", - {"Fn::Select": [{"Fn::GetAtt": "MyResource"}, ["bar"]]}, + "Invalid Fn::Length using an invalid function for index", + {"Fn::Select": [{"Fn::Length": [1, 2]}, ["bar"]]}, {"type": "string"}, [ ValidationError( - "{'Fn::GetAtt': 'MyResource'} is not of type 'integer'", + "{'Fn::Length': [1, 2]} is not of type 'integer'", path=deque(["Fn::Select", 0]), schema_path=deque(["fn_items", "type"]), validator="fn_select", diff --git a/test/unit/rules/functions/test_select_language_extension.py b/test/unit/rules/functions/test_select_language_extension.py new file mode 100644 index 0000000000..d71fd67888 --- /dev/null +++ b/test/unit/rules/functions/test_select_language_extension.py @@ -0,0 +1,53 @@ +""" +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: MIT-0 +""" + +from collections import deque + +import pytest + +from cfnlint.jsonschema import ValidationError +from cfnlint.rules.functions.Select import Select +from cfnlint.rules.functions.SelectResolved import SelectResolved + + +@pytest.fixture(scope="module") +def rule(): + rule = Select() + rule.child_rules["W1035"] = SelectResolved() + yield rule + + +@pytest.fixture +def template(): + return {"Transform": "AWS::LanguageExtensions"} + + +@pytest.mark.parametrize( + "name,instance,schema,expected", + [ + ( + "Invalid Fn::Select using an invalid function for index", + {"Fn::Select": [{"Fn::GetAtt": "MyResource"}, ["bar"]]}, + {"type": "string"}, + [ + ValidationError( + "{'Fn::GetAtt': 'MyResource'} is not of type 'integer'", + path=deque(["Fn::Select", 0]), + schema_path=deque(["fn_items", "type"]), + validator="fn_select", + ), + ], + ), + ( + "Valid Fn::Length with language extension", + {"Fn::Select": [{"Fn::Length": [1, 2]}, ["A", "B", "C"]]}, + {"type": "string"}, + [], + ), + ], +) +def test_validate(name, instance, schema, expected, rule, validator): + errs = list(rule.fn_select(validator, schema, instance, {})) + assert errs == expected, f"Test {name!r} got {errs!r}"