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 -> case SKIP ->
{ {
LOG.info("Skipping frontend step [" + step.getName() + "] in process [" + process.getName() + "] (as requested by caller)"); 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... // // 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); Optional<ProcessState> optionalProcessState = loadState(stateKey);
if(optionalProcessState.isEmpty()) 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 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.setStepName(backendStep.getName());
runBackendStepRequest.setSession(runProcessRequest.getSession()); runBackendStepRequest.setSession(runProcessRequest.getSession());
runBackendStepRequest.setCallback(runProcessRequest.getCallback()); runBackendStepRequest.setCallback(runProcessRequest.getCallback());
runBackendStepRequest.setAsyncJobCallback(runProcessRequest.getAsyncJobCallback());
RunBackendStepResult lastFunctionResult = new RunBackendStepAction().execute(runBackendStepRequest); RunBackendStepResult lastFunctionResult = new RunBackendStepAction().execute(runBackendStepRequest);
storeState(stateKey, lastFunctionResult.getProcessState()); 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 ** 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); 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 ** Load an instance of the appropriate state provider
** **
*******************************************************************************/ *******************************************************************************/
protected static StateProviderInterface getStateProvider() static StateProviderInterface getStateProvider()
{ {
// TODO - read this from somewhere in meta data eh? // TODO - read this from somewhere in meta data eh?
return InMemoryStateProvider.getInstance(); 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 ** 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 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.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Map; 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.callbacks.QProcessCallback;
import com.kingsrook.qqq.backend.core.model.actions.AbstractQRequest; import com.kingsrook.qqq.backend.core.model.actions.AbstractQRequest;
import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.data.QRecord;
@ -43,6 +46,7 @@ public class RunBackendStepRequest extends AbstractQRequest
private String processName; private String processName;
private String stepName; private String stepName;
private QProcessCallback callback; private QProcessCallback callback;
private AsyncJobCallback asyncJobCallback;
@ -312,4 +316,31 @@ public class RunBackendStepRequest extends AbstractQRequest
{ {
return processState; 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.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; 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) 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) 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) public LocalDate getValueDate(String fieldName)
{ {
// todo - rewrite using ValueUtils...
return ((LocalDate) values.get(fieldName)); return ((LocalDate) values.get(fieldName));
} }
@ -309,6 +333,7 @@ public class QRecord implements Serializable
} }
/******************************************************************************* /*******************************************************************************
** Get one backendDetail from this record as a String ** Get one backendDetail from this record as a String
** **

View File

@ -35,7 +35,7 @@ public class QFieldMetaData
private String label; private String label;
private String backendName; private String backendName;
private QFieldType type; private QFieldType type;
private boolean isRequired; private boolean isRequired = false;
private Serializable defaultValue; private Serializable defaultValue;
private String possibleValueSourceName; 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 ** 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 @Override
public List<QFieldMetaData> getInputFields() 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 ** 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 @Override
public List<QFieldMetaData> getOutputFields() 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) 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. ** 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) @JsonDeserialize(using = QStepMetaDataDeserializer.class)
public class QStepMetaData public abstract class QStepMetaData
{ {
private String name; private String name;
private String label; 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.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer; 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.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData; 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) Class<? extends QStepMetaData> targetClass = switch(stepType)
{ {
case "backend" -> QBackendStepMetaData.class; case "backend" -> QBackendStepMetaData.class;
case "frontend" -> QBackendStepMetaData.class; case "frontend" -> QFrontendStepMetaData.class;
default -> throw new IllegalArgumentException("Unsupported StepType " + stepType + " for deserialization"); default -> throw new IllegalArgumentException("Unsupported StepType " + stepType + " for deserialization");
}; };
return (DeserializerUtils.reflectivelyDeserialize(targetClass, treeNode)); 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 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 ** 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) 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 public static Integer getValueAsInteger(Object value) throws QValueException
{ {
@ -53,7 +105,7 @@ public class ValueUtils
} }
else if(value instanceof Float f) 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.")); throw (new QValueException(f + " does not have an exact integer representation."));
} }
@ -61,7 +113,7 @@ public class ValueUtils
} }
else if(value instanceof Double d) 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.")); 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.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.ProcessMetaDataRequest;
import com.kingsrook.qqq.backend.core.model.actions.metadata.process.ProcessMetaDataResult; import com.kingsrook.qqq.backend.core.model.actions.metadata.process.ProcessMetaDataResult;
import com.kingsrook.qqq.backend.core.utils.TestUtils; import com.kingsrook.qqq.backend.core.utils.TestUtils;
@ -67,7 +67,7 @@ class ProcessMetaDataActionTest
@Test @Test
public void test_notFound() public void test_notFound()
{ {
assertThrows(QUserFacingException.class, () -> { assertThrows(QNotFoundException.class, () -> {
ProcessMetaDataRequest request = new ProcessMetaDataRequest(TestUtils.defineInstance()); ProcessMetaDataRequest request = new ProcessMetaDataRequest(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession()); request.setSession(TestUtils.getMockSession());
request.setProcessName("willNotBeFound"); request.setProcessName("willNotBeFound");

View File

@ -27,21 +27,27 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback; import com.kingsrook.qqq.backend.core.callbacks.QProcessCallback;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.interfaces.mock.MockBackendStep; 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.RunProcessRequest;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessResult; 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.actions.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; 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.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData; 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 com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; 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.assertTrue;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
@ -99,6 +105,7 @@ public class RunProcessTest
Optional<String> breakingAtStep0 = result0.getProcessState().getNextStepName(); Optional<String> breakingAtStep0 = result0.getProcessState().getNextStepName();
assertTrue(breakingAtStep0.isPresent()); assertTrue(breakingAtStep0.isPresent());
assertInstanceOf(QFrontendStepMetaData.class, instance.getProcessStep(processName, breakingAtStep0.get())); assertInstanceOf(QFrontendStepMetaData.class, instance.getProcessStep(processName, breakingAtStep0.get()));
assertNull(result0.getValues().get(MockBackendStep.FIELD_MOCK_VALUE));
////////////////////////////////////////////// //////////////////////////////////////////////
// now run again, proceeding from this step // // now run again, proceeding from this step //
@ -113,6 +120,7 @@ public class RunProcessTest
assertTrue(breakingAtStep1.isPresent()); assertTrue(breakingAtStep1.isPresent());
assertInstanceOf(QFrontendStepMetaData.class, instance.getProcessStep(processName, breakingAtStep1.get())); assertInstanceOf(QFrontendStepMetaData.class, instance.getProcessStep(processName, breakingAtStep1.get()));
assertNotEquals(breakingAtStep0.get(), 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); RunProcessResult runProcessResult = new RunProcessAction().execute(request);
assertTrue(runProcessResult.getException().isEmpty()); assertTrue(runProcessResult.getException().isEmpty());
assertEquals(MockBackendStep.MOCK_VALUE, runProcessResult.getValues().get(MockBackendStep.FIELD_MOCK_VALUE)); 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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows; 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, () -> assertThrows(JobGoingAsyncException.class, () ->
{ {
AsyncJobManager asyncJobManager = new AsyncJobManager(); AsyncJobManager asyncJobManager = new AsyncJobManager();
Integer answer = asyncJobManager.startJob(1, TimeUnit.MICROSECONDS, (callback) -> asyncJobManager.startJob(1, TimeUnit.MICROSECONDS, (callback) ->
{ {
Thread.sleep(1_000); Thread.sleep(1_000);
return (ANSWER); return (ANSWER);
@ -108,6 +109,7 @@ class AsyncJobManagerTest
Thread.sleep(50); Thread.sleep(50);
throw (new IllegalArgumentException(message)); throw (new IllegalArgumentException(message));
}); });
fail("We should catch a JobGoingAsyncException");
} }
catch(JobGoingAsyncException jgae) catch(JobGoingAsyncException jgae)
{ {
@ -140,6 +142,7 @@ class AsyncJobManagerTest
callback.updateStatus(postMessage, 1, 1); callback.updateStatus(postMessage, 1, 1);
return (ANSWER); return (ANSWER);
}); });
fail("We should catch a JobGoingAsyncException");
} }
catch(JobGoingAsyncException jgae) catch(JobGoingAsyncException jgae)
{ {
@ -152,6 +155,7 @@ class AsyncJobManagerTest
assertEquals(1, jobStatus.get().getTotal()); assertEquals(1, jobStatus.get().getTotal());
Thread.sleep(200); Thread.sleep(200);
jobStatus = asyncJobManager.getJobStatus(jgae.getJobUUID());
assertEquals(AsyncJobState.COMPLETE, jobStatus.get().getState()); assertEquals(AsyncJobState.COMPLETE, jobStatus.get().getState());
assertEquals(postMessage, jobStatus.get().getMessage()); assertEquals(postMessage, jobStatus.get().getMessage());
assertEquals(1, jobStatus.get().getCurrent()); assertEquals(1, jobStatus.get().getCurrent());

View File

@ -56,6 +56,7 @@ class BasicETLProcessTest
RunProcessResult result = new RunProcessAction().execute(request); RunProcessResult result = new RunProcessAction().execute(request);
assertNotNull(result); assertNotNull(result);
assertTrue(result.getRecords().stream().allMatch(r -> r.getValues().containsKey("id")), "records should have an id, set by the process"); 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); RunProcessResult result = new RunProcessAction().execute(request);
assertNotNull(result); assertNotNull(result);
assertTrue(result.getRecords().stream().allMatch(r -> r.getValues().containsKey("id")), "records should have an id, set by the process"); 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 @Test
void test_safelyGetPage() void test_safelyGetPage()
{ {
@ -494,4 +497,5 @@ class CollectionUtilsTest
assertEquals(4, pageCount); assertEquals(4, pageCount);
assertEquals(list, accumulator); assertEquals(list, accumulator);
} }
} }

View File

@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.utils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext;
import com.kingsrook.qqq.backend.core.exceptions.QValueException; import com.kingsrook.qqq.backend.core.exceptions.QValueException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@ -34,6 +35,51 @@ import static org.junit.jupiter.api.Assertions.*;
class ValueUtilsTest 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)); 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()));
}
} }