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 a51d9551a1 Fix empty first row in Generate Random Value type picker
and add unit tests (#7473)
a51d9551a1 is described below
commit a51d9551a1b168a3ca5b2fd90ae303eb9a698ae1
Author: Lance <[email protected]>
AuthorDate: Fri Jul 10 16:15:29 2026 +0800
Fix empty first row in Generate Random Value type picker and add unit tests
(#7473)
Signed-off-by: lance <[email protected]>
---
.../transforms/randomvalue/RandomValueMeta.java | 6 +-
.../transforms/randomvalue/RandomTypeTest.java | 133 +++++++++
.../randomvalue/RandomValueDataTest.java | 100 +++++++
.../randomvalue/RandomValueMetaTest.java | 206 ++++++++++++++
.../transforms/randomvalue/RandomValueTest.java | 303 +++++++++++++++++++++
5 files changed, 747 insertions(+), 1 deletion(-)
diff --git
a/plugins/transforms/randomvalue/src/main/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueMeta.java
b/plugins/transforms/randomvalue/src/main/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueMeta.java
index 29d82e9d46..885c0f22ac 100644
---
a/plugins/transforms/randomvalue/src/main/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueMeta.java
+++
b/plugins/transforms/randomvalue/src/main/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueMeta.java
@@ -17,6 +17,7 @@
package org.apache.hop.pipeline.transforms.randomvalue;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
@@ -167,7 +168,10 @@ public class RandomValueMeta extends
BaseTransformMeta<RandomValue, RandomValueD
}
public static String[] getDescriptions() {
- return IEnumHasCodeAndDescription.getDescriptions(RandomType.class);
+ return Arrays.stream(RandomType.values())
+ .filter(t -> t != RandomType.NONE)
+ .map(RandomType::getDescription)
+ .toArray(String[]::new);
}
public static RandomType lookupDescription(String description) {
diff --git
a/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomTypeTest.java
b/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomTypeTest.java
new file mode 100644
index 0000000000..df03c1944f
--- /dev/null
+++
b/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomTypeTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.randomvalue;
+
+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.assertTrue;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.jupiter.api.Test;
+
+/** Unit test for {@link RandomValueMeta.RandomType}. */
+class RandomTypeTest {
+
+ @Test
+ void testEnumBasicProperties() {
+ RandomValueMeta.RandomType type = RandomValueMeta.RandomType.NUMBER;
+
+ assertEquals("random number", type.getCode());
+ assertNotNull(type.getDescription());
+ assertFalse(type.getDescription().isEmpty());
+ }
+
+ @Test
+ void testNoneDefault() {
+ RandomValueMeta.RandomType none = RandomValueMeta.RandomType.NONE;
+
+ assertEquals("", none.getCode());
+ assertEquals("", none.getDescription());
+ }
+
+ @Test
+ void testLookupDescriptionSuccess() {
+ String desc = RandomValueMeta.RandomType.INTEGER.getDescription();
+
+ RandomValueMeta.RandomType result =
RandomValueMeta.RandomType.lookupDescription(desc);
+ assertEquals(RandomValueMeta.RandomType.INTEGER, result);
+ }
+
+ @Test
+ void testLookupDescriptionNotFound() {
+ RandomValueMeta.RandomType result =
RandomValueMeta.RandomType.lookupDescription("not-exist");
+ assertEquals(RandomValueMeta.RandomType.NONE, result);
+ }
+
+ @Test
+ void testLookupCodeSuccess() {
+ RandomValueMeta.RandomType result =
RandomValueMeta.RandomType.lookupCode("random uuid4");
+ assertEquals(RandomValueMeta.RandomType.UUID4, result);
+ }
+
+ @Test
+ void testLookupCodeNotFound() {
+ RandomValueMeta.RandomType result =
RandomValueMeta.RandomType.lookupCode("not-exist");
+ assertEquals(RandomValueMeta.RandomType.NONE, result);
+ }
+
+ @Test
+ void testGetDescriptionsExcludesNone() {
+ String[] descriptions = RandomValueMeta.RandomType.getDescriptions();
+
+ assertNotNull(descriptions);
+ assertEquals(RandomValueMeta.RandomType.values().length - 1,
descriptions.length);
+ for (String description : descriptions) {
+ assertFalse(description.isEmpty());
+ }
+ }
+
+ @Test
+ void testGetDescriptionsContainsKnownType() {
+ String[] descriptions = RandomValueMeta.RandomType.getDescriptions();
+
+ boolean found = false;
+ for (String desc : descriptions) {
+ if (desc.equals(RandomValueMeta.RandomType.STRING.getDescription())) {
+ found = true;
+ break;
+ }
+ }
+
+ assertTrue(found);
+ }
+
+ @Test
+ void testAllEnumHaveCodeAndDescription() {
+ for (RandomValueMeta.RandomType type :
RandomValueMeta.RandomType.values()) {
+ assertNotNull(type.getCode(), type.name() + " code is null");
+ assertNotNull(type.getDescription(), type.name() + " description is
null");
+ }
+ }
+
+ @Test
+ void testDescriptionUniqueAmongSelectableTypes() {
+ Set<String> set = new HashSet<>();
+
+ for (RandomValueMeta.RandomType type :
RandomValueMeta.RandomType.values()) {
+ if (type == RandomValueMeta.RandomType.NONE) {
+ continue;
+ }
+ String desc = type.getDescription();
+ assertTrue(set.add(desc), "Duplicate description: " + desc);
+ }
+ }
+
+ @Test
+ void testDescriptionNotFallbackKey() {
+ for (RandomValueMeta.RandomType type :
RandomValueMeta.RandomType.values()) {
+ if (type == RandomValueMeta.RandomType.NONE) {
+ continue;
+ }
+
+ String desc = type.getDescription();
+ assertFalse(
+ desc.contains("RandomValueMeta.TypeDesc."), "i18n not resolved for:
" + type.name());
+ }
+ }
+}
diff --git
a/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueDataTest.java
b/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueDataTest.java
new file mode 100644
index 0000000000..048b1705bb
--- /dev/null
+++
b/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueDataTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.randomvalue;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Random;
+import javax.crypto.KeyGenerator;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.util.Uuid4Util;
+import org.apache.hop.pipeline.transform.BaseTransformData;
+import org.junit.jupiter.api.Test;
+
+/** Unit test for {@link RandomValueData}. */
+class RandomValueDataTest {
+
+ @Test
+ void testDefaultState() {
+ RandomValueData data = new RandomValueData();
+
+ assertFalse(data.readsRows);
+ assertNull(data.outputRowMeta);
+ assertNull(data.u4);
+ assertNull(data.keyGenHmacMD5);
+ assertNull(data.keyGenHmacSHA1);
+ assertNull(data.keyGenHmacSHA256);
+ assertNull(data.keyGenHmacSHA512);
+ assertNull(data.keyGenHmacSHA384);
+ assertNull(data.randomGenerator);
+ }
+
+ @Test
+ void testFieldAssignment() {
+ RandomValueData data = new RandomValueData();
+ RowMeta rowMeta = new RowMeta();
+ Random random = new Random(42);
+ Uuid4Util uuid4Util = new Uuid4Util();
+
+ data.readsRows = true;
+ data.outputRowMeta = rowMeta;
+ data.randomGenerator = random;
+ data.u4 = uuid4Util;
+
+ assertTrue(data.readsRows);
+ assertSame(rowMeta, data.outputRowMeta);
+ assertSame(random, data.randomGenerator);
+ assertSame(uuid4Util, data.u4);
+ }
+
+ @Test
+ void testKeyGeneratorAssignment() throws Exception {
+ RandomValueData data = new RandomValueData();
+
+ data.keyGenHmacMD5 = KeyGenerator.getInstance("HmacMD5");
+ data.keyGenHmacSHA1 = KeyGenerator.getInstance("HmacSHA1");
+
+ assertNotNull(data.keyGenHmacMD5);
+ assertNotNull(data.keyGenHmacSHA1);
+ assertEquals("HmacMD5", data.keyGenHmacMD5.getAlgorithm());
+ assertEquals("HmacSHA1", data.keyGenHmacSHA1.getAlgorithm());
+ }
+
+ @Test
+ void testInheritance() {
+ RandomValueData data = new RandomValueData();
+
+ assertNotNull(data);
+ assertInstanceOf(BaseTransformData.class, data);
+ }
+
+ @Test
+ void testThreadSafetyBasic() throws InterruptedException {
+ RandomValueData data = new RandomValueData();
+
+ Thread t = Thread.startVirtualThread(() -> data.readsRows = true);
+ t.join();
+
+ assertTrue(data.readsRows);
+ }
+}
diff --git
a/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueMetaTest.java
b/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueMetaTest.java
index e9ae94c26f..9740ea2bed 100644
---
a/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueMetaTest.java
+++
b/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueMetaTest.java
@@ -17,11 +17,30 @@
package org.apache.hop.pipeline.transforms.randomvalue;
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.assertNotSame;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
+import org.apache.hop.core.ICheckResult;
+import org.apache.hop.core.row.IValueMeta;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.core.xml.XmlHandler;
+import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider;
+import org.apache.hop.metadata.serializer.xml.XmlMetadataUtil;
+import org.apache.hop.pipeline.transform.TransformMeta;
import org.apache.hop.pipeline.transform.TransformSerializationTestUtil;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+/** Unit test for {@link RandomValueMeta}. */
class RandomValueMetaTest {
@Test
@@ -49,4 +68,191 @@ class RandomValueMetaTest {
assertEquals(RandomValueMeta.RandomType.HMAC_SHA1,
fields.get(6).getType());
assertEquals("hmac_sha1", fields.get(6).getName());
}
+
+ @Test
+ void testLoadSaveRoundTrip() throws Exception {
+ Path path =
+ Paths.get(
+
Objects.requireNonNull(getClass().getResource("/generate-random-values-transform.xml"))
+ .toURI());
+ String xml = Files.readString(path);
+ RandomValueMeta meta = new RandomValueMeta();
+ XmlMetadataUtil.deSerializeFromXml(
+ XmlHandler.loadXmlString(xml, TransformMeta.XML_TAG),
+ RandomValueMeta.class,
+ meta,
+ new MemoryMetadataProvider());
+
+ validateLoadedMeta(meta);
+
+ String xmlCopy =
+ XmlHandler.openTag(TransformMeta.XML_TAG)
+ + XmlMetadataUtil.serializeObjectToXml(meta)
+ + XmlHandler.closeTag(TransformMeta.XML_TAG);
+ RandomValueMeta metaCopy = new RandomValueMeta();
+ XmlMetadataUtil.deSerializeFromXml(
+ XmlHandler.loadXmlString(xmlCopy, TransformMeta.XML_TAG),
+ RandomValueMeta.class,
+ metaCopy,
+ new MemoryMetadataProvider());
+
+ validateLoadedMeta(metaCopy);
+ }
+
+ @Test
+ void testRvFieldDefaultsToNone() {
+ RandomValueMeta.RVField field = new RandomValueMeta.RVField();
+
+ assertEquals(RandomValueMeta.RandomType.NONE, field.getType());
+ }
+
+ @Test
+ void testRvFieldCopyConstructor() {
+ RandomValueMeta.RVField original = new RandomValueMeta.RVField();
+ original.setName("field1");
+ original.setType(RandomValueMeta.RandomType.STRING);
+
+ RandomValueMeta.RVField copy = new RandomValueMeta.RVField(original);
+
+ assertEquals("field1", copy.getName());
+ assertEquals(RandomValueMeta.RandomType.STRING, copy.getType());
+ assertNotSame(original, copy);
+ }
+
+ @Test
+ void testCloneDeepCopy() {
+ RandomValueMeta meta = sampleMeta();
+
+ RandomValueMeta cloned = meta.clone();
+
+ assertNotSame(meta, cloned);
+ assertEquals(meta.getSeed(), cloned.getSeed());
+ assertEquals(meta.getFields().size(), cloned.getFields().size());
+ assertNotSame(meta.getFields().getFirst(), cloned.getFields().getFirst());
+ assertEquals(meta.getFields().getFirst().getName(),
cloned.getFields().getFirst().getName());
+ assertEquals(meta.getFields().getFirst().getType(),
cloned.getFields().getFirst().getType());
+ }
+
+ @Test
+ void testCopyConstructor() {
+ RandomValueMeta meta = sampleMeta();
+
+ RandomValueMeta copy = new RandomValueMeta(meta);
+
+ assertEquals(meta.getSeed(), copy.getSeed());
+ assertEquals(1, copy.getFields().size());
+ assertNotSame(meta.getFields().getFirst(), copy.getFields().getFirst());
+ }
+
+ @ParameterizedTest
+ @EnumSource(RandomValueMeta.RandomType.class)
+ void testGetFieldsProducesExpectedValueMetaType(RandomValueMeta.RandomType
type) {
+ RandomValueMeta meta = new RandomValueMeta();
+ RandomValueMeta.RVField field = new RandomValueMeta.RVField();
+ field.setName("f1");
+ field.setType(type);
+ meta.getFields().add(field);
+
+ RowMeta rowMeta = new RowMeta();
+ meta.getFields(rowMeta, "transform", null, null, new Variables(), null);
+
+ assertEquals(1, rowMeta.size());
+ IValueMeta valueMeta = rowMeta.getValueMeta(0);
+ assertEquals("f1", valueMeta.getName());
+ assertEquals("transform", valueMeta.getOrigin());
+
+ switch (type) {
+ case NUMBER -> {
+ assertEquals(IValueMeta.TYPE_NUMBER, valueMeta.getType());
+ assertEquals(10, valueMeta.getLength());
+ assertEquals(5, valueMeta.getPrecision());
+ }
+ case INTEGER -> {
+ assertEquals(IValueMeta.TYPE_INTEGER, valueMeta.getType());
+ assertEquals(10, valueMeta.getLength());
+ assertEquals(0, valueMeta.getPrecision());
+ }
+ case STRING -> {
+ assertEquals(IValueMeta.TYPE_STRING, valueMeta.getType());
+ assertEquals(13, valueMeta.getLength());
+ }
+ case UUID, UUID4 -> {
+ assertEquals(IValueMeta.TYPE_STRING, valueMeta.getType());
+ assertEquals(36, valueMeta.getLength());
+ }
+ case HMAC_MD5, HMAC_SHA1, HMAC_SHA256, HMAC_SHA512, HMAC_SHA384 -> {
+ assertEquals(IValueMeta.TYPE_STRING, valueMeta.getType());
+ assertEquals(100, valueMeta.getLength());
+ }
+ case NONE -> assertEquals(IValueMeta.TYPE_NONE, valueMeta.getType());
+ }
+ }
+
+ @Test
+ void testCheckErrorWhenTypeNone() {
+ RandomValueMeta meta = new RandomValueMeta();
+ RandomValueMeta.RVField field = new RandomValueMeta.RVField();
+ field.setName("missing-type");
+ field.setType(RandomValueMeta.RandomType.NONE);
+ meta.getFields().add(field);
+
+ List<ICheckResult> remarks = new ArrayList<>();
+ meta.check(remarks, null, null, null, null, null, null, new Variables(),
null);
+
+ assertFalse(remarks.isEmpty());
+ assertEquals(ICheckResult.TYPE_RESULT_ERROR, remarks.getFirst().getType());
+ }
+
+ @Test
+ void testCheckOkWhenAllTypesSpecified() {
+ RandomValueMeta meta = sampleMeta();
+
+ List<ICheckResult> remarks = new ArrayList<>();
+ meta.check(remarks, null, null, null, null, null, null, new Variables(),
null);
+
+ assertEquals(1, remarks.size());
+ assertEquals(ICheckResult.TYPE_RESULT_OK, remarks.getFirst().getType());
+ }
+
+ @Test
+ void testCheckReportsOnlyMissingTypes() {
+ RandomValueMeta meta = new RandomValueMeta();
+
+ RandomValueMeta.RVField valid = new RandomValueMeta.RVField();
+ valid.setName("valid");
+ valid.setType(RandomValueMeta.RandomType.NUMBER);
+ meta.getFields().add(valid);
+
+ RandomValueMeta.RVField invalid = new RandomValueMeta.RVField();
+ invalid.setName("invalid");
+ invalid.setType(RandomValueMeta.RandomType.NONE);
+ meta.getFields().add(invalid);
+
+ List<ICheckResult> remarks = new ArrayList<>();
+ meta.check(remarks, null, null, null, null, null, null, new Variables(),
null);
+
+ assertEquals(1, remarks.size());
+ assertEquals(ICheckResult.TYPE_RESULT_ERROR, remarks.getFirst().getType());
+ }
+
+ private static RandomValueMeta sampleMeta() {
+ RandomValueMeta meta = new RandomValueMeta();
+ meta.setSeed("seed");
+
+ RandomValueMeta.RVField field = new RandomValueMeta.RVField();
+ field.setName("num");
+ field.setType(RandomValueMeta.RandomType.NUMBER);
+ meta.getFields().add(field);
+
+ return meta;
+ }
+
+ private static void validateLoadedMeta(RandomValueMeta meta) {
+ assertNotNull(meta.getFields());
+ assertFalse(meta.getFields().isEmpty());
+ assertEquals("12345", meta.getSeed());
+ assertEquals(7, meta.getFields().size());
+ assertEquals(RandomValueMeta.RandomType.NUMBER,
meta.getFields().getFirst().getType());
+ assertEquals("num", meta.getFields().getFirst().getName());
+ }
}
diff --git
a/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueTest.java
b/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueTest.java
new file mode 100644
index 0000000000..ebf6f44482
--- /dev/null
+++
b/plugins/transforms/randomvalue/src/test/java/org/apache/hop/pipeline/transforms/randomvalue/RandomValueTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.randomvalue;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+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 static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.UUID;
+import java.util.regex.Pattern;
+import org.apache.hop.core.Const;
+import org.apache.hop.core.QueueRowSet;
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.logging.ILoggingObject;
+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.TransformMeta;
+import org.apache.hop.pipeline.transforms.mock.TransformMockHelper;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+/** Unit tests for {@link RandomValue}. */
+@ExtendWith(RestoreHopEngineEnvironmentExtension.class)
+class RandomValueTest {
+
+ private static final Pattern UUID_PATTERN =
+ Pattern.compile(
+ "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
Pattern.CASE_INSENSITIVE);
+
+ private TransformMockHelper<RandomValueMeta, RandomValueData> mockHelper;
+
+ @BeforeEach
+ void setUp() {
+ mockHelper =
+ new TransformMockHelper<>("RANDOM_VALUE", RandomValueMeta.class,
RandomValueData.class);
+ when(mockHelper.logChannelFactory.create(any(), any(ILoggingObject.class)))
+ .thenReturn(mockHelper.iLogChannel);
+ when(mockHelper.pipeline.isRunning()).thenReturn(true);
+ when(mockHelper.pipeline.isStopped()).thenReturn(false);
+ }
+
+ @AfterEach
+ void tearDown() {
+ mockHelper.cleanUp();
+ }
+
+ @Test
+ void processRowSingleRowModeProducesOneOutputRowAndStops() throws
HopException {
+ RandomValueMeta meta = metaWithFields(field("num",
RandomValueMeta.RandomType.NUMBER));
+ when(mockHelper.pipelineMeta.findPreviousTransforms(any())).thenReturn(new
ArrayList<>());
+
+ RandomValueData data = new RandomValueData();
+ RandomValue transform = newTransform(meta, data);
+ QueueRowSet outputRowSet = new QueueRowSet();
+ transform.addRowSetToOutputRowSets(outputRowSet);
+
+ assertTrue(transform.init());
+ assertFalse(transform.processRow());
+
+ Object[] output = outputRowSet.getRow();
+ assertNotNull(output);
+ assertEquals(1, data.outputRowMeta.size());
+ assertNotNull(output[0]);
+ assertTrue(data.outputRowMeta.indexOfValue("num") >= 0);
+ }
+
+ @Test
+ void processRowSingleRowModeSeedProducesDeterministicValues() throws
HopException {
+ RandomValueMeta meta =
+ metaWithSeed(
+ "12345",
+ field("num", RandomValueMeta.RandomType.NUMBER),
+ field("int", RandomValueMeta.RandomType.INTEGER),
+ field("str", RandomValueMeta.RandomType.STRING));
+ when(mockHelper.pipelineMeta.findPreviousTransforms(any())).thenReturn(new
ArrayList<>());
+
+ Random r = new Random(Const.toLong("12345", 0));
+ double expectedNumber = r.nextDouble();
+ long expectedInteger = (long) r.nextInt();
+ String expectedString = Long.toString(Math.abs(r.nextLong()), 32);
+
+ RandomValueData data = new RandomValueData();
+ RandomValue transform = newTransform(meta, data);
+ QueueRowSet outputRowSet = new QueueRowSet();
+ transform.addRowSetToOutputRowSets(outputRowSet);
+
+ assertTrue(transform.init());
+ transform.processRow();
+
+ Object[] output = outputRowSet.getRow();
+ assertEquals(expectedNumber, output[0]);
+ assertEquals(expectedInteger, output[1]);
+ assertEquals(expectedString, output[2]);
+ }
+
+ @Test
+ void processRowReadsInputAndAppendsRandomValues() throws HopException {
+ RandomValueMeta meta =
+ metaWithSeed(
+ "99",
+ field("num", RandomValueMeta.RandomType.NUMBER),
+ field("str", RandomValueMeta.RandomType.STRING));
+ when(mockHelper.pipelineMeta.findPreviousTransforms(any()))
+ .thenReturn(List.of(mock(TransformMeta.class)));
+
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaString("input"));
+
+ RandomValueData data = new RandomValueData();
+ RandomValue transform = newTransform(meta, data);
+ transform.setInputRowMeta(inputRowMeta);
+ transform.addRowSetToInputRowSets(
+ mockHelper.getMockInputRowSet(new Object[][] {{"row-1"}, {"row-2"}}));
+ QueueRowSet outputRowSet = new QueueRowSet();
+ transform.addRowSetToOutputRowSets(outputRowSet);
+
+ assertTrue(transform.init());
+ assertTrue(transform.processRow());
+ Object[] firstOutput = outputRowSet.getRow();
+ assertEquals("row-1", firstOutput[0]);
+ assertNotNull(firstOutput[1]);
+ assertNotNull(firstOutput[2]);
+
+ assertTrue(transform.processRow());
+ Object[] secondOutput = outputRowSet.getRow();
+ assertEquals("row-2", secondOutput[0]);
+ assertNotNull(secondOutput[1]);
+ assertNotNull(secondOutput[2]);
+
+ assertFalse(transform.processRow());
+ assertNull(outputRowSet.getRow());
+ }
+
+ @ParameterizedTest
+ @EnumSource(
+ value = RandomValueMeta.RandomType.class,
+ names = {"NONE"},
+ mode = EnumSource.Mode.EXCLUDE)
+ void
processRowSingleRowModeSupportsAllConfiguredTypes(RandomValueMeta.RandomType
type)
+ throws HopException {
+ RandomValueMeta meta = metaWithFields(field("value", type));
+ when(mockHelper.pipelineMeta.findPreviousTransforms(any())).thenReturn(new
ArrayList<>());
+
+ RandomValueData data = new RandomValueData();
+ RandomValue transform = newTransform(meta, data);
+ QueueRowSet outputRowSet = new QueueRowSet();
+ transform.addRowSetToOutputRowSets(outputRowSet);
+
+ assertTrue(transform.init());
+ transform.processRow();
+
+ Object[] output = outputRowSet.getRow();
+ assertNotNull(output);
+ assertEquals(1, data.outputRowMeta.size());
+ assertNotNull(output[0], "Expected value for type " + type);
+
+ switch (type) {
+ case NUMBER -> assertInstanceOf(Double.class, output[0]);
+ case INTEGER -> assertInstanceOf(Long.class, output[0]);
+ case STRING, UUID, UUID4, HMAC_MD5, HMAC_SHA1, HMAC_SHA256, HMAC_SHA512,
HMAC_SHA384 ->
+ assertInstanceOf(String.class, output[0]);
+ default -> throw new AssertionError("Unhandled type: " + type);
+ }
+
+ if (type == RandomValueMeta.RandomType.UUID || type ==
RandomValueMeta.RandomType.UUID4) {
+ assertTrue(UUID_PATTERN.matcher((String) output[0]).matches());
+ }
+ if (type.name().startsWith("HMAC_")) {
+ assertFalse(((String) output[0]).isEmpty());
+ }
+ }
+
+ @Test
+ void initInitializesRandomGeneratorOnlyWhenNeeded() {
+ RandomValueMeta meta = metaWithFields(field("uuid",
RandomValueMeta.RandomType.UUID));
+ when(mockHelper.pipelineMeta.findPreviousTransforms(any())).thenReturn(new
ArrayList<>());
+
+ RandomValueData data = new RandomValueData();
+ RandomValue transform = newTransform(meta, data);
+
+ assertTrue(transform.init());
+ assertNull(data.randomGenerator);
+ assertNull(data.u4);
+ }
+
+ @Test
+ void initInitializesUuid4UtilWhenNeeded() {
+ RandomValueMeta meta = metaWithFields(field("uuid4",
RandomValueMeta.RandomType.UUID4));
+ when(mockHelper.pipelineMeta.findPreviousTransforms(any())).thenReturn(new
ArrayList<>());
+
+ RandomValueData data = new RandomValueData();
+ RandomValue transform = newTransform(meta, data);
+
+ assertTrue(transform.init());
+ assertNotNull(data.u4);
+ }
+
+ @Test
+ void initInitializesHmacGeneratorsWhenNeeded() {
+ RandomValueMeta meta =
+ metaWithFields(
+ field("md5", RandomValueMeta.RandomType.HMAC_MD5),
+ field("sha1", RandomValueMeta.RandomType.HMAC_SHA1),
+ field("sha256", RandomValueMeta.RandomType.HMAC_SHA256),
+ field("sha384", RandomValueMeta.RandomType.HMAC_SHA384),
+ field("sha512", RandomValueMeta.RandomType.HMAC_SHA512));
+ when(mockHelper.pipelineMeta.findPreviousTransforms(any())).thenReturn(new
ArrayList<>());
+
+ RandomValueData data = new RandomValueData();
+ RandomValue transform = newTransform(meta, data);
+
+ assertTrue(transform.init());
+ assertNotNull(data.keyGenHmacMD5);
+ assertNotNull(data.keyGenHmacSHA1);
+ assertNotNull(data.keyGenHmacSHA256);
+ assertNotNull(data.keyGenHmacSHA384);
+ assertNotNull(data.keyGenHmacSHA512);
+ }
+
+ @Test
+ void initSetsReadsRowsWhenPreviousTransformsExist() {
+ RandomValueMeta meta = metaWithFields(field("num",
RandomValueMeta.RandomType.NUMBER));
+ when(mockHelper.pipelineMeta.findPreviousTransforms(any()))
+ .thenReturn(List.of(mock(TransformMeta.class)));
+
+ RandomValueData data = new RandomValueData();
+ RandomValue transform = newTransform(meta, data);
+
+ assertTrue(transform.init());
+ assertTrue(data.readsRows);
+ assertNotNull(data.randomGenerator);
+ }
+
+ @Test
+ void processRowUuidValueHasStandardFormat() throws HopException {
+ RandomValueMeta meta = metaWithFields(field("uuid",
RandomValueMeta.RandomType.UUID));
+ when(mockHelper.pipelineMeta.findPreviousTransforms(any())).thenReturn(new
ArrayList<>());
+
+ RandomValueData data = new RandomValueData();
+ RandomValue transform = newTransform(meta, data);
+ QueueRowSet outputRowSet = new QueueRowSet();
+ transform.addRowSetToOutputRowSets(outputRowSet);
+
+ assertTrue(transform.init());
+ transform.processRow();
+
+ String uuid = (String) outputRowSet.getRow()[0];
+ assertNotNull(UUID.fromString(uuid));
+ }
+
+ private RandomValue newTransform(RandomValueMeta meta, RandomValueData data)
{
+ return new RandomValue(
+ mockHelper.transformMeta, meta, data, 0, mockHelper.pipelineMeta,
mockHelper.pipeline);
+ }
+
+ private static RandomValueMeta metaWithFields(RandomValueMeta.RVField...
fields) {
+ return metaWithSeed(null, fields);
+ }
+
+ private static RandomValueMeta metaWithSeed(String seed,
RandomValueMeta.RVField... fields) {
+ RandomValueMeta meta = new RandomValueMeta();
+ meta.setSeed(seed);
+ for (RandomValueMeta.RVField field : fields) {
+ meta.getFields().add(field);
+ }
+ return meta;
+ }
+
+ private static RandomValueMeta.RVField field(String name,
RandomValueMeta.RandomType type) {
+ RandomValueMeta.RVField field = new RandomValueMeta.RVField();
+ field.setName(name);
+ field.setType(type);
+ return field;
+ }
+}