QQQ-21 feedback from code review; add more types to ValueUtils

This commit is contained in:
2022-07-12 07:46:13 -05:00
parent d4657989dd
commit 86adccddd4
21 changed files with 409 additions and 21 deletions

View File

@ -106,7 +106,6 @@ public class RunProcessAction
case SKIP ->
{
LOG.info("Skipping frontend step [" + step.getName() + "] in process [" + process.getName() + "] (as requested by caller)");
processState.setNextStepName(step.getName());
//////////////////////////////////////////////////////////////////////
// much less error prone in case this code changes in the future... //
@ -166,9 +165,10 @@ public class RunProcessAction
/*******************************************************************************
**
** When we start running a process (or resuming it), get data in the RunProcessRequest
** either from the state provider (if they're found, for a resume).
*******************************************************************************/
private ProcessState primeProcessState(RunProcessRequest runProcessRequest, UUIDAndTypeStateKey stateKey) throws QException
ProcessState primeProcessState(RunProcessRequest runProcessRequest, UUIDAndTypeStateKey stateKey) throws QException
{
Optional<ProcessState> optionalProcessState = loadState(stateKey);
if(optionalProcessState.isEmpty())
@ -222,7 +222,7 @@ public class RunProcessAction
/*******************************************************************************
** return true if 'ok', false if error (and time to break loop)
** Run a single backend step.
*******************************************************************************/
private void runBackendStep(RunProcessRequest runProcessRequest, QProcessMetaData process, RunProcessResult runProcessResult, UUIDAndTypeStateKey stateKey, QBackendStepMetaData backendStep, ProcessState processState) throws Exception
{
@ -231,6 +231,7 @@ public class RunProcessAction
runBackendStepRequest.setStepName(backendStep.getName());
runBackendStepRequest.setSession(runProcessRequest.getSession());
runBackendStepRequest.setCallback(runProcessRequest.getCallback());
runBackendStepRequest.setAsyncJobCallback(runProcessRequest.getAsyncJobCallback());
RunBackendStepResult lastFunctionResult = new RunBackendStepAction().execute(runBackendStepRequest);
storeState(stateKey, lastFunctionResult.getProcessState());
@ -296,6 +297,16 @@ public class RunProcessAction
/*******************************************************************************
** public method to get a process state just by UUID.
*******************************************************************************/
public static Optional<ProcessState> getState(String processUUID)
{
return (getStateProvider().get(ProcessState.class, new UUIDAndTypeStateKey(UUID.fromString(processUUID), StateType.PROCESS_STATUS)));
}
/*******************************************************************************
** Store the process state from a function result to the state provider
**

View File

@ -90,7 +90,7 @@ public class AsyncJobCallback
/*******************************************************************************
**
*******************************************************************************/
private void storeUpdatedStatus()
protected void storeUpdatedStatus()
{
AsyncJobManager.getStateProvider().put(new UUIDAndTypeStateKey(jobUUID, StateType.ASYNC_JOB_STATUS), asyncJobStatus);
}

View File

@ -113,7 +113,7 @@ public class AsyncJobManager
** Load an instance of the appropriate state provider
**
*******************************************************************************/
protected static StateProviderInterface getStateProvider()
static StateProviderInterface getStateProvider()
{
// TODO - read this from somewhere in meta data eh?
return InMemoryStateProvider.getInstance();

View File

@ -39,6 +39,23 @@ public class AsyncJobStatus implements Serializable
/*******************************************************************************
**
*******************************************************************************/
@Override
public String toString()
{
return "AsyncJobStatus{"
+ "state=" + state
+ ", message='" + message + '\''
+ ", current=" + current
+ ", total=" + total
+ ", caughtException=" + caughtException
+ '}';
}
/*******************************************************************************
** Getter for state
**

View File

@ -23,7 +23,10 @@ package com.kingsrook.qqq.backend.core.actions.async;
/*******************************************************************************
** Exception thrown by AsyncJobManager, not to indicate an error, per se, but
** rather to indicate that a job has taken too long, as is now "going async".
**
** So, this exception contains the jobUUID.
*******************************************************************************/
public class JobGoingAsyncException extends Exception
{

View File

@ -25,6 +25,9 @@ package com.kingsrook.qqq.backend.core.model.actions.processes;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobCallback;
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobStatus;
import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback;
import com.kingsrook.qqq.backend.core.model.actions.AbstractQRequest;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
@ -43,6 +46,7 @@ public class RunBackendStepRequest extends AbstractQRequest
private String processName;
private String stepName;
private QProcessCallback callback;
private AsyncJobCallback asyncJobCallback;
@ -312,4 +316,31 @@ public class RunBackendStepRequest extends AbstractQRequest
{
return processState;
}
/*******************************************************************************
**
*******************************************************************************/
public void setAsyncJobCallback(AsyncJobCallback asyncJobCallback)
{
this.asyncJobCallback = asyncJobCallback;
}
/*******************************************************************************
**
*******************************************************************************/
public AsyncJobCallback getAsyncJobCallback()
{
if (asyncJobCallback == null)
{
/////////////////////////////////////////////////////////////////////////
// avoid NPE in case we didn't have one of these! create a new one... //
/////////////////////////////////////////////////////////////////////////
asyncJobCallback = new AsyncJobCallback(UUID.randomUUID(), new AsyncJobStatus());
}
return (asyncJobCallback);
}
}

View File

@ -23,9 +23,11 @@ package com.kingsrook.qqq.backend.core.model.data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.LinkedHashMap;
import java.util.Map;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
/*******************************************************************************
@ -227,7 +229,7 @@ public class QRecord implements Serializable
*******************************************************************************/
public String getValueString(String fieldName)
{
return ((String) values.get(fieldName));
return (ValueUtils.getValueAsString(values.get(fieldName)));
}
@ -238,7 +240,28 @@ public class QRecord implements Serializable
*******************************************************************************/
public Integer getValueInteger(String fieldName)
{
return ((Integer) values.get(fieldName));
return (ValueUtils.getValueAsInteger(values.get(fieldName)));
}
/*******************************************************************************
**
*******************************************************************************/
public BigDecimal getValueBigDecimal(String fieldName)
{
return (ValueUtils.getValueAsBigDecimal(values.get(fieldName)));
}
/*******************************************************************************
**
*******************************************************************************/
public Boolean getValueBoolean(String fieldName)
{
return (ValueUtils.getValueAsBoolean(values.get(fieldName)));
}
@ -249,6 +272,7 @@ public class QRecord implements Serializable
*******************************************************************************/
public LocalDate getValueDate(String fieldName)
{
// todo - rewrite using ValueUtils...
return ((LocalDate) values.get(fieldName));
}
@ -309,6 +333,7 @@ public class QRecord implements Serializable
}
/*******************************************************************************
** Get one backendDetail from this record as a String
**

View File

@ -35,7 +35,7 @@ public class QFieldMetaData
private String label;
private String backendName;
private QFieldType type;
private boolean isRequired;
private boolean isRequired = false;
private Serializable defaultValue;
private String possibleValueSourceName;

View File

@ -170,7 +170,7 @@ public class QBackendStepMetaData extends QStepMetaData
/*******************************************************************************
** Get a list of all of the input fields used by this function
*******************************************************************************/
@JsonIgnore
@JsonIgnore // because this is a computed property - we don't want it in our json.
@Override
public List<QFieldMetaData> getInputFields()
{
@ -187,7 +187,7 @@ public class QBackendStepMetaData extends QStepMetaData
/*******************************************************************************
** Get a list of all of the output fields used by this function
*******************************************************************************/
@JsonIgnore
@JsonIgnore // because this is a computed property - we don't want it in our json.
@Override
public List<QFieldMetaData> getOutputFields()
{

View File

@ -212,7 +212,7 @@ public class QProcessMetaData
/*******************************************************************************
** Wrapper to getStep, that internally casts t0 BackendStepMetaData
** Wrapper to getStep, that internally casts to BackendStepMetaData
*******************************************************************************/
public QBackendStepMetaData getBackendStep(String name)
{

View File

@ -33,9 +33,11 @@ import com.kingsrook.qqq.backend.core.model.metadata.serialization.QStepMetaData
/*******************************************************************************
** Meta-Data to define a step in a process in a QQQ instance.
**
** Specifically, this is a base-class for QFrontendStepMetaData and QBackendStepMetaData.
**
*******************************************************************************/
@JsonDeserialize(using = QStepMetaDataDeserializer.class)
public class QStepMetaData
public abstract class QStepMetaData
{
private String name;
private String label;

View File

@ -29,6 +29,7 @@ import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
@ -47,7 +48,7 @@ public class QStepMetaDataDeserializer extends JsonDeserializer<QStepMetaData>
Class<? extends QStepMetaData> targetClass = switch(stepType)
{
case "backend" -> QBackendStepMetaData.class;
case "frontend" -> QBackendStepMetaData.class;
case "frontend" -> QFrontendStepMetaData.class;
default -> throw new IllegalArgumentException("Unsupported StepType " + stepType + " for deserialization");
};
return (DeserializerUtils.reflectivelyDeserialize(targetClass, treeNode));

View File

@ -23,7 +23,10 @@ package com.kingsrook.qqq.backend.core.state;
/*******************************************************************************
** Possible types of states to be stored.
**
** Idea: could these have the corresponding Classes that they support?
** ala PROCESS_STATUS(ProcessStatus.class) ?
*******************************************************************************/
public enum StateType
{

View File

@ -35,7 +35,7 @@ public class ExceptionUtils
/*******************************************************************************
** Find a specific exception class in an exception's caused-by chain. Returns
** null if not found. Be aware, uses class.isInstaance (so sub-classes get found).
** null if not found. Be aware, uses class.isInstance (so sub-classes get found).
**
*******************************************************************************/
public static <T extends Throwable> T findClassInRootChain(Throwable e, Class<T> targetClass)

View File

@ -33,7 +33,59 @@ public class ValueUtils
{
/*******************************************************************************
**
** Type-safely make a String from any Object.
*******************************************************************************/
public static String getValueAsString(Object value)
{
if(value == null)
{
return (null);
}
else if(value instanceof String s)
{
return (s);
}
else
{
return (String.valueOf(value));
}
}
/*******************************************************************************
** Returns null for null input;
** Returns the input object for Boolean-typed inputs.
** Then, follows Boolean.parseBoolean, returning true iff value is a case-insensitive
** match for "true", for String.valueOf the input
*******************************************************************************/
public static Boolean getValueAsBoolean(Object value)
{
if(value == null)
{
return (null);
}
else if(value instanceof Boolean b)
{
return (b);
}
else if(value instanceof String s)
{
return (Boolean.parseBoolean(s));
}
else
{
return (Boolean.parseBoolean(String.valueOf(value)));
}
}
/*******************************************************************************
** Type-safely make an Integer from any Object.
** null and empty-string inputs return null.
** We try to strip away commas and decimals (as long as they are exactly equal to the int value)
** We may throw if the input can't be converted to an integer.
*******************************************************************************/
public static Integer getValueAsInteger(Object value) throws QValueException
{
@ -53,7 +105,7 @@ public class ValueUtils
}
else if(value instanceof Float f)
{
if (f.intValue() != f)
if(f.intValue() != f)
{
throw (new QValueException(f + " does not have an exact integer representation."));
}
@ -61,7 +113,7 @@ public class ValueUtils
}
else if(value instanceof Double d)
{
if (d.intValue() != d)
if(d.intValue() != d)
{
throw (new QValueException(d + " does not have an exact integer representation."));
}
@ -122,4 +174,78 @@ public class ValueUtils
}
}
/*******************************************************************************
** Type-safely make a BigDecimal from any Object.
** null and empty-string inputs return null.
** We may throw if the input can't be converted to a BigDecimal
*******************************************************************************/
public static BigDecimal getValueAsBigDecimal(Object value) throws QValueException
{
try
{
if(value == null)
{
return (null);
}
else if(value instanceof BigDecimal bd)
{
return (bd);
}
else if(value instanceof Integer i)
{
return new BigDecimal(i);
}
else if(value instanceof Long l)
{
return new BigDecimal(l);
}
else if(value instanceof Float f)
{
return new BigDecimal(f);
}
else if(value instanceof Double d)
{
return new BigDecimal(d);
}
else if(value instanceof String s)
{
if(!StringUtils.hasContent(s))
{
return (null);
}
try
{
return (new BigDecimal(s));
}
catch(NumberFormatException nfe)
{
if(s.contains(","))
{
String sWithoutCommas = s.replaceAll(",", "");
try
{
return (getValueAsBigDecimal(sWithoutCommas));
}
catch(Exception ignore)
{
throw (nfe);
}
}
throw (nfe);
}
}
else
{
throw (new IllegalArgumentException("Unsupported class " + value.getClass().getName() + " for converting to BigDecimal."));
}
}
catch(Exception e)
{
throw (new QValueException("Value [" + value + "] could not be converted to an BigDecimal.", e));
}
}
}

View File

@ -23,7 +23,7 @@ package com.kingsrook.qqq.backend.core.actions;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
import com.kingsrook.qqq.backend.core.model.actions.metadata.process.ProcessMetaDataRequest;
import com.kingsrook.qqq.backend.core.model.actions.metadata.process.ProcessMetaDataResult;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
@ -67,7 +67,7 @@ class ProcessMetaDataActionTest
@Test
public void test_notFound()
{
assertThrows(QUserFacingException.class, () -> {
assertThrows(QNotFoundException.class, () -> {
ProcessMetaDataRequest request = new ProcessMetaDataRequest(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setProcessName("willNotBeFound");

View File

@ -27,21 +27,27 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.interfaces.mock.MockBackendStep;
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessState;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessResult;
import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
import com.kingsrook.qqq.backend.core.state.StateType;
import com.kingsrook.qqq.backend.core.state.UUIDAndTypeStateKey;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
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.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@ -99,6 +105,7 @@ public class RunProcessTest
Optional<String> breakingAtStep0 = result0.getProcessState().getNextStepName();
assertTrue(breakingAtStep0.isPresent());
assertInstanceOf(QFrontendStepMetaData.class, instance.getProcessStep(processName, breakingAtStep0.get()));
assertNull(result0.getValues().get(MockBackendStep.FIELD_MOCK_VALUE));
//////////////////////////////////////////////
// now run again, proceeding from this step //
@ -113,6 +120,7 @@ public class RunProcessTest
assertTrue(breakingAtStep1.isPresent());
assertInstanceOf(QFrontendStepMetaData.class, instance.getProcessStep(processName, breakingAtStep1.get()));
assertNotEquals(breakingAtStep0.get(), breakingAtStep1.get());
assertEquals(MockBackendStep.MOCK_VALUE, result1.getValues().get(MockBackendStep.FIELD_MOCK_VALUE));
}
@ -136,6 +144,7 @@ public class RunProcessTest
RunProcessResult runProcessResult = new RunProcessAction().execute(request);
assertTrue(runProcessResult.getException().isEmpty());
assertEquals(MockBackendStep.MOCK_VALUE, runProcessResult.getValues().get(MockBackendStep.FIELD_MOCK_VALUE));
assertTrue(runProcessResult.getProcessState().getNextStepName().isEmpty());
}
@ -169,6 +178,79 @@ public class RunProcessTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testPrimeProcessRequestNewProcess() throws QException
{
////////////////////////////////////////////////////////////////////////////////
// this is a flow where it's a new process - so, we should create a new state //
////////////////////////////////////////////////////////////////////////////////
RunProcessRequest runProcessRequest = new RunProcessRequest();
UUIDAndTypeStateKey stateKey = new UUIDAndTypeStateKey(UUID.randomUUID(), StateType.PROCESS_STATUS);
ProcessState processState = new RunProcessAction().primeProcessState(runProcessRequest, stateKey);
assertNotNull(processState);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testPrimeProcessRequestAttemptToContinueButStateNotFound() throws QException
{
////////////////////////////////////////////////////////////////////////////////////////////////
// this is a flow where it's a continue, but we don't have a state stored, so it should throw //
////////////////////////////////////////////////////////////////////////////////////////////////
RunProcessRequest runProcessRequest = new RunProcessRequest();
runProcessRequest.setStartAfterStep("setupStep");
UUIDAndTypeStateKey stateKey = new UUIDAndTypeStateKey(UUID.randomUUID(), StateType.PROCESS_STATUS);
assertThrows(QException.class, () ->
{
new RunProcessAction().primeProcessState(runProcessRequest, stateKey);
});
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testPrimeProcessRequestAttemptToContinueAndStateIsFound() throws QException
{
////////////////////////////////////////////////////////////////////////////////////////////////
// this is a flow where it's a continue, but we don't have a state stored, so it should throw //
////////////////////////////////////////////////////////////////////////////////////////////////
RunProcessRequest runProcessRequest = new RunProcessRequest();
runProcessRequest.setStartAfterStep("setupStep");
runProcessRequest.addValue("foo", "bar");
runProcessRequest.addValue("alpha", "beta");
UUIDAndTypeStateKey stateKey = new UUIDAndTypeStateKey(UUID.randomUUID(), StateType.PROCESS_STATUS);
////////////////////////////////////////////////
// simulate the state being previously stored //
////////////////////////////////////////////////
ProcessState oldProcessState = new ProcessState();
oldProcessState.getValues().put("key", "myValue");
oldProcessState.getValues().put("foo", "fubu");
RunProcessAction.getStateProvider().put(stateKey, oldProcessState);
ProcessState primedProcessState = new RunProcessAction().primeProcessState(runProcessRequest, stateKey);
assertEquals("myValue", primedProcessState.getValues().get("key"));
/////////////////////////////////////////////////////////////////////////////////////////////
// make sure values that were in the original request trump values that had been in state. //
/////////////////////////////////////////////////////////////////////////////////////////////
assertEquals("bar", primedProcessState.getValues().get("foo"));
assertEquals("beta", primedProcessState.getValues().get("alpha"));
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -29,6 +29,7 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
/*******************************************************************************
@ -65,7 +66,7 @@ class AsyncJobManagerTest
assertThrows(JobGoingAsyncException.class, () ->
{
AsyncJobManager asyncJobManager = new AsyncJobManager();
Integer answer = asyncJobManager.startJob(1, TimeUnit.MICROSECONDS, (callback) ->
asyncJobManager.startJob(1, TimeUnit.MICROSECONDS, (callback) ->
{
Thread.sleep(1_000);
return (ANSWER);
@ -108,6 +109,7 @@ class AsyncJobManagerTest
Thread.sleep(50);
throw (new IllegalArgumentException(message));
});
fail("We should catch a JobGoingAsyncException");
}
catch(JobGoingAsyncException jgae)
{
@ -140,6 +142,7 @@ class AsyncJobManagerTest
callback.updateStatus(postMessage, 1, 1);
return (ANSWER);
});
fail("We should catch a JobGoingAsyncException");
}
catch(JobGoingAsyncException jgae)
{
@ -152,6 +155,7 @@ class AsyncJobManagerTest
assertEquals(1, jobStatus.get().getTotal());
Thread.sleep(200);
jobStatus = asyncJobManager.getJobStatus(jgae.getJobUUID());
assertEquals(AsyncJobState.COMPLETE, jobStatus.get().getState());
assertEquals(postMessage, jobStatus.get().getMessage());
assertEquals(1, jobStatus.get().getCurrent());

View File

@ -56,6 +56,7 @@ class BasicETLProcessTest
RunProcessResult result = new RunProcessAction().execute(request);
assertNotNull(result);
assertTrue(result.getRecords().stream().allMatch(r -> r.getValues().containsKey("id")), "records should have an id, set by the process");
assertTrue(result.getException().isEmpty());
}
@ -82,6 +83,7 @@ class BasicETLProcessTest
RunProcessResult result = new RunProcessAction().execute(request);
assertNotNull(result);
assertTrue(result.getRecords().stream().allMatch(r -> r.getValues().containsKey("id")), "records should have an id, set by the process");
assertTrue(result.getException().isEmpty());
}
}

View File

@ -415,6 +415,9 @@ class CollectionUtilsTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_safelyGetPage()
{
@ -494,4 +497,5 @@ class CollectionUtilsTest
assertEquals(4, pageCount);
assertEquals(list, accumulator);
}
}

View File

@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.utils;
import java.math.BigDecimal;
import java.math.MathContext;
import com.kingsrook.qqq.backend.core.exceptions.QValueException;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@ -34,6 +35,51 @@ import static org.junit.jupiter.api.Assertions.*;
class ValueUtilsTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetValueAsString() throws QValueException
{
assertNull(ValueUtils.getValueAsString(null));
assertEquals("", ValueUtils.getValueAsString(""));
assertEquals(" ", ValueUtils.getValueAsString(" "));
assertEquals("A", ValueUtils.getValueAsString("A"));
assertEquals("1", ValueUtils.getValueAsString("1"));
assertEquals("1", ValueUtils.getValueAsString(1));
assertEquals("1", ValueUtils.getValueAsString(1));
assertEquals("1.10", ValueUtils.getValueAsString(new BigDecimal("1.10")));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetValueAsBoolean() throws QValueException
{
assertNull(ValueUtils.getValueAsBoolean(null));
assertTrue(ValueUtils.getValueAsBoolean("true"));
assertTrue(ValueUtils.getValueAsBoolean("True"));
assertTrue(ValueUtils.getValueAsBoolean("TRUE"));
assertFalse(ValueUtils.getValueAsBoolean("false"));
assertFalse(ValueUtils.getValueAsBoolean("yes"));
assertFalse(ValueUtils.getValueAsBoolean("t"));
assertFalse(ValueUtils.getValueAsBoolean(new Object()));
assertFalse(ValueUtils.getValueAsBoolean(1));
assertTrue(ValueUtils.getValueAsBoolean(new Object()
{
@Override
public String toString()
{
return ("true");
}
}));
}
/*******************************************************************************
**
*******************************************************************************/
@ -63,4 +109,35 @@ class ValueUtilsTest
assertThrows(QValueException.class, () -> ValueUtils.getValueAsInteger(1.1D));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetValueAsBigDecimal() throws QValueException
{
assertNull(ValueUtils.getValueAsBigDecimal(null));
assertNull(ValueUtils.getValueAsBigDecimal(""));
assertNull(ValueUtils.getValueAsBigDecimal(" "));
assertEquals(new BigDecimal("1"), ValueUtils.getValueAsBigDecimal(1));
assertEquals(new BigDecimal("1"), ValueUtils.getValueAsBigDecimal("1"));
assertEquals(new BigDecimal("1000"), ValueUtils.getValueAsBigDecimal("1,000"));
assertEquals(new BigDecimal("1000000"), ValueUtils.getValueAsBigDecimal("1,000,000"));
assertEquals(new BigDecimal("1"), ValueUtils.getValueAsBigDecimal(new BigDecimal(1)));
assertEquals(new BigDecimal("1.00"), ValueUtils.getValueAsBigDecimal(new BigDecimal("1.00")));
assertEquals(new BigDecimal("-1.00"), ValueUtils.getValueAsBigDecimal("-1.00"));
assertEquals(new BigDecimal("1000.00"), ValueUtils.getValueAsBigDecimal("1,000.00"));
assertEquals(new BigDecimal("1000"), ValueUtils.getValueAsBigDecimal(1000L));
assertEquals(new BigDecimal("1"), ValueUtils.getValueAsBigDecimal(1.0F));
assertEquals(new BigDecimal("1"), ValueUtils.getValueAsBigDecimal(1.0D));
assertEquals(new BigDecimal("1000000000000"), ValueUtils.getValueAsBigDecimal(1_000_000_000_000L));
assertEquals(0, new BigDecimal("1.1").compareTo(ValueUtils.getValueAsBigDecimal(1.1F).round(MathContext.DECIMAL32)));
assertEquals(0, new BigDecimal("1.1").compareTo(ValueUtils.getValueAsBigDecimal(1.1D).round(MathContext.DECIMAL64)));
assertThrows(QValueException.class, () -> ValueUtils.getValueAsBigDecimal("a"));
assertThrows(QValueException.class, () -> ValueUtils.getValueAsBigDecimal("a,b"));
assertThrows(QValueException.class, () -> ValueUtils.getValueAsBigDecimal(new Object()));
}
}