Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand parameter overrides #7876

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add unit tests, apply related fixes
  • Loading branch information
sigJoe committed Feb 7, 2025
commit 124d78f0c7952aecc92a411be484190f19efd3e1
14 changes: 9 additions & 5 deletions samcli/cli/types.py
Original file line number Diff line number Diff line change
@@ -155,11 +155,15 @@ def _normalize_parameters(self, values, param, ctx):
elif isinstance(value, (dict, CommentedMap)):
# e.g. YAML key-value pairs
for k, v in value.items():
if isinstance(v, (list, CommentedSeq)):
# Collapse list values to comma delimited
parameters[str(k)] = ",".join(map(str, v))
if v is None:
parameters[str(k)] = ""
elif isinstance(v, (list, CommentedSeq)):
# Collapse list values to comma delimited, ignore empty strings and remove extra spaces
parameters[str(k)] = ",".join(str(x).strip() for x in v if x not in (None, ""))
else:
parameters[str(k)] = str(v)
elif value is None:
continue
else:
self.fail(
f"{value} is not valid in a way the code doesn't expect",
@@ -169,13 +173,13 @@ def _normalize_parameters(self, values, param, ctx):

result = {}
for key, param_value in parameters.items():
result[_unquote_wrapped_quotes(key.lower())] = _unquote_wrapped_quotes(param_value)
result[_unquote_wrapped_quotes(key)] = _unquote_wrapped_quotes(param_value)
LOG.debug("Output parameters: %s", result)
return result

def convert(self, values, param, ctx):
# Merge samconfig with CLI parameter-overrides
if isinstance(values, tuple): # Tuple implies CLI
if isinstance(values, tuple) and ctx: # ctx isn't set in all unit tests
# default_map was populated from samconfig
default_map = ctx.default_map.get("parameter_overrides", {})
LOG.debug("Default map: %s", default_map)
143 changes: 143 additions & 0 deletions tests/unit/cli/test_types.py
Original file line number Diff line number Diff line change
@@ -150,7 +150,150 @@ def test_successful_parsing(self, input, expected):
self.assertEqual(result, expected, msg="Failed with Input = " + str(input))


@parameterized.expand(
[
("", "", {}, ),
(("A=1 A=2"), ("A=3 A=4",), {"A": "4"}),
(("Path=C:\\Windows\\System32"),("Path=/usr/bin",), {"Path": "/usr/bin"}),
(
("A=1 B=2"),
("ParameterKey=C,ParameterValue=3", "ParameterKey=D,ParameterValue=4",),
{"A": "1", "B": "2", "C": "3", "D": "4"},
),
(
('Quoted="Hello, World!"'),
('ParameterKey=Quoted,ParameterValue="\'Hi\'"',),
{"Quoted": "'Hi'"}
),
(
("A=1 B=2"),
("ParameterKey=A,ParameterValue=3", "ParameterKey=D,ParameterValue=4",),
{"A": "3", "B": "2", "D": "4"},
),
(
["A=1", "B=2"],
("ParameterKey=A,ParameterValue=3", "ParameterKey=D,ParameterValue=4",),
{"A": "3", "B": "2", "D": "4"},
),
(
("A=1,2,3 B=1,2,3"),
("ParameterKey=A,ParameterValue=2,2", "ParameterKey=D,ParameterValue=4,5,6",),
{"A": "2,2", "B": "1,2,3", "D": "4,5,6"},
),
(
("A=1 B=2 C=3"),
("C=4",),
{"A": "1", "B": "2", "C": "4"},
),
(
{"Flag": False},
("Flag=True",),
{"Flag": "True"}
),
(
("A=1 B=2"),
"A=3 D=4", # String not tuple
{"A": "3", "D": "4"}
),
(
("A=1 B=2 C=3"),
["C=4"], # List not tuple
{"C": "4"},
),
(
[[[[[[[[[[[[[[[[[[]]]]]]]]]]]], {"A": "1"}, [[[{"B": "2"}]]]]]]]]],
("A=3",),
{"A": "3", "B": "2"},
),
(
{"A": "1"},
("B=2",),
{"A": "1", "B": "2"},
),
(
{"Spaces": "String with spaces"},
("Pam='Param Pam Pam Param'",),
{"Spaces": "String with spaces", "Pam": "Param Pam Pam Param"},
),
(
[None, {"Empty": None}],
("None=None",),
{"Empty": "", "None": "None"}
),
(
{"List": [" A ", 1, False, "", None]},
("OtherList='A,1,False'",),
{"List": "A,1,False", "OtherList": 'A,1,False'},
),
(
{"List": [" A ", "' B '", 1, False, "", None]},
("List2=' A ,1,False'",),
{"List": "A,' B ',1,False", "List2": ' A ,1,False'},
),
]
)
def test_merge_default_map_parsing(self, file_overrides, cli_overrides, expected):
ctx = MagicMock()
ctx.default_map = {"parameter_overrides": file_overrides}
result = self.param_type.convert(cli_overrides, None, ctx)
self.assertEqual(result, expected, msg="Failed with Input = " + str(cli_overrides))

@parameterized.expand(
[
(
("A=1 B=2"),
("file://params.toml",),
{"A": "toml", "B": "toml", "Toml": "toml"},
),
(
("A=1 B=2"),
("file://params.toml", "D=4"),
{"A": "toml", "B": "toml", "Toml": "toml", "D": "4"},
),
(
("A=1 B=2"),
("ParameterKey=Y,ParameterValue=y", "file://params.toml", "D=4", "file://params.yaml", "A=6"),
{"A": "6", "B": "yaml", "Y": "y", "Toml": "toml", "D": "4", "Yaml": "yaml"},
),
(
("A=1 B=2"),
("file://list.yaml",),
{"A": "a", "B": "2", "List": "1,2,3", "Yaml": "yaml"},
),
(
("A=1 B=2"),
("file://nested.yaml",),
{"A": "yaml", "B": "yaml", "Toml": "toml", "Yaml": "yaml"},
),
]
)
def test_merge_file_parsing(self, file_overrides, cli_overrides, expected):
mock_files = {
"params.toml": 'A = "toml"\nB = "toml"\nToml = "toml"',
"params.yaml": "A: yaml\nB: yaml\nYaml: yaml",
"list.yaml": "- - - - - - A: a\n- List:\n - 1\n - 2\n - 3\n- Yaml: yaml",
"nested.yaml": "- file://params.toml\n- file://params.yaml",
}

def mock_read_text(file_path):
file_name = file_path.name
return mock_files.get(file_name, "")

def mock_is_file(file_path):
return file_path.name in mock_files

with patch("pathlib.Path.is_file", new=mock_is_file), patch("pathlib.Path.read_text", new=mock_read_text):
ctx = MagicMock()
ctx.default_map = {"parameter_overrides": file_overrides}

result = self.param_type.convert(cli_overrides, None, ctx)
print(result)
self.assertEqual(result, expected, msg="Failed with Input = " + str(cli_overrides))



class TestCfnMetadataType(TestCase):

def setUp(self):
self.param_type = CfnMetadataType()