From 3e5f3f10f681601d0f9dddcdfa73b66a87a84f21 Mon Sep 17 00:00:00 2001 From: sparkhi Date: Wed, 20 Mar 2024 14:11:08 +0000 Subject: [PATCH] Fixed NPE when underlying data was null, added few tests --- .../template/ConstantStringColumnDef.java | 2 +- .../template/DataModifierColumnDef.java | 3 + .../template/ExportTemplateBuilder.java | 13 +++- .../ProfileResourceNodeColumnDef.java | 2 +- .../template/ConstantStringColumnDefTest.java | 75 ++++++++++++++++++ .../template/DataModifierColumnDefTest.java | 70 +++++++++++++++++ .../template/ExportTemplateBuilderTest.java | 30 ++++++++ .../ProfileResourceNodeColumnDefTest.java | 76 +++++++++++++++++++ 8 files changed, 267 insertions(+), 4 deletions(-) create mode 100644 droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ConstantStringColumnDefTest.java create mode 100644 droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/DataModifierColumnDefTest.java create mode 100644 droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ProfileResourceNodeColumnDefTest.java diff --git a/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ConstantStringColumnDef.java b/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ConstantStringColumnDef.java index 325e7f04c..d39b2cad6 100644 --- a/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ConstantStringColumnDef.java +++ b/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ConstantStringColumnDef.java @@ -88,6 +88,6 @@ public ColumnType getColumnType() { */ @Override public String getOperatedValue(String input) { - return input; + return input == null ? "" : input; } } diff --git a/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/DataModifierColumnDef.java b/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/DataModifierColumnDef.java index 36bbfb0b4..f855187ad 100644 --- a/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/DataModifierColumnDef.java +++ b/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/DataModifierColumnDef.java @@ -96,6 +96,9 @@ public ColumnType getColumnType() { */ @Override public String getOperatedValue(String input) { + if ((input == null) || (input.length() == 0)) { + return ""; + } switch(operation) { case LCASE: return input.toLowerCase(); diff --git a/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ExportTemplateBuilder.java b/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ExportTemplateBuilder.java index dd31c5e70..8e14465fc 100644 --- a/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ExportTemplateBuilder.java +++ b/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ExportTemplateBuilder.java @@ -33,10 +33,12 @@ import uk.gov.nationalarchives.droid.export.interfaces.ExportTemplate; import uk.gov.nationalarchives.droid.export.interfaces.ExportTemplateColumnDef; +import uk.gov.nationalarchives.droid.profile.CsvWriterConstants; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -119,8 +121,12 @@ private ExportTemplateColumnDef createDataModifierDef(String header, String para String column = tokens[1].trim().substring(0, tokens[1].trim().length() - 1); ProfileResourceNodeColumnDef inner = createProfileNodeDef(header, column); - ExportTemplateColumnDef.DataModification operation = ExportTemplateColumnDef.DataModification.valueOf(operationName); - return new DataModifierColumnDef(inner, operation); + try { + ExportTemplateColumnDef.DataModification operation = ExportTemplateColumnDef.DataModification.valueOf(operationName); + return new DataModifierColumnDef(inner, operation); + } catch (IllegalArgumentException iae) { + throw new ExportTemplateParseException("Undefined operation '" + operationName + "' encountered in export template"); + } } private ExportTemplateColumnDef createConstantStringDef(String header, String param2) { @@ -136,6 +142,9 @@ private ExportTemplateColumnDef createConstantStringDef(String header, String pa private ProfileResourceNodeColumnDef createProfileNodeDef(String header, String param2) { String originalColumnName = param2.substring(1); + if (!(Arrays.stream(CsvWriterConstants.HEADERS).collect(Collectors.toList()).contains(originalColumnName))) { + throw new ExportTemplateParseException("Invalid column name. " + originalColumnName + " does not exist in profile results"); + } return new ProfileResourceNodeColumnDef(originalColumnName, header); } diff --git a/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ProfileResourceNodeColumnDef.java b/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ProfileResourceNodeColumnDef.java index fb4a2acf4..2de4a7986 100644 --- a/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ProfileResourceNodeColumnDef.java +++ b/droid-export/src/main/java/uk/gov/nationalarchives/droid/export/template/ProfileResourceNodeColumnDef.java @@ -89,6 +89,6 @@ public ColumnType getColumnType() { */ @Override public String getOperatedValue(String input) { - return input; + return input == null ? "" : input; } } diff --git a/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ConstantStringColumnDefTest.java b/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ConstantStringColumnDefTest.java new file mode 100644 index 000000000..555c456c0 --- /dev/null +++ b/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ConstantStringColumnDefTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2016, The National Archives + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the The National Archives nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package uk.gov.nationalarchives.droid.export.template; + +import org.junit.Test; +import uk.gov.nationalarchives.droid.export.interfaces.ExportTemplateColumnDef; + +import static org.junit.Assert.*; + +public class ConstantStringColumnDefTest { + @Test + public void should_operate_on_null_value_and_return_empty_string() { + ConstantStringColumnDef def = new ConstantStringColumnDef("English (UK)", "Language"); + assertEquals("", def.getOperatedValue(null)); + } + + @Test + public void should_return_input_as_it_is_when_asked_for_operated_value() { + ConstantStringColumnDef def = new ConstantStringColumnDef("English (UK)", "Language"); + assertEquals("FoRMat", def.getOperatedValue("FoRMat")); + } + + @Test + public void should_return_header_label_as_set_when_defining_the_column_definition() { + ConstantStringColumnDef def = new ConstantStringColumnDef("English (UK)", "Language"); + assertEquals("Language", def.getHeaderLabel()); + } + @Test + public void should_throw_exception_when_asking_for_original_column_name() { + ConstantStringColumnDef def = new ConstantStringColumnDef("English (UK)", "Language"); + RuntimeException ex = assertThrows(RuntimeException.class, () -> def.getOriginalColumnName()); + assertEquals("Constant String Columns do not have an associated original column name", ex.getMessage()); + } + + @Test + public void should_return_data_as_set_in_the_definition() { + ConstantStringColumnDef def = new ConstantStringColumnDef("English (UK)", "Language"); + assertEquals("English (UK)", def.getDataValue()); + } + + @Test + public void should_return_column_type_as_data_modifier_column_def() { + ConstantStringColumnDef def = new ConstantStringColumnDef("English (UK)", "Language"); + assertEquals(ExportTemplateColumnDef.ColumnType.ConstantString, def.getColumnType()); + } +} \ No newline at end of file diff --git a/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/DataModifierColumnDefTest.java b/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/DataModifierColumnDefTest.java new file mode 100644 index 000000000..028642d1d --- /dev/null +++ b/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/DataModifierColumnDefTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016, The National Archives + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the The National Archives nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package uk.gov.nationalarchives.droid.export.template; + +import org.junit.Test; +import uk.gov.nationalarchives.droid.export.interfaces.ExportTemplateColumnDef; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DataModifierColumnDefTest { + @Test + public void should_operate_on_null_value_and_return_empty_string() { + ProfileResourceNodeColumnDef innerDef = mock(ProfileResourceNodeColumnDef.class); + DataModifierColumnDef def = new DataModifierColumnDef(innerDef, ExportTemplateColumnDef.DataModification.LCASE); + assertEquals("", def.getOperatedValue(null)); + } + + @Test + public void should_return_header_label_as_set_on_the_inner_profile_def() { + ProfileResourceNodeColumnDef innerDef = mock(ProfileResourceNodeColumnDef.class); + when(innerDef.getHeaderLabel()).thenReturn("InnerHeader"); + DataModifierColumnDef def = new DataModifierColumnDef(innerDef, ExportTemplateColumnDef.DataModification.LCASE); + assertEquals("InnerHeader", def.getHeaderLabel()); + } + @Test + public void should_return_original_column_name_as_set_on_the_inner_profile_def() { + ProfileResourceNodeColumnDef innerDef = mock(ProfileResourceNodeColumnDef.class); + when(innerDef.getOriginalColumnName()).thenReturn("FORMAT_NAME"); + DataModifierColumnDef def = new DataModifierColumnDef(innerDef, ExportTemplateColumnDef.DataModification.LCASE); + assertEquals("FORMAT_NAME", def.getOriginalColumnName()); + } + + @Test + public void should_return_column_type_as_data_modifier_column_def() { + ProfileResourceNodeColumnDef innerDef = mock(ProfileResourceNodeColumnDef.class); + DataModifierColumnDef def = new DataModifierColumnDef(innerDef, ExportTemplateColumnDef.DataModification.LCASE); + assertEquals(ExportTemplateColumnDef.ColumnType.DataModifier, def.getColumnType()); + } +} \ No newline at end of file diff --git a/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ExportTemplateBuilderTest.java b/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ExportTemplateBuilderTest.java index a382960e5..d1d9cf478 100644 --- a/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ExportTemplateBuilderTest.java +++ b/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ExportTemplateBuilderTest.java @@ -168,4 +168,34 @@ public void should_throw_an_exception_if_the_constant_string_value_does_not_have ExportTemplateParseException ex = assertThrows(ExportTemplateParseException.class, () -> builder.buildExportTemplate(tempFile.getAbsolutePath())); assertEquals("The line with a constant value ('\"English') in template definition does not have closing quotes", ex.getMessage()); } + + @Test + public void should_throw_an_exception_if_the_column_name_given_for_profile_resource_column_is_not_a_well_known_header() throws IOException { + ExportTemplateBuilder builder = new ExportTemplateBuilder(); + File tempFile = temporaryFolder.newFile("export-task-test-default-encoding"); + List data = Arrays.asList( + "version 1.0", + "", + "Identifier: $UUID", + ""); + Files.write(tempFile.toPath(), data, StandardOpenOption.WRITE); + + ExportTemplateParseException ex = assertThrows(ExportTemplateParseException.class, () -> builder.buildExportTemplate(tempFile.getAbsolutePath())); + assertEquals("Invalid column name. UUID does not exist in profile results", ex.getMessage()); + } + + @Test + public void should_throw_an_exception_when_the_operation_is_unknown() throws IOException { + ExportTemplateBuilder builder = new ExportTemplateBuilder(); + File tempFile = temporaryFolder.newFile("export-task-test-default-encoding"); + List data = Arrays.asList( + "version 1.0", + "", + "Identifier: Lower($ID)", + ""); + Files.write(tempFile.toPath(), data, StandardOpenOption.WRITE); + + ExportTemplateParseException ex = assertThrows(ExportTemplateParseException.class, () -> builder.buildExportTemplate(tempFile.getAbsolutePath())); + assertEquals("Undefined operation 'Lower' encountered in export template", ex.getMessage()); + } } \ No newline at end of file diff --git a/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ProfileResourceNodeColumnDefTest.java b/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ProfileResourceNodeColumnDefTest.java new file mode 100644 index 000000000..a8b8a9def --- /dev/null +++ b/droid-export/src/test/java/uk/gov/nationalarchives/droid/export/template/ProfileResourceNodeColumnDefTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016, The National Archives + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following + * conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the The National Archives nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package uk.gov.nationalarchives.droid.export.template; + +import org.junit.Test; +import uk.gov.nationalarchives.droid.export.interfaces.ExportTemplateColumnDef; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +public class ProfileResourceNodeColumnDefTest { + @Test + public void should_operate_on_null_value_and_return_empty_string() { + ProfileResourceNodeColumnDef def = new ProfileResourceNodeColumnDef("FORMAT_NAME", "Format"); + assertEquals("", def.getOperatedValue(null)); + } + + @Test + public void should_return_input_as_it_is_when_asked_for_operated_value() { + ProfileResourceNodeColumnDef def = new ProfileResourceNodeColumnDef("FORMAT_NAME", "Format"); + assertEquals("FoRMat", def.getOperatedValue("FoRMat")); + } + + @Test + public void should_return_header_label_as_set_when_defining_the_column_definition() { + ProfileResourceNodeColumnDef def = new ProfileResourceNodeColumnDef("FORMAT_NAME", "Format"); + assertEquals("Format", def.getHeaderLabel()); + } + @Test + public void should_return_original_column_name_as_set_when_defining_the_column_definition() { + ProfileResourceNodeColumnDef def = new ProfileResourceNodeColumnDef("FORMAT_NAME", "Format"); + assertEquals("FORMAT_NAME", def.getOriginalColumnName()); + } + + @Test + public void should_throw_exception_when_asking_for_data_from_the_definition() { + ProfileResourceNodeColumnDef def = new ProfileResourceNodeColumnDef("FORMAT_NAME", "Format"); + RuntimeException ex = assertThrows(RuntimeException.class, () -> def.getDataValue()); + assertEquals("Profile resource node column uses data from the profile results", ex.getMessage()); + } + + @Test + public void should_return_column_type_as_data_modifier_column_def() { + ProfileResourceNodeColumnDef def = new ProfileResourceNodeColumnDef("FORMAT_NAME", "Format"); + assertEquals(ExportTemplateColumnDef.ColumnType.ProfileResourceNode, def.getColumnType()); + } +} \ No newline at end of file