Skip to content

Commit 84bdd4a

Browse files
authored
[SFN][TestState] Make roleArn optional (#13459)
roleArn is now only required when tested state is Task and a mock is not provided. Add validation for roleArn presence. Add workaround for roleArn being not optional in Execution hierarchy. Remove roleArn from tests where it is not needed - much faster execution agaisnt AWS with only necessary fields provided. Snapshot recording hasn't changed.
1 parent 049201f commit 84bdd4a

20 files changed

+450
-476
lines changed

localstack-core/localstack/services/stepfunctions/asl/static_analyser/test_state/test_state_analyser.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.service.state_task_service_api_gateway import (
2525
StateTaskServiceApiGateway,
2626
)
27+
from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.state_task import (
28+
StateTask,
29+
)
2730
from localstack.services.stepfunctions.asl.component.state.state_type import StateType
2831
from localstack.services.stepfunctions.asl.component.test_state.program.test_state_program import (
2932
TestStateProgram,
@@ -58,6 +61,15 @@ def is_state_in_definition(definition: Definition, state_name: StateName) -> boo
5861

5962
return test_program.test_state is not None
6063

64+
@staticmethod
65+
def validate_role_arn_required(
66+
mock_input: MockInput, definition: Definition, state_name: StateName
67+
) -> None:
68+
test_program, _ = TestStateAmazonStateLanguageParser.parse(definition, state_name)
69+
test_state = test_program.test_state
70+
if isinstance(test_state, StateTask) and mock_input is None:
71+
raise ValidationException("RoleArn must be specified when testing a Task state")
72+
6173
@staticmethod
6274
def validate_mock(mock_input: MockInput, definition: Definition, state_name: StateName) -> None:
6375
test_program, _ = TestStateAmazonStateLanguageParser.parse(definition, state_name)

localstack-core/localstack/services/stepfunctions/provider.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@
162162
normalise_max_results,
163163
)
164164
from localstack.state import StateVisitor
165+
from localstack.utils.aws import arns
165166
from localstack.utils.aws.arns import (
166167
ARN_PARTITION_REGEX,
167168
stepfunctions_activity_arn,
@@ -1543,10 +1544,27 @@ def test_state(
15431544
arn = stepfunctions_state_machine_arn(
15441545
name=name, account_id=context.account_id, region_name=context.region
15451546
)
1547+
role_arn = request.get("roleArn")
1548+
if role_arn is None:
1549+
TestStateStaticAnalyser.validate_role_arn_required(
1550+
mock_input=mock_input, definition=definition, state_name=state_name
1551+
)
1552+
# HACK: Added dummy role ARN because it is a required field in Execution.
1553+
# To allow optional roleArn for the test state but preserve the mandatory one for regular executions
1554+
# we likely need to remove inheritance TestStateExecution(Execution) in favor of composition.
1555+
# TestState execution starts to have too many simplifications compared to a regular execution
1556+
# which renders the inheritance mechanism harmful.
1557+
# TODO make role_arn optional in TestStateExecution
1558+
role_arn = arns.iam_role_arn(
1559+
role_name=f"RoleFor-{name}",
1560+
account_id=context.account_id,
1561+
region_name=context.region,
1562+
)
1563+
15461564
state_machine = TestStateMachine(
15471565
name=name,
15481566
arn=arn,
1549-
role_arn=request["roleArn"],
1567+
role_arn=role_arn,
15501568
definition=request["definition"],
15511569
)
15521570

@@ -1561,7 +1579,7 @@ def test_state(
15611579

15621580
execution = TestStateExecution(
15631581
name=exec_name,
1564-
role_arn=request["roleArn"],
1582+
role_arn=role_arn,
15651583
exec_arn=exec_arn,
15661584
account_id=context.account_id,
15671585
region_name=context.region,

tests/aws/services/stepfunctions/v2/test_state/test_field_validation_mode.py

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ class TestFieldValidationMode:
3232
@pytest.mark.parametrize("result", EVENTBRIDGE_VALIDATION_PASS_FIELDS_NOT_IN_SPEC)
3333
def test_strict_mode_mock_result_field_not_in_api_spec(
3434
self,
35-
aws_client,
3635
aws_client_no_sync_prefix,
37-
create_state_machine_iam_role,
3836
sfn_snapshot,
3937
result,
4038
):
@@ -55,11 +53,8 @@ def test_strict_mode_mock_result_field_not_in_api_spec(
5553

5654
mock = {"result": json.dumps(result)}
5755

58-
sfn_role_arn = create_state_machine_iam_role(aws_client)
59-
6056
test_case_response = aws_client_no_sync_prefix.stepfunctions.test_state(
6157
definition=definition,
62-
roleArn=sfn_role_arn,
6358
input=exec_input,
6459
inspectionLevel=InspectionLevel.INFO,
6560
mock=mock,
@@ -96,7 +91,6 @@ def test_strict_mode_eventbridge_task(
9691
self,
9792
aws_client,
9893
aws_client_no_sync_prefix,
99-
create_state_machine_iam_role,
10094
sfn_snapshot,
10195
result,
10296
):
@@ -114,12 +108,9 @@ def test_strict_mode_eventbridge_task(
114108

115109
mock = {"result": json.dumps(result)}
116110

117-
sfn_role_arn = create_state_machine_iam_role(aws_client)
118-
119111
with pytest.raises(aws_client.stepfunctions.exceptions.ValidationException) as e:
120112
aws_client_no_sync_prefix.stepfunctions.test_state(
121113
definition=definition,
122-
roleArn=sfn_role_arn,
123114
input=exec_input,
124115
inspectionLevel=InspectionLevel.INFO,
125116
mock=mock,
@@ -151,7 +142,6 @@ def test_strict_mode_sfn_task(
151142
self,
152143
aws_client,
153144
aws_client_no_sync_prefix,
154-
create_state_machine_iam_role,
155145
account_id,
156146
region_name,
157147
sfn_snapshot,
@@ -176,12 +166,9 @@ def test_strict_mode_sfn_task(
176166

177167
mock = {"result": json.dumps(result)}
178168

179-
sfn_role_arn = create_state_machine_iam_role(aws_client)
180-
181169
with pytest.raises(aws_client.stepfunctions.exceptions.ValidationException) as e:
182170
aws_client_no_sync_prefix.stepfunctions.test_state(
183171
definition=definition,
184-
roleArn=sfn_role_arn,
185172
input=exec_input,
186173
inspectionLevel=InspectionLevel.INFO,
187174
mock=mock,
@@ -201,9 +188,6 @@ def test_strict_mode_lambda_task(
201188
self,
202189
aws_client,
203190
aws_client_no_sync_prefix,
204-
create_state_machine_iam_role,
205-
account_id,
206-
region_name,
207191
sfn_snapshot,
208192
result,
209193
):
@@ -213,12 +197,9 @@ def test_strict_mode_lambda_task(
213197

214198
mock = {"result": json.dumps(result)}
215199

216-
sfn_role_arn = create_state_machine_iam_role(aws_client)
217-
218200
with pytest.raises(aws_client.stepfunctions.exceptions.ValidationException) as e:
219201
aws_client_no_sync_prefix.stepfunctions.test_state(
220202
definition=definition,
221-
roleArn=sfn_role_arn,
222203
input=exec_input,
223204
inspectionLevel=InspectionLevel.INFO,
224205
mock=mock,
@@ -238,9 +219,6 @@ def test_strict_mode_dynamodb_task(
238219
self,
239220
aws_client,
240221
aws_client_no_sync_prefix,
241-
create_state_machine_iam_role,
242-
account_id,
243-
region_name,
244222
sfn_snapshot,
245223
result,
246224
):
@@ -255,12 +233,9 @@ def test_strict_mode_dynamodb_task(
255233

256234
mock = {"result": json.dumps(result)}
257235

258-
sfn_role_arn = create_state_machine_iam_role(aws_client)
259-
260236
with pytest.raises(aws_client.stepfunctions.exceptions.ValidationException) as e:
261237
aws_client_no_sync_prefix.stepfunctions.test_state(
262238
definition=definition,
263-
roleArn=sfn_role_arn,
264239
input=exec_input,
265240
inspectionLevel=InspectionLevel.INFO,
266241
mock=mock,
@@ -284,9 +259,6 @@ def test_strict_mode_aws_sdk_s3_task(
284259
self,
285260
aws_client,
286261
aws_client_no_sync_prefix,
287-
create_state_machine_iam_role,
288-
account_id,
289-
region_name,
290262
sfn_snapshot,
291263
result,
292264
):
@@ -296,12 +268,9 @@ def test_strict_mode_aws_sdk_s3_task(
296268

297269
mock = {"result": json.dumps(result)}
298270

299-
sfn_role_arn = create_state_machine_iam_role(aws_client)
300-
301271
with pytest.raises(aws_client.stepfunctions.exceptions.ValidationException) as e:
302272
aws_client_no_sync_prefix.stepfunctions.test_state(
303273
definition=definition,
304-
roleArn=sfn_role_arn,
305274
input=exec_input,
306275
inspectionLevel=InspectionLevel.INFO,
307276
mock=mock,
@@ -329,9 +298,6 @@ def test_strict_mode_aws_sdk_kms_task(
329298
self,
330299
aws_client,
331300
aws_client_no_sync_prefix,
332-
create_state_machine_iam_role,
333-
account_id,
334-
region_name,
335301
sfn_snapshot,
336302
result,
337303
):
@@ -341,12 +307,9 @@ def test_strict_mode_aws_sdk_kms_task(
341307

342308
mock = {"result": json.dumps(result)}
343309

344-
sfn_role_arn = create_state_machine_iam_role(aws_client)
345-
346310
with pytest.raises(aws_client.stepfunctions.exceptions.ValidationException) as e:
347311
aws_client_no_sync_prefix.stepfunctions.test_state(
348312
definition=definition,
349-
roleArn=sfn_role_arn,
350313
input=exec_input,
351314
inspectionLevel=InspectionLevel.INFO,
352315
mock=mock,
@@ -373,9 +336,6 @@ def test_strict_mode_aws_sdk_lambda_get_function_task(
373336
self,
374337
aws_client,
375338
aws_client_no_sync_prefix,
376-
create_state_machine_iam_role,
377-
account_id,
378-
region_name,
379339
sfn_snapshot,
380340
result,
381341
):
@@ -385,12 +345,9 @@ def test_strict_mode_aws_sdk_lambda_get_function_task(
385345

386346
mock = {"result": json.dumps(result)}
387347

388-
sfn_role_arn = create_state_machine_iam_role(aws_client)
389-
390348
with pytest.raises(aws_client.stepfunctions.exceptions.ValidationException) as e:
391349
aws_client_no_sync_prefix.stepfunctions.test_state(
392350
definition=definition,
393-
roleArn=sfn_role_arn,
394351
input=exec_input,
395352
inspectionLevel=InspectionLevel.INFO,
396353
mock=mock,

0 commit comments

Comments
 (0)