Skip to content

Commit 961f84d

Browse files
authored
DMP-5196 ExternalDataStoreDeleterAutomatedTask - Rethrow Interrupted Exception (#3077)
1 parent 2fa32a8 commit 961f84d

12 files changed

+501
-19
lines changed

src/integrationTest/java/uk/gov/hmcts/darts/task/service/ExternalDataStoreDeleterTest.java renamed to src/integrationTest/java/uk/gov/hmcts/darts/task/service/ExternalDataStoreDeleterIntTest.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import org.junit.jupiter.api.BeforeEach;
88
import org.junit.jupiter.api.Test;
99
import org.mockito.Mock;
10-
import org.mockito.Mockito;
1110
import org.springframework.beans.factory.annotation.Autowired;
1211
import org.springframework.test.context.bean.override.mockito.MockitoBean;
1312
import uk.gov.hmcts.darts.audio.deleter.impl.ExternalDetsDataStoreDeleter;
@@ -44,6 +43,7 @@
4443
import static org.mockito.ArgumentMatchers.eq;
4544
import static org.mockito.Mockito.never;
4645
import static org.mockito.Mockito.verify;
46+
import static org.mockito.Mockito.when;
4747
import static uk.gov.hmcts.darts.audio.enums.MediaRequestStatus.COMPLETED;
4848
import static uk.gov.hmcts.darts.common.enums.ExternalLocationTypeEnum.DETS;
4949
import static uk.gov.hmcts.darts.common.enums.ExternalLocationTypeEnum.INBOUND;
@@ -52,7 +52,8 @@
5252
import static uk.gov.hmcts.darts.common.enums.ObjectRecordStatusEnum.STORED;
5353
import static uk.gov.hmcts.darts.test.common.data.PersistableFactory.getMediaRequestTestData;
5454

55-
class ExternalDataStoreDeleterTest extends IntegrationBase {
55+
class ExternalDataStoreDeleterIntTest extends IntegrationBase {
56+
5657
@Autowired
5758
protected TransientObjectDirectoryStub transientObjectDirectoryStub;
5859
@Autowired
@@ -75,8 +76,7 @@ class ExternalDataStoreDeleterTest extends IntegrationBase {
7576

7677
@MockitoBean
7778
private DetsApiService detsApiService;
78-
79-
79+
8080
private UserAccountEntity requestor;
8181
private HearingEntity hearing;
8282

@@ -122,9 +122,9 @@ void setUp() {
122122
@Test
123123
void deleteMarkedForDeletionDataFromDataStores() {
124124
audioBuilder.setupTest();
125-
Mockito.when(dataManagementFactory.getBlobServiceClient(anyString())).thenReturn(blobServiceClient);
126-
Mockito.when(dataManagementFactory.getBlobContainerClient(anyString(), eq(blobServiceClient))).thenReturn(blobContainerClient);
127-
Mockito.when(dataManagementFactory.getBlobClient(any(), any())).thenReturn(blobClient);
125+
when(dataManagementFactory.getBlobServiceClient(anyString())).thenReturn(blobServiceClient);
126+
when(dataManagementFactory.getBlobContainerClient(anyString(), eq(blobServiceClient))).thenReturn(blobContainerClient);
127+
when(dataManagementFactory.getBlobClient(any(), any())).thenReturn(blobClient);
128128

129129
MediaRequestEntity currentMediaRequest = getMediaRequestTestData().createCurrentMediaRequest(
130130
hearing,
@@ -169,9 +169,9 @@ void deleteMarkedForDeletionDataFromDataStores() {
169169
@Test
170170
void dontDeleteWhenStatusIsNotMarkedForDeletionDataFromDataStores() {
171171
audioBuilder.setupTest();
172-
Mockito.when(dataManagementFactory.getBlobServiceClient(anyString())).thenReturn(blobServiceClient);
173-
Mockito.when(dataManagementFactory.getBlobContainerClient(anyString(), eq(blobServiceClient))).thenReturn(blobContainerClient);
174-
Mockito.when(dataManagementFactory.getBlobClient(any(), any())).thenReturn(blobClient);
172+
when(dataManagementFactory.getBlobServiceClient(anyString())).thenReturn(blobServiceClient);
173+
when(dataManagementFactory.getBlobContainerClient(anyString(), eq(blobServiceClient))).thenReturn(blobContainerClient);
174+
when(dataManagementFactory.getBlobClient(any(), any())).thenReturn(blobClient);
175175

176176
MediaRequestEntity currentMediaRequest = getMediaRequestTestData().createCurrentMediaRequest(
177177
hearing,

src/main/java/uk/gov/hmcts/darts/audio/deleter/AbstractExternalDataStoreDeleter.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import lombok.Getter;
44
import lombok.RequiredArgsConstructor;
5+
import lombok.SneakyThrows;
56
import lombok.extern.slf4j.Slf4j;
67
import org.springframework.data.jpa.repository.JpaRepository;
78
import uk.gov.hmcts.darts.common.entity.ObjectDirectory;
@@ -35,7 +36,8 @@ public boolean delete(T entityToBeDeleted) {
3536
return deletedFromDataStore;
3637
}
3738

38-
39+
@SneakyThrows
40+
@SuppressWarnings("PMD.AvoidInstanceofChecksInCatchClause")
3941
protected boolean deleteFromDataStore(T entityToBeDeleted) {
4042
String externalLocation = entityToBeDeleted.getLocation();
4143
Long entityId = entityToBeDeleted.getId();
@@ -52,6 +54,9 @@ protected boolean deleteFromDataStore(T entityToBeDeleted) {
5254
"Failed to delete storage data with externalLocation={} for entityId={} and statusId={}",
5355
externalLocation, entityId, statusId, e
5456
);
57+
if (e instanceof InterruptedException) {
58+
throw e;
59+
}
5560
}
5661
return false;
5762
}

src/main/java/uk/gov/hmcts/darts/datamanagement/api/impl/DataManagementApiImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.azure.core.util.BinaryData;
44
import com.azure.storage.blob.BlobClient;
55
import lombok.RequiredArgsConstructor;
6+
import lombok.SneakyThrows;
67
import org.springframework.stereotype.Service;
78
import uk.gov.hmcts.darts.common.datamanagement.StorageConfiguration;
89
import uk.gov.hmcts.darts.common.datamanagement.component.impl.DownloadResponseMetaData;
@@ -54,6 +55,7 @@ public void deleteBlobDataFromOutboundContainer(String blobId) throws AzureDelet
5455
dataManagementService.deleteBlobData(getOutboundContainerName(), blobId);
5556
}
5657

58+
@SneakyThrows
5759
@Override
5860
public void deleteBlobDataFromInboundContainer(String blobId) throws AzureDeleteBlobException {
5961
dataManagementService.deleteBlobData(getInboundContainerName(), blobId);

src/main/java/uk/gov/hmcts/darts/datamanagement/service/impl/AssociatedObjectDataExpiryDeleterServiceImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public class AssociatedObjectDataExpiryDeleterServiceImpl implements AssociatedO
5454
@Value("${darts.storage.arm.event-date-adjustment-years}")
5555
Integer eventDateAdjustmentYears;
5656

57-
@SuppressWarnings("PMD.ExcessiveParameterList")//TODO - refactor to reduce excessive parameter list when this class is next edited
57+
@SuppressWarnings("PMD.ExcessiveParameterList")
5858
public AssociatedObjectDataExpiryDeleterServiceImpl(UserIdentity userIdentity,
5959
CurrentTimeHelper currentTimeHelper,
6060
TranscriptionDocumentRepository transcriptionDocumentRepository,

src/main/java/uk/gov/hmcts/darts/datamanagement/service/impl/DataManagementServiceImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,6 @@ public String getChecksum(String containerName, String blobId) {
257257
return fileContentChecksum.encodeToString(checksumByte);
258258
}
259259

260-
261260
@Override
262261
public Response<Boolean> deleteBlobData(String containerName, String blobId) throws AzureDeleteBlobException {
263262
log.info("About to delete blob id {} from container {}", blobId, containerName);
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package uk.gov.hmcts.darts.audio.deleter.impl;
2+
3+
import org.junit.jupiter.api.AfterEach;
4+
import org.junit.jupiter.api.BeforeEach;
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.api.extension.ExtendWith;
7+
import org.mockito.Mock;
8+
import org.mockito.junit.jupiter.MockitoExtension;
9+
import uk.gov.hmcts.darts.audio.deleter.AbstractExternalDataStoreDeleter;
10+
import uk.gov.hmcts.darts.common.entity.ExternalObjectDirectoryEntity;
11+
import uk.gov.hmcts.darts.common.repository.ExternalObjectDirectoryRepository;
12+
import uk.gov.hmcts.darts.common.service.impl.EodHelperMocks;
13+
14+
import java.util.Collection;
15+
import java.util.List;
16+
17+
import static org.junit.jupiter.api.Assertions.assertThrows;
18+
import static org.mockito.Mockito.verifyNoInteractions;
19+
20+
@ExtendWith(MockitoExtension.class)
21+
@SuppressWarnings({"PMD.TestClassWithoutTestCases", "PMD.DoNotUseThreads"})
22+
class AbstractExternalDataStoreDeleterInterruptedTest {
23+
24+
@Mock
25+
private ExternalObjectDirectoryRepository repository;
26+
27+
private static EodHelperMocks eodHelperMocks;
28+
29+
@BeforeEach
30+
void setUp() {
31+
eodHelperMocks = new EodHelperMocks();
32+
}
33+
34+
@AfterEach
35+
void tearDown() {
36+
eodHelperMocks.close();
37+
}
38+
39+
// Minimal concrete for testing the base-class behaviour
40+
private static class TestDeleter extends AbstractExternalDataStoreDeleter<ExternalObjectDirectoryEntity, ExternalObjectDirectoryRepository> {
41+
TestDeleter(ExternalObjectDirectoryRepository repository) {
42+
super(repository);
43+
}
44+
45+
@Override
46+
protected void deleteFromDataStore(String externalLocation) throws Exception {
47+
// This will throw InterruptedException immediately if the thread is already interrupted
48+
Thread.sleep(1_000);
49+
}
50+
51+
@Override
52+
protected Collection<ExternalObjectDirectoryEntity> findItemsToDelete(int batchSize) {
53+
ExternalObjectDirectoryEntity externalObjectDirectoryEntity = new ExternalObjectDirectoryEntity();
54+
externalObjectDirectoryEntity.setStatus(eodHelperMocks.getMarkForDeletionStatus());
55+
externalObjectDirectoryEntity.setExternalLocation(eodHelperMocks.getInboundLocation().getDescription());
56+
return List.of(externalObjectDirectoryEntity);
57+
}
58+
}
59+
60+
@AfterEach
61+
void clearInterruptFlag() {
62+
// Ensure the interrupt flag doesn't bleed into other tests
63+
Thread.interrupted(); // clears the flag
64+
}
65+
66+
@Test
67+
void delete_shouldRethrowInterruptedException() {
68+
var deleter = new TestDeleter(repository);
69+
70+
Thread.currentThread().interrupt();
71+
72+
// Should rethrow the InterruptedException
73+
assertThrows(InterruptedException.class, () -> deleter.delete(1));
74+
75+
verifyNoInteractions(repository);
76+
}
77+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package uk.gov.hmcts.darts.audio.deleter.impl;
2+
3+
import org.junit.jupiter.api.AfterEach;
4+
import org.junit.jupiter.api.BeforeEach;
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.api.extension.ExtendWith;
7+
import org.mockito.Mock;
8+
import org.mockito.junit.jupiter.MockitoExtension;
9+
import uk.gov.hmcts.darts.common.exception.AzureDeleteBlobException;
10+
import uk.gov.hmcts.darts.common.repository.ExternalObjectDirectoryRepository;
11+
import uk.gov.hmcts.darts.common.service.impl.EodHelperMocks;
12+
import uk.gov.hmcts.darts.dets.api.DetsDataManagementApi;
13+
14+
import static org.junit.jupiter.api.Assertions.assertThrows;
15+
import static org.mockito.Mockito.doNothing;
16+
import static org.mockito.Mockito.doThrow;
17+
import static org.mockito.Mockito.times;
18+
import static org.mockito.Mockito.verify;
19+
20+
@ExtendWith(MockitoExtension.class)
21+
class ExternalDetsDataStoreDeleterTest {
22+
23+
@Mock
24+
private DetsDataManagementApi dataManagementApi;
25+
@Mock
26+
private ExternalObjectDirectoryRepository externalObjectDirectoryRepository;
27+
28+
private EodHelperMocks eodHelperMocks;
29+
private ExternalDetsDataStoreDeleter deleter;
30+
31+
@BeforeEach
32+
void setUp() {
33+
eodHelperMocks = new EodHelperMocks();
34+
deleter = new ExternalDetsDataStoreDeleter(externalObjectDirectoryRepository, dataManagementApi);
35+
}
36+
37+
@AfterEach
38+
void tearDown() {
39+
eodHelperMocks.close();
40+
}
41+
42+
@Test
43+
void deleteFromDataStore_ShouldCallDeleteBlobDataFromContainer() throws AzureDeleteBlobException {
44+
String location = eodHelperMocks.getDetsLocation().getDescription();
45+
doNothing().when(dataManagementApi).deleteBlobDataFromContainer(location);
46+
47+
deleter.deleteFromDataStore(location);
48+
49+
verify(dataManagementApi, times(1)).deleteBlobDataFromContainer(location);
50+
}
51+
52+
@Test
53+
void deleteFromDataStore_ShouldThrowAzureDeleteBlobException() throws AzureDeleteBlobException {
54+
String location = eodHelperMocks.getDetsLocation().getDescription();
55+
doThrow(new AzureDeleteBlobException("Test exception"))
56+
.when(dataManagementApi).deleteBlobDataFromContainer(location);
57+
58+
assertThrows(AzureDeleteBlobException.class, () -> deleter.deleteFromDataStore(location));
59+
60+
verify(dataManagementApi, times(1)).deleteBlobDataFromContainer(location);
61+
}
62+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package uk.gov.hmcts.darts.audio.deleter.impl;
2+
3+
import org.junit.jupiter.api.AfterEach;
4+
import org.junit.jupiter.api.BeforeEach;
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.api.extension.ExtendWith;
7+
import org.mockito.Mock;
8+
import org.mockito.junit.jupiter.MockitoExtension;
9+
import uk.gov.hmcts.darts.common.exception.AzureDeleteBlobException;
10+
import uk.gov.hmcts.darts.common.repository.ExternalObjectDirectoryRepository;
11+
import uk.gov.hmcts.darts.common.service.impl.EodHelperMocks;
12+
import uk.gov.hmcts.darts.datamanagement.api.DataManagementApi;
13+
import uk.gov.hmcts.darts.test.common.data.PersistableFactory;
14+
import uk.gov.hmcts.darts.test.common.data.builder.TestExternalObjectDirectoryEntity;
15+
16+
import java.util.List;
17+
18+
import static org.junit.jupiter.api.Assertions.assertNotNull;
19+
import static org.mockito.ArgumentMatchers.anyString;
20+
import static org.mockito.Mockito.doNothing;
21+
import static org.mockito.Mockito.doThrow;
22+
import static org.mockito.Mockito.spy;
23+
import static org.mockito.Mockito.when;
24+
25+
@ExtendWith(MockitoExtension.class)
26+
class ExternalInboundDataStoreDeleterTest {
27+
28+
@Mock
29+
private DataManagementApi dataManagementApi;
30+
@Mock
31+
private ExternalObjectDirectoryRepository externalObjectDirectoryRepository;
32+
33+
private EodHelperMocks eodHelperMocks;
34+
private ExternalInboundDataStoreDeleter deleter;
35+
36+
@BeforeEach
37+
void setUp() {
38+
eodHelperMocks = new EodHelperMocks();
39+
this.deleter = spy(new ExternalInboundDataStoreDeleter(
40+
externalObjectDirectoryRepository,
41+
dataManagementApi
42+
));
43+
44+
}
45+
46+
@AfterEach
47+
void tearDown() {
48+
eodHelperMocks.close();
49+
}
50+
51+
@Test
52+
void deleteFromDataStore_ShouldThrowAzureDeleteBlobException() throws AzureDeleteBlobException {
53+
54+
TestExternalObjectDirectoryEntity testEod = PersistableFactory.getExternalObjectDirectoryTestData()
55+
.someMinimalBuilder()
56+
.transcriptionDocumentEntity(PersistableFactory.getTranscriptionDocument()
57+
.someMinimalBuilder()
58+
.build())
59+
.externalLocation(eodHelperMocks.getInboundLocation().getDescription())
60+
.status(eodHelperMocks.getMarkForDeletionStatus())
61+
.id(1L)
62+
.build();
63+
64+
when(externalObjectDirectoryRepository.findByExternalLocationTypeAndObjectStatus(
65+
eodHelperMocks.getInboundLocation(),
66+
eodHelperMocks.getMarkForDeletionStatus(),
67+
org.springframework.data.domain.Limit.of(100)
68+
)).thenReturn(List.of(testEod));
69+
70+
doThrow(new AzureDeleteBlobException("Test exception"))
71+
.when(dataManagementApi).deleteBlobDataFromInboundContainer(anyString());
72+
73+
assertNotNull(deleter.delete(100));
74+
}
75+
76+
@Test
77+
void deleteFromDataStore_ShouldReturnTrue() throws AzureDeleteBlobException {
78+
79+
TestExternalObjectDirectoryEntity testEod = PersistableFactory.getExternalObjectDirectoryTestData()
80+
.someMinimalBuilder()
81+
.transcriptionDocumentEntity(PersistableFactory.getTranscriptionDocument()
82+
.someMinimalBuilder()
83+
.build())
84+
.externalLocation(eodHelperMocks.getInboundLocation().getDescription())
85+
.status(eodHelperMocks.getMarkForDeletionStatus())
86+
.id(1L)
87+
.build();
88+
89+
when(externalObjectDirectoryRepository.findByExternalLocationTypeAndObjectStatus(
90+
eodHelperMocks.getInboundLocation(),
91+
eodHelperMocks.getMarkForDeletionStatus(),
92+
org.springframework.data.domain.Limit.of(100)
93+
)).thenReturn(List.of(testEod));
94+
95+
doNothing().when(dataManagementApi).deleteBlobDataFromInboundContainer(anyString());
96+
97+
var result = deleter.delete(100);
98+
99+
assertNotNull(result);
100+
}
101+
102+
}

0 commit comments

Comments
 (0)