Skip to content

Commit 54097a7

Browse files
committed
[PRMP-1048] seperating handler from serbice
1 parent eb413a0 commit 54097a7

File tree

4 files changed

+103
-81
lines changed

4 files changed

+103
-81
lines changed

lambdas/handlers/concurrency_controller_handler.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,13 @@ def lambda_handler(event, _context):
2929
target_function, reserved_concurrency = validate_event(event)
3030

3131
service = ConcurrencyControllerService()
32-
return service.update_function_concurrency(target_function, reserved_concurrency)
32+
updated_concurrency = service.update_function_concurrency(target_function, reserved_concurrency)
33+
34+
return {
35+
"statusCode": 200,
36+
"body": {
37+
"message": "Concurrency updated successfully",
38+
"function": target_function,
39+
"reservedConcurrency": updated_concurrency
40+
}
41+
}

lambdas/services/concurrency_controller_service.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import boto3
2+
from botocore.exceptions import ClientError
23
from utils.audit_logging_setup import LoggingService
34

45
logger = LoggingService(__name__)
@@ -21,22 +22,27 @@ def update_function_concurrency(self, target_function, reserved_concurrency):
2122

2223
updated_concurrency = response.get("ReservedConcurrentExecutions")
2324

25+
if updated_concurrency is None:
26+
logger.error("Response did not contain ReservedConcurrentExecutions")
27+
raise ValueError("Failed to confirm concurrency update from AWS response")
28+
29+
if updated_concurrency != reserved_concurrency:
30+
logger.error(
31+
f"Concurrency mismatch: requested {reserved_concurrency}, "
32+
f"AWS returned {updated_concurrency}"
33+
)
34+
raise ValueError("Concurrency update verification failed")
35+
2436
logger.info(
2537
f"Successfully updated concurrency for '{target_function}'. "
2638
f"Reserved concurrency set to: {updated_concurrency}"
2739
)
2840

29-
return {
30-
"statusCode": 200,
31-
"body": {
32-
"message": "Concurrency updated successfully",
33-
"function": target_function,
34-
"reservedConcurrency": updated_concurrency
35-
}
36-
}
37-
except self.lambda_client.exceptions.ResourceNotFoundException:
38-
logger.error(f"Lambda function '{target_function}' not found")
39-
raise
40-
except self.lambda_client.exceptions.InvalidParameterValueException as e:
41-
logger.error(f"Invalid concurrency value: {e}")
41+
return updated_concurrency
42+
except ClientError as e:
43+
error_code = e.response.get("Error", {}).get("Code", "")
44+
if error_code == "ResourceNotFoundException":
45+
logger.error(f"Lambda function '{target_function}' not found")
46+
else:
47+
logger.error(f"Failed to update concurrency: {str(e)}")
4248
raise

lambdas/tests/unit/handlers/test_concurrency_controller_handler.py

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -36,47 +36,35 @@ def event_with_zero_concurrency():
3636

3737

3838
def test_lambda_handler_success(valid_event, context, mock_concurrency_controller_service):
39-
expected_response = {
40-
"statusCode": 200,
41-
"body": {
42-
"message": "Concurrency updated successfully",
43-
"function": "test-lambda-function",
44-
"reservedConcurrency": 10
45-
}
46-
}
47-
48-
mock_concurrency_controller_service.update_function_concurrency.return_value = expected_response
39+
mock_concurrency_controller_service.update_function_concurrency.return_value = 10
4940

5041
result = lambda_handler(valid_event, context)
5142

5243
mock_concurrency_controller_service.update_function_concurrency.assert_called_once_with(
5344
"test-lambda-function", 10
5445
)
5546

56-
assert result == expected_response
47+
assert result["statusCode"] == 200
48+
assert result["body"]["message"] == "Concurrency updated successfully"
49+
assert result["body"]["function"] == "test-lambda-function"
50+
assert result["body"]["reservedConcurrency"] == 10
5751

5852

5953
def test_lambda_handler_with_zero_concurrency(
6054
event_with_zero_concurrency, context, mock_concurrency_controller_service
6155
):
62-
expected_response = {
63-
"statusCode": 200,
64-
"body": {
65-
"message": "Concurrency updated successfully",
66-
"function": "test-lambda-function",
67-
"reservedConcurrency": 0
68-
}
69-
}
70-
71-
mock_concurrency_controller_service.update_function_concurrency.return_value = expected_response
56+
mock_concurrency_controller_service.update_function_concurrency.return_value = 0
7257

7358
result = lambda_handler(event_with_zero_concurrency, context)
7459

7560
mock_concurrency_controller_service.update_function_concurrency.assert_called_once_with(
7661
"test-lambda-function", 0
7762
)
7863

79-
assert result == expected_response
64+
assert result["statusCode"] == 200
65+
assert result["body"]["message"] == "Concurrency updated successfully"
66+
assert result["body"]["function"] == "test-lambda-function"
67+
assert result["body"]["reservedConcurrency"] == 0
8068

8169

8270
def test_lambda_handler_with_large_concurrency(context, mock_concurrency_controller_service):
@@ -85,23 +73,23 @@ def test_lambda_handler_with_large_concurrency(context, mock_concurrency_control
8573
"reservedConcurrency": 1000
8674
}
8775

88-
expected_response = {
89-
"statusCode": 200,
90-
"body": {
91-
"message": "Concurrency updated successfully",
92-
"function": "test-lambda-function",
93-
"reservedConcurrency": 1000
94-
}
95-
}
96-
97-
mock_concurrency_controller_service.update_function_concurrency.return_value = expected_response
76+
mock_concurrency_controller_service.update_function_concurrency.return_value = 1000
9877

9978
result = lambda_handler(event, context)
10079

10180
mock_concurrency_controller_service.update_function_concurrency.assert_called_once_with(
10281
"test-lambda-function", 1000
10382
)
10483

84+
assert result["statusCode"] == 200
85+
assert result["body"]["message"] == "Concurrency updated successfully"
86+
assert result["body"]["function"] == "test-lambda-function"
87+
assert result["body"]["reservedConcurrency"] == 1000
88+
89+
mock_concurrency_controller_service.update_function_concurrency.assert_called_once_with(
90+
"test-lambda-function", 1000
91+
)
92+
10593
assert result == expected_response
10694

10795

lambdas/tests/unit/services/test_concurrency_controller_service.py

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,7 @@ def test_update_function_concurrency_success(service, mock_lambda_client, mock_l
3939
ReservedConcurrentExecutions=reserved_concurrency
4040
)
4141

42-
assert result["statusCode"] == 200
43-
assert result["body"]["message"] == "Concurrency updated successfully"
44-
assert result["body"]["function"] == target_function
45-
assert result["body"]["reservedConcurrency"] == reserved_concurrency
42+
assert result == reserved_concurrency
4643

4744
mock_logger.info.assert_any_call(
4845
f"Updating reserved concurrency for function '{target_function}' to {reserved_concurrency}"
@@ -64,20 +61,11 @@ def test_update_function_concurrency_function_not_found(service, mock_lambda_cli
6461
'Error': {
6562
'Code': 'ResourceNotFoundException',
6663
'Message': 'Function not found'
67-
}
64+
},
65+
'ResponseMetadata': {'HTTPStatusCode': 404}
6866
}
6967

70-
# Create proper exception classes that inherit from ClientError
71-
resource_not_found_exception = type('ResourceNotFoundException', (ClientError,), {})
72-
invalid_param_exception = type('InvalidParameterValueException', (ClientError,), {})
73-
74-
# Create a mock exceptions object with both exception types
75-
mock_exceptions = MagicMock()
76-
mock_exceptions.ResourceNotFoundException = resource_not_found_exception
77-
mock_exceptions.InvalidParameterValueException = invalid_param_exception
78-
mock_client_instance.exceptions = mock_exceptions
79-
80-
mock_client_instance.put_function_concurrency.side_effect = resource_not_found_exception(
68+
mock_client_instance.put_function_concurrency.side_effect = ClientError(
8169
error_response, 'PutFunctionConcurrency'
8270
)
8371

@@ -102,20 +90,11 @@ def test_update_function_concurrency_invalid_parameter(service, mock_lambda_clie
10290
'Error': {
10391
'Code': 'InvalidParameterValueException',
10492
'Message': 'Reserved concurrency value must be non-negative'
105-
}
93+
},
94+
'ResponseMetadata': {'HTTPStatusCode': 400}
10695
}
10796

108-
# Create proper exception classes that inherit from ClientError
109-
resource_not_found_exception = type('ResourceNotFoundException', (ClientError,), {})
110-
invalid_param_exception = type('InvalidParameterValueException', (ClientError,), {})
111-
112-
# Create a mock exceptions object with both exception types
113-
mock_exceptions = MagicMock()
114-
mock_exceptions.ResourceNotFoundException = resource_not_found_exception
115-
mock_exceptions.InvalidParameterValueException = invalid_param_exception
116-
mock_client_instance.exceptions = mock_exceptions
117-
118-
mock_client_instance.put_function_concurrency.side_effect = invalid_param_exception(
97+
mock_client_instance.put_function_concurrency.side_effect = ClientError(
11998
error_response, 'PutFunctionConcurrency'
12099
)
121100

@@ -124,9 +103,9 @@ def test_update_function_concurrency_invalid_parameter(service, mock_lambda_clie
124103
with pytest.raises(ClientError):
125104
service.update_function_concurrency(target_function, reserved_concurrency)
126105

127-
assert mock_logger.error.call_count == 1
128-
error_call_args = str(mock_logger.error.call_args)
129-
assert "Invalid concurrency value" in error_call_args
106+
mock_logger.error.assert_called_once_with(
107+
f"Failed to update concurrency: An error occurred (InvalidParameterValueException) when calling the PutFunctionConcurrency operation: Reserved concurrency value must be non-negative"
108+
)
130109

131110

132111
def test_update_function_concurrency_with_zero_value(service, mock_lambda_client, mock_logger):
@@ -144,8 +123,7 @@ def test_update_function_concurrency_with_zero_value(service, mock_lambda_client
144123

145124
result = service.update_function_concurrency(target_function, reserved_concurrency)
146125

147-
assert result["statusCode"] == 200
148-
assert result["body"]["reservedConcurrency"] == 0
126+
assert result == 0
149127

150128

151129
def test_update_function_concurrency_with_large_value(service, mock_lambda_client, mock_logger):
@@ -163,12 +141,53 @@ def test_update_function_concurrency_with_large_value(service, mock_lambda_clien
163141

164142
result = service.update_function_concurrency(target_function, reserved_concurrency)
165143

166-
assert result["statusCode"] == 200
167-
assert result["body"]["reservedConcurrency"] == 1000
144+
assert result == 1000
168145

169146

170147
def test_init_creates_lambda_client(mock_lambda_client):
171148
service = ConcurrencyControllerService()
172149

173150
mock_lambda_client.assert_called_once_with("lambda")
174151
assert service.lambda_client is not None
152+
153+
154+
def test_update_function_concurrency_missing_response_field(service, mock_lambda_client, mock_logger):
155+
target_function = "test-lambda-function"
156+
reserved_concurrency = 10
157+
158+
mock_client_instance = MagicMock()
159+
mock_lambda_client.return_value = mock_client_instance
160+
161+
# Response missing ReservedConcurrentExecutions field
162+
mock_client_instance.put_function_concurrency.return_value = {}
163+
164+
service.lambda_client = mock_client_instance
165+
166+
with pytest.raises(ValueError) as exc_info:
167+
service.update_function_concurrency(target_function, reserved_concurrency)
168+
169+
assert str(exc_info.value) == "Failed to confirm concurrency update from AWS response"
170+
mock_logger.error.assert_called_with("Response did not contain ReservedConcurrentExecutions")
171+
172+
173+
def test_update_function_concurrency_value_mismatch(service, mock_lambda_client, mock_logger):
174+
target_function = "test-lambda-function"
175+
reserved_concurrency = 10
176+
177+
mock_client_instance = MagicMock()
178+
mock_lambda_client.return_value = mock_client_instance
179+
180+
# AWS returned different value than requested
181+
mock_client_instance.put_function_concurrency.return_value = {
182+
"ReservedConcurrentExecutions": 5
183+
}
184+
185+
service.lambda_client = mock_client_instance
186+
187+
with pytest.raises(ValueError) as exc_info:
188+
service.update_function_concurrency(target_function, reserved_concurrency)
189+
190+
assert str(exc_info.value) == "Concurrency update verification failed"
191+
mock_logger.error.assert_called_with(
192+
f"Concurrency mismatch: requested {reserved_concurrency}, AWS returned 5"
193+
)

0 commit comments

Comments
 (0)