From 2f2fd7513df1a4e2858723632cc58fdcfd4149d2 Mon Sep 17 00:00:00 2001 From: Jan Zickermann <jan.zickermann@dataport.de> Date: Tue, 1 Apr 2025 10:52:17 +0200 Subject: [PATCH] KOP-2949 Catch RuntimeExceptions, defensively --- .../FormSolutionsEngineBasedAdapter.java | 15 +- .../FormSolutionsEngineBasedAdapterTest.java | 156 ++++++++++++++---- 2 files changed, 135 insertions(+), 36 deletions(-) diff --git a/src/main/java/de/ozgcloud/eingang/semantik/enginebased/formsolutions/FormSolutionsEngineBasedAdapter.java b/src/main/java/de/ozgcloud/eingang/semantik/enginebased/formsolutions/FormSolutionsEngineBasedAdapter.java index 5bfe77e2..80189430 100644 --- a/src/main/java/de/ozgcloud/eingang/semantik/enginebased/formsolutions/FormSolutionsEngineBasedAdapter.java +++ b/src/main/java/de/ozgcloud/eingang/semantik/enginebased/formsolutions/FormSolutionsEngineBasedAdapter.java @@ -33,9 +33,11 @@ import de.ozgcloud.eingang.common.formdata.FormData; import de.ozgcloud.eingang.common.formdata.FormDataUtils; import de.ozgcloud.eingang.semantik.enginebased.EngineBasedSemantikAdapter; import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; @Component @RequiredArgsConstructor +@Log4j2 public class FormSolutionsEngineBasedAdapter implements EngineBasedSemantikAdapter { public static final String IDENTIFIER_KEY = "identifier"; @@ -51,13 +53,22 @@ public class FormSolutionsEngineBasedAdapter implements EngineBasedSemantikAdapt var processedFormData = formData; for (var mapper : mappers) { - processedFormData = mapper.parseFormData(processedFormData); + processedFormData = applyFormDataMapperAndCatchException(mapper, processedFormData); } return removeProcessedData(processedFormData); } - protected FormData removeProcessedData(FormData formData) { + private FormData applyFormDataMapperAndCatchException(FormSolutionsEngineBasedMapper mapper, FormData formData) { + try { + return mapper.parseFormData(formData); + } catch (RuntimeException e) { + LOG.error("Error while parsing form data with mapper {}", mapper.getClass().getSimpleName(), e); + return formData; + } + } + + FormData removeProcessedData(FormData formData) { return FormDataUtils.from(formData) .remove(ASSISTANT) .remove(ANLIEGEN_ID) diff --git a/src/test/java/de/ozgcloud/eingang/semantik/enginebased/formsolutions/FormSolutionsEngineBasedAdapterTest.java b/src/test/java/de/ozgcloud/eingang/semantik/enginebased/formsolutions/FormSolutionsEngineBasedAdapterTest.java index 4e5a07d9..cd2dce9d 100644 --- a/src/test/java/de/ozgcloud/eingang/semantik/enginebased/formsolutions/FormSolutionsEngineBasedAdapterTest.java +++ b/src/test/java/de/ozgcloud/eingang/semantik/enginebased/formsolutions/FormSolutionsEngineBasedAdapterTest.java @@ -29,7 +29,7 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.util.Collections; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; @@ -42,7 +42,6 @@ import org.mockito.Spy; import org.springframework.test.util.ReflectionTestUtils; import de.ozgcloud.eingang.common.formdata.FormData; -import de.ozgcloud.eingang.common.formdata.FormDataTestFactory; class FormSolutionsEngineBasedAdapterTest { @@ -50,73 +49,162 @@ class FormSolutionsEngineBasedAdapterTest { @InjectMocks private FormSolutionsEngineBasedAdapter adapter; @Mock - private FormSolutionsEngineBasedMapper mapper; + private FormSolutionsEngineBasedMapper mapper0; + + @Mock + private FormSolutionsEngineBasedMapper mapper1; @DisplayName("Parse formData") @Nested class TestParseFormData { - private final FormData formData = FormDataTestFactory.create(); + @Mock + private FormData formData0; + + @Mock + private FormData formData1; + + @Mock + private FormData formData2; + + @Mock + private FormData formData3; @BeforeEach void mockMappers() { - ReflectionTestUtils.setField(adapter, "mappers", Collections.singletonList(mapper)); + ReflectionTestUtils.setField(adapter, "mappers", List.of(mapper0, mapper1)); } - @Test - void shouldCallMappers() { - when(mapper.parseFormData(any())).thenReturn(formData); + @DisplayName("without exception") + @Nested + class TestWithoutException { - adapter.parseFormData(formData); + @BeforeEach + void mock() { + when(mapper0.parseFormData(any())).thenReturn(formData1); + when(mapper1.parseFormData(any())).thenReturn(formData2); + doReturn(formData3).when(adapter).removeProcessedData(any()); + } - verify(mapper).parseFormData(formData); - } + @DisplayName("should call first mapper") + @Test + void shouldCallFirstMapper() { + parseFormData(); - @Test - void shouldCallRemoveProcessedRawData() { - when(mapper.parseFormData(any())).thenReturn(formData); + verify(mapper0).parseFormData(formData0); + } - adapter.parseFormData(formData); + @DisplayName("should call second mapper") + @Test + void shouldCallSecondMapper() { + parseFormData(); + + verify(mapper1).parseFormData(formData1); + } + + @DisplayName("should call removeProcessedData") + @Test + void shouldCallRemoveProcessedData() { + parseFormData(); + + verify(adapter).removeProcessedData(formData2); + } + + @DisplayName("should return") + @Test + void shouldReturn() { + var result = parseFormData(); + + assertThat(result).isEqualTo(formData3); + } - verify(adapter).removeProcessedData(formData); } - @DisplayName("remove processed data") + @DisplayName("with exception") @Nested - class TestRemoveProcessedData { + class TestWithException { - private final Map<String, Object> formDataMap = Map.of(ASSISTANT, "testValue", - ANLIEGEN_ID, "testValue2", KOMMUNALVERWALTUNG_ID, "testValue3", - POSTKORBHANDLE, "testValue4"); - private final FormData formData = FormData.builder().formData(formDataMap).build(); + @BeforeEach + void mock() { + when(mapper0.parseFormData(any())).thenThrow(new RuntimeException()); + when(mapper1.parseFormData(any())).thenReturn(formData1); + doReturn(formData2).when(adapter).removeProcessedData(any()); + } + @DisplayName("should call first mapper") @Test - void shouldRemoveAssistant() { - var cleanedFormData = adapter.removeProcessedData(formData); + void shouldCallFirstMapper() { + parseFormData(); - assertThat(cleanedFormData.getFormData()).doesNotContainKey(ASSISTANT); + verify(mapper0).parseFormData(formData0); } + @DisplayName("should call second mapper with same data") @Test - void shouldRemoveAnliegenId() { - var cleanedFormData = adapter.removeProcessedData(formData); + void shouldCallSecondMapperWithSameData() { + parseFormData(); - assertThat(cleanedFormData.getFormData()).doesNotContainKey(ANLIEGEN_ID); + verify(mapper1).parseFormData(formData0); } + @DisplayName("should call removeProcessedData") @Test - void shouldRemoveKommunalVerwaltungId() { - var cleanedFormData = adapter.removeProcessedData(formData); + void shouldCallRemoveProcessedData() { + parseFormData(); - assertThat(cleanedFormData.getFormData()).doesNotContainKey(KOMMUNALVERWALTUNG_ID); + verify(adapter).removeProcessedData(formData1); } + @DisplayName("should return") @Test - void shouldRemovePostkorbhandle() { - var cleanedFormData = adapter.removeProcessedData(formData); + void shouldReturn() { + var result = parseFormData(); - assertThat(cleanedFormData.getFormData()).doesNotContainKey(POSTKORBHANDLE); + assertThat(result).isEqualTo(formData2); } + + } + + FormData parseFormData() { + return adapter.parseFormData(formData0); + } + } + + @DisplayName("remove processed data") + @Nested + class TestRemoveProcessedData { + + private final Map<String, Object> formDataMap = Map.of(ASSISTANT, "testValue", + ANLIEGEN_ID, "testValue2", KOMMUNALVERWALTUNG_ID, "testValue3", + POSTKORBHANDLE, "testValue4"); + private final FormData formData = FormData.builder().formData(formDataMap).build(); + + @Test + void shouldRemoveAssistant() { + var cleanedFormData = adapter.removeProcessedData(formData); + + assertThat(cleanedFormData.getFormData()).doesNotContainKey(ASSISTANT); + } + + @Test + void shouldRemoveAnliegenId() { + var cleanedFormData = adapter.removeProcessedData(formData); + + assertThat(cleanedFormData.getFormData()).doesNotContainKey(ANLIEGEN_ID); + } + + @Test + void shouldRemoveKommunalVerwaltungId() { + var cleanedFormData = adapter.removeProcessedData(formData); + + assertThat(cleanedFormData.getFormData()).doesNotContainKey(KOMMUNALVERWALTUNG_ID); + } + + @Test + void shouldRemovePostkorbhandle() { + var cleanedFormData = adapter.removeProcessedData(formData); + + assertThat(cleanedFormData.getFormData()).doesNotContainKey(POSTKORBHANDLE); } } } \ No newline at end of file -- GitLab