Skip to content

Commit ca3603a

Browse files
Bug/safo6 NRL-2089 Log error when delete fails (#1174)
1 parent 7e37b6d commit ca3603a

File tree

4 files changed

+138
-9
lines changed

4 files changed

+138
-9
lines changed

layer/nrlf/core/dynamodb/repository.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -344,14 +344,18 @@ def delete_by_id(self, id_: str, can_ignore_delete_fail: bool = False):
344344
key = f"D#{id_}"
345345
try:
346346
self.table.delete_item(Key={"pk": key, "sk": key})
347+
logger.log(LogReference.REPOSITORY027, id=id_)
347348
except Exception as exc:
348-
if can_ignore_delete_fail:
349-
logger.log(
350-
LogReference.REPOSITORY026a,
351-
exc_info=sys.exc_info(),
352-
stacklevel=5,
353-
error=str(exc),
354-
)
349+
logger.log(
350+
(
351+
LogReference.REPOSITORY026a
352+
if can_ignore_delete_fail
353+
else LogReference.REPOSITORY026b
354+
),
355+
exc_info=sys.exc_info(),
356+
stacklevel=5,
357+
error=str(exc),
358+
)
355359

356360
def _query(self, **kwargs) -> Iterator[DocumentPointer]:
357361
"""

layer/nrlf/core/dynamodb/tests/test_repository.py

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,32 @@
1+
from unittest.mock import patch
2+
13
import pytest
4+
from moto import mock_aws
25

36
from nrlf.core.constants import PointerTypes
4-
from nrlf.core.dynamodb.repository import _get_sk_ids_for_type
7+
from nrlf.core.dynamodb.model import DocumentPointer
8+
from nrlf.core.dynamodb.repository import (
9+
DocumentPointerRepository,
10+
_get_sk_ids_for_type,
11+
)
12+
from nrlf.core.log_references import LogReference
13+
from nrlf.tests.data import load_document_reference
14+
from nrlf.tests.dynamodb import mock_repository
15+
16+
# ---------------------------------------------------------------------------
17+
# Helpers
18+
# ---------------------------------------------------------------------------
19+
20+
21+
def make_document_pointer(id: str = "Y05868-99999-99999-999999") -> DocumentPointer:
22+
doc_ref = load_document_reference("Y05868-736253002-Valid")
23+
doc_ref.id = id
24+
return DocumentPointer.from_document_reference(doc_ref)
25+
26+
27+
# ---------------------------------------------------------------------------
28+
# _get_sk_ids_for_type
29+
# ---------------------------------------------------------------------------
530

631

732
def test_get_sk_ids_for_type_exception_thrown_for_invalid_type():
@@ -30,4 +55,89 @@ def test_get_sk_ids_for_type_exception_thrown_if_new_type_has_no_category():
3055
)
3156

3257

33-
# TODO: Add unit tests for Repository Class
58+
# ---------------------------------------------------------------------------
59+
# DocumentPointerRepository.supersede
60+
# ---------------------------------------------------------------------------
61+
62+
63+
@mock_aws
64+
@mock_repository
65+
def test_supersede_creates_new_and_deletes_old(repository: DocumentPointerRepository):
66+
old_doc = make_document_pointer(id="Y05868-OLD0001")
67+
repository.create(old_doc)
68+
assert repository.get_by_id("Y05868-OLD0001") is not None
69+
70+
new_doc = make_document_pointer(id="Y05868-NEW0001")
71+
result = repository.supersede(new_doc, ids_to_delete=["Y05868-OLD0001"])
72+
73+
assert result.id == "Y05868-NEW0001"
74+
assert repository.get_by_id("Y05868-NEW0001") is not None
75+
assert repository.get_by_id("Y05868-OLD0001") is None
76+
77+
78+
@mock_aws
79+
@mock_repository
80+
@patch("nrlf.core.dynamodb.repository.logger")
81+
def test_supersede_with_can_ignore_delete_fail(
82+
mock_logger,
83+
repository: DocumentPointerRepository,
84+
):
85+
new_doc = make_document_pointer(id="Y05868-NEW0003")
86+
87+
with patch.object(
88+
repository.table,
89+
"delete_item",
90+
side_effect=Exception("simulated delete failure"),
91+
):
92+
result = repository.supersede(
93+
new_doc,
94+
ids_to_delete=["Y05868-NONEXISTENT"],
95+
can_ignore_delete_fail=True,
96+
)
97+
98+
assert result.id == "Y05868-NEW0003"
99+
log_codes = [c.args[0] for c in mock_logger.log.call_args_list]
100+
assert LogReference.REPOSITORY026a in log_codes
101+
102+
103+
# ---------------------------------------------------------------------------
104+
# DocumentPointerRepository.delete_by_id
105+
# ---------------------------------------------------------------------------
106+
107+
108+
@mock_aws
109+
@mock_repository
110+
def test_delete_by_id_removes_document_pointer(repository: DocumentPointerRepository):
111+
doc = make_document_pointer()
112+
repository.create(doc)
113+
assert repository.get_by_id(doc.id) is not None
114+
115+
repository.delete_by_id(doc.id)
116+
117+
assert repository.get_by_id(doc.id) is None
118+
119+
120+
@pytest.mark.parametrize(
121+
"ignore_delete_fail, log_reference",
122+
[(True, LogReference.REPOSITORY026a), (False, LogReference.REPOSITORY026b)],
123+
)
124+
@mock_aws
125+
@mock_repository
126+
@patch("nrlf.core.dynamodb.repository.logger")
127+
def test_delete_by_id_logs_correct_reference_based_on_can_ignore_delete_fail(
128+
mock_logger,
129+
repository: DocumentPointerRepository,
130+
ignore_delete_fail,
131+
log_reference,
132+
):
133+
with patch.object(
134+
repository.table,
135+
"delete_item",
136+
side_effect=Exception("simulated delete failure"),
137+
):
138+
repository.delete_by_id(
139+
"Y05868-NONEXISTENT", can_ignore_delete_fail=ignore_delete_fail
140+
)
141+
142+
log_codes = [c.args[0] for c in mock_logger.log.call_args_list]
143+
assert log_reference in log_codes

layer/nrlf/core/log_references.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ class LogReference(Enum):
151151
REPOSITORY026a = _Reference(
152152
"EXCEPTION", "Ignoring failure to delete resource from DynamoDB"
153153
)
154+
REPOSITORY026b = _Reference(
155+
"EXCEPTION", "Failed to delete superseded resource from DynamoDB"
156+
)
154157
REPOSITORY027 = _Reference("INFO", "Successfully deleted item from DynamoDB")
155158
REPOSITORY028 = _Reference("INFO", "Received page of search results")
156159
REPOSITORY028a = _Reference("DEBUG", "Received page of search results with result")

layer/nrlf/tests/dynamodb.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import functools
2+
import inspect
3+
14
from nrlf.core.boto import get_dynamodb_resource
25
from nrlf.core.config import Config
36
from nrlf.core.dynamodb.repository import DocumentPointerRepository
@@ -40,6 +43,7 @@ def create_document_pointer_table(config: Config, dynamodb: DynamoDBServiceResou
4043

4144

4245
def mock_repository(func):
46+
@functools.wraps(func)
4347
def wrapped_function(*args, **kwargs):
4448
config = Config()
4549
dynamodb = get_dynamodb_resource()
@@ -49,4 +53,12 @@ def wrapped_function(*args, **kwargs):
4953

5054
return func(*args, **kwargs, repository=repository)
5155

56+
# Remove 'repository' from the visible signature so pytest doesn't try
57+
# to inject it as a test parameter (it's already injected by this decorator).
58+
sig = inspect.signature(func)
59+
params_minus_repository = [
60+
p for name, p in sig.parameters.items() if name != "repository"
61+
]
62+
wrapped_function.__signature__ = sig.replace(parameters=params_minus_repository)
63+
5264
return wrapped_function

0 commit comments

Comments
 (0)