This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new 5f6097121f Add output field to error rows for unique rows, extra
tests, fixes #5230 (#5893)
5f6097121f is described below
commit 5f6097121f2a32290edbb0e4f4cbbdca78050ec9
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Sat Oct 25 14:38:37 2025 +0200
Add output field to error rows for unique rows, extra tests, fixes #5230
(#5893)
---
.../transforms/uniquerows/UniqueField.java | 2 +-
.../pipeline/transforms/uniquerows/UniqueRows.java | 6 +-
.../transforms/uniquerows/UniqueFieldTest.java | 366 +++++++++++++++++
.../transforms/uniquerows/UniqueRowsDataTest.java | 207 ++++++++++
.../uniquerows/UniqueRowsMetaExtendedTest.java | 446 +++++++++++++++++++++
.../transforms/uniquerows/UniqueRowsTest.java | 330 +++++++++++++++
6 files changed, 1353 insertions(+), 4 deletions(-)
diff --git
a/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueField.java
b/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueField.java
index 4deb1db511..c5b9e74a43 100644
---
a/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueField.java
+++
b/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueField.java
@@ -61,7 +61,7 @@ public class UniqueField {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UniqueField that = (UniqueField) o;
- return name.equals(that.name) && caseInsensitive == that.caseInsensitive;
+ return Objects.equals(name, that.name) && caseInsensitive ==
that.caseInsensitive;
}
@Override
diff --git
a/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRows.java
b/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRows.java
index 83e96868a5..82fa7221ac 100644
---
a/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRows.java
+++
b/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRows.java
@@ -123,16 +123,16 @@ public class UniqueRows extends
BaseTransform<UniqueRowsMeta, UniqueRowsData> {
data.previous = data.inputRowMeta.cloneRow(r);
data.counter = 1;
} else {
- data.counter++;
if (data.sendDuplicateRows && !first) {
putError(
- getInputRowMeta(),
- r,
+ data.outputRowMeta,
+ RowDataUtil.addValueData(r, data.outputRowMeta.size() - 1,
data.counter),
1,
data.realErrorDescription,
Utils.isEmpty(data.compareFields) ? null : data.compareFields,
"UNR001");
}
+ data.counter++;
}
if (checkFeedback(getLinesRead()) && isBasic()) {
diff --git
a/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueFieldTest.java
b/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueFieldTest.java
new file mode 100644
index 0000000000..70b960b100
--- /dev/null
+++
b/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueFieldTest.java
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.uniquerows;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class UniqueFieldTest {
+
+ @RegisterExtension
+ static RestoreHopEngineEnvironmentExtension env = new
RestoreHopEngineEnvironmentExtension();
+
+ @Test
+ void testDefaultConstructor() {
+ UniqueField field = new UniqueField();
+
+ assertNotNull(field, "UniqueField instance should be created
successfully.");
+ assertNull(field.getName(), "name should be null by default");
+ assertFalse(field.isCaseInsensitive(), "caseInsensitive should be false by
default");
+ }
+
+ @Test
+ void testParameterizedConstructor() {
+ String fieldName = "testField";
+ boolean caseInsensitive = true;
+
+ UniqueField field = new UniqueField(fieldName, caseInsensitive);
+
+ assertNotNull(field, "UniqueField instance should be created
successfully.");
+ assertEquals(fieldName, field.getName(), "name should match constructor
parameter");
+ assertEquals(
+ caseInsensitive,
+ field.isCaseInsensitive(),
+ "caseInsensitive should match constructor parameter");
+ }
+
+ @Test
+ void testParameterizedConstructorWithFalseCaseInsensitive() {
+ String fieldName = "testField";
+ boolean caseInsensitive = false;
+
+ UniqueField field = new UniqueField(fieldName, caseInsensitive);
+
+ assertNotNull(field, "UniqueField instance should be created
successfully.");
+ assertEquals(fieldName, field.getName(), "name should match constructor
parameter");
+ assertEquals(
+ caseInsensitive,
+ field.isCaseInsensitive(),
+ "caseInsensitive should match constructor parameter");
+ }
+
+ @Test
+ void testParameterizedConstructorWithNullName() {
+ String fieldName = null;
+ boolean caseInsensitive = true;
+
+ UniqueField field = new UniqueField(fieldName, caseInsensitive);
+
+ assertNotNull(field, "UniqueField instance should be created
successfully.");
+ assertNull(field.getName(), "name should be null");
+ assertEquals(
+ caseInsensitive,
+ field.isCaseInsensitive(),
+ "caseInsensitive should match constructor parameter");
+ }
+
+ @Test
+ void testParameterizedConstructorWithEmptyName() {
+ String fieldName = "";
+ boolean caseInsensitive = false;
+
+ UniqueField field = new UniqueField(fieldName, caseInsensitive);
+
+ assertNotNull(field, "UniqueField instance should be created
successfully.");
+ assertEquals(fieldName, field.getName(), "name should match constructor
parameter");
+ assertEquals(
+ caseInsensitive,
+ field.isCaseInsensitive(),
+ "caseInsensitive should match constructor parameter");
+ }
+
+ @Test
+ void testGetName() {
+ UniqueField field = new UniqueField();
+ String testName = "testField";
+
+ field.setName(testName);
+ assertEquals(testName, field.getName(), "getName should return the set
name");
+ }
+
+ @Test
+ void testSetName() {
+ UniqueField field = new UniqueField();
+ String testName = "testField";
+
+ field.setName(testName);
+ assertEquals(testName, field.getName(), "setName should set the name
correctly");
+ }
+
+ @Test
+ void testSetNameWithNull() {
+ UniqueField field = new UniqueField();
+ field.setName("initialName");
+ field.setName(null);
+
+ assertNull(field.getName(), "setName with null should set name to null");
+ }
+
+ @Test
+ void testSetNameWithEmptyString() {
+ UniqueField field = new UniqueField();
+ String emptyName = "";
+
+ field.setName(emptyName);
+ assertEquals(emptyName, field.getName(), "setName with empty string should
work");
+ }
+
+ @Test
+ void testIsCaseInsensitive() {
+ UniqueField field = new UniqueField();
+ boolean caseInsensitive = true;
+
+ field.setCaseInsensitive(caseInsensitive);
+ assertEquals(
+ caseInsensitive,
+ field.isCaseInsensitive(),
+ "isCaseInsensitive should return the set value");
+ }
+
+ @Test
+ void testSetCaseInsensitive() {
+ UniqueField field = new UniqueField();
+ boolean caseInsensitive = true;
+
+ field.setCaseInsensitive(caseInsensitive);
+ assertEquals(
+ caseInsensitive,
+ field.isCaseInsensitive(),
+ "setCaseInsensitive should set the value correctly");
+ }
+
+ @Test
+ void testSetCaseInsensitiveFalse() {
+ UniqueField field = new UniqueField();
+ boolean caseInsensitive = false;
+
+ field.setCaseInsensitive(caseInsensitive);
+ assertEquals(
+ caseInsensitive,
+ field.isCaseInsensitive(),
+ "setCaseInsensitive should set the value correctly");
+ }
+
+ @Test
+ void testEqualsWithSameObject() {
+ UniqueField field = new UniqueField("testField", true);
+
+ assertTrue(field.equals(field), "field should equal itself");
+ }
+
+ @Test
+ void testEqualsWithNull() {
+ UniqueField field = new UniqueField("testField", true);
+
+ assertFalse(field.equals(null), "field should not equal null");
+ }
+
+ @Test
+ void testEqualsWithDifferentClass() {
+ UniqueField field = new UniqueField("testField", true);
+ String otherObject = "not a UniqueField";
+
+ assertFalse(field.equals(otherObject), "field should not equal object of
different class");
+ }
+
+ @Test
+ void testEqualsWithSameValues() {
+ UniqueField field1 = new UniqueField("testField", true);
+ UniqueField field2 = new UniqueField("testField", true);
+
+ assertTrue(field1.equals(field2), "fields with same values should be
equal");
+ assertTrue(field2.equals(field1), "equals should be symmetric");
+ }
+
+ @Test
+ void testEqualsWithDifferentName() {
+ UniqueField field1 = new UniqueField("testField1", true);
+ UniqueField field2 = new UniqueField("testField2", true);
+
+ assertFalse(field1.equals(field2), "fields with different names should not
be equal");
+ assertFalse(field2.equals(field1), "equals should be symmetric");
+ }
+
+ @Test
+ void testEqualsWithDifferentCaseInsensitive() {
+ UniqueField field1 = new UniqueField("testField", true);
+ UniqueField field2 = new UniqueField("testField", false);
+
+ assertFalse(field1.equals(field2), "fields with different caseInsensitive
should not be equal");
+ assertFalse(field2.equals(field1), "equals should be symmetric");
+ }
+
+ @Test
+ void testEqualsWithNullName() {
+ UniqueField field1 = new UniqueField(null, true);
+ UniqueField field2 = new UniqueField(null, true);
+
+ assertTrue(
+ field1.equals(field2), "fields with null names should be equal if
other values match");
+ }
+
+ @Test
+ void testEqualsWithOneNullName() {
+ UniqueField field1 = new UniqueField(null, true);
+ UniqueField field2 = new UniqueField("testField", true);
+
+ assertFalse(field1.equals(field2), "fields with one null name should not
be equal");
+ assertFalse(field2.equals(field1), "equals should be symmetric");
+ }
+
+ @Test
+ void testHashCodeWithSameValues() {
+ UniqueField field1 = new UniqueField("testField", true);
+ UniqueField field2 = new UniqueField("testField", true);
+
+ assertEquals(
+ field1.hashCode(), field2.hashCode(), "fields with same values should
have same hashCode");
+ }
+
+ @Test
+ void testHashCodeWithDifferentName() {
+ UniqueField field1 = new UniqueField("testField1", true);
+ UniqueField field2 = new UniqueField("testField2", true);
+
+ assertNotEquals(
+ field1.hashCode(),
+ field2.hashCode(),
+ "fields with different names should have different hashCode");
+ }
+
+ @Test
+ void testHashCodeWithDifferentCaseInsensitive() {
+ UniqueField field1 = new UniqueField("testField", true);
+ UniqueField field2 = new UniqueField("testField", false);
+
+ assertNotEquals(
+ field1.hashCode(),
+ field2.hashCode(),
+ "fields with different caseInsensitive should have different
hashCode");
+ }
+
+ @Test
+ void testHashCodeWithNullName() {
+ UniqueField field1 = new UniqueField(null, true);
+ UniqueField field2 = new UniqueField(null, true);
+
+ assertEquals(
+ field1.hashCode(),
+ field2.hashCode(),
+ "fields with null names should have same hashCode if other values
match");
+ }
+
+ @Test
+ void testHashCodeConsistency() {
+ UniqueField field = new UniqueField("testField", true);
+ int hashCode1 = field.hashCode();
+ int hashCode2 = field.hashCode();
+
+ assertEquals(hashCode1, hashCode2, "hashCode should be consistent across
multiple calls");
+ }
+
+ @Test
+ void testHashCodeAfterModification() {
+ UniqueField field = new UniqueField("testField", true);
+ int originalHashCode = field.hashCode();
+
+ field.setName("newField");
+ int newHashCode = field.hashCode();
+
+ assertNotEquals(
+ originalHashCode, newHashCode, "hashCode should change after name
modification");
+ }
+
+ @Test
+ void testHashCodeAfterCaseInsensitiveModification() {
+ UniqueField field = new UniqueField("testField", true);
+ int originalHashCode = field.hashCode();
+
+ field.setCaseInsensitive(false);
+ int newHashCode = field.hashCode();
+
+ assertNotEquals(
+ originalHashCode, newHashCode, "hashCode should change after
caseInsensitive modification");
+ }
+
+ @Test
+ void testToString() {
+ UniqueField field = new UniqueField("testField", true);
+ String toString = field.toString();
+
+ assertNotNull(toString, "toString should not return null");
+ // The default toString() method from Object doesn't contain the field name
+ // This test just verifies that toString() doesn't return null
+ }
+
+ @Test
+ void testToStringWithNullName() {
+ UniqueField field = new UniqueField(null, true);
+ String toString = field.toString();
+
+ assertNotNull(toString, "toString should not return null even with null
name");
+ }
+
+ @Test
+ void testFieldModification() {
+ UniqueField field = new UniqueField("initialName", false);
+
+ // Test name modification
+ field.setName("newName");
+ assertEquals("newName", field.getName(), "name should be updated");
+ assertFalse(field.isCaseInsensitive(), "caseInsensitive should remain
unchanged");
+
+ // Test caseInsensitive modification
+ field.setCaseInsensitive(true);
+ assertEquals("newName", field.getName(), "name should remain unchanged");
+ assertTrue(field.isCaseInsensitive(), "caseInsensitive should be updated");
+ }
+
+ @Test
+ void testFieldEqualityAfterModification() {
+ UniqueField field1 = new UniqueField("testField", true);
+ UniqueField field2 = new UniqueField("testField", true);
+
+ // Initially equal
+ assertTrue(field1.equals(field2), "fields should be equal initially");
+
+ // Modify one field
+ field1.setName("newName");
+
+ // Should no longer be equal
+ assertFalse(field1.equals(field2), "fields should not be equal after
modification");
+ }
+}
diff --git
a/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsDataTest.java
b/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsDataTest.java
new file mode 100644
index 0000000000..676641ec54
--- /dev/null
+++
b/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsDataTest.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.uniquerows;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.row.value.ValueMetaString;
+import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.apache.hop.pipeline.transform.BaseTransformData;
+import org.apache.hop.pipeline.transform.ITransformData;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class UniqueRowsDataTest {
+
+ @RegisterExtension
+ static RestoreHopEngineEnvironmentExtension env = new
RestoreHopEngineEnvironmentExtension();
+
+ @Test
+ void testUniqueRowsDataInitialization() {
+ // Create instance
+ UniqueRowsData data = new UniqueRowsData();
+
+ // Verify object is not null
+ assertNotNull(data, "UniqueRowsData instance should be created
successfully.");
+
+ // Verify inheritance
+ assertNotNull(data, "UniqueRowsData should extend BaseTransformData.");
+ assertNotNull(data, "UniqueRowsData should implement ITransformData.");
+
+ // Verify initial state
+ assertNull(data.outputRowMeta, "outputRowMeta should be null initially");
+ assertNull(data.compareRowMeta, "compareRowMeta should be null initially");
+ assertNull(data.inputRowMeta, "inputRowMeta should be null initially");
+ assertEquals(0, data.counter, "counter should be 0 initially");
+ assertNull(data.previous, "previous should be null initially");
+ assertNull(data.fieldnrs, "fieldnrs should be null initially");
+ assertNull(data.compareFields, "compareFields should be null initially");
+ assertNull(data.realErrorDescription, "realErrorDescription should be null
initially");
+ assertFalse(data.sendDuplicateRows, "sendDuplicateRows should be false
initially");
+ }
+
+ @Test
+ void testUniqueRowsDataFieldAccess() {
+ UniqueRowsData data = new UniqueRowsData();
+
+ // Test setting and getting outputRowMeta
+ IRowMeta outputRowMeta = new RowMeta();
+ outputRowMeta.addValueMeta(new ValueMetaString("test"));
+ data.outputRowMeta = outputRowMeta;
+ assertEquals(outputRowMeta, data.outputRowMeta);
+
+ // Test setting and getting compareRowMeta
+ IRowMeta compareRowMeta = new RowMeta();
+ compareRowMeta.addValueMeta(new ValueMetaString("compare"));
+ data.compareRowMeta = compareRowMeta;
+ assertEquals(compareRowMeta, data.compareRowMeta);
+
+ // Test setting and getting inputRowMeta
+ IRowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaString("input"));
+ data.inputRowMeta = inputRowMeta;
+ assertEquals(inputRowMeta, data.inputRowMeta);
+
+ // Test setting and getting counter
+ data.counter = 42L;
+ assertEquals(42L, data.counter);
+
+ // Test setting and getting previous
+ Object[] previous = new Object[] {"test", "data"};
+ data.previous = previous;
+ assertEquals(previous, data.previous);
+
+ // Test setting and getting fieldnrs
+ int[] fieldnrs = new int[] {0, 1, 2};
+ data.fieldnrs = fieldnrs;
+ assertEquals(fieldnrs, data.fieldnrs);
+
+ // Test setting and getting compareFields
+ String compareFields = "field1,field2";
+ data.compareFields = compareFields;
+ assertEquals(compareFields, data.compareFields);
+
+ // Test setting and getting realErrorDescription
+ String errorDescription = "Test error";
+ data.realErrorDescription = errorDescription;
+ assertEquals(errorDescription, data.realErrorDescription);
+
+ // Test setting and getting sendDuplicateRows
+ data.sendDuplicateRows = true;
+ assertTrue(data.sendDuplicateRows);
+ }
+
+ @Test
+ void testUniqueRowsDataInheritance() {
+ UniqueRowsData data = new UniqueRowsData();
+
+ // Verify inheritance from BaseTransformData
+ assertNotNull(data, "Should be instance of BaseTransformData");
+ assertNotNull(data, "Should be instance of ITransformData");
+
+ // Test that we can cast to parent types
+ BaseTransformData baseData = data;
+ ITransformData transformData = data;
+
+ assertNotNull(baseData, "Should be castable to BaseTransformData");
+ assertNotNull(transformData, "Should be castable to ITransformData");
+ }
+
+ @Test
+ void testUniqueRowsDataDefaultValues() {
+ UniqueRowsData data = new UniqueRowsData();
+
+ // Verify all default values are as expected
+ assertNull(data.outputRowMeta, "outputRowMeta should be null by default");
+ assertNull(data.compareRowMeta, "compareRowMeta should be null by
default");
+ assertNull(data.inputRowMeta, "inputRowMeta should be null by default");
+ assertEquals(0, data.counter, "counter should be 0 by default");
+ assertNull(data.previous, "previous should be null by default");
+ assertNull(data.fieldnrs, "fieldnrs should be null by default");
+ assertNull(data.compareFields, "compareFields should be null by default");
+ assertNull(data.realErrorDescription, "realErrorDescription should be null
by default");
+ assertFalse(data.sendDuplicateRows, "sendDuplicateRows should be false by
default");
+ }
+
+ @Test
+ void testUniqueRowsDataFieldModification() {
+ UniqueRowsData data = new UniqueRowsData();
+
+ // Test counter modification
+ data.counter = 10L;
+ assertEquals(10L, data.counter);
+
+ data.counter++;
+ assertEquals(11L, data.counter);
+
+ data.counter = 0L;
+ assertEquals(0L, data.counter);
+
+ // Test sendDuplicateRows modification
+ data.sendDuplicateRows = true;
+ assertTrue(data.sendDuplicateRows);
+
+ data.sendDuplicateRows = false;
+ assertFalse(data.sendDuplicateRows);
+
+ // Test previous row modification
+ Object[] row1 = new Object[] {"value1", "value2"};
+ Object[] row2 = new Object[] {"value3", "value4"};
+
+ data.previous = row1;
+ assertEquals(row1, data.previous);
+
+ data.previous = row2;
+ assertEquals(row2, data.previous);
+
+ data.previous = null;
+ assertNull(data.previous);
+ }
+
+ @Test
+ void testUniqueRowsDataArrayFields() {
+ UniqueRowsData data = new UniqueRowsData();
+
+ // Test fieldnrs array
+ int[] fieldnrs1 = new int[] {0, 2, 4};
+ int[] fieldnrs2 = new int[] {1, 3, 5};
+
+ data.fieldnrs = fieldnrs1;
+ assertEquals(fieldnrs1, data.fieldnrs);
+ assertEquals(3, data.fieldnrs.length);
+ assertEquals(0, data.fieldnrs[0]);
+ assertEquals(2, data.fieldnrs[1]);
+ assertEquals(4, data.fieldnrs[2]);
+
+ data.fieldnrs = fieldnrs2;
+ assertEquals(fieldnrs2, data.fieldnrs);
+ assertEquals(3, data.fieldnrs.length);
+ assertEquals(1, data.fieldnrs[0]);
+ assertEquals(3, data.fieldnrs[1]);
+ assertEquals(5, data.fieldnrs[2]);
+
+ data.fieldnrs = null;
+ assertNull(data.fieldnrs);
+ }
+}
diff --git
a/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsMetaExtendedTest.java
b/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsMetaExtendedTest.java
new file mode 100644
index 0000000000..fe7e2654ab
--- /dev/null
+++
b/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsMetaExtendedTest.java
@@ -0,0 +1,446 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.uniquerows;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.hop.core.ICheckResult;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.row.value.ValueMetaInteger;
+import org.apache.hop.core.row.value.ValueMetaString;
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.TransformMeta;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.mockito.Mockito;
+
+class UniqueRowsMetaExtendedTest {
+
+ @RegisterExtension
+ static RestoreHopEngineEnvironmentExtension env = new
RestoreHopEngineEnvironmentExtension();
+
+ @Test
+ void testDefaultConstructor() {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+
+ assertNotNull(meta, "UniqueRowsMeta instance should be created
successfully.");
+ assertFalse(meta.isCountRows(), "countRows should be false by default");
+ assertNull(meta.getCountField(), "countField should be null by default");
+ assertNotNull(meta.getCompareFields(), "compareFields should not be null");
+ assertTrue(meta.getCompareFields().isEmpty(), "compareFields should be
empty by default");
+ assertFalse(meta.isRejectDuplicateRow(), "rejectDuplicateRow should be
false by default");
+ assertNull(meta.getErrorDescription(), "errorDescription should be null by
default");
+ }
+
+ @Test
+ void testCopyConstructor() {
+ // Create original meta with some values
+ UniqueRowsMeta original = new UniqueRowsMeta();
+ original.setCountRows(true);
+ original.setCountField("countField");
+ original.setRejectDuplicateRow(true);
+ original.setErrorDescription("Test error");
+
+ List<UniqueField> compareFields = new ArrayList<>();
+ compareFields.add(new UniqueField("field1", true));
+ compareFields.add(new UniqueField("field2", false));
+ original.setCompareFields(compareFields);
+
+ // Create copy using copy constructor
+ UniqueRowsMeta copy = new UniqueRowsMeta(original);
+
+ assertNotNull(copy, "Copy should be created successfully.");
+ assertEquals(original.isCountRows(), copy.isCountRows(), "countRows should
be copied");
+ assertEquals(original.getCountField(), copy.getCountField(), "countField
should be copied");
+ assertEquals(
+ original.isRejectDuplicateRow(),
+ copy.isRejectDuplicateRow(),
+ "rejectDuplicateRow should be copied");
+ assertEquals(
+ original.getErrorDescription(),
+ copy.getErrorDescription(),
+ "errorDescription should be copied");
+
+ // Compare fields should be copied but not the same reference
+ assertNotNull(copy.getCompareFields(), "compareFields should not be null");
+ assertEquals(
+ original.getCompareFields().size(),
+ copy.getCompareFields().size(),
+ "compareFields size should match");
+ assertFalse(
+ original.getCompareFields() == copy.getCompareFields(),
+ "compareFields should be different instances");
+
+ // Verify individual fields are copied correctly
+ for (int i = 0; i < original.getCompareFields().size(); i++) {
+ UniqueField originalField = original.getCompareFields().get(i);
+ UniqueField copyField = copy.getCompareFields().get(i);
+ assertEquals(originalField.getName(), copyField.getName(), "Field name
should be copied");
+ assertEquals(
+ originalField.isCaseInsensitive(),
+ copyField.isCaseInsensitive(),
+ "Field caseInsensitive should be copied");
+ }
+ }
+
+ @Test
+ void testCopyConstructorWithEmptyCompareFields() {
+ UniqueRowsMeta original = new UniqueRowsMeta();
+ original.setCountRows(false);
+ original.setCountField("");
+ original.setRejectDuplicateRow(false);
+ original.setErrorDescription(null);
+ original.setCompareFields(new ArrayList<>());
+
+ UniqueRowsMeta copy = new UniqueRowsMeta(original);
+
+ assertNotNull(copy, "Copy should be created successfully.");
+ assertEquals(original.isCountRows(), copy.isCountRows(), "countRows should
be copied");
+ assertEquals(original.getCountField(), copy.getCountField(), "countField
should be copied");
+ assertEquals(
+ original.isRejectDuplicateRow(),
+ copy.isRejectDuplicateRow(),
+ "rejectDuplicateRow should be copied");
+ assertEquals(
+ original.getErrorDescription(),
+ copy.getErrorDescription(),
+ "errorDescription should be copied");
+ assertNotNull(copy.getCompareFields(), "compareFields should not be null");
+ assertTrue(copy.getCompareFields().isEmpty(), "compareFields should be
empty");
+ }
+
+ @Test
+ void testGettersAndSetters() {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+
+ // Test countRows
+ meta.setCountRows(true);
+ assertTrue(meta.isCountRows(), "setCountRows should set the value
correctly");
+ meta.setCountRows(false);
+ assertFalse(meta.isCountRows(), "setCountRows should set the value
correctly");
+
+ // Test countField
+ String countField = "testCountField";
+ meta.setCountField(countField);
+ assertEquals(countField, meta.getCountField(), "setCountField should set
the value correctly");
+ meta.setCountField(null);
+ assertNull(meta.getCountField(), "setCountField with null should work");
+
+ // Test rejectDuplicateRow
+ meta.setRejectDuplicateRow(true);
+ assertTrue(meta.isRejectDuplicateRow(), "setRejectDuplicateRow should set
the value correctly");
+ meta.setRejectDuplicateRow(false);
+ assertFalse(
+ meta.isRejectDuplicateRow(), "setRejectDuplicateRow should set the
value correctly");
+
+ // Test errorDescription
+ String errorDescription = "Test error description";
+ meta.setErrorDescription(errorDescription);
+ assertEquals(
+ errorDescription,
+ meta.getErrorDescription(),
+ "setErrorDescription should set the value correctly");
+ meta.setErrorDescription(null);
+ assertNull(meta.getErrorDescription(), "setErrorDescription with null
should work");
+
+ // Test compareFields
+ List<UniqueField> compareFields = new ArrayList<>();
+ compareFields.add(new UniqueField("field1", true));
+ compareFields.add(new UniqueField("field2", false));
+ meta.setCompareFields(compareFields);
+ assertEquals(
+ compareFields, meta.getCompareFields(), "setCompareFields should set
the value correctly");
+ meta.setCompareFields(null);
+ assertNull(meta.getCompareFields(), "setCompareFields with null should
work");
+ }
+
+ @Test
+ void testClone() {
+ UniqueRowsMeta original = new UniqueRowsMeta();
+ original.setCountRows(true);
+ original.setCountField("countField");
+ original.setRejectDuplicateRow(true);
+ original.setErrorDescription("Test error");
+
+ List<UniqueField> compareFields = new ArrayList<>();
+ compareFields.add(new UniqueField("field1", true));
+ compareFields.add(new UniqueField("field2", false));
+ original.setCompareFields(compareFields);
+
+ UniqueRowsMeta clone = (UniqueRowsMeta) original.clone();
+
+ assertNotNull(clone, "Clone should be created successfully.");
+ assertTrue(clone instanceof UniqueRowsMeta, "Clone should be instance of
UniqueRowsMeta");
+ assertEquals(original.isCountRows(), clone.isCountRows(), "countRows
should be cloned");
+ assertEquals(original.getCountField(), clone.getCountField(), "countField
should be cloned");
+ assertEquals(
+ original.isRejectDuplicateRow(),
+ clone.isRejectDuplicateRow(),
+ "rejectDuplicateRow should be cloned");
+ assertEquals(
+ original.getErrorDescription(),
+ clone.getErrorDescription(),
+ "errorDescription should be cloned");
+
+ // Compare fields should be cloned but not the same reference
+ assertNotNull(clone.getCompareFields(), "compareFields should not be
null");
+ assertEquals(
+ original.getCompareFields().size(),
+ clone.getCompareFields().size(),
+ "compareFields size should match");
+ assertFalse(
+ original.getCompareFields() == clone.getCompareFields(),
+ "compareFields should be different instances");
+ }
+
+ @Test
+ void testSetDefault() {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+
+ // Set some values first
+ meta.setCountRows(true);
+ meta.setCountField("testField");
+ meta.setRejectDuplicateRow(true);
+ meta.setErrorDescription("Test error");
+
+ List<UniqueField> compareFields = new ArrayList<>();
+ compareFields.add(new UniqueField("field1", true));
+ meta.setCompareFields(compareFields);
+
+ // Call setDefault
+ meta.setDefault();
+
+ // Verify default values
+ assertFalse(meta.isCountRows(), "countRows should be false after
setDefault");
+ assertEquals("", meta.getCountField(), "countField should be empty string
after setDefault");
+ assertFalse(meta.isRejectDuplicateRow(), "rejectDuplicateRow should be
false after setDefault");
+ assertNull(meta.getErrorDescription(), "errorDescription should be null
after setDefault");
+ assertNotNull(meta.getCompareFields(), "compareFields should not be null
after setDefault");
+ assertTrue(meta.getCompareFields().isEmpty(), "compareFields should be
empty after setDefault");
+ }
+
+ @Test
+ void testGetFieldsWithCountRows() throws Exception {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+ meta.setCountRows(true);
+ meta.setCountField("countField");
+
+ IRowMeta row = new RowMeta();
+ row.addValueMeta(new ValueMetaString("name"));
+ row.addValueMeta(new ValueMetaInteger("age"));
+
+ IHopMetadataProvider metadataProvider =
Mockito.mock(IHopMetadataProvider.class);
+ Variables variables = new Variables();
+
+ meta.getFields(row, "testTransform", null, null, variables,
metadataProvider);
+
+ // Should have original fields plus count field
+ assertEquals(3, row.size(), "Row should have 3 fields after adding count
field");
+ assertEquals("name", row.getValueMeta(0).getName(), "First field should be
name");
+ assertEquals("age", row.getValueMeta(1).getName(), "Second field should be
age");
+ assertEquals("countField", row.getValueMeta(2).getName(), "Third field
should be countField");
+ assertTrue(
+ row.getValueMeta(2) instanceof ValueMetaInteger, "Count field should
be ValueMetaInteger");
+ }
+
+ @Test
+ void testGetFieldsWithoutCountRows() throws Exception {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+ meta.setCountRows(false);
+
+ IRowMeta row = new RowMeta();
+ row.addValueMeta(new ValueMetaString("name"));
+ row.addValueMeta(new ValueMetaInteger("age"));
+
+ IHopMetadataProvider metadataProvider =
Mockito.mock(IHopMetadataProvider.class);
+ Variables variables = new Variables();
+
+ meta.getFields(row, "testTransform", null, null, variables,
metadataProvider);
+
+ // Should have original fields only
+ assertEquals(2, row.size(), "Row should have 2 fields without count
field");
+ assertEquals("name", row.getValueMeta(0).getName(), "First field should be
name");
+ assertEquals("age", row.getValueMeta(1).getName(), "Second field should be
age");
+ }
+
+ @Test
+ void testGetFieldsWithCompareFields() throws Exception {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+ meta.setCountRows(false);
+
+ List<UniqueField> compareFields = new ArrayList<>();
+ compareFields.add(new UniqueField("name", true)); // Case insensitive
+ compareFields.add(new UniqueField("age", false)); // Case sensitive
+ meta.setCompareFields(compareFields);
+
+ IRowMeta row = new RowMeta();
+ ValueMetaString nameMeta = new ValueMetaString("name");
+ ValueMetaInteger ageMeta = new ValueMetaInteger("age");
+ row.addValueMeta(nameMeta);
+ row.addValueMeta(ageMeta);
+
+ IHopMetadataProvider metadataProvider =
Mockito.mock(IHopMetadataProvider.class);
+ Variables variables = new Variables();
+
+ meta.getFields(row, "testTransform", null, null, variables,
metadataProvider);
+
+ // Verify case insensitive settings
+ assertTrue(row.getValueMeta(0).isCaseInsensitive(), "name field should be
case insensitive");
+ assertFalse(row.getValueMeta(1).isCaseInsensitive(), "age field should be
case sensitive");
+ }
+
+ @Test
+ void testCheckWithInput() {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+ PipelineMeta pipelineMeta = Mockito.mock(PipelineMeta.class);
+ TransformMeta transformMeta = Mockito.mock(TransformMeta.class);
+ IRowMeta prev = new RowMeta();
+ String[] input = {"input1", "input2"};
+ String[] output = {"output1"};
+ IRowMeta info = new RowMeta();
+ Variables variables = new Variables();
+ IHopMetadataProvider metadataProvider =
Mockito.mock(IHopMetadataProvider.class);
+
+ List<ICheckResult> remarks = new ArrayList<>();
+ meta.check(
+ remarks,
+ pipelineMeta,
+ transformMeta,
+ prev,
+ input,
+ output,
+ info,
+ variables,
+ metadataProvider);
+
+ assertEquals(1, remarks.size(), "Should have one check result");
+ ICheckResult result = remarks.get(0);
+ assertEquals(ICheckResult.TYPE_RESULT_OK, result.getType(), "Check result
should be OK");
+ }
+
+ @Test
+ void testCheckWithoutInput() {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+ PipelineMeta pipelineMeta = Mockito.mock(PipelineMeta.class);
+ TransformMeta transformMeta = Mockito.mock(TransformMeta.class);
+ IRowMeta prev = new RowMeta();
+ String[] input = {}; // No input
+ String[] output = {"output1"};
+ IRowMeta info = new RowMeta();
+ Variables variables = new Variables();
+ IHopMetadataProvider metadataProvider =
Mockito.mock(IHopMetadataProvider.class);
+
+ List<ICheckResult> remarks = new ArrayList<>();
+ meta.check(
+ remarks,
+ pipelineMeta,
+ transformMeta,
+ prev,
+ input,
+ output,
+ info,
+ variables,
+ metadataProvider);
+
+ assertEquals(1, remarks.size(), "Should have one check result");
+ ICheckResult result = remarks.get(0);
+ assertEquals(ICheckResult.TYPE_RESULT_ERROR, result.getType(), "Check
result should be ERROR");
+ }
+
+ @Test
+ void testSupportsErrorHandling() {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+
+ // Test with rejectDuplicateRow = true
+ meta.setRejectDuplicateRow(true);
+ assertTrue(
+ meta.supportsErrorHandling(),
+ "Should support error handling when rejectDuplicateRow is true");
+
+ // Test with rejectDuplicateRow = false
+ meta.setRejectDuplicateRow(false);
+ assertFalse(
+ meta.supportsErrorHandling(),
+ "Should not support error handling when rejectDuplicateRow is false");
+ }
+
+ @Test
+ void testCompareFieldsModification() {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+
+ // Initially empty
+ assertTrue(meta.getCompareFields().isEmpty(), "compareFields should be
empty initially");
+
+ // Add fields
+ List<UniqueField> compareFields = new ArrayList<>();
+ compareFields.add(new UniqueField("field1", true));
+ compareFields.add(new UniqueField("field2", false));
+ meta.setCompareFields(compareFields);
+
+ assertEquals(2, meta.getCompareFields().size(), "compareFields should have
2 fields");
+ assertEquals(
+ "field1", meta.getCompareFields().get(0).getName(), "First field name
should be field1");
+ assertTrue(
+ meta.getCompareFields().get(0).isCaseInsensitive(),
+ "First field should be case insensitive");
+ assertEquals(
+ "field2", meta.getCompareFields().get(1).getName(), "Second field name
should be field2");
+ assertFalse(
+ meta.getCompareFields().get(1).isCaseInsensitive(),
+ "Second field should be case sensitive");
+
+ // Modify existing field
+ meta.getCompareFields().get(0).setName("modifiedField1");
+ assertEquals(
+ "modifiedField1",
+ meta.getCompareFields().get(0).getName(),
+ "Field name should be modified");
+
+ // Add new field
+ meta.getCompareFields().add(new UniqueField("field3", true));
+ assertEquals(
+ 3, meta.getCompareFields().size(), "compareFields should have 3 fields
after addition");
+ }
+
+ @Test
+ void testErrorDescriptionWithVariables() {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+ String errorDescription = "Error in ${TRANSFORM_NAME}";
+ meta.setErrorDescription(errorDescription);
+ assertEquals(
+ errorDescription, meta.getErrorDescription(), "Error description
should be set correctly");
+ }
+
+ @Test
+ void testCountFieldWithVariables() {
+ UniqueRowsMeta meta = new UniqueRowsMeta();
+ String countField = "count_${TRANSFORM_NAME}";
+ meta.setCountField(countField);
+ assertEquals(countField, meta.getCountField(), "Count field should be set
correctly");
+ }
+}
diff --git
a/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsTest.java
b/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsTest.java
new file mode 100644
index 0000000000..625cb0dee7
--- /dev/null
+++
b/plugins/transforms/uniquerows/src/test/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsTest.java
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.uniquerows;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.hop.core.HopEnvironment;
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.logging.ILoggingObject;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.row.value.ValueMetaInteger;
+import org.apache.hop.core.row.value.ValueMetaString;
+import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.apache.hop.pipeline.transforms.mock.TransformMockHelper;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class UniqueRowsTest {
+
+ @RegisterExtension
+ static RestoreHopEngineEnvironmentExtension env = new
RestoreHopEngineEnvironmentExtension();
+
+ private TransformMockHelper<UniqueRowsMeta, UniqueRowsData>
transformMockHelper;
+
+ @BeforeAll
+ static void setUpBeforeClass() throws HopException {
+ HopEnvironment.init();
+ }
+
+ @BeforeEach
+ void setUp() {
+ transformMockHelper =
+ new TransformMockHelper<>("UniqueRows", UniqueRowsMeta.class,
UniqueRowsData.class);
+ when(transformMockHelper.logChannelFactory.create(any(),
any(ILoggingObject.class)))
+ .thenReturn(transformMockHelper.iLogChannel);
+ when(transformMockHelper.pipeline.isRunning()).thenReturn(true);
+ when(transformMockHelper.transformMeta.getName()).thenReturn("UniqueRows");
+ }
+
+ @AfterEach
+ void tearDown() {
+ transformMockHelper.cleanUp();
+ }
+
+ @Test
+ void testInitWithErrorHandling() {
+ // Setup error handling
+ when(transformMockHelper.transformMeta.getTransformErrorMeta())
+
.thenReturn(mock(org.apache.hop.pipeline.transform.TransformErrorMeta.class));
+
when(transformMockHelper.iTransformMeta.supportsErrorHandling()).thenReturn(true);
+
when(transformMockHelper.iTransformMeta.isRejectDuplicateRow()).thenReturn(true);
+ when(transformMockHelper.iTransformMeta.getErrorDescription())
+ .thenReturn("Duplicate row error");
+
+ UniqueRows uniqueRows =
+ new UniqueRows(
+ transformMockHelper.transformMeta,
+ transformMockHelper.iTransformMeta,
+ transformMockHelper.iTransformData,
+ 0,
+ transformMockHelper.pipelineMeta,
+ transformMockHelper.pipeline);
+
+ boolean result = uniqueRows.init();
+
+ assertTrue(result);
+ assertTrue(transformMockHelper.iTransformData.sendDuplicateRows);
+ }
+
+ @Test
+ void testInitWithoutErrorHandling() {
+ // Setup without error handling
+
when(transformMockHelper.transformMeta.getTransformErrorMeta()).thenReturn(null);
+
when(transformMockHelper.iTransformMeta.supportsErrorHandling()).thenReturn(false);
+
+ UniqueRows uniqueRows =
+ new UniqueRows(
+ transformMockHelper.transformMeta,
+ transformMockHelper.iTransformMeta,
+ transformMockHelper.iTransformData,
+ 0,
+ transformMockHelper.pipelineMeta,
+ transformMockHelper.pipeline);
+
+ boolean result = uniqueRows.init();
+
+ assertTrue(result);
+ assertFalse(transformMockHelper.iTransformData.sendDuplicateRows);
+ }
+
+ @Test
+ void testProcessRowWithNoCompareFields() throws Exception {
+ // Setup meta with no compare fields
+ when(transformMockHelper.iTransformMeta.getCompareFields()).thenReturn(new
ArrayList<>());
+ when(transformMockHelper.iTransformMeta.isCountRows()).thenReturn(false);
+
+ UniqueRows uniqueRows =
+ new UniqueRows(
+ transformMockHelper.transformMeta,
+ transformMockHelper.iTransformMeta,
+ transformMockHelper.iTransformData,
+ 0,
+ transformMockHelper.pipelineMeta,
+ transformMockHelper.pipeline);
+
+ assertTrue(uniqueRows.init());
+
+ // Setup input row meta
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaString("name"));
+ inputRowMeta.addValueMeta(new ValueMetaInteger("age"));
+ uniqueRows.setInputRowMeta(inputRowMeta);
+
+ // Test first row - should not output anything yet
+ uniqueRows = spy(uniqueRows);
+ doReturn(new Object[] {"Alice", 30L}).when(uniqueRows).getRow();
+ assertTrue(uniqueRows.processRow());
+ // First row is stored but not output yet
+
+ // Test second row (different) - should output previous row
+ doReturn(new Object[] {"Bob", 25L}).when(uniqueRows).getRow();
+ assertTrue(uniqueRows.processRow());
+ verify(uniqueRows).putRow(any(IRowMeta.class), any(Object[].class));
+ }
+
+ @Test
+ void testProcessRowWithCompareFields() throws Exception {
+ // Setup meta with compare fields
+ List<UniqueField> compareFields = new ArrayList<>();
+ compareFields.add(new UniqueField("name", false));
+
when(transformMockHelper.iTransformMeta.getCompareFields()).thenReturn(compareFields);
+ when(transformMockHelper.iTransformMeta.isCountRows()).thenReturn(false);
+
+ UniqueRows uniqueRows =
+ new UniqueRows(
+ transformMockHelper.transformMeta,
+ transformMockHelper.iTransformMeta,
+ transformMockHelper.iTransformData,
+ 0,
+ transformMockHelper.pipelineMeta,
+ transformMockHelper.pipeline);
+
+ assertTrue(uniqueRows.init());
+
+ // Setup input row meta
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaString("name"));
+ inputRowMeta.addValueMeta(new ValueMetaInteger("age"));
+ uniqueRows.setInputRowMeta(inputRowMeta);
+
+ // Test first row - should not output anything yet
+ uniqueRows = spy(uniqueRows);
+ doReturn(new Object[] {"Alice", 30L}).when(uniqueRows).getRow();
+ assertTrue(uniqueRows.processRow());
+ // First row is stored but not output yet
+
+ // Test second row (different name) - should output previous row
+ doReturn(new Object[] {"Bob", 25L}).when(uniqueRows).getRow();
+ assertTrue(uniqueRows.processRow());
+ verify(uniqueRows).putRow(any(IRowMeta.class), any(Object[].class));
+ }
+
+ @Test
+ void testProcessRowWithFieldNotFound() throws Exception {
+ // Setup meta with a field that doesn't exist
+ List<UniqueField> compareFields = new ArrayList<>();
+ compareFields.add(new UniqueField("nonexistent", false));
+
when(transformMockHelper.iTransformMeta.getCompareFields()).thenReturn(compareFields);
+
+ UniqueRows uniqueRows =
+ new UniqueRows(
+ transformMockHelper.transformMeta,
+ transformMockHelper.iTransformMeta,
+ transformMockHelper.iTransformData,
+ 0,
+ transformMockHelper.pipelineMeta,
+ transformMockHelper.pipeline);
+
+ assertTrue(uniqueRows.init());
+
+ // Setup input row meta
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaString("name"));
+ uniqueRows.setInputRowMeta(inputRowMeta);
+
+ // Spy on the transform to mock getRow()
+ uniqueRows = spy(uniqueRows);
+ doReturn(new Object[] {"Alice"}).when(uniqueRows).getRow();
+
+ // Execute processRow - should fail due to field not found
+ boolean result = uniqueRows.processRow();
+
+ assertFalse(result);
+ // Verify that errors were set
+ assertTrue(uniqueRows.getErrors() > 0);
+ }
+
+ @Test
+ void testBatchComplete() throws Exception {
+ when(transformMockHelper.iTransformMeta.getCompareFields()).thenReturn(new
ArrayList<>());
+ when(transformMockHelper.iTransformMeta.isCountRows()).thenReturn(false);
+
+ UniqueRows uniqueRows =
+ new UniqueRows(
+ transformMockHelper.transformMeta,
+ transformMockHelper.iTransformMeta,
+ transformMockHelper.iTransformData,
+ 0,
+ transformMockHelper.pipelineMeta,
+ transformMockHelper.pipeline);
+
+ assertTrue(uniqueRows.init());
+
+ // Setup input row meta
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaString("name"));
+ uniqueRows.setInputRowMeta(inputRowMeta);
+
+ // Set up previous row in data
+ transformMockHelper.iTransformData.previous = new Object[] {"Alice"};
+ transformMockHelper.iTransformData.outputRowMeta = inputRowMeta;
+ transformMockHelper.iTransformData.counter = 2;
+
+ // Spy on the transform to mock putRow
+ uniqueRows = spy(uniqueRows);
+
+ // Call batchComplete
+ uniqueRows.batchComplete();
+
+ // Verify that putRow was called and previous was cleared
+ verify(uniqueRows).putRow(any(IRowMeta.class), any(Object[].class));
+ assertNull(transformMockHelper.iTransformData.previous);
+ }
+
+ @Test
+ void testBatchCompleteWithNoPreviousRow() throws Exception {
+ when(transformMockHelper.iTransformMeta.getCompareFields()).thenReturn(new
ArrayList<>());
+
+ UniqueRows uniqueRows =
+ new UniqueRows(
+ transformMockHelper.transformMeta,
+ transformMockHelper.iTransformMeta,
+ transformMockHelper.iTransformData,
+ 0,
+ transformMockHelper.pipelineMeta,
+ transformMockHelper.pipeline);
+
+ assertTrue(uniqueRows.init());
+
+ // Ensure no previous row
+ transformMockHelper.iTransformData.previous = null;
+
+ // Spy on the transform to mock putRow
+ uniqueRows = spy(uniqueRows);
+
+ // Call batchComplete
+ uniqueRows.batchComplete();
+
+ // Verify that putRow was NOT called
+ verify(uniqueRows, org.mockito.Mockito.never())
+ .putRow(any(IRowMeta.class), any(Object[].class));
+ }
+
+ @Test
+ void testProcessRowWithNullInput() throws Exception {
+ when(transformMockHelper.iTransformMeta.getCompareFields()).thenReturn(new
ArrayList<>());
+ when(transformMockHelper.iTransformMeta.isCountRows()).thenReturn(false);
+
+ UniqueRows uniqueRows =
+ new UniqueRows(
+ transformMockHelper.transformMeta,
+ transformMockHelper.iTransformMeta,
+ transformMockHelper.iTransformData,
+ 0,
+ transformMockHelper.pipelineMeta,
+ transformMockHelper.pipeline);
+
+ assertTrue(uniqueRows.init());
+
+ // Setup input row meta
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaString("name"));
+ uniqueRows.setInputRowMeta(inputRowMeta);
+
+ // Set up previous row in data
+ transformMockHelper.iTransformData.previous = new Object[] {"Alice"};
+ transformMockHelper.iTransformData.outputRowMeta = inputRowMeta;
+ transformMockHelper.iTransformData.counter = 1;
+
+ // Spy on the transform to mock getRow() to return null
+ uniqueRows = spy(uniqueRows);
+ doReturn(null).when(uniqueRows).getRow();
+
+ // Execute processRow - should handle null input
+ boolean result = uniqueRows.processRow();
+
+ assertFalse(result);
+ // Verify that the previous row was output
+ verify(uniqueRows).putRow(any(IRowMeta.class), any(Object[].class));
+ }
+}