Skip to content

Commit e892cef

Browse files
committed
Add tests for writing to s3
1 parent 93eb82c commit e892cef

File tree

3 files changed

+59
-65
lines changed

3 files changed

+59
-65
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies = [
1313
"jupyter>=1.1.1",
1414
"matplotlib>=3.10.0",
1515
"scikit-learn>=1.6.1",
16+
"moto>=5.0.28",
1617
]
1718

1819
[tool.pytest.ini_options]

src/validation_lambda/app.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ def valid_transaction(event, _) -> dict[str, list[ValidTransaction]]:
6565
)
6666
except ValidationError as e:
6767
invalid_trx = InvalidTransaction(
68-
transaction=Transaction(**payload),
68+
transaction=payload,
6969
error=str(e)
7070
)
7171
invalid_data.append(invalid_trx.model_dump())
7272
write_to_s3(
7373
invalid_data_bucket_name,
74-
invalid_data.transaction.transaction_id,
75-
invalid_data.model_dump_json()
74+
hash(str(invalid_trx.transaction)),
75+
invalid_trx.model_dump_json()
7676
)
7777
return {
7878
"data": data
Lines changed: 55 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
import json
2-
from datetime import datetime
3-
from src.validation_lambda.app import valid_transaction, Status
42
import base64
3+
import boto3
4+
import os
5+
from datetime import datetime
6+
from moto import mock_aws
7+
import pytest
8+
from src.validation_lambda.app import valid_transaction, Status, write_to_s3
9+
10+
# Setup environment variables for S3
11+
VALID_BUCKET = "valid-bucket"
12+
INVALID_BUCKET = "invalid-bucket"
13+
os.environ["VALIDDATA_BUCKET_NAME"] = VALID_BUCKET
14+
os.environ["INVALIDDATA_BUCKET_NAME"] = INVALID_BUCKET
15+
16+
17+
@pytest.fixture(scope="function")
18+
def s3_mock():
19+
with mock_aws():
20+
s3 = boto3.client("s3")
21+
s3.create_bucket(Bucket=VALID_BUCKET)
22+
s3.create_bucket(Bucket=INVALID_BUCKET)
23+
yield s3
524

625

7-
# Helper function to encode payloadds
826
def encode_payload(payload):
927
payload_json = json.dumps(payload)
10-
return base64.b64encode(payload_json.encode('utf-8')).decode('utf-8')
28+
return base64.b64encode(payload_json.encode("utf-8")).decode("utf-8")
1129

1230

13-
def decode_payload(payload):
14-
return payload
15-
1631
# Sample valid transaction
1732
def dummy_valid_transaction():
1833
return {
@@ -22,100 +37,78 @@ def dummy_valid_transaction():
2237
"amount": 254.67,
2338
"device_type": "mobile",
2439
"location": "California, USA",
25-
"is_vpn": "false",
40+
"is_vpn": False,
2641
"card_type": "credit",
2742
"status": "approved",
2843
}
2944

3045

31-
# Test case for valid transaction
32-
def test_valid_transaction_should_process():
33-
event = {"Records": [{"kinesis": {"data": encode_payload(dummy_valid_transaction())}}]}
34-
35-
response = decode_payload(valid_transaction(event, None))['data']
46+
def test_valid_transaction_should_process(s3_mock):
47+
event = {
48+
"Records": [{"kinesis": {"data": encode_payload(dummy_valid_transaction())}}]
49+
}
50+
response = valid_transaction(event, None)["data"]
3651

3752
assert len(response) == 1
3853
assert response[0]["status"] == Status.ok.value
3954
assert response[0]["error"] == ""
4055

56+
# Check if data is written to S3
57+
s3_objects = s3_mock.list_objects(Bucket=VALID_BUCKET)
58+
assert "Contents" in s3_objects
59+
assert len(s3_objects["Contents"]) == 1
4160

42-
def test_transaction_with_json_data_in_kinesis_event():
43-
event = {"Records": [{"kinesis": {"data": dummy_valid_transaction()}}]}
61+
# should not write to invalid bucket
62+
s3_objects = s3_mock.list_objects(Bucket=INVALID_BUCKET)
63+
assert "Contents" not in s3_objects
4464

45-
response = decode_payload(valid_transaction(event, None))['data']
4665

47-
assert len(response) == 1
48-
assert response[0]["status"] == Status.ok.value
49-
assert response[0]["error"] == ""
50-
51-
52-
# Test case for invalid transaction with missing fields
53-
def test_invalid_transaction_missing_fields_should_return_empty_response():
66+
def test_invalid_transaction_should_write_to_invalid_s3(s3_mock):
5467
invalid_payload = dummy_valid_transaction()
5568
del invalid_payload["transaction_id"] # Remove a required field
5669
event = {"Records": [{"kinesis": {"data": encode_payload(invalid_payload)}}]}
5770

58-
response = decode_payload(valid_transaction(event, None))['data']
59-
60-
assert len(response) == 0
71+
valid_transaction(event, None) # Process transaction
6172

73+
# Check if invalid transaction is written to INVALID S3 bucket
74+
s3_objects = s3_mock.list_objects(Bucket=INVALID_BUCKET)
75+
assert "Contents" in s3_objects
76+
assert len(s3_objects["Contents"]) == 1
6277

63-
# Test case for invalid transaction with incorrect format
64-
def test_invalid_transaction_incorrect_format_should_return_empty_response():
65-
invalid_payload = dummy_valid_transaction()
66-
invalid_payload["transaction_id"] = "12345" # Does not match regex
67-
event = {"Records": [{"kinesis": {"data": encode_payload(invalid_payload)}}]}
78+
# should not write to valid bucket
79+
s3_objects = s3_mock.list_objects(Bucket=VALID_BUCKET)
80+
assert "Contents" not in s3_objects
6881

69-
response = decode_payload(valid_transaction(event, None))['data']
7082

71-
assert len(response) == 0
72-
73-
74-
# Test case for multiple records
75-
def test_multiple_transactions():
83+
def test_multiple_transactions(s3_mock):
7684
event = {
7785
"Records": [
7886
{"kinesis": {"data": encode_payload(dummy_valid_transaction())}},
7987
{"kinesis": {"data": encode_payload(dummy_valid_transaction())}},
8088
]
8189
}
82-
83-
response = decode_payload(valid_transaction(event, None))['data']
90+
response = valid_transaction(event, None)['data']
8491

8592
assert len(response) == 2
8693
assert all(tx["status"] == Status.ok.value for tx in response)
8794

8895

89-
def test_multiple_mix_transactions_should_filter_invalid_transactions():
90-
invalid_payload = dummy_valid_transaction()
91-
invalid_payload["transaction_id"] = "12345"
92-
event = {
93-
"Records": [
94-
{"kinesis": {"data": encode_payload(dummy_valid_transaction())}},
95-
{"kinesis": {"data": encode_payload(dummy_valid_transaction())}},
96-
{"kinesis": {"data": encode_payload(invalid_payload)}},
97-
]
98-
}
99-
100-
response = decode_payload(valid_transaction(event, None))['data']
101-
102-
assert len(response) == 2
103-
104-
105-
def test_output_is_json_serializable():
96+
def test_output_is_json_serializable(s3_mock):
10697
"""Lambda expects output to be serializable"""
10798
event = {
10899
"Records": [
109100
{"kinesis": {"data": encode_payload(dummy_valid_transaction())}},
110101
]
111102
}
112103

113-
response = decode_payload(valid_transaction(event, None))['data']
104+
response = valid_transaction(event, None)['data']
114105
assert json.dumps(response)
115106

116107

117-
# Test case for empty event
118-
def test_empty_event():
119-
event = {"Records": []}
120-
response = valid_transaction(event, None)
121-
assert response == {'data': []}
108+
def test_write_to_s3(s3_mock):
109+
content = json.dumps({"test": "data"})
110+
write_to_s3(VALID_BUCKET, "test_transaction", content)
111+
112+
s3_objects = s3_mock.list_objects(Bucket=VALID_BUCKET)
113+
assert "Contents" in s3_objects
114+
assert len(s3_objects["Contents"]) == 1

0 commit comments

Comments
 (0)