Skip to content

Commit f5d850d

Browse files
pinzonsimonrw
andauthored
merge CFn V1 and CFnV2 tests (#12944)
Co-authored-by: Simon Walker <[email protected]>
1 parent 2455669 commit f5d850d

File tree

248 files changed

+811
-25778
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

248 files changed

+811
-25778
lines changed

.github/workflows/aws-tests.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ jobs:
267267
events-v1:
268268
- 'tests/aws/services/events/**'
269269
cloudformation-v2:
270-
- 'tests/aws/services/cloudformation/v2/**'
270+
- 'tests/aws/services/cloudformation/**'
271271
272272
- name: Run Unit Tests
273273
timeout-minutes: 8
@@ -394,7 +394,7 @@ jobs:
394394
env:
395395
# add the GitHub API token to avoid rate limit issues
396396
GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
397-
PYTEST_ARGS: "${{ env.TINYBIRD_PYTEST_ARGS }}${{ env.TESTSELECTION_PYTEST_ARGS }} --splits 4 --group ${{ matrix.group }} --store-durations --clean-durations --ignore=tests/unit/ --ignore=tests/bootstrap --ignore=tests/aws/services/cloudformation/v2"
397+
PYTEST_ARGS: "${{ env.TINYBIRD_PYTEST_ARGS }}${{ env.TESTSELECTION_PYTEST_ARGS }} --splits 4 --group ${{ matrix.group }} --store-durations --clean-durations --ignore=tests/unit/ --ignore=tests/bootstrap"
398398
COVERAGE_FILE: "target/.coverage.integration-${{ env.PLATFORM }}-${{ matrix.group }}"
399399
JUNIT_REPORTS_FILE: "target/pytest-junit-integration-${{ env.PLATFORM }}-${{ matrix.group }}.xml"
400400
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_PULL_USERNAME }}
@@ -822,11 +822,11 @@ jobs:
822822
path: dist/testselection/
823823

824824
- name: Run CloudFormation Engine v2 Tests
825-
timeout-minutes: 30
825+
timeout-minutes: 60
826826
env:
827827
# add the GitHub API token to avoid rate limit issues
828828
GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
829-
TEST_PATH: "tests/aws/services/cloudformation/v2"
829+
TEST_PATH: "tests/aws/services/cloudformation"
830830
PYTEST_ARGS: "${{ env.TINYBIRD_PYTEST_ARGS }}${{ env.TESTSELECTION_PYTEST_ARGS }} --reruns 3 -o junit_suite_name='cloudformation_v2'"
831831
PROVIDER_OVERRIDE_CLOUDFORMATION: "engine-v2"
832832
run: make test-coverage

.github/workflows/tests-pro-integration.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ jobs:
339339
AWS_DEFAULT_REGION: "us-east-1"
340340
JUNIT_REPORTS_FILE: "pytest-junit-community-${{ matrix.group }}.xml"
341341
TEST_PATH: "../../localstack/tests/aws/" # TODO: run tests in tests/integration
342-
PYTEST_ARGS: "${{ env.TINYBIRD_PYTEST_ARGS }}${{ env.TESTSELECTION_PYTEST_ARGS }}--splits ${{ strategy.job-total }} --group ${{ matrix.group }} --durations-path ../../localstack/.test_durations --store-durations --ignore ../../localstack/tests/aws/services/cloudformation/v2"
342+
PYTEST_ARGS: "${{ env.TINYBIRD_PYTEST_ARGS }}${{ env.TESTSELECTION_PYTEST_ARGS }}--splits ${{ strategy.job-total }} --group ${{ matrix.group }} --durations-path ../../localstack/.test_durations --store-durations"
343343
working-directory: localstack-pro
344344
run: |
345345
# Remove the host tmp folder (might contain remnant files with different permissions)

localstack-core/localstack/utils/collections.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,3 +534,17 @@ def is_comma_delimited_list(string: str, item_regex: Optional[str] = None) -> bo
534534
if pattern.match(string) is None:
535535
return False
536536
return True
537+
538+
539+
_E = TypeVar("_E")
540+
541+
542+
def optional_list(condition: bool, items: Iterable[_E]) -> list[_E]:
543+
"""
544+
Given an iterable, either create a list out of the entire iterable (if `condition` is `True`), or return the empty list.
545+
>>> print(optional_list(True, [1, 2, 3]))
546+
[1, 2, 3]
547+
>>> print(optional_list(False, [1, 2, 3]))
548+
[]
549+
"""
550+
return list(filter(lambda _: condition, items))

tests/aws/services/cloudformation/__init__.py

Whitespace-only changes.

tests/aws/services/cloudformation/api/__init__.py

Whitespace-only changes.

tests/aws/services/cloudformation/api/test_changesets.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@
44

55
import pytest
66
from botocore.exceptions import ClientError
7+
from tests.aws.services.cloudformation.api.test_stacks import (
8+
MINIMAL_TEMPLATE,
9+
)
10+
from tests.aws.services.cloudformation.conftest import (
11+
skip_if_v2_provider,
12+
skipped_v2_items,
13+
)
714

815
from localstack.aws.connect import ServiceLevelClientFactory
9-
from localstack.services.cloudformation.v2.utils import is_v2_engine
1016
from localstack.testing.aws.cloudformation_utils import (
1117
load_template_file,
1218
load_template_raw,
@@ -16,9 +22,6 @@
1622
from localstack.testing.pytest import markers
1723
from localstack.utils.strings import short_uid
1824
from localstack.utils.sync import ShortCircuitWaitException, poll_condition, wait_until
19-
from tests.aws.services.cloudformation.api.test_stacks import (
20-
MINIMAL_TEMPLATE,
21-
)
2225

2326

2427
class TestUpdates:
@@ -62,9 +65,6 @@ def test_simple_update_single_resource(
6265

6366
res.destroy()
6467

65-
@pytest.mark.skipif(
66-
condition=not is_v2_engine() and not is_aws_cloud(), reason="Not working in v2 yet"
67-
)
6868
@markers.aws.validated
6969
def test_simple_update_two_resources(
7070
self, aws_client: ServiceLevelClientFactory, deploy_cfn_template
@@ -111,9 +111,6 @@ def test_simple_update_two_resources(
111111
# TODO: the error response is incorrect, however the test is otherwise validated and raises
112112
# an error because the SSM parameter has been deleted (removed from the stack).
113113
@markers.snapshot.skip_snapshot_verify(paths=["$..Error.Message", "$..message"])
114-
@pytest.mark.skipif(
115-
condition=not is_v2_engine() and not is_aws_cloud(), reason="Test fails with the old engine"
116-
)
117114
def test_deleting_resource(
118115
self, aws_client: ServiceLevelClientFactory, deploy_cfn_template, snapshot
119116
):
@@ -285,11 +282,11 @@ def test_create_change_set_update_without_parameters(
285282
cleanup_stacks(stacks=[stack_id])
286283

287284

288-
# def test_create_change_set_with_template_url():
289-
# pass
290-
291-
292-
@pytest.mark.skipif(condition=not is_aws_cloud(), reason="change set type not implemented")
285+
# TODO: Key error during deletion
286+
# File "/Users/simon/work/localstack/localstack/localstack-core/localstack/services/cloudformation/v2/provider.py", line 162, in find_change_set_v2
287+
# return state.change_sets[change_set_name]
288+
# ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
289+
# KeyError: 'arn:aws:cloudformation:us-east-1:000000000000:changeSet/change-set-926829fe/d065e78c'
293290
@markers.aws.validated
294291
def test_create_change_set_create_existing(cleanup_changesets, cleanup_stacks, aws_client):
295292
"""tries to create an already existing stack"""
@@ -389,6 +386,7 @@ def test_create_change_set_missing_stackname(aws_client):
389386
)
390387

391388

389+
@skip_if_v2_provider(reason="CFNV2:Resolve")
392390
@markers.aws.validated
393391
def test_create_change_set_with_ssm_parameter(
394392
cleanup_changesets,
@@ -612,6 +610,7 @@ def delete(suppress_exception: bool = False):
612610
deploy()
613611

614612

613+
@skip_if_v2_provider(reason="CFNV2:Metadata, CFNV2:Other")
615614
@markers.aws.validated
616615
def test_create_and_then_remove_non_supported_resource_change_set(deploy_cfn_template):
617616
# first deploy cfn with a CodeArtifact resource that is not actually supported
@@ -763,6 +762,10 @@ def assert_bucket_gone():
763762
"$..IncludeNestedStacks",
764763
"$..Parameters",
765764
]
765+
+ skipped_v2_items(
766+
"$..Changes..ResourceChange.Details",
767+
"$..Changes..ResourceChange.Scope",
768+
)
766769
)
767770
@markers.aws.validated
768771
def test_empty_changeset(snapshot, cleanups, aws_client):
@@ -844,6 +847,7 @@ def _check_changeset_success():
844847
snapshot.match("error_execute_failed", e.value)
845848

846849

850+
@skip_if_v2_provider(reason="CFNV2:DeleteChangeSet")
847851
@markers.aws.validated
848852
def test_deleted_changeset(snapshot, cleanups, aws_client):
849853
"""simple case verifying that proper exception is thrown when trying to get a deleted changeset"""
@@ -967,6 +971,10 @@ def test_create_while_in_review(aws_client, snapshot, cleanups):
967971

968972
@markers.snapshot.skip_snapshot_verify(
969973
paths=["$..Capabilities", "$..IncludeNestedStacks", "$..NotificationARNs", "$..Parameters"]
974+
+ skipped_v2_items(
975+
"$..Changes..ResourceChange.Details",
976+
"$..Changes..ResourceChange.Scope",
977+
)
970978
)
971979
@markers.aws.validated
972980
def test_multiple_create_changeset(aws_client, snapshot, cleanups):
@@ -1003,6 +1011,7 @@ def test_multiple_create_changeset(aws_client, snapshot, cleanups):
10031011
)
10041012

10051013

1014+
@skip_if_v2_provider(reason="CFNV2:DescribeStacks")
10061015
@markers.snapshot.skip_snapshot_verify(paths=["$..LastUpdatedTime", "$..StackStatusReason"])
10071016
@markers.aws.validated
10081017
def test_create_changeset_with_stack_id(aws_client, snapshot, cleanups):
@@ -1085,6 +1094,10 @@ def test_create_changeset_with_stack_id(aws_client, snapshot, cleanups):
10851094
"$..StatusReason",
10861095
"$..StackStatusReason",
10871096
]
1097+
+ skipped_v2_items(
1098+
"$..Changes..ResourceChange.Details",
1099+
"$..Changes..ResourceChange.Scope",
1100+
),
10881101
)
10891102
@markers.aws.validated
10901103
def test_name_conflicts(aws_client, snapshot, cleanups):

tests/aws/services/cloudformation/api/test_changesets.snapshot.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,5 +498,20 @@
498498
}
499499
}
500500
}
501+
},
502+
"tests/aws/services/cloudformation/api/test_changesets.py::TestUpdates::test_deleting_resource": {
503+
"recorded-date": "02-06-2025, 10:29:41",
504+
"recorded-content": {
505+
"get-parameter-error": {
506+
"Error": {
507+
"Code": "ParameterNotFound",
508+
"Message": ""
509+
},
510+
"ResponseMetadata": {
511+
"HTTPHeaders": {},
512+
"HTTPStatusCode": 400
513+
}
514+
}
515+
}
501516
}
502517
}

tests/aws/services/cloudformation/api/test_nested_stacks.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import pytest
44
from botocore.exceptions import ClientError, WaiterError
5+
from tests.aws.services.cloudformation.conftest import skip_if_v2_provider
56

67
from localstack import config
78
from localstack.testing.aws.util import is_aws_cloud
@@ -294,6 +295,7 @@ def test_nested_stacks_conditions(deploy_cfn_template, s3_create_bucket, aws_cli
294295
assert ":" not in nested_stack["Stacks"][0]["StackName"]
295296

296297

298+
@skip_if_v2_provider(reason="CFNV2:Deletion")
297299
@markers.aws.validated
298300
def test_deletion_of_failed_nested_stack(s3_create_bucket, aws_client, region_name, snapshot):
299301
"""

tests/aws/services/cloudformation/v2/ported_from_v1/api/test_resources.py renamed to tests/aws/services/cloudformation/api/test_resources.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33

44
import pytest
55
from botocore.exceptions import ClientError
6+
from tests.aws.services.cloudformation.conftest import skip_if_v1_provider
67

78
from localstack.testing.pytest import markers
89

910

11+
@skip_if_v1_provider(reason="Not implemented for v1")
1012
@markers.aws.validated
1113
def test_describe_non_existent_stack(aws_client, deploy_cfn_template, snapshot):
1214
with pytest.raises(ClientError) as err:
@@ -20,7 +22,7 @@ def test_describe_non_existent_stack(aws_client, deploy_cfn_template, snapshot):
2022
@markers.aws.validated
2123
def test_describe_non_existent_resource(aws_client, deploy_cfn_template, snapshot):
2224
template_path = os.path.join(
23-
os.path.dirname(__file__), "../../../../../templates/ssm_parameter_defaultname.yaml"
25+
os.path.dirname(__file__), "../../../templates/ssm_parameter_defaultname.yaml"
2426
)
2527
stack = deploy_cfn_template(template_path=template_path, parameters={"Input": "myvalue"})
2628
snapshot.add_transformer(snapshot.transform.regex(stack.stack_id, "<stack-id>"))
@@ -33,6 +35,7 @@ def test_describe_non_existent_resource(aws_client, deploy_cfn_template, snapsho
3335
snapshot.match("error", err.value)
3436

3537

38+
@skip_if_v1_provider(reason="Not implemented for v1")
3639
@markers.aws.validated
3740
def test_invalid_logical_resource_id(deploy_cfn_template, snapshot):
3841
template = {

tests/aws/services/cloudformation/v2/ported_from_v1/api/test_resources.snapshot.json renamed to tests/aws/services/cloudformation/api/test_resources.snapshot.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{
2-
"tests/aws/services/cloudformation/v2/ported_from_v1/api/test_resources.py::test_describe_non_existent_resource": {
2+
"tests/aws/services/cloudformation/api/test_resources.py::test_describe_non_existent_resource": {
33
"recorded-date": "25-07-2025, 22:01:35",
44
"recorded-content": {
55
"error": "An error occurred (ValidationError) when calling the DescribeStackResource operation: Resource not-a-valid-resource does not exist for stack <stack-id>"
66
}
77
},
8-
"tests/aws/services/cloudformation/v2/ported_from_v1/api/test_resources.py::test_describe_non_existent_stack": {
8+
"tests/aws/services/cloudformation/api/test_resources.py::test_describe_non_existent_stack": {
99
"recorded-date": "25-07-2025, 22:02:38",
1010
"recorded-content": {
1111
"error": "An error occurred (ValidationError) when calling the DescribeStackResource operation: Stack 'not-a-valid-stack' does not exist"
1212
}
1313
},
14-
"tests/aws/services/cloudformation/v2/ported_from_v1/api/test_resources.py::test_invalid_logical_resource_id": {
14+
"tests/aws/services/cloudformation/api/test_resources.py::test_invalid_logical_resource_id": {
1515
"recorded-date": "25-07-2025, 22:21:31",
1616
"recorded-content": {
1717
"error": "An error occurred (ValidationError) when calling the CreateChangeSet operation: Template format error: Resource name my-bad-resource-id is non alphanumeric."

0 commit comments

Comments
 (0)