Skip to content

Commit edd031c

Browse files
[PRMP-1182] added tests
1 parent cba7d2a commit edd031c

File tree

1 file changed

+175
-16
lines changed

1 file changed

+175
-16
lines changed

lambdas/tests/unit/services/test_expedite_kill_switch_service.py

Lines changed: 175 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from services.expedite_transfer_family_kill_switch_service import (
55
ExpediteKillSwitchService,
66
EXPECTED_SCAN_RESULTS,
7+
STOP_WORTHY_SCAN_RESULTS,
78
response,
89
)
910

@@ -12,6 +13,7 @@
1213
def mock_transfer_client(mocker):
1314
transfer_client = mocker.Mock()
1415
cloudwatch_client = mocker.Mock()
16+
1517
class ResourceNotFoundException(Exception):
1618
pass
1719

@@ -61,6 +63,7 @@ def sns_event():
6163
def extract_message(resp):
6264
return json.loads(resp["body"])["message"]
6365

66+
6467
def test_response_builds_expected_http_shape():
6568
msg = "hello world"
6669
resp = response(msg)
@@ -71,7 +74,7 @@ def test_response_builds_expected_http_shape():
7174

7275

7376
def test_handle_sns_event_happy_path_infected_expedite(
74-
service, sns_event, mock_transfer_client
77+
service, sns_event, mock_transfer_client
7578
):
7679
mock_transfer_client.list_servers.return_value = {
7780
"Servers": [{"ServerId": "srv-12345"}]
@@ -87,22 +90,22 @@ def test_handle_sns_event_happy_path_infected_expedite(
8790

8891

8992
def test_handle_sns_event_no_servers_disables_kill_switch(
90-
service, sns_event, mock_transfer_client
93+
service, sns_event, mock_transfer_client
9194
):
9295
mock_transfer_client.list_servers.return_value = {"Servers": []}
9396

9497
resp = service.handle_sns_event(sns_event)
9598

9699
assert (
97-
extract_message(resp)
98-
== "Transfer family kill switch disabled – no Transfer server ID discovered"
100+
extract_message(resp)
101+
== "Transfer family kill switch disabled – no Transfer server ID discovered"
99102
)
100103
mock_transfer_client.describe_server.assert_not_called()
101104
mock_transfer_client.stop_server.assert_not_called()
102105

103106

104107
def test_get_transfer_server_id_happy_path_reads_from_list_servers(
105-
service, mock_transfer_client
108+
service, mock_transfer_client
106109
):
107110
mock_transfer_client.list_servers.return_value = {
108111
"Servers": [{"ServerId": " srv-9999 "}]
@@ -115,7 +118,7 @@ def test_get_transfer_server_id_happy_path_reads_from_list_servers(
115118

116119

117120
def test_get_transfer_server_id_returns_empty_when_no_servers(
118-
service, mock_transfer_client
121+
service, mock_transfer_client
119122
):
120123
mock_transfer_client.list_servers.return_value = {"Servers": []}
121124

@@ -125,16 +128,17 @@ def test_get_transfer_server_id_returns_empty_when_no_servers(
125128

126129

127130
def test_get_transfer_server_id_returns_empty_on_generic_error(
128-
service, mock_transfer_client
131+
service, mock_transfer_client
129132
):
130133
mock_transfer_client.list_servers.side_effect = Exception("boom")
131134

132135
server_id = service.get_transfer_server_id()
133136

134137
assert server_id == ""
135138

139+
136140
def test_handle_scan_message_calls_stop_server_for_infected_expedite(
137-
service, sns_event, mocker
141+
service, sns_event, mocker
138142
):
139143
message = json.loads(sns_event["Records"][0]["Sns"]["Message"])
140144
server_id = "srv-abc"
@@ -150,6 +154,7 @@ def test_handle_scan_message_calls_stop_server_for_infected_expedite(
150154
mock_stop.assert_called_once_with(server_id)
151155
assert extract_message(resp) == "Server stopped"
152156

157+
153158
def test_is_relevant_scan_result_true_for_expected_values(service):
154159
for value in EXPECTED_SCAN_RESULTS:
155160
assert service.is_relevant_scan_result(value) is True
@@ -160,6 +165,7 @@ def test_is_relevant_scan_result_false_for_other_values(service):
160165
assert service.is_relevant_scan_result("") is False
161166
assert service.is_relevant_scan_result(None) is False
162167

168+
163169
def test_has_required_fields_true_when_bucket_and_key_present(service):
164170
assert service.has_required_fields("bucket", "key") is True
165171

@@ -170,6 +176,7 @@ def test_has_required_fields_false_when_bucket_or_key_missing(service):
170176
assert service.has_required_fields(None, "key") is False
171177
assert service.has_required_fields("bucket", None) is False
172178

179+
173180
def test_is_quarantine_expedite_true_for_valid_quarantine_key(service):
174181
bucket = "cloudstoragesecquarantine-xyz"
175182
key = "pre-prod-staging-bulk-store/expedite/path/file.pdf"
@@ -185,7 +192,7 @@ def test_is_quarantine_expedite_false_for_non_quarantine_bucket(service):
185192

186193

187194
def test_is_quarantine_expedite_false_if_staging_bucket_not_set(
188-
mock_transfer_client, monkeypatch
195+
mock_transfer_client, monkeypatch
189196
):
190197
monkeypatch.delenv("STAGING_STORE_BUCKET_NAME", raising=False)
191198
monkeypatch.setenv("WORKSPACE", "pre-prod")
@@ -213,8 +220,9 @@ def test_extract_sns_message_returns_none_for_invalid_shapes(service):
213220
assert service.extract_sns_message({"Records": [{}]}) is None
214221
assert service.extract_sns_message({"Records": [{"Sns": {}}]}) is None
215222

223+
216224
def test_stop_transfer_family_server_happy_path_stops_server(
217-
service, mock_transfer_client
225+
service, mock_transfer_client
218226
):
219227
mock_transfer_client.describe_server.return_value = {"Server": {"State": "ONLINE"}}
220228

@@ -226,7 +234,7 @@ def test_stop_transfer_family_server_happy_path_stops_server(
226234

227235

228236
def test_stop_transfer_family_server_returns_not_found_if_server_missing(
229-
service, mock_transfer_client
237+
service, mock_transfer_client
230238
):
231239
NotFound = mock_transfer_client.exceptions.ResourceNotFoundException
232240
mock_transfer_client.describe_server.side_effect = NotFound()
@@ -238,7 +246,7 @@ def test_stop_transfer_family_server_returns_not_found_if_server_missing(
238246

239247

240248
def test_stop_transfer_family_server_handles_generic_exception(
241-
service, mock_transfer_client
249+
service, mock_transfer_client
242250
):
243251
mock_transfer_client.describe_server.side_effect = Exception("boom")
244252

@@ -247,6 +255,7 @@ def test_stop_transfer_family_server_handles_generic_exception(
247255
mock_transfer_client.stop_server.assert_not_called()
248256
assert extract_message(resp) == "Failed to stop server"
249257

258+
250259
def test_handle_scan_message_ignores_irrelevant_scan_result(service, mocker):
251260
message = {
252261
"scanResult": "Clean",
@@ -327,11 +336,12 @@ def test_handle_scan_message_non_infected_expedite(service, mocker):
327336
resp = service.handle_scan_message(server_id="srv-abc", message=message)
328337

329338
assert (
330-
extract_message(resp)
331-
== "Non-infected result for expedite file, no kill switch action"
339+
extract_message(resp)
340+
== "Non-infected result for expedite file, no kill switch action"
332341
)
333342
mock_stop.assert_not_called()
334343

344+
335345
def test_extract_sns_message_returns_none_on_invalid_json(service):
336346
event = {
337347
"Records": [
@@ -347,8 +357,9 @@ def test_extract_sns_message_returns_none_on_invalid_json(service):
347357

348358
assert msg is None
349359

360+
350361
def test_stop_transfer_family_server_handles_metric_failure(
351-
service, mock_transfer_client, mocker
362+
service, mock_transfer_client, mocker
352363
):
353364
mock_transfer_client.describe_server.return_value = {"Server": {"State": "ONLINE"}}
354365

@@ -362,7 +373,155 @@ def test_stop_transfer_family_server_handles_metric_failure(
362373

363374
mock_transfer_client.describe_server.assert_called_once_with(ServerId="srv-xyz")
364375
mock_transfer_client.stop_server.assert_called_once_with(ServerId="srv-xyz")
376+
assert (
377+
extract_message(resp)
378+
== "Server srv-xyz stopped, but failed to alert the team"
379+
)
380+
381+
def test_handle_sns_event_stops_for_each_stop_worthy_scan_result(
382+
service, sns_event, mock_transfer_client, monkeypatch
383+
):
384+
mock_transfer_client.list_servers.return_value = {
385+
"Servers": [{"ServerId": "srv-12345"}]
386+
}
387+
mock_transfer_client.describe_server.return_value = {"Server": {"State": "ONLINE"}}
388+
389+
for scan_result in STOP_WORTHY_SCAN_RESULTS:
390+
message = json.loads(sns_event["Records"][0]["Sns"]["Message"])
391+
message["scanResult"] = scan_result
392+
393+
event = {
394+
"Records": [
395+
{
396+
"Sns": {
397+
"Message": json.dumps(message),
398+
}
399+
}
400+
]
401+
}
402+
403+
resp = service.handle_sns_event(event)
404+
405+
assert extract_message(resp) == "Server srv-12345 stopped"
406+
407+
assert mock_transfer_client.stop_server.call_count == len(STOP_WORTHY_SCAN_RESULTS)
408+
409+
410+
def test_handle_sns_event_does_not_stop_for_error_scan_result(
411+
service, sns_event, mock_transfer_client
412+
):
413+
mock_transfer_client.list_servers.return_value = {
414+
"Servers": [{"ServerId": "srv-12345"}]
415+
}
416+
417+
message = json.loads(sns_event["Records"][0]["Sns"]["Message"])
418+
message["scanResult"] = "Error"
419+
event = {
420+
"Records": [
421+
{
422+
"Sns": {
423+
"Message": json.dumps(message),
424+
}
425+
}
426+
]
427+
}
428+
429+
resp = service.handle_sns_event(event)
430+
431+
assert extract_message(resp) == "No action taken"
432+
mock_transfer_client.describe_server.assert_not_called()
433+
mock_transfer_client.stop_server.assert_not_called()
434+
435+
436+
def test_handle_sns_event_does_not_stop_for_clean_or_unknown_scan_result(
437+
service, sns_event, mock_transfer_client
438+
):
439+
mock_transfer_client.list_servers.return_value = {
440+
"Servers": [{"ServerId": "srv-12345"}]
441+
}
442+
443+
for scan_result in ["Clean", "CLEAN", "Unknown", "NoThreatsFound"]:
444+
message = json.loads(sns_event["Records"][0]["Sns"]["Message"])
445+
message["scanResult"] = scan_result
446+
447+
event = {
448+
"Records": [
449+
{
450+
"Sns": {
451+
"Message": json.dumps(message),
452+
}
453+
}
454+
]
455+
}
456+
457+
resp = service.handle_sns_event(event)
458+
assert extract_message(resp) == "No action taken"
459+
460+
mock_transfer_client.describe_server.assert_not_called()
461+
mock_transfer_client.stop_server.assert_not_called()
462+
463+
464+
def test_handle_sns_event_returns_no_action_when_scan_result_missing(
465+
service, sns_event, mock_transfer_client
466+
):
467+
mock_transfer_client.list_servers.return_value = {
468+
"Servers": [{"ServerId": "srv-12345"}]
469+
}
470+
471+
message = json.loads(sns_event["Records"][0]["Sns"]["Message"])
472+
message.pop("scanResult", None)
473+
474+
event = {
475+
"Records": [
476+
{
477+
"Sns": {
478+
"Message": json.dumps(message),
479+
}
480+
}
481+
]
482+
}
483+
484+
resp = service.handle_sns_event(event)
485+
486+
assert extract_message(resp) == "No action taken"
487+
mock_transfer_client.describe_server.assert_not_called()
488+
mock_transfer_client.stop_server.assert_not_called()
489+
490+
491+
def test_handle_sns_event_returns_invalid_sns_message_on_bad_json(
492+
service, mock_transfer_client
493+
):
494+
mock_transfer_client.list_servers.return_value = {
495+
"Servers": [{"ServerId": "srv-12345"}]
496+
}
497+
498+
event = {
499+
"Records": [
500+
{
501+
"Sns": {
502+
"Message": "not-json-at-all",
503+
}
504+
}
505+
]
506+
}
507+
508+
resp = service.handle_sns_event(event)
509+
510+
assert extract_message(resp) == "Invalid SNS message; no action taken"
511+
mock_transfer_client.describe_server.assert_not_called()
512+
mock_transfer_client.stop_server.assert_not_called()
513+
514+
515+
def test_handle_sns_event_no_servers_message_contract(
516+
service, sns_event, mock_transfer_client
517+
):
518+
mock_transfer_client.list_servers.return_value = {"Servers": []}
519+
520+
resp = service.handle_sns_event(sns_event)
521+
365522
assert (
366523
extract_message(resp)
367-
== "Server srv-xyz stopped, but failed to alert the team"
524+
== "Transfer family kill switch disabled – no Transfer server ID discovered"
368525
)
526+
mock_transfer_client.describe_server.assert_not_called()
527+
mock_transfer_client.stop_server.assert_not_called()

0 commit comments

Comments
 (0)