Skip to content

Commit 679fc56

Browse files
NRL-721 Update readme and add handler unit tests
1 parent 23331bc commit 679fc56

File tree

2 files changed

+179
-16
lines changed

2 files changed

+179
-16
lines changed

lambdas/seed_sandbox/index.py

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
"""
2-
Lambda handler for resetting specified DynamoDB tables with seed test data.
3-
4-
This Lambda function runs on a schedule to clear and reseed specified
5-
pointers tables with fresh test data.
6-
"""
7-
81
# flake8: noqa: T201
92

103
import json
@@ -16,17 +9,10 @@
169

1710
def handler(event, context):
1811
"""
19-
Lambda handler that orchestrates the reset of specified tables
20-
21-
The tables to be reset are specified by the TABLE_NAMES environment variable
22-
as a comma-separated list
12+
Lambda handler that orchestrates the reset of specified pointer tables in the dev & test accounts, deleting all items and reseeding with fresh data .
2313
24-
Args:
25-
event: Lambda event (from EventBridge schedule)
26-
context: Lambda context
14+
The tables to be reset and number of pointers per type can be specified in `terraform/account-wide-infrastructure/{env}/lambda__seed-sandbox.tf`
2715
28-
Returns:
29-
dict: Response with status and details for each table
3016
"""
3117
table_names_str = os.environ.get("TABLE_NAMES", "")
3218
pointers_per_type = int(os.environ.get("POINTERS_PER_TYPE", "2"))
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import json
2+
from unittest.mock import MagicMock, patch
3+
4+
import pytest
5+
6+
from lambdas.seed_sandbox.index import handler
7+
8+
9+
@pytest.fixture
10+
def mock_lambda_context():
11+
mock_context = MagicMock()
12+
mock_context.function_name = "test-function"
13+
mock_context.invoked_function_arn = (
14+
"arn:aws:lambda:eu-west-2:123456789012:function:test-function"
15+
)
16+
return mock_context
17+
18+
19+
@pytest.fixture
20+
def mock_env_vars():
21+
with patch.dict(
22+
"os.environ", {"TABLE_NAMES": "test-table", "POINTERS_PER_TYPE": "2"}
23+
):
24+
yield
25+
26+
27+
class TestHandler:
28+
29+
@patch("lambdas.seed_sandbox.index.seed_sandbox_table")
30+
@patch("lambdas.seed_sandbox.index.delete_all_table_items")
31+
def test_single_table_reset_success(
32+
self, mock_delete, mock_seed, mock_lambda_context, mock_env_vars
33+
):
34+
mock_delete.return_value = 10
35+
mock_seed.return_value = {"successful": 8, "attempted": 8, "failed": 0}
36+
37+
result = handler({}, mock_lambda_context)
38+
39+
assert result["statusCode"] == 200
40+
body = json.loads(result["body"])
41+
assert body["message"] == "Successfully reset 1 table(s)"
42+
assert body["tables_processed"] == 1
43+
assert body["tables_succeeded"] == 1
44+
assert body["tables_failed"] == 0
45+
assert len(body["results"]) == 1
46+
assert body["results"][0]["table_name"] == "test-table"
47+
assert body["results"][0]["status"] == "success"
48+
assert body["results"][0]["pointers_deleted"] == 10
49+
assert body["results"][0]["pointers_created"] == 8
50+
51+
mock_delete.assert_called_once_with(table_name="test-table")
52+
mock_seed.assert_called_once_with(
53+
table_name="test-table", pointers_per_type=2, force=True, write_csv=False
54+
)
55+
56+
@patch("lambdas.seed_sandbox.index.seed_sandbox_table")
57+
@patch("lambdas.seed_sandbox.index.delete_all_table_items")
58+
def test_multiple_table_reset_success(
59+
self, mock_delete, mock_seed, mock_lambda_context, mock_env_vars
60+
):
61+
with patch.dict(
62+
"os.environ",
63+
{"TABLE_NAMES": "table1,table2,table3", "POINTERS_PER_TYPE": "5"},
64+
):
65+
mock_delete.return_value = 15
66+
mock_seed.return_value = {"successful": 20, "attempted": 20, "failed": 0}
67+
68+
result = handler({}, mock_lambda_context)
69+
70+
assert result["statusCode"] == 200
71+
body = json.loads(result["body"])
72+
assert body["message"] == "Successfully reset 3 table(s)"
73+
assert body["tables_processed"] == 3
74+
assert body["tables_succeeded"] == 3
75+
assert body["tables_failed"] == 0
76+
assert len(body["results"]) == 3
77+
assert all(r["status"] == "success" for r in body["results"])
78+
79+
assert mock_delete.call_count == 3
80+
assert mock_seed.call_count == 3
81+
82+
@patch("lambdas.seed_sandbox.index.seed_sandbox_table")
83+
@patch("lambdas.seed_sandbox.index.delete_all_table_items")
84+
def test_partial_failure(self, mock_delete, mock_seed, mock_lambda_context):
85+
with patch.dict(
86+
"os.environ",
87+
{"TABLE_NAMES": "table1,table2,table3", "POINTERS_PER_TYPE": "2"},
88+
):
89+
# First and third tables succeed, second fails during delete
90+
mock_delete.side_effect = [10, Exception("Access denied"), 5]
91+
mock_seed.side_effect = [
92+
{"successful": 8, "attempted": 8, "failed": 0},
93+
{"successful": 8, "attempted": 8, "failed": 0},
94+
]
95+
96+
result = handler({}, mock_lambda_context)
97+
98+
assert result["statusCode"] == 207
99+
body = json.loads(result["body"])
100+
assert "Failed to reset 1 table(s): table2" in body["message"]
101+
assert body["tables_processed"] == 3
102+
assert body["tables_succeeded"] == 2
103+
assert body["tables_failed"] == 1
104+
assert len(body["results"]) == 3
105+
assert body["results"][0]["status"] == "success"
106+
assert body["results"][1]["status"] == "failed"
107+
assert body["results"][1]["error"] == "Access denied"
108+
assert body["results"][2]["status"] == "success"
109+
110+
@patch("lambdas.seed_sandbox.index.seed_sandbox_table")
111+
@patch("lambdas.seed_sandbox.index.delete_all_table_items")
112+
def test_complete_failure(self, mock_delete, mock_seed, mock_lambda_context):
113+
with patch.dict(
114+
"os.environ", {"TABLE_NAMES": "table1,table2", "POINTERS_PER_TYPE": "2"}
115+
):
116+
mock_delete.side_effect = Exception("Database error")
117+
118+
result = handler({}, mock_lambda_context)
119+
120+
assert result["statusCode"] == 500
121+
body = json.loads(result["body"])
122+
assert "Failed to reset 2 table(s)" in body["message"]
123+
assert body["tables_processed"] == 2
124+
assert body["tables_succeeded"] == 0
125+
assert body["tables_failed"] == 2
126+
127+
def test_missing_table_names_env_var(self, mock_lambda_context):
128+
with patch.dict("os.environ", {}, clear=True):
129+
result = handler({}, mock_lambda_context)
130+
131+
assert result["statusCode"] == 500
132+
body = json.loads(result["body"])
133+
assert body["error"] == "TABLE_NAMES environment variable is required"
134+
135+
def test_empty_table_names(self, mock_lambda_context):
136+
with patch.dict("os.environ", {"TABLE_NAMES": " ", "POINTERS_PER_TYPE": "2"}):
137+
result = handler({}, mock_lambda_context)
138+
139+
assert result["statusCode"] == 500
140+
body = json.loads(result["body"])
141+
assert body["error"] == "No valid table names provided in TABLE_NAMES"
142+
143+
@patch("lambdas.seed_sandbox.index.seed_sandbox_table")
144+
@patch("lambdas.seed_sandbox.index.delete_all_table_items")
145+
def test_table_names_with_whitespace(
146+
self, mock_delete, mock_seed, mock_lambda_context
147+
):
148+
with patch.dict(
149+
"os.environ",
150+
{"TABLE_NAMES": " table1 , table2 , ", "POINTERS_PER_TYPE": "2"},
151+
):
152+
mock_delete.return_value = 5
153+
mock_seed.return_value = {"successful": 4, "attempted": 4, "failed": 0}
154+
155+
result = handler({}, mock_lambda_context)
156+
157+
assert result["statusCode"] == 200
158+
body = json.loads(result["body"])
159+
assert body["tables_processed"] == 2
160+
assert body["results"][0]["table_name"] == "table1"
161+
assert body["results"][1]["table_name"] == "table2"
162+
163+
@patch("lambdas.seed_sandbox.index.seed_sandbox_table")
164+
@patch("lambdas.seed_sandbox.index.delete_all_table_items")
165+
def test_seed_with_failures(
166+
self, mock_delete, mock_seed, mock_lambda_context, mock_env_vars
167+
):
168+
mock_delete.return_value = 5
169+
mock_seed.return_value = {"successful": 6, "attempted": 8, "failed": 2}
170+
171+
result = handler({}, mock_lambda_context)
172+
173+
assert result["statusCode"] == 200
174+
body = json.loads(result["body"])
175+
assert body["results"][0]["pointers_created"] == 6
176+
assert body["results"][0]["pointers_attempted"] == 8
177+
assert body["results"][0]["pointers_failed"] == 2

0 commit comments

Comments
 (0)