Merged dev into feature/process-locks-bulk

This commit is contained in:
2024-12-20 15:30:19 -06:00
43 changed files with 2727 additions and 132 deletions

View File

@ -0,0 +1,123 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.tables.helpers;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.AfterAll;
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 static org.junit.jupiter.api.Assertions.assertEquals;
/*******************************************************************************
** Unit test for UniqueKeyHelper
*******************************************************************************/
class UniqueKeyHelperTest extends BaseTest
{
private static Integer originalPageSize;
/*******************************************************************************
**
*******************************************************************************/
@BeforeAll
static void beforeAll()
{
originalPageSize = UniqueKeyHelper.getPageSize();
UniqueKeyHelper.setPageSize(5);
}
/*******************************************************************************
**
*******************************************************************************/
@AfterAll
static void afterAll()
{
UniqueKeyHelper.setPageSize(originalPageSize);
}
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
@AfterEach
void beforeAndAfterEach()
{
MemoryRecordStore.fullReset();
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testUniqueKey() throws QException
{
List<QRecord> recordsWithKey1Equals1AndKey2In1Through10 = List.of(
new QRecord().withValue("key1", 1).withValue("key2", 1),
new QRecord().withValue("key1", 1).withValue("key2", 2),
new QRecord().withValue("key1", 1).withValue("key2", 3),
new QRecord().withValue("key1", 1).withValue("key2", 4),
new QRecord().withValue("key1", 1).withValue("key2", 5),
new QRecord().withValue("key1", 1).withValue("key2", 6),
new QRecord().withValue("key1", 1).withValue("key2", 7),
new QRecord().withValue("key1", 1).withValue("key2", 8),
new QRecord().withValue("key1", 1).withValue("key2", 9),
new QRecord().withValue("key1", 1).withValue("key2", 10)
);
InsertInput insertInput = new InsertInput();
insertInput.setTableName(TestUtils.TABLE_NAME_TWO_KEYS);
insertInput.setRecords(recordsWithKey1Equals1AndKey2In1Through10);
InsertOutput insertOutput = new InsertAction().execute(insertInput);
MemoryRecordStore.resetStatistics();
MemoryRecordStore.setCollectStatistics(true);
QTableMetaData table = QContext.getQInstance().getTable(TestUtils.TABLE_NAME_TWO_KEYS);
Map<List<Serializable>, Serializable> existingKeys = UniqueKeyHelper.getExistingKeys(null, table, recordsWithKey1Equals1AndKey2In1Through10, table.getUniqueKeys().get(0), false);
assertEquals(recordsWithKey1Equals1AndKey2In1Through10.size(), existingKeys.size());
assertEquals(2, MemoryRecordStore.getStatistics().get(MemoryRecordStore.STAT_QUERIES_RAN));
}
}

View File

@ -2054,16 +2054,41 @@ public class QInstanceValidatorTest extends BaseTest
assertValidationFailureReasons((qInstance -> qInstance.getTable(TestUtils.TABLE_NAME_ORDER).withAssociation(new Association().withName("myAssociation"))),
"missing joinName for Association myAssociation on table " + TestUtils.TABLE_NAME_ORDER,
"missing associatedTableName for Association myAssociation on table " + TestUtils.TABLE_NAME_ORDER
);
"missing associatedTableName for Association myAssociation on table " + TestUtils.TABLE_NAME_ORDER);
assertValidationFailureReasons((qInstance -> qInstance.getTable(TestUtils.TABLE_NAME_ORDER).withAssociation(new Association().withName("myAssociation").withJoinName("notAJoin").withAssociatedTableName(TestUtils.TABLE_NAME_LINE_ITEM))),
"unrecognized joinName notAJoin for Association myAssociation on table " + TestUtils.TABLE_NAME_ORDER
);
"unrecognized joinName notAJoin for Association myAssociation on table " + TestUtils.TABLE_NAME_ORDER);
assertValidationFailureReasons((qInstance -> qInstance.getTable(TestUtils.TABLE_NAME_ORDER).withAssociation(new Association().withName("myAssociation").withJoinName("orderLineItem").withAssociatedTableName("notATable"))),
"unrecognized associatedTableName notATable for Association myAssociation on table " + TestUtils.TABLE_NAME_ORDER
);
"unrecognized associatedTableName notATable for Association myAssociation on table " + TestUtils.TABLE_NAME_ORDER);
//////////////////////////////////
// wrong join on an association //
//////////////////////////////////
assertValidationFailureReasons((qInstance ->
{
Association association = qInstance.getTable(TestUtils.TABLE_NAME_ORDER).getAssociationByName("orderLine").orElseThrow();
association.setJoinName("orderOrderExtrinsic");
}),
"join [orderOrderExtrinsic] does not connect tables [order] and [orderLine]");
//////////////////////////////////////////
// wrong table (doesn't match the join) //
//////////////////////////////////////////
assertValidationFailureReasons((qInstance ->
{
Association association = qInstance.getTable(TestUtils.TABLE_NAME_ORDER).getAssociationByName("orderLine").orElseThrow();
association.setAssociatedTableName(TestUtils.TABLE_NAME_ORDER_EXTRINSIC);
}),
"join [orderLineItem] does not connect tables [order] and [orderExtrinsic]");
//////////////////////////////
// invalid type on the join //
//////////////////////////////
assertValidationFailureReasons((qInstance -> qInstance.getJoin("orderLineItem").setType(JoinType.MANY_TO_MANY)),
"Join type does not have 'one' on this table's side side (left)");
assertValidationFailureReasons((qInstance -> qInstance.getJoin("orderLineItem").setType(JoinType.MANY_TO_ONE)),
"Join type does not have 'one' on this table's side side (left)");
}
@ -2323,7 +2348,7 @@ public class QInstanceValidatorTest extends BaseTest
{
int noOfReasons = actualReasons == null ? 0 : actualReasons.size();
assertEquals(expectedReasons.length, noOfReasons, "Expected number of validation failure reasons.\nExpected reasons: " + String.join(",", expectedReasons)
+ "\nActual reasons: " + (noOfReasons > 0 ? String.join("\n", actualReasons) : "--"));
+ "\nActual reasons: " + (noOfReasons > 0 ? String.join("\n", actualReasons) : "--"));
}
for(String reason : expectedReasons)
@ -2451,6 +2476,7 @@ public class QInstanceValidatorTest extends BaseTest
public static class ValidAuthCustomizer implements QAuthenticationModuleCustomizerInterface {}
/***************************************************************************
**
***************************************************************************/
@ -2468,6 +2494,7 @@ public class QInstanceValidatorTest extends BaseTest
}
/***************************************************************************
**
***************************************************************************/

View File

@ -23,14 +23,26 @@ package com.kingsrook.qqq.backend.core.model.metadata;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.WidgetType;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
import com.kingsrook.qqq.backend.core.model.metadata.producers.TestAbstractMetaDataProducer;
import com.kingsrook.qqq.backend.core.model.metadata.producers.TestDisabledMetaDataProducer;
import com.kingsrook.qqq.backend.core.model.metadata.producers.TestImplementsMetaDataProducer;
import com.kingsrook.qqq.backend.core.model.metadata.producers.TestMetaDataProducer;
import com.kingsrook.qqq.backend.core.model.metadata.producers.TestMetaDataProducingChildEntity;
import com.kingsrook.qqq.backend.core.model.metadata.producers.TestMetaDataProducingEntity;
import com.kingsrook.qqq.backend.core.model.metadata.producers.TestMetaDataProducingPossibleValueEnum;
import com.kingsrook.qqq.backend.core.model.metadata.producers.TestNoInterfacesExtendsObject;
import com.kingsrook.qqq.backend.core.model.metadata.producers.TestNoValidConstructorMetaDataProducer;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -54,6 +66,48 @@ class MetaDataProducerHelperTest
assertFalse(qInstance.getTables().containsKey(TestNoInterfacesExtendsObject.NAME));
assertFalse(qInstance.getTables().containsKey(TestAbstractMetaDataProducer.NAME));
assertFalse(qInstance.getTables().containsKey(TestDisabledMetaDataProducer.NAME));
/////////////////////////////////////////////
// annotation on PVS enum -> PVS meta data //
/////////////////////////////////////////////
assertTrue(qInstance.getPossibleValueSources().containsKey(TestMetaDataProducingPossibleValueEnum.class.getSimpleName()));
QPossibleValueSource enumPVS = qInstance.getPossibleValueSource(TestMetaDataProducingPossibleValueEnum.class.getSimpleName());
assertEquals(QPossibleValueSourceType.ENUM, enumPVS.getType());
assertEquals(2, enumPVS.getEnumValues().size());
assertEquals(new QPossibleValue<>(1, "One"), enumPVS.getEnumValues().get(0));
//////////////////////////////////////////////
// annotation on PVS table -> PVS meta data //
//////////////////////////////////////////////
assertTrue(qInstance.getPossibleValueSources().containsKey(TestMetaDataProducingEntity.TABLE_NAME));
QPossibleValueSource tablePVS = qInstance.getPossibleValueSource(TestMetaDataProducingEntity.TABLE_NAME);
assertEquals(QPossibleValueSourceType.TABLE, tablePVS.getType());
assertEquals(TestMetaDataProducingEntity.TABLE_NAME, tablePVS.getTableName());
//////////////////////////////////////////////////////////////////
// annotation on parent table w/ joined child -> join meta data //
//////////////////////////////////////////////////////////////////
String joinName = QJoinMetaData.makeInferredJoinName(TestMetaDataProducingEntity.TABLE_NAME, TestMetaDataProducingChildEntity.TABLE_NAME);
assertTrue(qInstance.getJoins().containsKey(joinName));
QJoinMetaData join = qInstance.getJoin(joinName);
assertEquals(TestMetaDataProducingEntity.TABLE_NAME, join.getLeftTable());
assertEquals(TestMetaDataProducingChildEntity.TABLE_NAME, join.getRightTable());
assertEquals(JoinType.ONE_TO_MANY, join.getType());
assertEquals("id", join.getJoinOns().get(0).getLeftField());
assertEquals("parentId", join.getJoinOns().get(0).getRightField());
//////////////////////////////////////////////////////////////////////////////////////
// annotation on parent table w/ joined child -> child record list widget meta data //
//////////////////////////////////////////////////////////////////////////////////////
assertTrue(qInstance.getWidgets().containsKey(joinName));
QWidgetMetaDataInterface widget = qInstance.getWidget(joinName);
assertEquals(WidgetType.CHILD_RECORD_LIST.getType(), widget.getType());
assertEquals("Test Children", widget.getLabel());
assertEquals(joinName, widget.getDefaultValues().get("joinName"));
assertEquals(false, widget.getDefaultValues().get("canAddChildRecord"));
assertNull(widget.getDefaultValues().get("manageAssociationName"));
assertEquals(15, widget.getDefaultValues().get("maxRows"));
}
}

View File

@ -0,0 +1,96 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.metadata.possiblevalues;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/*******************************************************************************
** Unit test for QPossibleValueSource
*******************************************************************************/
class QPossibleValueSourceTest extends BaseTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testWithValuesFromEnum()
{
assertThatThrownBy(() -> new QPossibleValueSource().withValuesFromEnum(DupeIds.values()))
.isInstanceOf(QRuntimeException.class)
.hasMessageContaining("Duplicated id(s)")
.hasMessageMatching(".*: \\[1]$");
}
/***************************************************************************
**
***************************************************************************/
private enum DupeIds implements PossibleValueEnum<Integer>
{
ONE_A(1, "A"),
TWO_B(2, "B"),
ONE_C(1, "C");
private final int id;
private final String label;
/***************************************************************************
**
***************************************************************************/
DupeIds(int id, String label)
{
this.id = id;
this.label = label;
}
/***************************************************************************
**
***************************************************************************/
@Override
public Integer getPossibleValueId()
{
return id;
}
/***************************************************************************
**
***************************************************************************/
@Override
public String getPossibleValueLabel()
{
return label;
}
}
}

View File

@ -0,0 +1,140 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.metadata.producers;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.data.QField;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
/*******************************************************************************
** QRecord Entity for TestMetaDataProducingEntity table
*******************************************************************************/
public class TestMetaDataProducingChildEntity extends QRecordEntity implements MetaDataProducerInterface<QTableMetaData>
{
public static final String TABLE_NAME = "testMetaDataProducingChildEntity";
@QField(isEditable = false, isPrimaryKey = true)
private Integer id;
@QField(possibleValueSourceName = TestMetaDataProducingEntity.TABLE_NAME)
private Integer parentId;
/***************************************************************************
**
***************************************************************************/
@Override
public QTableMetaData produce(QInstance qInstance) throws QException
{
return new QTableMetaData()
.withName(TABLE_NAME)
.withFieldsFromEntity(TestMetaDataProducingChildEntity.class);
}
/*******************************************************************************
** Default constructor
*******************************************************************************/
public TestMetaDataProducingChildEntity()
{
}
/*******************************************************************************
** Constructor that takes a QRecord
*******************************************************************************/
public TestMetaDataProducingChildEntity(QRecord record)
{
populateFromQRecord(record);
}
/*******************************************************************************
** Getter for id
*******************************************************************************/
public Integer getId()
{
return (this.id);
}
/*******************************************************************************
** Setter for id
*******************************************************************************/
public void setId(Integer id)
{
this.id = id;
}
/*******************************************************************************
** Fluent setter for id
*******************************************************************************/
public TestMetaDataProducingChildEntity withId(Integer id)
{
this.id = id;
return (this);
}
/*******************************************************************************
** Getter for parentId
*******************************************************************************/
public Integer getParentId()
{
return (this.parentId);
}
/*******************************************************************************
** Setter for parentId
*******************************************************************************/
public void setParentId(Integer parentId)
{
this.parentId = parentId;
}
/*******************************************************************************
** Fluent setter for parentId
*******************************************************************************/
public TestMetaDataProducingChildEntity withParentId(Integer parentId)
{
this.parentId = parentId;
return (this);
}
}

View File

@ -0,0 +1,119 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.metadata.producers;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.data.QField;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.producers.annotations.ChildJoin;
import com.kingsrook.qqq.backend.core.model.metadata.producers.annotations.ChildRecordListWidget;
import com.kingsrook.qqq.backend.core.model.metadata.producers.annotations.ChildTable;
import com.kingsrook.qqq.backend.core.model.metadata.producers.annotations.QMetaDataProducingEntity;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
/*******************************************************************************
** QRecord Entity for TestMetaDataProducingEntity table
*******************************************************************************/
@QMetaDataProducingEntity(producePossibleValueSource = true,
childTables =
{
@ChildTable(childTableEntityClass = TestMetaDataProducingChildEntity.class,
childJoin = @ChildJoin(enabled = true),
childRecordListWidget = @ChildRecordListWidget(enabled = true, label = "Test Children", maxRows = 15))
}
)
public class TestMetaDataProducingEntity extends QRecordEntity implements MetaDataProducerInterface<QTableMetaData>
{
public static final String TABLE_NAME = "testMetaDataProducingEntity";
@QField(isEditable = false, isPrimaryKey = true)
private Integer id;
/***************************************************************************
**
***************************************************************************/
@Override
public QTableMetaData produce(QInstance qInstance) throws QException
{
return new QTableMetaData()
.withName(TABLE_NAME)
.withFieldsFromEntity(TestMetaDataProducingEntity.class);
}
/*******************************************************************************
** Default constructor
*******************************************************************************/
public TestMetaDataProducingEntity()
{
}
/*******************************************************************************
** Constructor that takes a QRecord
*******************************************************************************/
public TestMetaDataProducingEntity(QRecord record)
{
populateFromQRecord(record);
}
/*******************************************************************************
** Getter for id
*******************************************************************************/
public Integer getId()
{
return (this.id);
}
/*******************************************************************************
** Setter for id
*******************************************************************************/
public void setId(Integer id)
{
this.id = id;
}
/*******************************************************************************
** Fluent setter for id
*******************************************************************************/
public TestMetaDataProducingEntity withId(Integer id)
{
this.id = id;
return (this);
}
}

View File

@ -0,0 +1,74 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.metadata.producers;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PossibleValueEnum;
import com.kingsrook.qqq.backend.core.model.metadata.producers.annotations.QMetaDataProducingPossibleValueEnum;
/*******************************************************************************
**
*******************************************************************************/
@QMetaDataProducingPossibleValueEnum(producePossibleValueSource = true)
public enum TestMetaDataProducingPossibleValueEnum implements PossibleValueEnum<Integer>
{
ONE(1, "One"),
TWO(2, "Two");
private final int id;
private final String label;
/***************************************************************************
**
***************************************************************************/
TestMetaDataProducingPossibleValueEnum(int id, String label)
{
this.id = id;
this.label = label;
}
/***************************************************************************
**
***************************************************************************/
@Override
public String getPossibleValueLabel()
{
return label;
}
/***************************************************************************
**
***************************************************************************/
@Override
public Integer getPossibleValueId()
{
return id;
}
}

View File

@ -26,10 +26,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Function;
import com.google.gson.reflect.TypeToken;
@ -618,4 +620,23 @@ class CollectionUtilsTest extends BaseTest
4, Map.of("B", "B4")), output);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testAddIfNotNull()
{
HashSet<String> s = new HashSet<>();
CollectionUtils.addIfNotNull(s, null);
assertEquals(Set.of(), s);
CollectionUtils.addIfNotNull(s, "");
assertEquals(Set.of(""), s);
CollectionUtils.addIfNotNull(s, "1");
assertEquals(Set.of("", "1"), s);
}
}

View File

@ -318,4 +318,19 @@ class StringUtilsTest extends BaseTest
assertEquals("Apples were eaten", StringUtils.pluralFormat(2, "Apple{,s} {was,were} eaten"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testEmptyToNull()
{
assertNull(StringUtils.emptyToNull(null));
assertNull(StringUtils.emptyToNull(""));
assertNull(StringUtils.emptyToNull(" "));
assertNull(StringUtils.emptyToNull(" "));
assertEquals("a", StringUtils.emptyToNull("a"));
}
}