Skip to content

Commit 0dbc62a

Browse files
committed
WIP
1 parent 18e35f0 commit 0dbc62a

File tree

7 files changed

+587
-268
lines changed

7 files changed

+587
-268
lines changed

ack_backend/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ package: build
66
docker run --rm -v $(shell pwd)/build:/build ack-lambda-build
77

88
test:
9-
python -m unittest
9+
@PYTHONPATH=src:tests python -m unittest
1010

1111
.PHONY: build package

ack_backend/src/update_ack_file.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,12 @@ def upload_ack_file(
7676
) -> None:
7777
"""Adds the data row to the uploaded ack file"""
7878
for row in ack_data_rows:
79-
print(f"add row {row} to ack file")
8079
data_row_str = [str(item) for item in row.values()]
8180
cleaned_row = "|".join(data_row_str).replace(" |", "|").replace("| ", "|").strip()
8281
accumulated_csv_content.write(cleaned_row + "\n")
8382

84-
8583
csv_file_like_object = BytesIO(accumulated_csv_content.getvalue().encode("utf-8"))
8684
s3_client.upload_fileobj(csv_file_like_object, ACK_BUCKET_NAME, temp_ack_file_key)
87-
88-
debug_print("upload_ack_file...2")
8985

9086
row_count_source = get_row_count(SOURCE_BUCKET_NAME, f"processing/{file_key}")
9187
row_count_destination = get_row_count(ACK_BUCKET_NAME, temp_ack_file_key)
Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,11 @@
11
"""Utils for ack lambda"""
22

33
from clients import s3_client
4-
from tests.utils.values_for_ack_backend_tests import MessageDetails
54

65
def get_row_count(bucket_name: str, file_key: str) -> int:
76
"""
87
Looks in the given bucket and returns the count of the number of lines in the given file.
98
NOTE: Blank lines are not included in the count.
109
"""
11-
try:
12-
response = s3_client.get_object(Bucket=bucket_name, Key=file_key)
13-
if "Body" not in response or response["Body"] is None:
14-
print(f"Error getting row count: No Body in response for {bucket_name}/{file_key}")
15-
return 0
16-
ret=sum(1 for line in response["Body"].iter_lines() if line.strip())
17-
print(f"Row count {bucket_name}-{file_key}: {ret}")
18-
except Exception as e:
19-
print(f"Row count {bucket_name}-{file_key}: 0")
20-
return 0
21-
22-
from constants import ACK_HEADERS, SOURCE_BUCKET_NAME, ACK_BUCKET_NAME, FILE_NAME_PROC_LAMBDA_NAME
23-
24-
def debug_print(title, show_source=False):
25-
rsv_ravs = MessageDetails("RSV", "RAVS", "X26")
26-
print(f"Debug rowcount: {title}")
27-
28-
if show_source:
29-
row_count_source = get_row_count(SOURCE_BUCKET_NAME, f"processing/{rsv_ravs.file_key}")
30-
print(f"Row count in source bucket: {row_count_source}")
31-
32-
row_count_destination = get_row_count(ACK_BUCKET_NAME, "TempAck/RSV_Vaccinations_v5_X26_20210730T12000000_BusAck_20211120T12000000.csv")
33-
34-
print(f"Row count in destination bucket: {row_count_destination}")
10+
response = s3_client.get_object(Bucket=bucket_name, Key=file_key)
11+
return sum(1 for line in response["Body"].iter_lines() if line.strip())
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import unittest
2+
from unittest.mock import patch, MagicMock
3+
import json
4+
5+
from ack_processor import lambda_handler
6+
7+
8+
class TestAckLambdaHandler(unittest.TestCase):
9+
def setUp(self):
10+
convert_patcher = patch("ack_processor.convert_message_to_ack_row")
11+
self.mock_convert = convert_patcher.start()
12+
update_patcher = patch("ack_processor.update_ack_file")
13+
self.mock_update = update_patcher.start()
14+
decorator_patcher = patch("ack_processor.ack_lambda_handler_logging_decorator", lambda x: x)
15+
self.mock_decorator = decorator_patcher.start()
16+
17+
# Mock behavior
18+
self.mock_convert.side_effect = lambda message, created_at: {
19+
"row_id": message["row_id"],
20+
"status": "ACK"
21+
}
22+
23+
def tearDown(self):
24+
patch.stopall()
25+
26+
def test_lambda_handler_success(self):
27+
message_body = [
28+
{
29+
"file_key": "file1.csv",
30+
"row_id": "123^abc",
31+
"vaccine_type": "typeA",
32+
"supplier": "supplierX",
33+
"created_at_formatted_string": "2025-01-01T00:00:00Z"
34+
},
35+
{
36+
"file_key": "file1.csv",
37+
"row_id": "124^def",
38+
"vaccine_type": "typeA",
39+
"supplier": "supplierX",
40+
"created_at_formatted_string": "2025-01-01T00:00:00Z"
41+
}
42+
]
43+
event = {
44+
"Records": [
45+
{"body": json.dumps(message_body)}
46+
]
47+
}
48+
49+
result = lambda_handler(event, None)
50+
51+
self.assertEqual(result["statusCode"], 200)
52+
self.assertIn("Lambda function executed successfully", result["body"])
53+
self.mock_update.assert_called_once()
54+
self.assertEqual(self.mock_convert.call_count, 2)
55+
56+
def test_lambda_handler_no_records(self):
57+
event = {}
58+
59+
with self.assertRaises(ValueError) as context:
60+
lambda_handler(event, None)
61+
62+
self.assertIn("No records found in the event", str(context.exception))
63+
self.mock_update.assert_not_called()
64+
self.mock_convert.assert_not_called()
65+
66+
def test_lambda_handler_body_json_decode_error(self):
67+
event = {
68+
"Records": [
69+
{"body": "{invalid_json"}
70+
]
71+
}
72+
73+
with self.assertRaises(ValueError) as context:
74+
lambda_handler(event, None)
75+
76+
self.assertIn("Could not load incoming message body", str(context.exception))
77+
self.mock_update.assert_not_called()
78+
self.mock_convert.assert_not_called()
79+
80+
81+
if __name__ == "__main__":
82+
unittest.main()
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import unittest
2+
from unittest.mock import patch, MagicMock, call
3+
import logging_decorators
4+
import json
5+
import time
6+
7+
class TestLoggingDecorators(unittest.TestCase):
8+
def setUp(self):
9+
# Patch logger and firehose_client
10+
self.logger_patcher = patch('logging_decorators.logger')
11+
self.mock_logger = self.logger_patcher.start()
12+
self.firehose_patcher = patch('logging_decorators.firehose_client')
13+
self.mock_firehose = self.firehose_patcher.start()
14+
15+
def tearDown(self):
16+
self.logger_patcher.stop()
17+
self.firehose_patcher.stop()
18+
19+
def test_send_log_to_firehose_success(self):
20+
log_data = {"foo": "bar"}
21+
logging_decorators.send_log_to_firehose(log_data)
22+
self.mock_firehose.put_record.assert_called_once()
23+
self.mock_logger.info.assert_called_with("Log sent to Firehose")
24+
25+
def test_send_log_to_firehose_exception(self):
26+
self.mock_firehose.put_record.side_effect = Exception("fail!")
27+
log_data = {"foo": "bar"}
28+
logging_decorators.send_log_to_firehose(log_data)
29+
self.mock_logger.exception.assert_called()
30+
self.assertIn("Error sending log to Firehose", self.mock_logger.exception.call_args[0][0])
31+
32+
def test_generate_and_send_logs_info(self):
33+
start_time = time.time() - 1
34+
base_log_data = {"base": "data"}
35+
additional_log_data = {"extra": "info"}
36+
logging_decorators.generate_and_send_logs(start_time, base_log_data, additional_log_data)
37+
self.mock_logger.info.assert_called()
38+
self.mock_firehose.put_record.assert_called_once()
39+
40+
def test_generate_and_send_logs_error(self):
41+
start_time = time.time() - 1
42+
base_log_data = {"base": "data"}
43+
additional_log_data = {"extra": "info"}
44+
logging_decorators.generate_and_send_logs(start_time, base_log_data, additional_log_data, is_error_log=True)
45+
self.mock_logger.error.assert_called()
46+
self.mock_firehose.put_record.assert_called_once()
47+
48+
def test_process_diagnostics_dict(self):
49+
diagnostics = {"statusCode": 400, "error_message": "bad request"}
50+
result = logging_decorators.process_diagnostics(diagnostics, "file.csv", "msg-1")
51+
self.assertEqual(result["status"], "fail")
52+
self.assertEqual(result["statusCode"], 400)
53+
self.assertEqual(result["diagnostics"], "bad request")
54+
55+
def test_process_diagnostics_string(self):
56+
diagnostics = "some error"
57+
result = logging_decorators.process_diagnostics(diagnostics, "file.csv", "msg-1")
58+
self.assertEqual(result["status"], "fail")
59+
self.assertEqual(result["statusCode"], 500)
60+
self.assertEqual(result["diagnostics"], "Unable to determine diagnostics issue")
61+
62+
def test_process_diagnostics_missing_keys(self):
63+
result = logging_decorators.process_diagnostics(None, "file_key_missing", "unknown")
64+
self.assertEqual(result["status"], "fail")
65+
self.assertEqual(result["statusCode"], 500)
66+
self.assertIn("unhandled error", result["diagnostics"])
67+
68+
def test_process_diagnostics_success(self):
69+
result = logging_decorators.process_diagnostics(None, "file.csv", "msg-1")
70+
self.assertEqual(result["status"], "success")
71+
self.assertEqual(result["statusCode"], 200)
72+
self.assertIn("Operation completed successfully", result["diagnostics"])
73+
74+
def test_convert_message_to_ack_row_logging_decorator_success(self):
75+
@logging_decorators.convert_message_to_ack_row_logging_decorator
76+
def dummy_func(message, created_at_formatted_string):
77+
return "ok"
78+
79+
message = {
80+
"file_key": "file.csv",
81+
"row_id": "row-1",
82+
"vaccine_type": "type",
83+
"supplier": "sup",
84+
"local_id": "loc",
85+
"operation_requested": "op"
86+
}
87+
result = dummy_func(message, "2024-08-20T12:00:00Z")
88+
self.assertEqual(result, "ok")
89+
self.mock_logger.info.assert_called()
90+
self.mock_firehose.put_record.assert_called()
91+
92+
def test_convert_message_to_ack_row_logging_decorator_exception(self):
93+
@logging_decorators.convert_message_to_ack_row_logging_decorator
94+
def dummy_func(message, created_at_formatted_string):
95+
raise ValueError("fail!")
96+
97+
message = {
98+
"file_key": "file.csv",
99+
"row_id": "row-1",
100+
"vaccine_type": "type",
101+
"supplier": "sup",
102+
"local_id": "loc",
103+
"operation_requested": "op"
104+
}
105+
with self.assertRaises(ValueError):
106+
dummy_func(message, "2024-08-20T12:00:00Z")
107+
self.mock_logger.error.assert_called()
108+
self.mock_firehose.put_record.assert_called()
109+
110+
def test_ack_lambda_handler_logging_decorator_success(self):
111+
@logging_decorators.ack_lambda_handler_logging_decorator
112+
def dummy_lambda(event, context):
113+
return "lambda-ok"
114+
115+
result = dummy_lambda({}, {})
116+
self.assertEqual(result, "lambda-ok")
117+
self.mock_logger.info.assert_called()
118+
self.mock_firehose.put_record.assert_called()
119+
120+
def test_ack_lambda_handler_logging_decorator_exception(self):
121+
@logging_decorators.ack_lambda_handler_logging_decorator
122+
def dummy_lambda(event, context):
123+
raise RuntimeError("fail!")
124+
125+
with self.assertRaises(RuntimeError):
126+
dummy_lambda({}, {})
127+
self.mock_logger.error.assert_called()
128+
self.mock_firehose.put_record.assert_called()
129+
130+
if __name__ == "__main__":
131+
unittest.main()

0 commit comments

Comments
 (0)