Moving qqq-backend-core into its own subdir

This commit is contained in:
2022-07-28 12:02:54 -05:00
parent 1c3fafbf73
commit adfe472b70
217 changed files with 0 additions and 0 deletions

View File

@ -0,0 +1,166 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.async;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import com.kingsrook.qqq.backend.core.exceptions.QException;
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;
/*******************************************************************************
** Unit test for AsyncJobManager
*******************************************************************************/
class AsyncJobManagerTest
{
public static final int ANSWER = 42;
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testCompletesInTime() throws JobGoingAsyncException, QException
{
AsyncJobManager asyncJobManager = new AsyncJobManager();
Integer answer = asyncJobManager.startJob(5, TimeUnit.SECONDS, (callback) ->
{
return (ANSWER);
});
assertEquals(ANSWER, answer);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testJobGoesAsync()
{
assertThrows(JobGoingAsyncException.class, () ->
{
AsyncJobManager asyncJobManager = new AsyncJobManager();
asyncJobManager.startJob(1, TimeUnit.MICROSECONDS, (callback) ->
{
Thread.sleep(1_000);
return (ANSWER);
});
});
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testJobThatThrowsBeforeTimeout()
{
assertThrows(QException.class, () ->
{
AsyncJobManager asyncJobManager = new AsyncJobManager();
asyncJobManager.startJob(1, TimeUnit.SECONDS, (callback) ->
{
throw (new IllegalArgumentException("I must throw."));
});
});
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testJobThatThrowsAfterTimeout() throws QException, InterruptedException
{
String message = "I must throw.";
AsyncJobManager asyncJobManager = new AsyncJobManager();
try
{
asyncJobManager.startJob(1, TimeUnit.MILLISECONDS, (callback) ->
{
Thread.sleep(50);
throw (new IllegalArgumentException(message));
});
fail("We should catch a JobGoingAsyncException");
}
catch(JobGoingAsyncException jgae)
{
Thread.sleep(100);
Optional<AsyncJobStatus> jobStatus = asyncJobManager.getJobStatus(jgae.getJobUUID());
assertEquals(AsyncJobState.ERROR, jobStatus.get().getState());
assertNotNull(jobStatus.get().getCaughtException());
assertEquals(message, jobStatus.get().getCaughtException().getMessage());
}
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testGettingStatusOfAsyncJob() throws InterruptedException, QException
{
AsyncJobManager asyncJobManager = new AsyncJobManager();
String preMessage = "Going to sleep";
String postMessage = "Waking up";
try
{
asyncJobManager.startJob(50, TimeUnit.MILLISECONDS, (callback) ->
{
callback.updateStatus(preMessage);
callback.updateStatus(0, 1);
Thread.sleep(100);
callback.updateStatus(postMessage, 1, 1);
return (ANSWER);
});
fail("We should catch a JobGoingAsyncException");
}
catch(JobGoingAsyncException jgae)
{
assertNotNull(jgae.getJobUUID());
Optional<AsyncJobStatus> jobStatus = asyncJobManager.getJobStatus(jgae.getJobUUID());
assertEquals(AsyncJobState.RUNNING, jobStatus.get().getState());
assertEquals(preMessage, jobStatus.get().getMessage());
assertEquals(0, jobStatus.get().getCurrent());
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());
assertEquals(1, jobStatus.get().getTotal());
}
}
}

View File

@ -0,0 +1,55 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.metadata;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/*******************************************************************************
** Unit test for MetaDataAction
**
*******************************************************************************/
class MetaDataActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test() throws QException
{
MetaDataInput request = new MetaDataInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
MetaDataOutput result = new MetaDataAction().execute(request);
assertNotNull(result);
assertNotNull(result.getTables());
assertNotNull(result.getTables().get("person"));
assertEquals("person", result.getTables().get("person").getName());
assertEquals("Person", result.getTables().get("person").getLabel());
}
}

View File

@ -0,0 +1,78 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.metadata;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
import com.kingsrook.qqq.backend.core.model.actions.metadata.ProcessMetaDataInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.ProcessMetaDataOutput;
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.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/*******************************************************************************
** Unit test for ProcessMetaDataAction
**
*******************************************************************************/
class ProcessMetaDataActionTest
{
/*******************************************************************************
** Test basic success case.
**
*******************************************************************************/
@Test
public void test() throws QException
{
ProcessMetaDataInput request = new ProcessMetaDataInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setProcessName(TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE);
ProcessMetaDataOutput result = new ProcessMetaDataAction().execute(request);
assertNotNull(result);
assertNotNull(result.getProcess());
assertEquals("greetInteractive", result.getProcess().getName());
assertEquals("Greet Interactive", result.getProcess().getLabel());
assertEquals(2, result.getProcess().getFrontendSteps().size());
}
/*******************************************************************************
** Test exception is thrown for the "not-found" case.
**
*******************************************************************************/
@Test
public void test_notFound()
{
assertThrows(QNotFoundException.class, () -> {
ProcessMetaDataInput request = new ProcessMetaDataInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setProcessName("willNotBeFound");
new ProcessMetaDataAction().execute(request);
});
}
}

View File

@ -0,0 +1,77 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.metadata;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.model.actions.metadata.TableMetaDataInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.TableMetaDataOutput;
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.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/*******************************************************************************
** Unit test for TableMetaDataAction
**
*******************************************************************************/
class TableMetaDataActionTest
{
/*******************************************************************************
** Test basic success case.
**
*******************************************************************************/
@Test
public void test() throws QException
{
TableMetaDataInput request = new TableMetaDataInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setTableName("person");
TableMetaDataOutput result = new TableMetaDataAction().execute(request);
assertNotNull(result);
assertNotNull(result.getTable());
assertEquals("person", result.getTable().getName());
assertEquals("Person", result.getTable().getLabel());
}
/*******************************************************************************
** Test exeption is thrown for the "not-found" case.
**
*******************************************************************************/
@Test
public void test_notFound()
{
assertThrows(QUserFacingException.class, () -> {
TableMetaDataInput request = new TableMetaDataInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setTableName("willNotBeFound");
new TableMetaDataAction().execute(request);
});
}
}

View File

@ -0,0 +1,118 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.processes;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
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.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
**
*******************************************************************************/
public class RunBackendStepActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test() throws QException
{
TestCallback callback = new TestCallback();
RunBackendStepInput request = new RunBackendStepInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setProcessName("greet");
request.setStepName("prepare");
request.setCallback(callback);
RunBackendStepOutput result = new RunBackendStepAction().execute(request);
assertNotNull(result);
assertTrue(result.getRecords().stream().allMatch(r -> r.getValues().containsKey("mockValue")), "records should have a mock value");
assertTrue(result.getValues().containsKey("mockValue"), "result object should have a mock value");
assertEquals("ABC", result.getValues().get("greetingPrefix"), "result object should have value from our callback");
assertTrue(callback.wasCalledForQueryFilter, "callback was used for query filter");
assertTrue(callback.wasCalledForFieldValues, "callback was used for field values");
}
/*******************************************************************************
**
*******************************************************************************/
private static class TestCallback implements QProcessCallback
{
private boolean wasCalledForQueryFilter = false;
private boolean wasCalledForFieldValues = false;
/*******************************************************************************
**
*******************************************************************************/
@Override
public QQueryFilter getQueryFilter()
{
wasCalledForQueryFilter = true;
return (new QQueryFilter());
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public Map<String, Serializable> getFieldValues(List<QFieldMetaData> fields)
{
wasCalledForFieldValues = true;
Map<String, Serializable> rs = new HashMap<>();
for(QFieldMetaData field : fields)
{
rs.put(field.getName(), switch(field.getType())
{
case STRING -> "ABC";
case INTEGER -> 42;
case DECIMAL -> new BigDecimal("47");
case DATE, DATE_TIME -> null;
case TEXT -> """
ABC
XYZ""";
case HTML -> "<b>Oh my</b>";
case PASSWORD -> "myPa**word";
case BLOB -> new byte[] { 1, 2, 3, 4 };
});
}
return (rs);
}
}
}

View File

@ -0,0 +1,290 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.processes;
import java.io.Serializable;
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.exceptions.QException;
import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackendStep;
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessState;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.metadata.fields.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;
/*******************************************************************************
**
*******************************************************************************/
public class RunProcessTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test() throws QException
{
TestCallback callback = new TestCallback();
RunProcessInput request = new RunProcessInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setProcessName("addToPeoplesAge");
request.setCallback(callback);
RunProcessOutput result = new RunProcessAction().execute(request);
assertNotNull(result);
assertTrue(result.getRecords().stream().allMatch(r -> r.getValues().containsKey("age")), "records should have a value set by the process");
assertTrue(result.getValues().containsKey("maxAge"), "process result object should have a value set by the first function in the process");
assertTrue(result.getValues().containsKey("totalYearsAdded"), "process result object should have a value set by the second function in the process");
assertTrue(callback.wasCalledForQueryFilter, "callback was used for query filter");
assertTrue(callback.wasCalledForFieldValues, "callback was used for field values");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testBreakOnFrontendSteps() throws QException
{
TestCallback callback = new TestCallback();
QInstance instance = TestUtils.defineInstance();
RunProcessInput request = new RunProcessInput(instance);
String processName = TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE;
request.setSession(TestUtils.getMockSession());
request.setProcessName(processName);
request.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
request.setCallback(callback);
RunProcessOutput result0 = new RunProcessAction().execute(request);
assertNotNull(result0);
///////////////////////////////////////////////////////////////
// make sure we were told that we broke at a (frontend) step //
///////////////////////////////////////////////////////////////
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 //
//////////////////////////////////////////////
request.setStartAfterStep(breakingAtStep0.get());
RunProcessOutput result1 = new RunProcessAction().execute(request);
////////////////////////////////////////////////////////////////////
// make sure we were told that we broke at the next frontend step //
////////////////////////////////////////////////////////////////////
Optional<String> breakingAtStep1 = result1.getProcessState().getNextStepName();
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));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testSkipFrontendSteps() throws QException
{
TestCallback callback = new TestCallback();
QInstance instance = TestUtils.defineInstance();
RunProcessInput request = new RunProcessInput(instance);
String processName = TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE;
request.setSession(TestUtils.getMockSession());
request.setProcessName(processName);
request.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
request.setCallback(callback);
RunProcessOutput runProcessOutput = new RunProcessAction().execute(request);
assertTrue(runProcessOutput.getException().isEmpty());
assertEquals(MockBackendStep.MOCK_VALUE, runProcessOutput.getValues().get(MockBackendStep.FIELD_MOCK_VALUE));
assertTrue(runProcessOutput.getProcessState().getNextStepName().isEmpty());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testFailOnFrontendSteps()
{
TestCallback callback = new TestCallback();
QInstance instance = TestUtils.defineInstance();
RunProcessInput request = new RunProcessInput(instance);
String processName = TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE;
request.setSession(TestUtils.getMockSession());
request.setProcessName(processName);
request.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.FAIL);
request.setCallback(callback);
try
{
new RunProcessAction().execute(request);
fail("This should have thrown...");
}
catch(Exception e)
{
assertTrue(e.getMessage().contains("fail on frontend steps"));
}
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testPrimeProcessRequestNewProcess() throws QException
{
////////////////////////////////////////////////////////////////////////////////
// this is a flow where it's a new process - so, we should create a new state //
////////////////////////////////////////////////////////////////////////////////
RunProcessInput runProcessInput = new RunProcessInput();
UUIDAndTypeStateKey stateKey = new UUIDAndTypeStateKey(UUID.randomUUID(), StateType.PROCESS_STATUS);
ProcessState processState = new RunProcessAction().primeProcessState(runProcessInput, 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 //
////////////////////////////////////////////////////////////////////////////////////////////////
RunProcessInput runProcessInput = new RunProcessInput();
runProcessInput.setStartAfterStep("setupStep");
UUIDAndTypeStateKey stateKey = new UUIDAndTypeStateKey(UUID.randomUUID(), StateType.PROCESS_STATUS);
assertThrows(QException.class, () ->
{
new RunProcessAction().primeProcessState(runProcessInput, 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 //
////////////////////////////////////////////////////////////////////////////////////////////////
RunProcessInput runProcessInput = new RunProcessInput();
runProcessInput.setStartAfterStep("setupStep");
runProcessInput.addValue("foo", "bar");
runProcessInput.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(runProcessInput, 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"));
}
/*******************************************************************************
**
*******************************************************************************/
private static class TestCallback implements QProcessCallback
{
private boolean wasCalledForQueryFilter = false;
private boolean wasCalledForFieldValues = false;
/*******************************************************************************
**
*******************************************************************************/
@Override
public QQueryFilter getQueryFilter()
{
wasCalledForQueryFilter = true;
return (new QQueryFilter());
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public Map<String, Serializable> getFieldValues(List<QFieldMetaData> fields)
{
wasCalledForFieldValues = true;
Map<String, Serializable> rs = new HashMap<>();
if(fields.stream().anyMatch(f -> f.getName().equals("yearsToAdd")))
{
rs.put("yearsToAdd", 42);
}
return (rs);
}
}
}

View File

@ -0,0 +1,50 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.processes.person.addtopeoplesage;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
/*******************************************************************************
**
*******************************************************************************/
public class AddAge implements BackendStep
{
@Override
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput)
{
int totalYearsAdded = 0;
Integer yearsToAdd = runBackendStepInput.getValueInteger("yearsToAdd");
for(QRecord record : runBackendStepInput.getRecords())
{
Integer age = record.getValueInteger("age");
age += yearsToAdd;
totalYearsAdded += yearsToAdd;
record.setValue("age", age);
}
runBackendStepOutput.addValue("totalYearsAdded", totalYearsAdded);
}
}

View File

@ -0,0 +1,58 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.processes.person.addtopeoplesage;
import java.time.LocalDate;
import java.time.Period;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
/*******************************************************************************
**
*******************************************************************************/
public class GetAgeStatistics implements BackendStep
{
@Override
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput)
{
Integer min = null;
Integer max = null;
LocalDate now = LocalDate.now();
for(QRecord record : runBackendStepInput.getRecords())
{
LocalDate birthDate = record.getValueLocalDate("birthDate");
Period until = birthDate.until(now);
int age = until.getYears();
record.setValue("age", age);
System.out.println(birthDate + " -> " + age);
min = (min == null || age < min) ? age : min;
max = (max == null || age > max) ? age : max;
}
runBackendStepOutput.addValue("minAge", min);
runBackendStepOutput.addValue("maxAge", max);
}
}

View File

@ -0,0 +1,213 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.reporting;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportInput;
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.apache.commons.io.FileUtils;
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.assertTrue;
/*******************************************************************************
** Unit test for the ReportAction
*******************************************************************************/
class ReportActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testCSV() throws Exception
{
int recordCount = 1000;
String filename = "/tmp/ReportActionTest.csv";
runReport(recordCount, filename, ReportFormat.CSV, false);
File file = new File(filename);
List<String> fileLines = FileUtils.readLines(file, StandardCharsets.UTF_8.name());
assertEquals(recordCount + 1, fileLines.size());
assertTrue(file.delete());
}
/*******************************************************************************
** This test runs for more records, to stress more of the pipe-filling and
** other bits of the ReportAction.
*******************************************************************************/
@Test
public void testBigger() throws Exception
{
// int recordCount = 2_000_000; // to really stress locally, use this.
int recordCount = 200_000;
String filename = "/tmp/ReportActionTest.csv";
runReport(recordCount, filename, ReportFormat.CSV, false);
File file = new File(filename);
List<String> fileLines = FileUtils.readLines(file, StandardCharsets.UTF_8.name());
assertEquals(recordCount + 1, fileLines.size());
assertTrue(file.delete());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testExcel() throws Exception
{
int recordCount = 1000;
String filename = "/tmp/ReportActionTest.xlsx";
runReport(recordCount, filename, ReportFormat.XLSX, true);
File file = new File(filename);
assertTrue(file.delete());
}
/*******************************************************************************
**
*******************************************************************************/
private void runReport(int recordCount, String filename, ReportFormat reportFormat, boolean specifyFields) throws IOException, QException
{
try(FileOutputStream outputStream = new FileOutputStream(filename))
{
ReportInput reportInput = new ReportInput(TestUtils.defineInstance(), TestUtils.getMockSession());
reportInput.setTableName("person");
QTableMetaData table = reportInput.getTable();
reportInput.setReportFormat(reportFormat);
reportInput.setReportOutputStream(outputStream);
reportInput.setQueryFilter(new QQueryFilter());
reportInput.setLimit(recordCount);
if(specifyFields)
{
reportInput.setFieldNames(table.getFields().values().stream().map(QFieldMetaData::getName).collect(Collectors.toList()));
}
ReportOutput reportOutput = new ReportAction().execute(reportInput);
assertNotNull(reportOutput);
assertEquals(recordCount, reportOutput.getRecordCount());
}
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testBadFieldNames()
{
ReportInput reportInput = new ReportInput(TestUtils.defineInstance(), TestUtils.getMockSession());
reportInput.setTableName("person");
reportInput.setFieldNames(List.of("Foo", "Bar", "Baz"));
assertThrows(QUserFacingException.class, () ->
{
new ReportAction().execute(reportInput);
});
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testPreExecuteCount() throws QException
{
ReportInput reportInput = new ReportInput(TestUtils.defineInstance(), TestUtils.getMockSession());
reportInput.setTableName("person");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// use xlsx, which has a max-rows limit, to verify that code runs, but doesn't throw when there aren't too many rows //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
reportInput.setReportFormat(ReportFormat.XLSX);
new ReportAction().preExecute(reportInput);
////////////////////////////////////////////////////////////////////////////
// nothing to assert - but if preExecute throws, then the test will fail. //
////////////////////////////////////////////////////////////////////////////
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testTooManyColumns() throws QException
{
QTableMetaData wideTable = new QTableMetaData()
.withName("wide")
.withBackendName(TestUtils.DEFAULT_BACKEND_NAME);
for(int i = 0; i < ReportFormat.XLSX.getMaxCols() + 1; i++)
{
wideTable.addField(new QFieldMetaData("field" + i, QFieldType.STRING));
}
QInstance qInstance = TestUtils.defineInstance();
qInstance.addTable(wideTable);
ReportInput reportInput = new ReportInput(qInstance, TestUtils.getMockSession());
reportInput.setTableName("wide");
////////////////////////////////////////////////////////////////
// use xlsx, which has a max-cols limit, to verify that code. //
////////////////////////////////////////////////////////////////
reportInput.setReportFormat(ReportFormat.XLSX);
assertThrows(QUserFacingException.class, () ->
{
new ReportAction().preExecute(reportInput);
});
}
}

View File

@ -0,0 +1,54 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/*******************************************************************************
** Unit test for CountAction
**
*******************************************************************************/
class CountActionTest
{
/*******************************************************************************
** At the core level, there isn't much that can be asserted, as it uses the
** mock implementation - just confirming that all of the "wiring" works.
**
*******************************************************************************/
@Test
public void test() throws QException
{
CountInput request = new CountInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setTableName("person");
CountOutput result = new CountAction().execute(request);
assertNotNull(result);
}
}

View File

@ -0,0 +1,84 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
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.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
** Unit test for DeleteAction
**
*******************************************************************************/
class DeleteActionTest
{
/*******************************************************************************
** At the core level, there isn't much that can be asserted, as it uses the
** mock implementation - just confirming that all of the "wiring" works.
**
*******************************************************************************/
@Test
public void test() throws QException
{
DeleteInput request = new DeleteInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setTableName("person");
request.setPrimaryKeys(List.of(1, 2));
DeleteOutput result = new DeleteAction().execute(request);
assertNotNull(result);
assertEquals(2, result.getDeletedRecordCount());
assertTrue(CollectionUtils.nullSafeIsEmpty(result.getRecordsWithErrors()));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testErrorIfBothPrimaryKeysAndFilter()
{
DeleteInput request = new DeleteInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setTableName("person");
request.setPrimaryKeys(List.of(1, 2));
request.setQueryFilter(new QQueryFilter());
assertThrows(QException.class, () ->
{
new DeleteAction().execute(request);
});
}
}

View File

@ -0,0 +1,63 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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;
import java.util.ArrayList;
import java.util.List;
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.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/*******************************************************************************
** Unit test for InsertAction
**
*******************************************************************************/
class InsertActionTest
{
/*******************************************************************************
** At the core level, there isn't much that can be asserted, as it uses the
** mock implementation - just confirming that all of the "wiring" works.
**
*******************************************************************************/
@Test
public void test() throws QException
{
InsertInput request = new InsertInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setTableName("person");
List<QRecord> records =new ArrayList<>();
QRecord record = new QRecord();
record.setValue("firstName", "James");
records.add(record);
request.setRecords(records);
InsertOutput result = new InsertAction().execute(request);
assertNotNull(result);
}
}

View File

@ -0,0 +1,54 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/*******************************************************************************
** Unit test for QueryAction
**
*******************************************************************************/
class QueryActionTest
{
/*******************************************************************************
** At the core level, there isn't much that can be asserted, as it uses the
** mock implementation - just confirming that all of the "wiring" works.
**
*******************************************************************************/
@Test
public void test() throws QException
{
QueryInput request = new QueryInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setTableName("person");
QueryOutput result = new QueryAction().execute(request);
assertNotNull(result);
}
}

View File

@ -0,0 +1,64 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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;
import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/*******************************************************************************
** Unit test for UpdateAction
**
*******************************************************************************/
class UpdateActionTest
{
/*******************************************************************************
** At the core level, there isn't much that can be asserted, as it uses the
** mock implementation - just confirming that all of the "wiring" works.
**
*******************************************************************************/
@Test
public void test() throws QException
{
UpdateInput request = new UpdateInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setTableName("person");
List<QRecord> records =new ArrayList<>();
QRecord record = new QRecord();
record.setValue("id", "47");
record.setValue("firstName", "James");
records.add(record);
request.setRecords(records);
UpdateOutput result = new UpdateAction().execute(request);
assertNotNull(result);
}
}

View File

@ -0,0 +1,284 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.adapters;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.QIndexBasedFieldMapping;
import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.QKeyBasedFieldMapping;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Assertions;
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.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/*******************************************************************************
** Unit test for CsvToQRecordAdapter
**
*******************************************************************************/
class CsvToQRecordAdapterTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromCsv_nullInput()
{
testExpectedToThrow(null);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromCsv_emptyStringInput()
{
testExpectedToThrow("");
}
/*******************************************************************************
**
*******************************************************************************/
private void testExpectedToThrow(String csv)
{
try
{
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(csv, TestUtils.defineTablePerson(), null);
System.out.println(qRecords);
}
catch(IllegalArgumentException iae)
{
System.out.println("Threw expected exception");
return;
}
fail("Didn't throw expected exception");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromCsv_emptyList()
{
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(getPersonCsvHeader(), TestUtils.defineTablePerson(), null);
assertNotNull(qRecords);
assertTrue(qRecords.isEmpty());
}
/*******************************************************************************
**
*******************************************************************************/
private String getPersonCsvHeader()
{
return ("""
"id","createDate","modifyDate","firstName","lastName","birthDate","email"\r
""");
}
/*******************************************************************************
**
*******************************************************************************/
private String getPersonCsvRow2()
{
return ("""
"0","2021-10-26 14:39:37","2021-10-26 14:39:37","Jane","Doe","1981-01-01","john@doe.com"\r
""");
}
/*******************************************************************************
**
*******************************************************************************/
private String getPersonCsvRow1()
{
return ("""
"0","2021-10-26 14:39:37","2021-10-26 14:39:37","John","Doe","1980-01-01","john@doe.com"\r
""");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromCsv_oneRowStandardHeaderNoMapping()
{
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(getPersonCsvHeader() + getPersonCsvRow1(), TestUtils.defineTablePerson(), null);
assertNotNull(qRecords);
assertEquals(1, qRecords.size());
QRecord qRecord = qRecords.get(0);
assertEquals("John", qRecord.getValue("firstName"));
assertEquals("1980-01-01", qRecord.getValue("birthDate"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromCsv_twoRowsStandardHeaderNoMapping()
{
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(getPersonCsvHeader() + getPersonCsvRow1() + getPersonCsvRow2(), TestUtils.defineTablePerson(), null);
assertNotNull(qRecords);
assertEquals(2, qRecords.size());
QRecord qRecord1 = qRecords.get(0);
assertEquals("John", qRecord1.getValue("firstName"));
assertEquals("1980-01-01", qRecord1.getValue("birthDate"));
QRecord qRecord2 = qRecords.get(1);
assertEquals("Jane", qRecord2.getValue("firstName"));
assertEquals("1981-01-01", qRecord2.getValue("birthDate"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromCsv_oneRowCustomKeyBasedMapping()
{
String csvCustomHeader = """
"id","created","modified","first","last","birthday","email"\r
""";
QKeyBasedFieldMapping mapping = new QKeyBasedFieldMapping()
.withMapping("id", "id")
.withMapping("createDate", "created")
.withMapping("modifyDate", "modified")
.withMapping("firstName", "first")
.withMapping("lastName", "last")
.withMapping("birthDate", "birthday")
.withMapping("email", "email");
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(csvCustomHeader + getPersonCsvRow1(), TestUtils.defineTablePerson(), mapping);
assertNotNull(qRecords);
assertEquals(1, qRecords.size());
QRecord qRecord = qRecords.get(0);
assertEquals("John", qRecord.getValue("firstName"));
assertEquals("1980-01-01", qRecord.getValue("birthDate"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromCsv_twoRowsCustomIndexBasedMapping()
{
int index = 1;
QIndexBasedFieldMapping mapping = new QIndexBasedFieldMapping()
.withMapping("id", index++)
.withMapping("createDate", index++)
.withMapping("modifyDate", index++)
.withMapping("firstName", index++)
.withMapping("lastName", index++)
.withMapping("birthDate", index++)
.withMapping("email", index++);
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(getPersonCsvRow1() + getPersonCsvRow2(), TestUtils.defineTablePerson(), mapping);
assertNotNull(qRecords);
assertEquals(2, qRecords.size());
QRecord qRecord1 = qRecords.get(0);
assertEquals("John", qRecord1.getValue("firstName"));
assertEquals("1980-01-01", qRecord1.getValue("birthDate"));
QRecord qRecord2 = qRecords.get(1);
assertEquals("Jane", qRecord2.getValue("firstName"));
assertEquals("1981-01-01", qRecord2.getValue("birthDate"));
}
/*******************************************************************************
** In this test - we've got CSV data with duplicated header names.
** In our mapping, we're seeing the suffixes of " 2" and " 3" addd to those
** header names on the RHS.
*******************************************************************************/
@Test
public void test_duplicatedColumnHeaders()
{
QKeyBasedFieldMapping mapping = new QKeyBasedFieldMapping()
.withMapping("id", "id")
.withMapping("createDate", "date")
.withMapping("modifyDate", "date 2")
.withMapping("firstName", "name")
.withMapping("lastName", "name 2")
.withMapping("birthDate", "date 3")
.withMapping("email", "email");
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv("""
id,date,date,name,name,date,email
1,2022-06-26,2022-06-26,John,Doe,1980-01-01,john@kingsrook.com
""", TestUtils.defineTablePerson(), mapping);
assertNotNull(qRecords);
assertEquals(1, qRecords.size());
QRecord qRecord1 = qRecords.get(0);
assertEquals("John", qRecord1.getValue("firstName"));
assertEquals("Doe", qRecord1.getValue("lastName"));
assertEquals("1980-01-01", qRecord1.getValue("birthDate"));
assertEquals("2022-06-26", qRecord1.getValue("createDate"));
assertEquals("2022-06-26", qRecord1.getValue("modifyDate"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testMakeHeadersUnique()
{
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
Assertions.assertEquals(List.of("A", "B", "C"), csvToQRecordAdapter.makeHeadersUnique(List.of("A", "B", "C")));
Assertions.assertEquals(List.of("A", "B", "C", "C 2", "C 3"), csvToQRecordAdapter.makeHeadersUnique(List.of("A", "B", "C", "C", "C")));
Assertions.assertEquals(List.of("C", "A", "C 2", "B", "C 3"), csvToQRecordAdapter.makeHeadersUnique(List.of("C", "A", "C", "B", "C")));
Assertions.assertEquals(List.of("A", "B", "C", "C 2", "C 3"), csvToQRecordAdapter.makeHeadersUnique(List.of("A", "B", "C", "C 2", "C")));
Assertions.assertEquals(List.of("A", "B", "C", "C 2", "C 3"), csvToQRecordAdapter.makeHeadersUnique(List.of("A", "B", "C", "C 2", "C 3")));
// todo - this is what the method header comment means when it says we don't handle all cases well...
// Assertions.assertEquals(List.of("A", "B", "C", "C 2", "C 3"), csvToQRecordAdapter.makeHeadersUnique(List.of("A", "B", "C 2", "C", "C 3")));
}
}

View File

@ -0,0 +1,209 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.adapters;
import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.AbstractQFieldMapping;
import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.QIndexBasedFieldMapping;
import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.QKeyBasedFieldMapping;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import org.assertj.core.api.Assertions;
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.fail;
/*******************************************************************************
** Unit test for JsonToQFieldMappingAdapter
**
*******************************************************************************/
class JsonToQFieldMappingAdapterTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_nullInput()
{
testExpectedToThrow(null);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_emptyStringInput()
{
testExpectedToThrow("");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_malformedJsonInput()
{
testExpectedToThrow("{foo=bar}");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_validKeyBasedInput()
{
JsonToQFieldMappingAdapter jsonToQFieldMappingAdapter = new JsonToQFieldMappingAdapter();
AbstractQFieldMapping<String> mapping = (QKeyBasedFieldMapping) jsonToQFieldMappingAdapter.buildMappingFromJson("""
{
"Field1": "source1",
"Field2": "source2",
}
""");
System.out.println(mapping);
assertNotNull(mapping);
assertEquals("source1", mapping.getFieldSource("Field1"));
assertEquals("source2", mapping.getFieldSource("Field2"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_validIndexBasedInput()
{
JsonToQFieldMappingAdapter jsonToQFieldMappingAdapter = new JsonToQFieldMappingAdapter();
AbstractQFieldMapping<Integer> mapping = (QIndexBasedFieldMapping) jsonToQFieldMappingAdapter.buildMappingFromJson("""
{
"Field1": 1,
"Field2": 2,
}
""");
System.out.println(mapping);
assertNotNull(mapping);
assertEquals(1, mapping.getFieldSource("Field1"));
assertEquals(2, mapping.getFieldSource("Field2"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_unsupportedTypeForSource()
{
testExpectedToThrow("""
{
"Field1": [1, 2],
"Field2": {"A": "B"}
}
""");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_deserializeSerializedQKeyBasedFieldMapping()
{
QIndexBasedFieldMapping original = new QIndexBasedFieldMapping()
.withMapping("foo", 0)
.withMapping("bar", 1);
String json = JsonUtils.toJson(original);
AbstractQFieldMapping<?> deserialized = new JsonToQFieldMappingAdapter().buildMappingFromJson(json);
Assertions.assertThat(deserialized).usingRecursiveComparison().isEqualTo(original);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_deserializeSerializedQIndexBasedFieldMapping()
{
QKeyBasedFieldMapping original = new QKeyBasedFieldMapping()
.withMapping("foo", "Fu")
.withMapping("bar", "Bahr");
String json = JsonUtils.toJson(original);
AbstractQFieldMapping<?> deserialized = new JsonToQFieldMappingAdapter().buildMappingFromJson(json);
Assertions.assertThat(deserialized).usingRecursiveComparison().isEqualTo(original);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_emptyMapping()
{
testExpectedToThrow("{}");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_inputJsonList()
{
testExpectedToThrow("[]");
}
/*******************************************************************************
**
*******************************************************************************/
private void testExpectedToThrow(String json)
{
try
{
JsonToQFieldMappingAdapter jsonToQFieldMappingAdapter = new JsonToQFieldMappingAdapter();
AbstractQFieldMapping<?> mapping = jsonToQFieldMappingAdapter.buildMappingFromJson(json);
System.out.println(mapping);
}
catch(IllegalArgumentException iae)
{
System.out.println("Threw expected exception");
return;
}
fail("Didn't throw expected exception");
}
}

View File

@ -0,0 +1,172 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.adapters;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/*******************************************************************************
** Unit test for JsonToQRecordAdapter
**
*******************************************************************************/
class JsonToQRecordAdapterTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_nullInput()
{
testExpectedToThrow(null);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_emptyStringInput()
{
testExpectedToThrow("");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_inputDoesntLookLikeJson()
{
testExpectedToThrow("<HTML>");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_inputLooksLikeJsonButIsMalformed()
{
testExpectedToThrow("{json=not}");
}
/*******************************************************************************
**
*******************************************************************************/
private void testExpectedToThrow(String json)
{
try
{
JsonToQRecordAdapter jsonToQRecordAdapter = new JsonToQRecordAdapter();
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson(json, TestUtils.defineTablePerson(), null);
System.out.println(qRecords);
}
catch(IllegalArgumentException iae)
{
System.out.println("Threw expected exception");
return;
}
fail("Didn't throw expected exception");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_emptyList()
{
JsonToQRecordAdapter jsonToQRecordAdapter = new JsonToQRecordAdapter();
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson("[]", TestUtils.defineTablePerson(), null);
assertNotNull(qRecords);
assertTrue(qRecords.isEmpty());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_inputObject()
{
JsonToQRecordAdapter jsonToQRecordAdapter = new JsonToQRecordAdapter();
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson("""
{
"firstName":"Joe",
"lastName":"Dimagio"
}
""", TestUtils.defineTablePerson(), null);
assertNotNull(qRecords);
assertEquals(1, qRecords.size());
assertEquals("Joe", qRecords.get(0).getValue("firstName"));
assertEquals("Dimagio", qRecords.get(0).getValue("lastName"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_inputList()
{
JsonToQRecordAdapter jsonToQRecordAdapter = new JsonToQRecordAdapter();
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson("""
[
{ "firstName":"Tyler", "lastName":"Samples" },
{ "firstName":"Tim", "lastName":"Chamberlain" }
]
""", TestUtils.defineTablePerson(), null);
assertNotNull(qRecords);
assertEquals(2, qRecords.size());
assertEquals("Tyler", qRecords.get(0).getValue("firstName"));
assertEquals("Samples", qRecords.get(0).getValue("lastName"));
assertEquals("Tim", qRecords.get(1).getValue("firstName"));
assertEquals("Chamberlain", qRecords.get(1).getValue("lastName"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_inputListWithNonObjectMembers()
{
testExpectedToThrow("[ 1701 ]");
}
}

View File

@ -0,0 +1,95 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.adapters;
import java.io.File;
import java.io.IOException;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
**
*******************************************************************************/
class QInstanceAdapterTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void qInstanceToJson()
{
QInstance qInstance = TestUtils.defineInstance();
String json = new QInstanceAdapter().qInstanceToJson(qInstance);
System.out.println(json);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void qInstanceToJsonIncludingBackend()
{
QInstance qInstance = TestUtils.defineInstance();
String json = new QInstanceAdapter().qInstanceToJsonIncludingBackend(qInstance);
System.out.println(json);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
@Disabled("Pending custom deserializer on QStepMetaData")
void jsonToQInstance() throws IOException
{
String json = FileUtils.readFileToString(new File("src/test/resources/personQInstance.json"));
QInstance qInstance = new QInstanceAdapter().jsonToQInstance(json);
System.out.println(qInstance);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
@Disabled("Pending custom deserializer on QStepMetaData")
void jsonToQInstanceIncludingBackend() throws IOException
{
String json = FileUtils.readFileToString(new File("src/test/resources/personQInstanceIncludingBackend.json"));
QInstance qInstance = new QInstanceAdapter().jsonToQInstanceIncludingBackends(json);
System.out.println(qInstance);
assertNotNull(qInstance.getBackends());
assertTrue(qInstance.getBackends().size() > 0);
}
}

View File

@ -0,0 +1,92 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.instances;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
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.assertNull;
/*******************************************************************************
** Unit test for QInstanceEnricher
**
*******************************************************************************/
class QInstanceEnricherTest
{
/*******************************************************************************
** Test that a table missing a label gets the default label applied (name w/ UC-first).
**
*******************************************************************************/
@Test
public void test_nullTableLabelComesFromName()
{
QInstance qInstance = TestUtils.defineInstance();
QTableMetaData personTable = qInstance.getTable("person");
personTable.setLabel(null);
assertNull(personTable.getLabel());
new QInstanceEnricher().enrich(qInstance);
assertEquals("Person", personTable.getLabel());
}
/*******************************************************************************
** Test that a table missing a label and a name doesn't NPE, but just keeps
** the name & label both null.
**
*******************************************************************************/
@Test
public void test_nullNameGivesNullLabel()
{
QInstance qInstance = TestUtils.defineInstance();
QTableMetaData personTable = qInstance.getTable("person");
personTable.setLabel(null);
personTable.setName(null);
assertNull(personTable.getLabel());
assertNull(personTable.getName());
new QInstanceEnricher().enrich(qInstance);
assertNull(personTable.getLabel());
assertNull(personTable.getName());
}
/*******************************************************************************
** Test that a field missing a label gets the default label applied (name w/ UC-first)
**
*******************************************************************************/
@Test
public void test_nullFieldLabelComesFromName()
{
QInstance qInstance = TestUtils.defineInstance();
QFieldMetaData idField = qInstance.getTable("person").getField("id");
idField.setLabel(null);
assertNull(idField.getLabel());
new QInstanceEnricher().enrich(qInstance);
assertEquals("Id", idField.getLabel());
}
}

View File

@ -0,0 +1,277 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.instances;
import java.util.HashMap;
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/*******************************************************************************
** Unit test for QInstanceValidator.
**
*******************************************************************************/
class QInstanceValidatorTest
{
/*******************************************************************************
** Test a valid instance - should just pass
**
*******************************************************************************/
@Test
public void test_validatePass() throws QInstanceValidationException
{
new QInstanceValidator().validate(TestUtils.defineInstance());
}
/*******************************************************************************
** Test an instance with null backends - should throw.
**
*******************************************************************************/
@Test
public void test_validateNullBackends()
{
try
{
QInstance qInstance = TestUtils.defineInstance();
qInstance.setBackends(null);
new QInstanceValidator().validate(qInstance);
fail("Should have thrown validationException");
}
catch(QInstanceValidationException e)
{
assertReason("At least 1 backend must be defined", e);
}
}
/*******************************************************************************
** Test an instance with empty map of backends - should throw.
**
*******************************************************************************/
@Test
public void test_validateEmptyBackends()
{
try
{
QInstance qInstance = TestUtils.defineInstance();
qInstance.setBackends(new HashMap<>());
new QInstanceValidator().validate(qInstance);
fail("Should have thrown validationException");
}
catch(QInstanceValidationException e)
{
assertReason("At least 1 backend must be defined", e);
}
}
/*******************************************************************************
** Test an instance with null tables - should throw.
**
*******************************************************************************/
@Test
public void test_validateNullTables()
{
try
{
QInstance qInstance = TestUtils.defineInstance();
qInstance.setTables(null);
new QInstanceValidator().validate(qInstance);
fail("Should have thrown validationException");
}
catch(QInstanceValidationException e)
{
assertReason("At least 1 table must be defined", e);
}
}
/*******************************************************************************
** Test an instance with empty map of tables - should throw.
**
*******************************************************************************/
@Test
public void test_validateEmptyTables()
{
try
{
QInstance qInstance = TestUtils.defineInstance();
qInstance.setTables(new HashMap<>());
new QInstanceValidator().validate(qInstance);
fail("Should have thrown validationException");
}
catch(QInstanceValidationException e)
{
assertReason("At least 1 table must be defined", e);
}
}
/*******************************************************************************
** Test an instance where a table and a backend each have a name attribute that
** doesn't match the key that those objects have in the instance's maps - should throw.
**
*******************************************************************************/
@Test
public void test_validateInconsistentNames()
{
try
{
QInstance qInstance = TestUtils.defineInstance();
qInstance.getTable("person").setName("notPerson");
qInstance.getBackend("default").setName("notDefault");
new QInstanceValidator().validate(qInstance);
fail("Should have thrown validationException");
}
catch(QInstanceValidationException e)
{
assertReason("Inconsistent naming for table", e);
assertReason("Inconsistent naming for backend", e);
}
}
/*******************************************************************************
** Test that if a table has a null backend, that it fails.
**
*******************************************************************************/
@Test
public void test_validateTableWithoutBackend()
{
try
{
QInstance qInstance = TestUtils.defineInstance();
qInstance.getTable("person").setBackendName(null);
new QInstanceValidator().validate(qInstance);
fail("Should have thrown validationException");
}
catch(QInstanceValidationException e)
{
assertReason("Missing backend name for table", e);
}
}
/*******************************************************************************
** Test that if a table specifies a backend that doesn't exist, that it fails.
**
*******************************************************************************/
@Test
public void test_validateTableWithMissingBackend()
{
try
{
QInstance qInstance = TestUtils.defineInstance();
qInstance.getTable("person").setBackendName("notARealBackend");
new QInstanceValidator().validate(qInstance);
fail("Should have thrown validationException");
}
catch(QInstanceValidationException e)
{
assertReason("Unrecognized backend", e);
}
}
/*******************************************************************************
** Test that a table with no fields fails.
**
*******************************************************************************/
@Test
public void test_validateTableWithNoFields()
{
try
{
QInstance qInstance = TestUtils.defineInstance();
qInstance.getTable("person").setFields(null);
new QInstanceValidator().validate(qInstance);
fail("Should have thrown validationException");
}
catch(QInstanceValidationException e)
{
assertReason("At least 1 field", e);
}
try
{
QInstance qInstance = TestUtils.defineInstance();
qInstance.getTable("person").setFields(new HashMap<>());
new QInstanceValidator().validate(qInstance);
fail("Should have thrown validationException");
}
catch(QInstanceValidationException e)
{
assertReason("At least 1 field", e);
}
}
/*******************************************************************************
** Test that if a field specifies a backend that doesn't exist, that it fails.
**
*******************************************************************************/
@Test
public void test_validateFieldWithMissingPossibleValueSource()
{
try
{
QInstance qInstance = TestUtils.defineInstance();
qInstance.getTable("person").getField("homeState").setPossibleValueSourceName("not a real possible value source");
new QInstanceValidator().validate(qInstance);
fail("Should have thrown validationException");
}
catch(QInstanceValidationException e)
{
assertReason("Unrecognized possibleValueSourceName", e);
}
}
/*******************************************************************************
** utility method for asserting that a specific reason string is found within
** the list of reasons in the QInstanceValidationException.
**
*******************************************************************************/
private void assertReason(String reason, QInstanceValidationException e)
{
assertNotNull(e.getReasons());
assertTrue(e.getReasons().stream().anyMatch(s -> s.contains(reason)));
}
}

View File

@ -0,0 +1,281 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.instances;
import java.util.Map;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/*******************************************************************************
** Unit test for QSecretReader
*******************************************************************************/
class QMetaDataVariableInterpreterTest
{
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
public void beforeEach()
{
System.setProperty("username", "joe");
System.setProperty("password", "b1d3n");
}
/*******************************************************************************
**
*******************************************************************************/
@AfterEach
public void afterEach()
{
System.clearProperty("username");
System.clearProperty("password");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testInterpretObject() throws QException
{
GoodTestClass goodTestClass = new GoodTestClass();
goodTestClass.setUsername("${prop.username}");
goodTestClass.setPassword("${prop.password}");
new QMetaDataVariableInterpreter().interpretObject(goodTestClass);
assertEquals("joe", goodTestClass.getUsername());
assertEquals("b1d3n", goodTestClass.getPassword());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testBadAnnotatedObjects()
{
assertThrows(QException.class, () -> new QMetaDataVariableInterpreter().interpretObject(new BadTestClassAnnotatedInteger()));
assertThrows(QException.class, () -> new QMetaDataVariableInterpreter().interpretObject(new BadTestClassNoGetter()));
assertThrows(QException.class, () -> new QMetaDataVariableInterpreter().interpretObject(new BadTestClassNoSetter()));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testInterpretFromEnvironment()
{
QMetaDataVariableInterpreter secretReader = new QMetaDataVariableInterpreter();
String key = "CUSTOM_PROPERTY";
String value = "ABCD-9876";
secretReader.setCustomEnvironment(Map.of(key, value));
assertNull(secretReader.interpret(null));
assertEquals("foo", secretReader.interpret("foo"));
assertNull(secretReader.interpret("${env.NOT-" + key + "}"));
assertEquals(value, secretReader.interpret("${env." + key + "}"));
assertEquals("${env.NOT-" + key, secretReader.interpret("${env.NOT-" + key));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testInterpretFromProperties()
{
QMetaDataVariableInterpreter secretReader = new QMetaDataVariableInterpreter();
String key = "MY_PROPERTY";
String value = "WXYZ-6789";
System.setProperty(key, value);
assertNull(secretReader.interpret(null));
assertEquals("foo", secretReader.interpret("foo"));
assertNull(secretReader.interpret("${prop.NOT-" + key + "}"));
assertEquals(value, secretReader.interpret("${prop." + key + "}"));
assertEquals("${prop.NOT-" + key, secretReader.interpret("${prop.NOT-" + key));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testInterpretLiterals()
{
QMetaDataVariableInterpreter secretReader = new QMetaDataVariableInterpreter();
assertEquals("${env.X}", secretReader.interpret("${literal.${env.X}}"));
assertEquals("${prop.X}", secretReader.interpret("${literal.${prop.X}}"));
assertEquals("${literal.X}", secretReader.interpret("${literal.${literal.X}}"));
}
/*******************************************************************************
**
*******************************************************************************/
@InterpretableFields(fieldNames = { "username", "password" })
public static class GoodTestClass
{
private String username;
private String password;
/*******************************************************************************
** Getter for username
**
*******************************************************************************/
public String getUsername()
{
return username;
}
/*******************************************************************************
** Setter for username
**
*******************************************************************************/
public void setUsername(String username)
{
this.username = username;
}
/*******************************************************************************
** Getter for password
**
*******************************************************************************/
public String getPassword()
{
return password;
}
/*******************************************************************************
** Setter for password
**
*******************************************************************************/
public void setPassword(String password)
{
this.password = password;
}
}
/*******************************************************************************
**
*******************************************************************************/
@InterpretableFields(fieldNames = { "port" })
public static class BadTestClassAnnotatedInteger
{
private Integer port;
/*******************************************************************************
** Getter for port
**
*******************************************************************************/
public Integer getPort()
{
return port;
}
/*******************************************************************************
** Setter for port
**
*******************************************************************************/
public void setPort(Integer port)
{
this.port = port;
}
}
/*******************************************************************************
**
*******************************************************************************/
@InterpretableFields(fieldNames = { "foo" })
public static class BadTestClassNoGetter
{
private String foo;
/*******************************************************************************
** Setter for foo
**
*******************************************************************************/
public void setFoo(String foo)
{
this.foo = foo;
}
}
/*******************************************************************************
**
*******************************************************************************/
@InterpretableFields(fieldNames = { "foo" })
public static class BadTestClassNoSetter
{
private String foo;
/*******************************************************************************
** Getter for foo
**
*******************************************************************************/
public String getFoo()
{
return foo;
}
}
}

View File

@ -0,0 +1,195 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.data;
import java.math.BigDecimal;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.data.testentities.Item;
import com.kingsrook.qqq.backend.core.model.data.testentities.ItemWithPrimitives;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
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;
/*******************************************************************************
**
*******************************************************************************/
class QRecordEntityTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testItemToQRecord() throws QException
{
Item item = new Item();
item.setSku("ABC-123");
item.setDescription("My Item");
item.setQuantity(47);
item.setPrice(new BigDecimal("3.50"));
item.setFeatured(true);
QRecord qRecord = item.toQRecord();
assertEquals("ABC-123", qRecord.getValueString("sku"));
assertEquals("My Item", qRecord.getValueString("description"));
assertEquals(47, qRecord.getValueInteger("quantity"));
assertEquals(new BigDecimal("3.50"), qRecord.getValueBigDecimal("price"));
assertTrue(qRecord.getValueBoolean("featured"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testQRecordToItem() throws QException
{
QRecord qRecord = new QRecord()
.withValue("sku", "WXYZ-9876")
.withValue("description", "Items are cool")
.withValue("quantity", 42)
.withValue("price", new BigDecimal("3.50"))
.withValue("featured", false);
Item item = qRecord.toEntity(Item.class);
assertEquals("WXYZ-9876", item.getSku());
assertEquals("Items are cool", item.getDescription());
assertEquals(42, item.getQuantity());
assertEquals(new BigDecimal("3.50"), item.getPrice());
assertFalse(item.getFeatured());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testItemWithPrimitivesToQRecord() throws QException
{
ItemWithPrimitives item = new ItemWithPrimitives();
item.setSku("ABC-123");
item.setDescription(null);
item.setQuantity(47);
item.setPrice(new BigDecimal("3.50"));
item.setFeatured(true);
QRecord qRecord = item.toQRecord();
assertEquals("ABC-123", qRecord.getValueString("sku"));
assertNull(qRecord.getValueString("description"));
assertEquals(47, qRecord.getValueInteger("quantity"));
assertEquals(new BigDecimal("3.50"), qRecord.getValueBigDecimal("price"));
assertTrue(qRecord.getValueBoolean("featured"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testQRecordToItemWithPrimitives() throws QException
{
QRecord qRecord = new QRecord()
.withValue("sku", "WXYZ-9876")
.withValue("description", null)
.withValue("quantity", 42)
.withValue("price", new BigDecimal("3.50"))
.withValue("featured", false);
ItemWithPrimitives item = qRecord.toEntity(ItemWithPrimitives.class);
assertEquals("WXYZ-9876", item.getSku());
assertNull(item.getDescription());
assertEquals(42, item.getQuantity());
assertEquals(new BigDecimal("3.50"), item.getPrice());
assertFalse(item.getFeatured());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testQRecordWithAllStringValuesToItem() throws QException
{
QRecord qRecord = new QRecord()
.withValue("sku", "WXYZ-9876")
.withValue("description", "Items are cool")
.withValue("quantity", "42")
.withValue("price", "3.50")
.withValue("featured", "false");
Item item = qRecord.toEntity(Item.class);
assertEquals("WXYZ-9876", item.getSku());
assertEquals("Items are cool", item.getDescription());
assertEquals(42, item.getQuantity());
assertEquals(new BigDecimal("3.50"), item.getPrice());
assertFalse(item.getFeatured());
}
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
void testQTableConstructionFromEntity() throws QException
{
QTableMetaData qTableMetaData = new QTableMetaData()
.withField(new QFieldMetaData(Item::getSku))
.withField(new QFieldMetaData(Item::getDescription))
.withField(new QFieldMetaData(Item::getQuantity));
assertEquals(QFieldType.STRING, qTableMetaData.getField("sku").getType());
assertEquals(QFieldType.INTEGER, qTableMetaData.getField("quantity").getType());
}
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
void testQTableConstructionWithPrimitives() throws QException
{
QTableMetaData qTableMetaData = new QTableMetaData()
.withField(new QFieldMetaData(ItemWithPrimitives::getSku))
.withField(new QFieldMetaData(ItemWithPrimitives::getDescription))
.withField(new QFieldMetaData(ItemWithPrimitives::getQuantity));
assertEquals(QFieldType.STRING, qTableMetaData.getField("sku").getType());
assertEquals(QFieldType.INTEGER, qTableMetaData.getField("quantity").getType());
}
}

View File

@ -0,0 +1,149 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.data.testentities;
import java.math.BigDecimal;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
/*******************************************************************************
** Sample of an entity that can be converted to & from a QRecord
*******************************************************************************/
public class Item extends QRecordEntity
{
private String sku;
private String description;
private Integer quantity;
private BigDecimal price;
private Boolean featured;
/*******************************************************************************
** Getter for sku
**
*******************************************************************************/
public String getSku()
{
return sku;
}
/*******************************************************************************
** Setter for sku
**
*******************************************************************************/
public void setSku(String sku)
{
this.sku = sku;
}
/*******************************************************************************
** Getter for description
**
*******************************************************************************/
public String getDescription()
{
return description;
}
/*******************************************************************************
** Setter for description
**
*******************************************************************************/
public void setDescription(String description)
{
this.description = description;
}
/*******************************************************************************
** Getter for quantity
**
*******************************************************************************/
public Integer getQuantity()
{
return quantity;
}
/*******************************************************************************
** Setter for quantity
**
*******************************************************************************/
public void setQuantity(Integer quantity)
{
this.quantity = quantity;
}
/*******************************************************************************
** Getter for price
**
*******************************************************************************/
public BigDecimal getPrice()
{
return price;
}
/*******************************************************************************
** Setter for price
**
*******************************************************************************/
public void setPrice(BigDecimal price)
{
this.price = price;
}
/*******************************************************************************
** Getter for featured
**
*******************************************************************************/
public Boolean getFeatured()
{
return featured;
}
/*******************************************************************************
** Setter for featured
**
*******************************************************************************/
public void setFeatured(Boolean featured)
{
this.featured = featured;
}
}

View File

@ -0,0 +1,149 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.data.testentities;
import java.math.BigDecimal;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
/*******************************************************************************
** Sample of an entity that can be converted to & from a QRecord
*******************************************************************************/
public class ItemWithPrimitives extends QRecordEntity
{
private String sku;
private String description;
private int quantity;
private BigDecimal price;
private boolean featured;
/*******************************************************************************
** Getter for sku
**
*******************************************************************************/
public String getSku()
{
return sku;
}
/*******************************************************************************
** Setter for sku
**
*******************************************************************************/
public void setSku(String sku)
{
this.sku = sku;
}
/*******************************************************************************
** Getter for description
**
*******************************************************************************/
public String getDescription()
{
return description;
}
/*******************************************************************************
** Setter for description
**
*******************************************************************************/
public void setDescription(String description)
{
this.description = description;
}
/*******************************************************************************
** Getter for quantity
**
*******************************************************************************/
public int getQuantity()
{
return quantity;
}
/*******************************************************************************
** Setter for quantity
**
*******************************************************************************/
public void setQuantity(int quantity)
{
this.quantity = quantity;
}
/*******************************************************************************
** Getter for price
**
*******************************************************************************/
public BigDecimal getPrice()
{
return price;
}
/*******************************************************************************
** Setter for price
**
*******************************************************************************/
public void setPrice(BigDecimal price)
{
this.price = price;
}
/*******************************************************************************
** Getter for featured
**
*******************************************************************************/
public boolean getFeatured()
{
return featured;
}
/*******************************************************************************
** Setter for featured
**
*******************************************************************************/
public void setFeatured(boolean featured)
{
this.featured = featured;
}
}

View File

@ -0,0 +1,223 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.modules.authentication;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import com.kingsrook.qqq.backend.core.exceptions.QAuthenticationException;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.modules.authentication.metadata.Auth0AuthenticationMetaData;
import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData;
import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
import com.kingsrook.qqq.backend.core.state.StateProviderInterface;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule.AUTH0_ID_TOKEN_KEY;
import static com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule.COULD_NOT_DECODE_ERROR;
import static com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule.EXPIRED_TOKEN_ERROR;
import static com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule.INVALID_TOKEN_ERROR;
import static com.kingsrook.qqq.backend.core.modules.authentication.Auth0AuthenticationModule.TOKEN_NOT_PROVIDED_ERROR;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
/*******************************************************************************
** Unit test for the FullyAnonymousAuthenticationModule
*******************************************************************************/
public class Auth0AuthenticationModuleTest
{
private static final String VALID_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IllrY2FkWTA0Q3RFVUFxQUdLNTk3ayJ9.eyJnaXZlbl9uYW1lIjoiVGltIiwiZmFtaWx5X25hbWUiOiJDaGFtYmVybGFpbiIsIm5pY2tuYW1lIjoidGltLmNoYW1iZXJsYWluIiwibmFtZSI6IlRpbSBDaGFtYmVybGFpbiIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQUZkWnVjcXVSaUFvTzk1RG9URklnbUtseVA1akVBVnZmWXFnS0lHTkVubzE9czk2LWMiLCJsb2NhbGUiOiJlbiIsInVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE5VDE2OjI0OjQ1LjgyMloiLCJlbWFpbCI6InRpbS5jaGFtYmVybGFpbkBraW5nc3Jvb2suY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8va2luZ3Nyb29rLnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJnb29nbGUtb2F1dGgyfDEwODk2NDEyNjE3MjY1NzAzNDg2NyIsImF1ZCI6InNwQ1NtczAzcHpVZGRYN1BocHN4ZDlUd2FLMDlZZmNxIiwiaWF0IjoxNjU4MjQ3OTAyLCJleHAiOjE2NTgyODM5MDIsIm5vbmNlIjoiZUhOdFMxbEtUR2N5ZG5KS1VVY3RkRTFVT0ZKNmJFNUxVVkEwZEdsRGVXOXZkVkl4UW41eVRrUlJlZz09In0.hib7JR8NDU2kx8Fj1bnzo3IUuabE6Hb-Z7HHZAJPQuF_Zdg3L1KDypn6SY7HAd_dsz2N8RkXfvQto-Y2g2ukuz7FxzNFgcVL99cyEO3YqmyCa6JTOTCrxdeaIE8QZpCEKvC28oeJBv0wO1Dwc--OVJMsK2vSzyxj1WNok64YYjWKLL4c0dFf-nj0KWFr1IU-tMiyWLDDiJw2Sa8M4YxXZYqdlkgNmrBPExgcm9l9SiT2l3Ts3Sgc_IyMVyMrnV8XX50EWdsm6vuCOSUcqf0XhjDQ7urZveoVwVLnYq3GcLhVBcy1Hr9RL8zPdPynOzsbX6uCww2Esrv6iwWrgQ5zBA";
private static final String INVALID_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IllrY2FkWTA0Q3RFVUFxQUdLNTk3ayJ9.eyJnaXZlbl9uYW1lIjoiVGltIiwiZmFtaWx5X25hbWUiOiJDaGFtYmVybGFpbiIsIm5pY2tuYW1lIjoidGltLmNoYW1iZXJsYWluIiwibmFtZSI6IlRpbSBDaGFtYmVybGFpbiIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQUZkWnVjcXVSaUFvTzk1RG9URklnbUtseVA1akVBVnZmWXFnS0lHTkVubzE9czk2LWMiLCJsb2NhbGUiOiJlbiIsInVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE5VDE2OjI0OjQ1LjgyMloiLCJlbWFpbCI6InRpbS5jaGFtYmVybGFpbkBraW5nc3Jvb2suY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8va2luZ3Nyb29rLnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJnb29nbGUtb2F1dGgyfDEwODk2NDEyNjE3MjY1NzAzNDg2NyIsImF1ZCI6InNwQ1NtczAzcHpVZGRYN1BocHN4ZDlUd2FLMDlZZmNxIiwiaWF0IjoxNjU4MjQ3OTAyLCJleHAiOjE2NTgyODM5MDIsIm5vbmNlIjoiZUhOdFMxbEtUR2N5ZG5KS1VVY3RkRTFVT0ZKNmJFNUxVVkEwZEdsRGVXOXZkVkl4UW41eVRrUlJlZz09In0.hib7JR8NDU2kx8Fj1bnzo3IUuabE6Hb-Z7HHZAJPQuF_Zdg3L1KDypn6SY7HAd_dsz2N8RkXfvQto-Y2g2ukuz7FxzNFgcVL99cyEO3YqmyCa6JTOTCrxdeaIE8QZpCEKvC28oeJBv0wO1Dwc--OVJMsK2vSzyxj1WNok64YYjWKLL4c0dFf-nj0KWFr1IU-tMiyWLDDiJw2Sa8M4YxXZYqdlkgNmrBPExgcm9l9SiT2l3Ts3Sgc_IyMVyMrnV8XX50EWdsm6vuCOSUcqf0XhjDQ7urZveoVwVLnYq3GcLhVBcy1Hr9RL8zPdPynOzsbX6uCww2Esrv6iwWrgQ5zBA-thismakesinvalid";
private static final String EXPIRED_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IllrY2FkWTA0Q3RFVUFxQUdLNTk3ayJ9.eyJnaXZlbl9uYW1lIjoiVGltIiwiZmFtaWx5X25hbWUiOiJDaGFtYmVybGFpbiIsIm5pY2tuYW1lIjoidGltLmNoYW1iZXJsYWluIiwibmFtZSI6IlRpbSBDaGFtYmVybGFpbiIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQUZkWnVjcXVSaUFvTzk1RG9URklnbUtseVA1akVBVnZmWXFnS0lHTkVubzE9czk2LWMiLCJsb2NhbGUiOiJlbiIsInVwZGF0ZWRfYXQiOiIyMDIyLTA3LTE4VDIxOjM4OjE1LjM4NloiLCJlbWFpbCI6InRpbS5jaGFtYmVybGFpbkBraW5nc3Jvb2suY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8va2luZ3Nyb29rLnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJnb29nbGUtb2F1dGgyfDEwODk2NDEyNjE3MjY1NzAzNDg2NyIsImF1ZCI6InNwQ1NtczAzcHpVZGRYN1BocHN4ZDlUd2FLMDlZZmNxIiwiaWF0IjoxNjU4MTgwNDc3LCJleHAiOjE2NTgyMTY0NzcsIm5vbmNlIjoiVkZkQlYzWmplR2hvY1cwMk9WZEtabHBLU0c1K1ZXbElhMEV3VkZaeFpVdEJVMDErZUZaT1RtMTNiZz09In0.fU7EwUgNrupOPz_PX_aQKON2xG1-LWD85xVo1Bn41WNEek-iMyJoch8l6NUihi7Bou14BoOfeWIG_sMqsLHqI2Pk7el7l1kigsjURx0wpiXadBt8piMxdIlxdToZEMuZCBzg7eJvXh4sM8tlV5cm0gPa6FT9Ih3VGJajNlXi5BcYS_JRpIvFvHn8-Bxj4KiAlZ5XPPkopjnDgP8kFfc4cMn_nxDkqWYlhj-5TaGW2xCLC9Qr_9UNxX0fm-CkKjYs3Z5ezbiXNkc-bxrCYvxeBeDPf8-T3EqrxCRVqCZSJ85BHdOc_E7UZC_g8bNj0umoplGwlCbzO4XIuOO-KlIaOg";
private static final String UNDECODABLE_TOKEN = "UNDECODABLE";
public static final String AUTH0_BASE_URL = "https://kingsrook.us.auth0.com/";
/*******************************************************************************
** Test a valid token where 'now' is set to a time that would be valid for it
**
*******************************************************************************/
@Test
public void testLastTimeChecked() throws QAuthenticationException
{
//////////////////////////////////////////////////////////
// Tuesday, July 19, 2022 12:40:27.299 PM GMT-05:00 DST //
//////////////////////////////////////////////////////////
Instant now = Instant.now();
/////////////////////////////////////////////////////////
// put the 'now' from the past into the state provider //
/////////////////////////////////////////////////////////
StateProviderInterface spi = InMemoryStateProvider.getInstance();
Auth0AuthenticationModule.Auth0StateKey key = new Auth0AuthenticationModule.Auth0StateKey(VALID_TOKEN);
spi.put(key, now);
//////////////////////
// build up session //
//////////////////////
QSession session = new QSession();
session.setIdReference(VALID_TOKEN);
Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule();
assertEquals(true, auth0AuthenticationModule.isSessionValid(session), "Session should return as still valid.");
}
/*******************************************************************************
** Test failure case, token is invalid
**
*******************************************************************************/
@Test
public void testInvalidToken()
{
Map<String, String> context = new HashMap<>();
context.put(AUTH0_ID_TOKEN_KEY, INVALID_TOKEN);
try
{
Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule();
auth0AuthenticationModule.createSession(getQInstance(), context);
fail("Should never get here");
}
catch(QAuthenticationException qae)
{
assertThat(qae.getMessage()).contains(INVALID_TOKEN_ERROR);
}
}
/*******************************************************************************
** Test failure case, token cant be decoded
**
*******************************************************************************/
@Test
public void testUndecodableToken()
{
Map<String, String> context = new HashMap<>();
context.put(AUTH0_ID_TOKEN_KEY, UNDECODABLE_TOKEN);
try
{
Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule();
auth0AuthenticationModule.createSession(getQInstance(), context);
fail("Should never get here");
}
catch(QAuthenticationException qae)
{
assertThat(qae.getMessage()).contains(COULD_NOT_DECODE_ERROR);
}
}
/*******************************************************************************
** Test failure case, token is expired
**
*******************************************************************************/
@Test
public void testProperlyFormattedButExpiredToken()
{
Map<String, String> context = new HashMap<>();
context.put(AUTH0_ID_TOKEN_KEY, EXPIRED_TOKEN);
try
{
Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule();
auth0AuthenticationModule.createSession(getQInstance(), context);
fail("Should never get here");
}
catch(QAuthenticationException qae)
{
assertThat(qae.getMessage()).contains(EXPIRED_TOKEN_ERROR);
}
}
/*******************************************************************************
** Test failure case, empty context
**
*******************************************************************************/
@Test
public void testEmptyContext()
{
try
{
Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule();
auth0AuthenticationModule.createSession(getQInstance(), new HashMap<>());
fail("Should never get here");
}
catch(QAuthenticationException qae)
{
assertThat(qae.getMessage()).contains(TOKEN_NOT_PROVIDED_ERROR);
}
}
/*******************************************************************************
** Test failure case, null token
**
*******************************************************************************/
@Test
public void testNullToken()
{
Map<String, String> context = new HashMap<>();
context.put(AUTH0_ID_TOKEN_KEY, null);
try
{
Auth0AuthenticationModule auth0AuthenticationModule = new Auth0AuthenticationModule();
auth0AuthenticationModule.createSession(getQInstance(), context);
fail("Should never get here");
}
catch(QAuthenticationException qae)
{
assertThat(qae.getMessage()).contains(TOKEN_NOT_PROVIDED_ERROR);
}
}
/*******************************************************************************
** utility method to prime a qInstance for auth0 tests
**
*******************************************************************************/
private QInstance getQInstance()
{
QAuthenticationMetaData authenticationMetaData = new Auth0AuthenticationMetaData()
.withBaseUrl(AUTH0_BASE_URL)
.withName("auth0");
QInstance qInstance = TestUtils.defineInstance();
qInstance.setAuthentication(authenticationMetaData);
return (qInstance);
}
}

View File

@ -0,0 +1,56 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.modules.authentication;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
** Unit test for the FullyAnonymousAuthenticationModule
*******************************************************************************/
public class FullyAnonymousAuthenticationModuleTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test()
{
FullyAnonymousAuthenticationModule fullyAnonymousAuthenticationModule = new FullyAnonymousAuthenticationModule();
QSession session = fullyAnonymousAuthenticationModule.createSession(null, null);
assertNotNull(session, "Session should not be null");
assertNotNull(session.getIdReference(), "Session id ref should not be null");
assertNotNull(session.getUser(), "Session User should not be null");
assertNotNull(session.getUser().getIdReference(), "Session User id ref should not be null");
assertTrue(fullyAnonymousAuthenticationModule.isSessionValid(session), "Any session should be valid");
assertFalse(fullyAnonymousAuthenticationModule.isSessionValid(null), "null should be not valid");
}
}

View File

@ -0,0 +1,94 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.modules.authentication;
import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException;
import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/*******************************************************************************
** Unit test for QModuleDispatcher
**
*******************************************************************************/
class QAuthenticationModuleDispatcherTest
{
/*******************************************************************************
** Test success case - a valid backend.
**
*******************************************************************************/
@Test
public void test_getQAuthenticationModule() throws QModuleDispatchException
{
QAuthenticationModuleInterface mock = new QAuthenticationModuleDispatcher().getQModule(TestUtils.defineAuthentication());
assertNotNull(mock);
}
/*******************************************************************************
** Test success case - a valid backend.
**
*******************************************************************************/
@Test
public void test_getQAuthenticationModuleByType_valid() throws QModuleDispatchException
{
QAuthenticationModuleInterface mock = new QAuthenticationModuleDispatcher().getQModule("mock");
assertNotNull(mock);
}
/*******************************************************************************
** Test failure case, a backend name that doesn't exist
**
*******************************************************************************/
@Test
public void test_getQAuthenticationModule_typeNotFound()
{
assertThrows(QModuleDispatchException.class, () ->
{
new QAuthenticationModuleDispatcher().getQModule("aTypeThatWontExist");
});
}
/*******************************************************************************
** Test failure case, null argument
**
*******************************************************************************/
@Test
public void test_getQAuthenticationModule_nullArgument()
{
assertThrows(QModuleDispatchException.class, () ->
{
new QAuthenticationModuleDispatcher().getQModule((QAuthenticationMetaData) null);
});
}
}

View File

@ -0,0 +1,69 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.modules.backend;
import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException;
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/*******************************************************************************
** Unit test for QModuleDispatcher
**
*******************************************************************************/
class QBackendModuleDispatcherTest
{
/*******************************************************************************
** Test success case - a valid backend.
**
*******************************************************************************/
@Test
public void test_getQModule_valid() throws QModuleDispatchException
{
QBackendModuleInterface qModule = new QBackendModuleDispatcher().getQBackendModule(TestUtils.defineBackend());
assertNotNull(qModule);
}
/*******************************************************************************
** Test failure case, a backend name that doesn't exist
**
*******************************************************************************/
@Test
public void test_getQModule_typeNotFound()
{
assertThrows(QModuleDispatchException.class, () ->
{
QBackendMetaData qBackendMetaData = TestUtils.defineBackend();
qBackendMetaData.setBackendType("aTypeThatWontEverExist");
new QBackendModuleDispatcher().getQBackendModule(qBackendMetaData);
});
}
}

View File

@ -0,0 +1,74 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.processes.implementations.bulk.delete;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/*******************************************************************************
** Unit test for BulkDeleteStoreStep
*******************************************************************************/
class BulkDeleteStoreStepTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testWithoutFilter() throws QException
{
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
stepInput.setSession(TestUtils.getMockSession());
stepInput.setTableName(TestUtils.defineTablePerson().getName());
stepInput.setRecords(TestUtils.queryTable(TestUtils.defineTablePerson().getName()));
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
new BulkDeleteStoreStep().run(stepInput, stepOutput);
assertEquals(0, stepOutput.getValueInteger(BulkDeleteStoreStep.ERROR_COUNT));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testWithFilter() throws QException
{
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
stepInput.setSession(TestUtils.getMockSession());
stepInput.setTableName(TestUtils.defineTablePerson().getName());
stepInput.addValue("queryFilterJSON", JsonUtils.toJson(new QQueryFilter()));
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
new BulkDeleteStoreStep().run(stepInput, stepOutput);
assertEquals(0, stepOutput.getValueInteger(BulkDeleteStoreStep.ERROR_COUNT));
}
}

View File

@ -0,0 +1,78 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.processes.implementations.bulk.edit;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
/*******************************************************************************
** Unit test for BulkEditReceiveValuesStep
*******************************************************************************/
class BulkEditReceiveValuesStepTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test() throws QException
{
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
stepInput.setSession(TestUtils.getMockSession());
stepInput.setTableName(TestUtils.defineTablePerson().getName());
stepInput.addValue(BulkEditReceiveValuesStep.FIELD_ENABLED_FIELDS, "firstName,email,birthDate");
stepInput.addValue("firstName", "Johnny");
stepInput.addValue("email", null);
stepInput.addValue("birthDate", "1909-01-09");
List<QRecord> records = TestUtils.queryTable(TestUtils.defineTablePerson().getName());
stepInput.setRecords(records);
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
new BulkEditReceiveValuesStep().run(stepInput, stepOutput);
String valuesBeingUpdated = stepOutput.getValueString(BulkEditReceiveValuesStep.FIELD_VALUES_BEING_UPDATED);
assertThat(valuesBeingUpdated).matches("(?s).*First Name.*Johnny.*");
assertThat(valuesBeingUpdated).matches("(?s).*Email will be cleared.*");
assertThat(valuesBeingUpdated).matches("(?s).*Birth Date.*1909-01-09.*");
int count = 0;
for(QRecord record : stepOutput.getRecords())
{
assertEquals("Johnny", record.getValueString("firstName"));
assertNull(record.getValue("email"));
// todo value utils needed in getValueDate... assertEquals(LocalDate.of(1909, 1, 9), record.getValueDate("birthDate"));
count++;
}
assertEquals(records.size(), count);
}
}

View File

@ -0,0 +1,85 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.processes.implementations.bulk.edit;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
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.assertNull;
/*******************************************************************************
** Unit test for BulkEditStoreRecordsStep
*******************************************************************************/
class BulkEditStoreRecordsStepTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test() throws QException
{
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
stepInput.setSession(TestUtils.getMockSession());
stepInput.setTableName(TestUtils.defineTablePerson().getName());
stepInput.addValue(BulkEditReceiveValuesStep.FIELD_ENABLED_FIELDS, "firstName,email,birthDate");
stepInput.addValue("firstName", "Johnny");
stepInput.addValue("email", null);
stepInput.addValue("birthDate", "1909-01-09");
List<QRecord> records = TestUtils.queryTable(TestUtils.defineTablePerson().getName());
stepInput.setRecords(records);
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
new BulkEditStoreRecordsStep().run(stepInput, stepOutput);
assertRecordValues(stepOutput.getRecords());
// re-fetch the records, make sure they are updated.
// but since Mock backend doesn't actually update them, we can't do this..
// todo - implement an in-memory backend, that would do this.
// List<QRecord> updatedRecords = TestUtils.queryTable(TestUtils.defineTablePerson().getName());
// assertRecordValues(updatedRecords);
}
/*******************************************************************************
**
*******************************************************************************/
private void assertRecordValues(List<QRecord> records)
{
for(QRecord record : records)
{
assertEquals("Johnny", record.getValueString("firstName"));
assertNull(record.getValue("email"));
// todo value utils needed in getValueDate... assertEquals(LocalDate.of(1909, 1, 9), record.getValueDate("birthDate"));
}
}
}

View File

@ -0,0 +1,114 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.processes.implementations.bulk.insert;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.model.actions.processes.QUploadedFile;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.state.StateType;
import com.kingsrook.qqq.backend.core.state.TempFileStateProvider;
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.*;
/*******************************************************************************
** Unit test for BulkInsertReceiveFileStep
*******************************************************************************/
class BulkInsertReceiveFileStepTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test() throws QException
{
////////////////////////////////////////////////////////////////
// create an uploaded file, similar to how an http server may //
////////////////////////////////////////////////////////////////
QUploadedFile qUploadedFile = new QUploadedFile();
qUploadedFile.setBytes((TestUtils.getPersonCsvHeaderUsingLabels() + TestUtils.getPersonCsvRow1() + TestUtils.getPersonCsvRow2()).getBytes());
qUploadedFile.setFilename("test.csv");
UUIDAndTypeStateKey key = new UUIDAndTypeStateKey(StateType.UPLOADED_FILE);
TempFileStateProvider.getInstance().put(key, qUploadedFile);
////////////////////////////
// setup and run the step //
////////////////////////////
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
stepInput.setSession(TestUtils.getMockSession());
stepInput.setTableName(TestUtils.defineTablePerson().getName());
stepInput.addValue(QUploadedFile.DEFAULT_UPLOADED_FILE_FIELD_NAME, key);
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
new BulkInsertReceiveFileStep().run(stepInput, stepOutput);
List<QRecord> records = stepOutput.getRecords();
assertEquals(2, records.size());
assertEquals("John", records.get(0).getValueString("firstName"));
assertEquals("Jane", records.get(1).getValueString("firstName"));
assertNull(records.get(0).getValue("id"));
assertNull(records.get(1).getValue("id"));
assertEquals(2, stepOutput.getValueInteger("noOfFileRows"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testBadFileType() throws QException
{
////////////////////////////////////////////////////////////////
// create an uploaded file, similar to how an http server may //
////////////////////////////////////////////////////////////////
QUploadedFile qUploadedFile = new QUploadedFile();
qUploadedFile.setBytes((TestUtils.getPersonCsvHeaderUsingLabels() + TestUtils.getPersonCsvRow1() + TestUtils.getPersonCsvRow2()).getBytes()); // todo - this is NOT excel content...
qUploadedFile.setFilename("test.xslx");
UUIDAndTypeStateKey key = new UUIDAndTypeStateKey(StateType.UPLOADED_FILE);
TempFileStateProvider.getInstance().put(key, qUploadedFile);
////////////////////////////
// setup and run the step //
////////////////////////////
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
stepInput.setSession(TestUtils.getMockSession());
stepInput.setTableName(TestUtils.defineTablePerson().getName());
stepInput.addValue(QUploadedFile.DEFAULT_UPLOADED_FILE_FIELD_NAME, key);
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
assertThrows(QUserFacingException.class, () ->
{
new BulkInsertReceiveFileStep().run(stepInput, stepOutput);
});
}
}

View File

@ -0,0 +1,80 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.processes.implementations.bulk.insert;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.processes.QUploadedFile;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.state.StateType;
import com.kingsrook.qqq.backend.core.state.TempFileStateProvider;
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.assertNotNull;
/*******************************************************************************
** Unit test for BulkInsertStoreRecordsStep
*******************************************************************************/
class BulkInsertStoreRecordsStepTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test() throws QException
{
////////////////////////////////////////////////////////////////
// create an uploaded file, similar to how an http server may //
////////////////////////////////////////////////////////////////
QUploadedFile qUploadedFile = new QUploadedFile();
qUploadedFile.setBytes((TestUtils.getPersonCsvHeaderUsingLabels() + TestUtils.getPersonCsvRow1() + TestUtils.getPersonCsvRow2()).getBytes());
qUploadedFile.setFilename("test.csv");
UUIDAndTypeStateKey key = new UUIDAndTypeStateKey(StateType.UPLOADED_FILE);
TempFileStateProvider.getInstance().put(key, qUploadedFile);
////////////////////////////
// setup and run the step //
////////////////////////////
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
stepInput.setSession(TestUtils.getMockSession());
stepInput.setTableName(TestUtils.defineTablePerson().getName());
stepInput.addValue(QUploadedFile.DEFAULT_UPLOADED_FILE_FIELD_NAME, key);
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
new BulkInsertStoreRecordsStep().run(stepInput, stepOutput);
List<QRecord> records = stepOutput.getRecords();
assertEquals(2, records.size());
assertEquals("John", records.get(0).getValueString("firstName"));
assertEquals("Jane", records.get(1).getValueString("firstName"));
assertNotNull(records.get(0).getValue("id"));
assertNotNull(records.get(1).getValue("id"));
}
}

View File

@ -0,0 +1,89 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.processes.implementations.etl.basic;
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.QKeyBasedFieldMapping;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
** Unit test for BasicETLProcess
*******************************************************************************/
class BasicETLProcessTest
{
/*******************************************************************************
** Simplest happy path
*******************************************************************************/
@Test
public void test() throws QException
{
RunProcessInput request = new RunProcessInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setProcessName(BasicETLProcess.PROCESS_NAME);
request.addValue(BasicETLProcess.FIELD_SOURCE_TABLE, TestUtils.defineTablePerson().getName());
request.addValue(BasicETLProcess.FIELD_DESTINATION_TABLE, TestUtils.definePersonFileTable().getName());
request.addValue(BasicETLProcess.FIELD_MAPPING_JSON, "");
RunProcessOutput 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());
}
/*******************************************************************************
** Basic example of doing a mapping transformation
*******************************************************************************/
@Test
public void testMappingTransformation() throws QException
{
RunProcessInput request = new RunProcessInput(TestUtils.defineInstance());
request.setSession(TestUtils.getMockSession());
request.setProcessName(BasicETLProcess.PROCESS_NAME);
request.addValue(BasicETLProcess.FIELD_SOURCE_TABLE, TestUtils.definePersonFileTable().getName());
request.addValue(BasicETLProcess.FIELD_DESTINATION_TABLE, TestUtils.defineTableIdAndNameOnly().getName());
///////////////////////////////////////////////////////////////////////////////////////
// define our mapping from destination-table field names to source-table field names //
///////////////////////////////////////////////////////////////////////////////////////
QKeyBasedFieldMapping mapping = new QKeyBasedFieldMapping().withMapping("name", "firstName");
// request.addValue(BasicETLProcess.FIELD_MAPPING_JSON, JsonUtils.toJson(mapping.getMapping()));
request.addValue(BasicETLProcess.FIELD_MAPPING_JSON, JsonUtils.toJson(mapping));
RunProcessOutput 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

@ -0,0 +1,76 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.processes.implementations.etl.basic;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
/*******************************************************************************
** Unit test for BasicETLTransformFunction
*******************************************************************************/
class BasicETLTransformFunctionTest
{
/*******************************************************************************
** Test the removeNonNumericValuesFromMappedRecords function
*******************************************************************************/
@Test
void testRemoveNonNumericValuesFromMappedRecords()
{
assertNull(doRemoveNonNumericValuesFromMappedRecords(QFieldType.INTEGER, null));
assertNull(doRemoveNonNumericValuesFromMappedRecords(QFieldType.INTEGER, "foo"));
assertNull(doRemoveNonNumericValuesFromMappedRecords(QFieldType.INTEGER, "1foo"));
assertEquals("1", doRemoveNonNumericValuesFromMappedRecords(QFieldType.INTEGER, "1"));
assertEquals("1000", doRemoveNonNumericValuesFromMappedRecords(QFieldType.INTEGER, "1,000"));
assertEquals("1000000", doRemoveNonNumericValuesFromMappedRecords(QFieldType.INTEGER, "1,000,000"));
assertNull(doRemoveNonNumericValuesFromMappedRecords(QFieldType.DECIMAL, null));
assertNull(doRemoveNonNumericValuesFromMappedRecords(QFieldType.DECIMAL, "foo"));
assertNull(doRemoveNonNumericValuesFromMappedRecords(QFieldType.DECIMAL, "1foo"));
assertEquals("1", doRemoveNonNumericValuesFromMappedRecords(QFieldType.DECIMAL, "1"));
assertEquals("1000", doRemoveNonNumericValuesFromMappedRecords(QFieldType.DECIMAL, "1,000"));
assertEquals("1.0", doRemoveNonNumericValuesFromMappedRecords(QFieldType.DECIMAL, "1.0"));
assertEquals("1000.00", doRemoveNonNumericValuesFromMappedRecords(QFieldType.DECIMAL, "1,000.00"));
assertEquals("1000000", doRemoveNonNumericValuesFromMappedRecords(QFieldType.DECIMAL, "1,000,000"));
}
private String doRemoveNonNumericValuesFromMappedRecords(QFieldType fieldType, String inputValue)
{
String field = "field";
QTableMetaData table = new QTableMetaData()
.withField(new QFieldMetaData(field, fieldType));
List<QRecord> records = List.of(new QRecord().withValue(field, inputValue));
new BasicETLTransformFunction().removeNonNumericValuesFromMappedRecords(table, records);
return (records.get(0).getValueString(field));
}
}

View File

@ -0,0 +1,89 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.state;
import java.util.UUID;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/*******************************************************************************
** Unit test for InMemoryStateProvider
*******************************************************************************/
public class InMemoryStateProviderTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testStateNotFound()
{
InMemoryStateProvider stateProvider = InMemoryStateProvider.getInstance();
UUIDAndTypeStateKey key = new UUIDAndTypeStateKey(StateType.PROCESS_STATUS);
Assertions.assertTrue(stateProvider.get(QRecord.class, key).isEmpty(), "Key not found in state should return empty");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testSimpleStateFound()
{
InMemoryStateProvider stateProvider = InMemoryStateProvider.getInstance();
UUIDAndTypeStateKey key = new UUIDAndTypeStateKey(StateType.PROCESS_STATUS);
String uuid = UUID.randomUUID().toString();
QRecord qRecord = new QRecord().withValue("uuid", uuid);
stateProvider.put(key, qRecord);
QRecord qRecordFromState = stateProvider.get(QRecord.class, key).get();
Assertions.assertEquals(uuid, qRecordFromState.getValueString("uuid"), "Should read value from state persistence");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testWrongTypeOnGet()
{
InMemoryStateProvider stateProvider = InMemoryStateProvider.getInstance();
UUIDAndTypeStateKey key = new UUIDAndTypeStateKey(StateType.PROCESS_STATUS);
String uuid = UUID.randomUUID().toString();
QRecord qRecord = new QRecord().withValue("uuid", uuid);
stateProvider.put(key, qRecord);
Assertions.assertThrows(Exception.class, () ->
{
stateProvider.get(QTableMetaData.class, key);
});
}
}

View File

@ -0,0 +1,89 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.state;
import java.util.UUID;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/*******************************************************************************
** Unit test for TempFileStateProvider
*******************************************************************************/
public class TempFileStateProviderTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testStateNotFound()
{
TempFileStateProvider stateProvider = TempFileStateProvider.getInstance();
UUIDAndTypeStateKey key = new UUIDAndTypeStateKey(StateType.PROCESS_STATUS);
Assertions.assertTrue(stateProvider.get(QRecord.class, key).isEmpty(), "Key not found in state should return empty");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testSimpleStateFound()
{
TempFileStateProvider stateProvider = TempFileStateProvider.getInstance();
UUIDAndTypeStateKey key = new UUIDAndTypeStateKey(StateType.PROCESS_STATUS);
String uuid = UUID.randomUUID().toString();
QRecord qRecord = new QRecord().withValue("uuid", uuid);
stateProvider.put(key, qRecord);
QRecord qRecordFromState = stateProvider.get(QRecord.class, key).get();
Assertions.assertEquals(uuid, qRecordFromState.getValueString("uuid"), "Should read value from state persistence");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testWrongTypeOnGet()
{
TempFileStateProvider stateProvider = TempFileStateProvider.getInstance();
UUIDAndTypeStateKey key = new UUIDAndTypeStateKey(StateType.PROCESS_STATUS);
String uuid = UUID.randomUUID().toString();
QRecord qRecord = new QRecord().withValue("uuid", uuid);
stateProvider.put(key, qRecord);
Assertions.assertThrows(Exception.class, () ->
{
stateProvider.get(QTableMetaData.class, key);
});
}
}

View File

@ -0,0 +1,501 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
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.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
** Unit test for CollectionUtils
**
*******************************************************************************/
class CollectionUtilsTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_nullSafeIsEmpty_Map()
{
assertTrue(CollectionUtils.nullSafeIsEmpty(Collections.emptyMap()));
assertTrue(CollectionUtils.nullSafeIsEmpty(new HashMap<>()));
assertTrue(CollectionUtils.nullSafeIsEmpty((Map<?, ?>) null));
Map<String, Integer> myMap = new HashMap<>();
assertTrue(CollectionUtils.nullSafeIsEmpty(myMap));
myMap.put("A", 1);
assertFalse(CollectionUtils.nullSafeIsEmpty(myMap));
myMap.clear();
assertTrue(CollectionUtils.nullSafeIsEmpty(myMap));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_nullSafeIsEmpty_Collection()
{
assertTrue(CollectionUtils.nullSafeIsEmpty(Collections.emptyList()));
assertTrue(CollectionUtils.nullSafeIsEmpty(new ArrayList<>()));
assertTrue(CollectionUtils.nullSafeIsEmpty((List<?>) null));
List<String> myList = new ArrayList<>();
assertTrue(CollectionUtils.nullSafeIsEmpty(myList));
myList.add("A");
assertFalse(CollectionUtils.nullSafeIsEmpty(myList));
myList.clear();
assertTrue(CollectionUtils.nullSafeIsEmpty(myList));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_nullSafeHasContents_Map()
{
assertFalse(CollectionUtils.nullSafeHasContents(Collections.emptyMap()));
assertFalse(CollectionUtils.nullSafeHasContents(new HashMap<>()));
assertFalse(CollectionUtils.nullSafeHasContents((Map<?, ?>) null));
Map<String, Integer> myMap = new HashMap<>();
assertFalse(CollectionUtils.nullSafeHasContents(myMap));
myMap.put("A", 1);
assertTrue(CollectionUtils.nullSafeHasContents(myMap));
myMap.clear();
assertFalse(CollectionUtils.nullSafeHasContents(myMap));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_nullSafeHasContents_Collection()
{
assertFalse(CollectionUtils.nullSafeHasContents(Collections.emptyList()));
assertFalse(CollectionUtils.nullSafeHasContents(new ArrayList<>()));
assertFalse(CollectionUtils.nullSafeHasContents((List<?>) null));
List<String> myList = new ArrayList<>();
assertFalse(CollectionUtils.nullSafeHasContents(myList));
myList.add("A");
assertTrue(CollectionUtils.nullSafeHasContents(myList));
myList.clear();
assertFalse(CollectionUtils.nullSafeHasContents(myList));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_nullSafeSize_Map()
{
assertEquals(0, CollectionUtils.nullSafeSize(Collections.emptyMap()));
assertEquals(0, CollectionUtils.nullSafeSize(new HashMap<>()));
assertEquals(0, CollectionUtils.nullSafeSize((Map<?, ?>) null));
Map<String, Integer> myMap = new HashMap<>();
assertEquals(0, CollectionUtils.nullSafeSize(myMap));
myMap.put("A", 1);
assertEquals(1, CollectionUtils.nullSafeSize(myMap));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_nullSafeSize_Collection()
{
assertEquals(0, CollectionUtils.nullSafeSize(Collections.emptyList()));
assertEquals(0, CollectionUtils.nullSafeSize(new ArrayList<>()));
assertEquals(0, CollectionUtils.nullSafeSize((List<?>) null));
List<String> myList = new ArrayList<>();
assertEquals(0, CollectionUtils.nullSafeSize(myList));
myList.add("A");
assertEquals(1, CollectionUtils.nullSafeSize(myList));
}
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("ConstantConditions")
@Test
void test_addAllToMap()
{
Map<String, Integer> to = new HashMap<>();
Map<String, Integer> from = new HashMap<>();
assertThrows(NullPointerException.class, () -> CollectionUtils.addAllToMap(null, null));
assertThrows(NullPointerException.class, () -> CollectionUtils.addAllToMap(to, null));
// this case does not currently throw - capture that fact here in case we change it.
CollectionUtils.addAllToMap(null, from);
CollectionUtils.addAllToMap(to, from);
assertEquals(0, to.size());
from.put("A", 1);
CollectionUtils.addAllToMap(to, from);
assertEquals(1, to.size());
assertEquals(1, to.get("A"));
from.put("B", 2);
CollectionUtils.addAllToMap(to, from);
assertEquals(2, to.size());
assertEquals(1, to.get("A"));
assertEquals(2, to.get("B"));
from.put("B", 3);
CollectionUtils.addAllToMap(to, from);
assertEquals(2, to.size());
assertEquals(1, to.get("A"));
assertEquals(3, to.get("B"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_listToMap()
{
assertNull(CollectionUtils.listToMap(null, null));
List<String> myList = new ArrayList<>();
myList.add("Apple");
myList.add("Banana");
assertThrows(NullPointerException.class, () -> CollectionUtils.listToMap(myList, null));
Map<String, String> myMap = CollectionUtils.listToMap(myList, first());
assertEquals(2, myMap.size());
assertEquals("Apple", myMap.get("A"));
assertEquals("Banana", myMap.get("B"));
// confirm what a clobbered key does
myList.add("Airplane");
myMap = CollectionUtils.listToMap(myList, first());
assertEquals(2, myMap.size());
assertEquals("Airplane", myMap.get("A"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_listToMap_valueFunction()
{
assertNull(CollectionUtils.listToMap(null, null, null));
List<String> myList = new ArrayList<>();
myList.add("Apple");
myList.add("Banana");
assertThrows(NullPointerException.class, () -> CollectionUtils.listToMap(myList, null, null));
Map<String, String> myMap = CollectionUtils.listToMap(myList, first(), rest());
assertEquals(2, myMap.size());
assertEquals("pple", myMap.get("A"));
assertEquals("anana", myMap.get("B"));
// confirm what a clobbered key does
myList.add("Airplane");
myMap = CollectionUtils.listToMap(myList, first(), rest());
assertEquals(2, myMap.size());
assertEquals("irplane", myMap.get("A"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_listToListingHash()
{
assertNull(CollectionUtils.listToListingHash(null, null));
List<String> myList = new ArrayList<>();
myList.add("Apple");
myList.add("Banana");
assertThrows(NullPointerException.class, () -> CollectionUtils.listToListingHash(myList, null));
ListingHash<String, String> myListingHash = CollectionUtils.listToListingHash(myList, first());
assertEquals(2, myListingHash.size());
assertEquals(1, myListingHash.get("A").size());
assertEquals("Apple", myListingHash.get("A").get(0));
assertEquals(1, myListingHash.get("B").size());
assertEquals("Banana", myListingHash.get("B").get(0));
myList.add("Airplane");
myListingHash = CollectionUtils.listToListingHash(myList, first());
assertEquals(2, myListingHash.size());
assertEquals(2, myListingHash.get("A").size());
assertEquals("Apple", myListingHash.get("A").get(0));
assertEquals("Airplane", myListingHash.get("A").get(1));
assertEquals(1, myListingHash.get("B").size());
assertEquals("Banana", myListingHash.get("B").get(0));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_listToListingHash_valueFunction()
{
assertNull(CollectionUtils.listToListingHash(null, null, null));
List<String> myList = new ArrayList<>();
myList.add("Apple");
myList.add("Banana");
assertThrows(NullPointerException.class, () -> CollectionUtils.listToListingHash(myList, null, null));
ListingHash<String, String> myListingHash = CollectionUtils.listToListingHash(myList, first(), rest());
assertEquals(2, myListingHash.size());
assertEquals(1, myListingHash.get("A").size());
assertEquals("pple", myListingHash.get("A").get(0));
assertEquals(1, myListingHash.get("B").size());
assertEquals("anana", myListingHash.get("B").get(0));
myList.add("Airplane");
myListingHash = CollectionUtils.listToListingHash(myList, first(), rest());
assertEquals(2, myListingHash.size());
assertEquals(2, myListingHash.get("A").size());
assertEquals("pple", myListingHash.get("A").get(0));
assertEquals("irplane", myListingHash.get("A").get(1));
assertEquals(1, myListingHash.get("B").size());
assertEquals("anana", myListingHash.get("B").get(0));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_listTo2LevelMap()
{
assertNull(CollectionUtils.listTo2LevelMap(null, null, null));
List<String> myList = new ArrayList<>();
myList.add("Apple");
myList.add("Banana");
myList.add("Airplane");
assertThrows(NullPointerException.class, () -> CollectionUtils.listTo2LevelMap(myList, null, null));
assertThrows(NullPointerException.class, () -> CollectionUtils.listTo2LevelMap(myList, first(), null));
assertThrows(NullPointerException.class, () -> CollectionUtils.listTo2LevelMap(myList, null, second()));
Map<String, Map<String, String>> myMap = CollectionUtils.listTo2LevelMap(myList, first(), second());
assertEquals(2, myMap.size());
assertEquals(2, myMap.get("A").size());
assertEquals("Apple", myMap.get("A").get("p"));
assertEquals("Airplane", myMap.get("A").get("i"));
assertEquals(1, myMap.get("B").size());
assertEquals("Banana", myMap.get("B").get("a"));
// demonstrate clobbering behavior
myList.add("Ape");
myMap = CollectionUtils.listTo2LevelMap(myList, first(), second());
assertEquals(2, myMap.get("A").size());
assertEquals("Ape", myMap.get("A").get("p"));
}
/*******************************************************************************
** helper method to get rest of string (unsafely)
*******************************************************************************/
private Function<String, String> rest()
{
return s -> s.substring(1);
}
/*******************************************************************************
** helper method to get first char of string (unsafely)
*******************************************************************************/
private Function<String, String> first()
{
return s -> s.substring(0, 1);
}
/*******************************************************************************
** helper method to get second char of string (unsafely)
*******************************************************************************/
private Function<String, String> second()
{
return s -> s.substring(1, 2);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_getPages()
{
List<List<Integer>> pages = CollectionUtils.getPages(null, 5);
assertEquals(0, pages.size());
pages = CollectionUtils.getPages(Collections.emptyList(), 5);
assertEquals(0, pages.size());
pages = CollectionUtils.getPages(List.of(1, 2, 3), 5);
assertEquals(1, pages.size());
assertEquals(3, pages.get(0).size());
pages = CollectionUtils.getPages(List.of(1, 2, 3, 4, 5), 5);
assertEquals(1, pages.size());
assertEquals(5, pages.get(0).size());
pages = CollectionUtils.getPages(List.of(1, 2, 3, 4, 5, 6, 7), 5);
assertEquals(2, pages.size());
assertEquals(5, pages.get(0).size());
assertEquals(2, pages.get(1).size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_getQuestionMarks()
{
assertEquals("", CollectionUtils.getQuestionMarks(null));
assertEquals("", CollectionUtils.getQuestionMarks(Collections.emptyList()));
assertEquals("?", CollectionUtils.getQuestionMarks(List.of(1)));
assertEquals("?,?,?", CollectionUtils.getQuestionMarks(List.of(1, 2, 3)));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_safelyGetPage()
{
List<Integer> empty = Collections.emptyList();
List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
/////////////////////
// null list input //
/////////////////////
assertNull(CollectionUtils.safelyGetPage(null, null, null));
assertNull(CollectionUtils.safelyGetPage(null, 1, 1));
assertNull(CollectionUtils.safelyGetPage(null, null, 1));
assertNull(CollectionUtils.safelyGetPage(null, 1, null));
//////////////////////
// empty list input //
//////////////////////
assertEquals(empty, CollectionUtils.safelyGetPage(empty, null, null));
assertEquals(empty, CollectionUtils.safelyGetPage(empty, 1, 1));
assertEquals(empty, CollectionUtils.safelyGetPage(empty, null, 1));
assertEquals(empty, CollectionUtils.safelyGetPage(empty, 1, null));
////////////////////////////////////////
// cases that give back the full list //
////////////////////////////////////////
assertEquals(list, CollectionUtils.safelyGetPage(list, null, null));
assertEquals(list, CollectionUtils.safelyGetPage(list, 0, null));
///////////////////
// empty outputs //
///////////////////
assertEquals(empty, CollectionUtils.safelyGetPage(list, 0, 0));
assertEquals(empty, CollectionUtils.safelyGetPage(list, 10, 1));
assertEquals(empty, CollectionUtils.safelyGetPage(list, 20, 10));
///////////////////////
// illegal arguments //
///////////////////////
assertThrows(IllegalArgumentException.class, () -> CollectionUtils.safelyGetPage(list, -1, 1));
assertThrows(IllegalArgumentException.class, () -> CollectionUtils.safelyGetPage(list, 1, -1));
assertThrows(IllegalArgumentException.class, () -> CollectionUtils.safelyGetPage(list, -1, -1));
assertThrows(IllegalArgumentException.class, () -> CollectionUtils.safelyGetPage(null, -1, -1));
/////////////////////////////
// normal kinds of outputs //
/////////////////////////////
assertEquals(List.of(1), CollectionUtils.safelyGetPage(list, null, 1));
assertEquals(List.of(1), CollectionUtils.safelyGetPage(list, 0, 1));
assertEquals(List.of(2), CollectionUtils.safelyGetPage(list, 1, 1));
assertEquals(List.of(2, 3), CollectionUtils.safelyGetPage(list, 1, 2));
assertEquals(List.of(2, 3, 4), CollectionUtils.safelyGetPage(list, 1, 3));
assertEquals(List.of(9), CollectionUtils.safelyGetPage(list, 8, 1));
assertEquals(List.of(9, 10), CollectionUtils.safelyGetPage(list, 8, 2));
assertEquals(List.of(9, 10), CollectionUtils.safelyGetPage(list, 8, 10));
assertEquals(List.of(10), CollectionUtils.safelyGetPage(list, 9, 1));
assertEquals(List.of(10), CollectionUtils.safelyGetPage(list, 9, 2));
assertEquals(List.of(10), CollectionUtils.safelyGetPage(list, 9, 10));
/////////////////////////////////////////////////////////
// make sure scrolling through pages works as expected //
/////////////////////////////////////////////////////////
int skip = 0;
int limit = 3;
int pageCount = 0;
List<Integer> accumulator = new ArrayList<>();
while (true)
{
List<Integer> nextPage = CollectionUtils.safelyGetPage(list, skip, limit);
if (nextPage.isEmpty())
{
break;
}
accumulator.addAll(nextPage);
skip += limit;
pageCount++;
}
assertEquals(4, pageCount);
assertEquals(list, accumulator);
}
}

View File

@ -0,0 +1,126 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.utils;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
/*******************************************************************************
** Unit test for ExceptionUtils
**
*******************************************************************************/
class ExceptionUtilsTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testFindClassInRootChain()
{
assertNull(ExceptionUtils.findClassInRootChain(null, QUserFacingException.class));
QUserFacingException target = new QUserFacingException("target");
assertSame(target, ExceptionUtils.findClassInRootChain(target, QUserFacingException.class));
assertSame(target, ExceptionUtils.findClassInRootChain(new QException("decoy", target), QUserFacingException.class));
assertNull(ExceptionUtils.findClassInRootChain(new QException("decoy", target), IllegalArgumentException.class));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetRootException()
{
assertNull(ExceptionUtils.getRootException(null));
Exception root = new Exception("root");
assertSame(root, ExceptionUtils.getRootException(root));
Exception container = new Exception("container", root);
assertSame(root, ExceptionUtils.getRootException(container));
Exception middle = new Exception("middle", root);
Exception top = new Exception("top", middle);
assertSame(root, ExceptionUtils.getRootException(top));
////////////////////////////////////////////////////////////////////////////////////////
// without the code that checks for loops, these next two checks cause infinite loops //
////////////////////////////////////////////////////////////////////////////////////////
MyException selfCaused = new MyException("selfCaused");
selfCaused.setCause(selfCaused);
assertSame(selfCaused, ExceptionUtils.getRootException(selfCaused));
MyException cycle1 = new MyException("cycle1");
MyException cycle2 = new MyException("cycle2");
cycle1.setCause(cycle2);
cycle2.setCause(cycle1);
assertSame(cycle1, ExceptionUtils.getRootException(cycle1));
assertSame(cycle2, ExceptionUtils.getRootException(cycle2));
}
/*******************************************************************************
** Test exception class - lets you set the cause, easier to create a loop.
*******************************************************************************/
public class MyException extends Exception
{
private Throwable myCause = null;
public MyException(String message)
{
super(message);
}
public MyException(Throwable cause)
{
super(cause);
}
public void setCause(Throwable cause)
{
myCause = cause;
}
@Override
public synchronized Throwable getCause()
{
return (myCause);
}
}
}

View File

@ -0,0 +1,296 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.utils;
import java.io.IOException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
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.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/*******************************************************************************
** Unit test for JsonUtils.
**
*******************************************************************************/
class JsonUtilsTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJsonQRecordInput()
{
QRecord qRecord = getQRecord();
String json = JsonUtils.toJson(qRecord);
assertEquals("""
{"tableName":"foo","values":{"foo":"Foo","bar":3.14159}}""", json);
}
/*******************************************************************************
**
*******************************************************************************/
private static class LooksLikeAnEntityButJustThrowsInItsGetterMethod
{
public String getValue()
{
throw new IllegalStateException("Test");
}
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJsonNull()
{
assertThrowsExactly(IllegalArgumentException.class,
() -> JsonUtils.toJson(new LooksLikeAnEntityButJustThrowsInItsGetterMethod()));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toPrettyJsonNull()
{
assertThrowsExactly(IllegalArgumentException.class,
() -> JsonUtils.toPrettyJson(new LooksLikeAnEntityButJustThrowsInItsGetterMethod()));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toPrettyJsonQRecordInput()
{
QRecord qRecord = getQRecord();
String json = JsonUtils.toPrettyJson(qRecord);
// todo assertEquals("{\"tableName\":\"foo\",\"primaryKey\":1,\"values\":{\"foo\":\"Foo\",\"bar\":3.14159}}", json);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJSONObject()
{
JSONObject jsonObject = JsonUtils.toJSONObject("""
{
"Foo": "Bar",
"Baz": [1, 2, 3]
}
""");
assertNotNull(jsonObject);
assertTrue(jsonObject.has("Foo"));
assertEquals("Bar", jsonObject.getString("Foo"));
assertTrue(jsonObject.has("Baz"));
assertEquals(3, jsonObject.getJSONArray("Baz").length());
assertEquals(1, jsonObject.getJSONArray("Baz").get(0));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJSONObject_malformed()
{
// todo - what do we want to throw here?
assertThrows(JSONException.class, () ->
{
JsonUtils.toJSONObject("""
{
"Foo": "Bar",
""");
});
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJSONObjectNonJsonObject()
{
try
{
JsonUtils.toJSONObject("");
}
catch(JSONException je)
{
System.out.println("Caught Expected exception");
return;
}
fail("Did not catch expected exception");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJSONArray()
{
JSONArray jsonArray = JsonUtils.toJSONArray("""
[
{"Foo": "Bar"},
{"Baz": [1, 2, 3]}
]
""");
assertNotNull(jsonArray);
assertEquals(2, jsonArray.length());
assertTrue(jsonArray.getJSONObject(0).has("Foo"));
assertEquals("Bar", jsonArray.getJSONObject(0).getString("Foo"));
assertTrue(jsonArray.getJSONObject(1).has("Baz"));
assertEquals(3, jsonArray.getJSONObject(1).getJSONArray("Baz").length());
}
/*******************************************************************************
**
*******************************************************************************/
private QRecord getQRecord()
{
QRecord qRecord = new QRecord();
qRecord.setTableName("foo");
qRecord.setValue("id", 1);
Map<String, Serializable> values = new LinkedHashMap<>();
qRecord.setValues(values);
values.put("foo", "Foo");
values.put("bar", new BigDecimal("3.14159"));
return qRecord;
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJsonNullInput()
{
String json = JsonUtils.toJson(null);
assertEquals("null", json);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_looksLikeObject()
{
assertFalse(JsonUtils.looksLikeObject(""));
assertFalse(JsonUtils.looksLikeObject(null));
assertFalse(JsonUtils.looksLikeObject("json"));
assertFalse(JsonUtils.looksLikeObject("[]"));
assertTrue(JsonUtils.looksLikeObject("{}"));
assertTrue(JsonUtils.looksLikeObject(" {}"));
assertTrue(JsonUtils.looksLikeObject("\n\n\n{}"));
assertTrue(JsonUtils.looksLikeObject("\n{\n[]\n}\n"));
assertTrue(JsonUtils.looksLikeObject("\n\n\n { \n}\n\n\n"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_looksLikeArray()
{
assertFalse(JsonUtils.looksLikeArray(""));
assertFalse(JsonUtils.looksLikeArray(null));
assertFalse(JsonUtils.looksLikeArray("json"));
assertFalse(JsonUtils.looksLikeArray("{json[]}"));
assertFalse(JsonUtils.looksLikeArray("{}"));
assertTrue(JsonUtils.looksLikeArray("[]"));
assertTrue(JsonUtils.looksLikeArray(" []"));
assertTrue(JsonUtils.looksLikeArray("\n\n\n[]"));
assertTrue(JsonUtils.looksLikeArray("\n[\n{}\n}\n"));
assertTrue(JsonUtils.looksLikeArray("\n\n\n [ \n]\n\n\n"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toObject() throws IOException
{
String json = JsonUtils.toJson(new QQueryFilter()
.withCriteria(new QFilterCriteria().withFieldName("id").withOperator(QCriteriaOperator.EQUALS).withValues(List.of(1)))
.withCriteria(new QFilterCriteria().withFieldName("name").withOperator(QCriteriaOperator.IN).withValues(List.of("Darin", "James")))
.withOrderBy(new QFilterOrderBy().withFieldName("age").withIsAscending(true)));
System.out.println(json);
QQueryFilter qQueryFilter = JsonUtils.toObject(json, QQueryFilter.class);
assertEquals(2, qQueryFilter.getCriteria().size());
assertEquals("id", qQueryFilter.getCriteria().get(0).getFieldName());
assertEquals(QCriteriaOperator.EQUALS, qQueryFilter.getCriteria().get(0).getOperator());
assertEquals(List.of(1), qQueryFilter.getCriteria().get(0).getValues());
assertEquals("name", qQueryFilter.getCriteria().get(1).getFieldName());
assertEquals(QCriteriaOperator.IN, qQueryFilter.getCriteria().get(1).getOperator());
assertEquals(List.of("Darin", "James"), qQueryFilter.getCriteria().get(1).getValues());
assertEquals(1, qQueryFilter.getOrderBys().size());
assertEquals("age", qQueryFilter.getOrderBys().get(0).getFieldName());
assertTrue(qQueryFilter.getOrderBys().get(0).getIsAscending());
}
}

View File

@ -0,0 +1,354 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.utils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
** Unit test for ListingHash
**
*******************************************************************************/
class ListingHashTest
{
/*******************************************************************************
**
*******************************************************************************/
private ListingHash<String, String> makeDefaultTestListingHash()
{
ListingHash<String, String> listingHash = new ListingHash<>();
listingHash.add("A", "Apple");
listingHash.add("A", "Acorn");
listingHash.add("B", "Ball");
return listingHash;
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_add()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
assertEquals(2, listingHash.size());
assertEquals(2, listingHash.get("A").size());
assertEquals(1, listingHash.get("B").size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_addAllCollectionKeyFunction()
{
ListingHash<String, String> listingHash = new ListingHash<>();
listingHash.addAll(Set.of("Apple", "Acorn", "Ball"), (s -> s.substring(0, 1)));
assertEquals(2, listingHash.size());
assertEquals(2, listingHash.get("A").size());
assertEquals(1, listingHash.get("B").size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_addAllListingHash()
{
ListingHash<String, String> source = makeDefaultTestListingHash();
ListingHash<String, String> listingHash = new ListingHash<>();
source.add("A", "Arrow");
source.add("C", "Car");
listingHash.addAll(source);
assertEquals(3, listingHash.size());
assertEquals(3, listingHash.get("A").size());
assertEquals(1, listingHash.get("B").size());
assertEquals(1, listingHash.get("C").size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_addAllKeyCollection()
{
ListingHash<String, String> listingHash = new ListingHash<>();
listingHash.add("A", "Arrow");
listingHash.addAll("A", Set.of("Apple", "Acorn"));
listingHash.addAll("B", Set.of("Ball"));
assertEquals(2, listingHash.size());
assertEquals(3, listingHash.get("A").size());
assertEquals(1, listingHash.get("B").size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_clear()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
listingHash.clear();
assertEquals(0, listingHash.size());
assertFalse(listingHash.containsKey("A"));
assertFalse(listingHash.containsKey("B"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_containsKey()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
assertTrue(listingHash.containsKey("A"));
assertTrue(listingHash.containsKey("B"));
assertFalse(listingHash.containsKey("C"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_containsValue()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
assertTrue(listingHash.containsValue(List.of("Apple", "Acorn")));
assertFalse(listingHash.containsValue("Apple"));
assertFalse(listingHash.containsValue(List.of("Apple")));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_entrySet()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
Set<Map.Entry<String, List<String>>> entrySet = listingHash.entrySet();
assertEquals(2, entrySet.size());
}
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings({ "SimplifiableAssertion", "EqualsWithItself" })
@Test
void test_equals()
{
ListingHash<String, String> listingHash1 = makeDefaultTestListingHash();
ListingHash<String, String> listingHash2 = makeDefaultTestListingHash();
assertTrue(listingHash1.equals(listingHash2));
assertTrue(listingHash1.equals(listingHash1));
assertFalse(listingHash1.equals(new ListingHash<>()));
assertFalse(new ListingHash<>().equals(listingHash1));
listingHash2.add("C", "Car");
assertFalse(listingHash1.equals(listingHash2));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_get()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
List<String> aList = listingHash.get("A");
assertEquals(2, aList.size());
assertEquals("Apple", aList.get(0));
assertEquals("Acorn", aList.get(1));
List<String> bList = listingHash.get("B");
assertEquals(1, bList.size());
assertEquals("Ball", bList.get(0));
assertNull(listingHash.get("C"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_hashCode()
{
ListingHash<String, String> listingHash1 = makeDefaultTestListingHash();
ListingHash<String, String> listingHash2 = makeDefaultTestListingHash();
assertEquals(listingHash1.hashCode(), listingHash2.hashCode());
assertEquals(listingHash1.hashCode(), listingHash1.hashCode());
assertNotEquals(listingHash1.hashCode(), new ListingHash<>().hashCode());
listingHash2.add("C", "Car");
assertNotEquals(listingHash1.hashCode(), listingHash2.hashCode());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_isEmpty()
{
assertTrue(new ListingHash<>().isEmpty());
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
assertFalse(listingHash.isEmpty());
listingHash.clear();
assertTrue(listingHash.isEmpty());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_keySet()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
Set<String> keySet = listingHash.keySet();
assertEquals(2, keySet.size());
assertTrue(keySet.contains("A"));
assertTrue(keySet.contains("B"));
assertFalse(keySet.contains("C"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_put()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
listingHash.put("A", List.of("Android"));
assertEquals(2, listingHash.size());
assertEquals(1, listingHash.get("A").size());
assertEquals("Android", listingHash.get("A").get(0));
listingHash.put("C", List.of("Car"));
assertEquals(3, listingHash.size());
assertEquals(1, listingHash.get("C").size());
assertEquals("Car", listingHash.get("C").get(0));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_putAll()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
listingHash.putAll(Map.of("C", List.of("Car"), "D", List.of("Dog", "Door")));
assertEquals(4, listingHash.size());
assertEquals(2, listingHash.get("A").size());
assertEquals("Apple", listingHash.get("A").get(0));
assertEquals("Acorn", listingHash.get("A").get(1));
assertEquals(1, listingHash.get("C").size());
assertEquals("Car", listingHash.get("C").get(0));
assertEquals(2, listingHash.get("D").size());
assertEquals("Dog", listingHash.get("D").get(0));
assertEquals("Door", listingHash.get("D").get(1));
listingHash.putAll(Map.of("A", List.of("Android")));
assertEquals(4, listingHash.size());
assertEquals(1, listingHash.get("A").size());
assertEquals("Android", listingHash.get("A").get(0));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_remove()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
listingHash.remove("A");
assertEquals(1, listingHash.size());
assertFalse(listingHash.containsKey("A"));
listingHash.remove("B");
assertEquals(0, listingHash.size());
listingHash.remove("B");
listingHash.remove("C");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_size()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
assertEquals(2, listingHash.size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_values()
{
ListingHash<String, String> listingHash = makeDefaultTestListingHash();
Collection<List<String>> values = listingHash.values();
assertEquals(2, values.size());
assertTrue(values.contains(List.of("Apple", "Acorn")));
assertTrue(values.contains(List.of("Ball")));
}
}

View File

@ -0,0 +1,50 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.utils;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
** Unit test for SleepUtils
*******************************************************************************/
class SleepUtilsTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void testSleep()
{
long start = System.currentTimeMillis();
SleepUtils.sleep(10, TimeUnit.MILLISECONDS);
long end = System.currentTimeMillis();
long sleptFor = end - start;
System.out.println("Slept for: " + sleptFor);
assertTrue(sleptFor >= 10);
}
}

View File

@ -0,0 +1,249 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.utils;
import java.util.List;
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;
/*******************************************************************************
** Unit test for StringUtils
**
*******************************************************************************/
class StringUtilsTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_hasContent()
{
assertFalse(StringUtils.hasContent(""));
assertFalse(StringUtils.hasContent(" "));
assertFalse(StringUtils.hasContent(" \n "));
assertFalse(StringUtils.hasContent(null));
assertTrue(StringUtils.hasContent("a"));
assertTrue(StringUtils.hasContent(" a "));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_nvlString()
{
assertEquals("foo", StringUtils.nvl("foo", "bar"));
assertEquals("bar", StringUtils.nvl(null, "bar"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_nvlObject()
{
assertEquals("1701", StringUtils.nvl(1701, "bar"));
assertEquals("bar", StringUtils.nvl((Integer) null, "bar"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_allCapsToMixedCase()
{
assertNull(StringUtils.allCapsToMixedCase(null));
assertEquals("Foo", StringUtils.allCapsToMixedCase("FOO"));
assertEquals("Foo Bar", StringUtils.allCapsToMixedCase("FOO BAR"));
assertEquals("Foo bar", StringUtils.allCapsToMixedCase("FOO bar"));
assertEquals("Foo bar", StringUtils.allCapsToMixedCase("FOo bar"));
assertEquals("Foo Bar", StringUtils.allCapsToMixedCase("FOo BAr"));
assertEquals("foo bar", StringUtils.allCapsToMixedCase("foo bar"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_safeTruncate2()
{
assertNull(StringUtils.safeTruncate(null, 5));
assertEquals("123", StringUtils.safeTruncate("123", 5));
assertEquals("12345", StringUtils.safeTruncate("1234567", 5));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_safeTruncate3()
{
assertNull(StringUtils.safeTruncate(null, 5, "..."));
assertEquals("123", StringUtils.safeTruncate("123", 5, "..."));
assertEquals("12345", StringUtils.safeTruncate("12345", 5, "..."));
assertEquals("12...", StringUtils.safeTruncate("123456", 5, "..."));
assertEquals("12...", StringUtils.safeTruncate("1234567890", 5, "..."));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_safeTrim()
{
assertNull(StringUtils.safeTrim(null));
assertEquals("foo", StringUtils.safeTrim("foo "));
assertEquals("foo", StringUtils.safeTrim(" foo "));
assertEquals("foo", StringUtils.safeTrim(" foo"));
assertEquals("foo", StringUtils.safeTrim(" foo \n "));
assertEquals("foo", StringUtils.safeTrim("\nfoo \r \n "));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_join()
{
assertNull(StringUtils.join(",", null));
assertEquals("1", StringUtils.join(",", List.of(1)));
assertEquals("", StringUtils.join(",", List.of()));
assertEquals("1,2,3", StringUtils.join(",", List.of(1, 2, 3)));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_joinWithCommasAndAnd()
{
assertNull(StringUtils.joinWithCommasAndAnd(null));
assertEquals("", StringUtils.joinWithCommasAndAnd(List.of()));
assertEquals("A", StringUtils.joinWithCommasAndAnd(List.of("A")));
assertEquals("A and B", StringUtils.joinWithCommasAndAnd(List.of("A", "B")));
assertEquals("A, B, and C", StringUtils.joinWithCommasAndAnd(List.of("A", "B", "C")));
assertEquals("A, B, C, and D", StringUtils.joinWithCommasAndAnd(List.of("A", "B", "C", "D")));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_ltrim()
{
assertEquals("", StringUtils.ltrim(null));
assertEquals("123", StringUtils.ltrim("123"));
assertEquals("123", StringUtils.ltrim(" 123"));
assertEquals("123", StringUtils.ltrim(" 123"));
assertEquals("123", StringUtils.ltrim(" \n\n 123"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_rtrim()
{
assertEquals("", StringUtils.rtrim(null));
assertEquals("123", StringUtils.rtrim("123"));
assertEquals("123", StringUtils.rtrim("123 "));
assertEquals("123", StringUtils.rtrim("123 "));
assertEquals("123", StringUtils.rtrim("123 \n\n "));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_pluralCollection()
{
assertEquals("s", StringUtils.plural(List.of()));
assertEquals("", StringUtils.plural(List.of(1)));
assertEquals("s", StringUtils.plural(List.of(1, 2)));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_pluralInteger()
{
assertEquals("s", StringUtils.plural(0));
assertEquals("", StringUtils.plural(1));
assertEquals("s", StringUtils.plural(2));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_pluralCollectionStringString()
{
assertEquals("es", StringUtils.plural(List.of(), "", "es"));
assertEquals("", StringUtils.plural(List.of(1), "", "es"));
assertEquals("es", StringUtils.plural(List.of(1, 2), "", "es"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void test_pluralIntegerStringString()
{
assertEquals("es", StringUtils.plural(0, "", "es"));
assertEquals("", StringUtils.plural(1, "", "es"));
assertEquals("es", StringUtils.plural(2, "", "es"));
}
}

View File

@ -0,0 +1,378 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.utils;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.AddAge;
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.GetAgeStatistics;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackendStep;
import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
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.processes.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionOutputMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaData;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.modules.authentication.MockAuthenticationModule;
import com.kingsrook.qqq.backend.core.modules.backend.implementations.mock.MockBackendModule;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.basic.BasicETLProcess;
/*******************************************************************************
** Utility class for backend-core test classes
**
*******************************************************************************/
public class TestUtils
{
public static String DEFAULT_BACKEND_NAME = "default";
public static String PROCESS_NAME_GREET_PEOPLE = "greet";
public static String PROCESS_NAME_GREET_PEOPLE_INTERACTIVE = "greetInteractive";
/*******************************************************************************
** Define the instance used in standard tests.
**
*******************************************************************************/
public static QInstance defineInstance()
{
QInstance qInstance = new QInstance();
qInstance.setAuthentication(defineAuthentication());
qInstance.addBackend(defineBackend());
qInstance.addTable(defineTablePerson());
qInstance.addTable(definePersonFileTable());
qInstance.addTable(defineTableIdAndNameOnly());
qInstance.addPossibleValueSource(defineStatesPossibleValueSource());
qInstance.addProcess(defineProcessGreetPeople());
qInstance.addProcess(defineProcessGreetPeopleInteractive());
qInstance.addProcess(defineProcessAddToPeoplesAge());
qInstance.addProcess(new BasicETLProcess().defineProcessMetaData());
System.out.println(new QInstanceAdapter().qInstanceToJson(qInstance));
return (qInstance);
}
/*******************************************************************************
** Define the "states" possible value source used in standard tests
**
*******************************************************************************/
private static QPossibleValueSource<String> defineStatesPossibleValueSource()
{
return new QPossibleValueSource<String>()
.withName("state")
.withType(QPossibleValueSourceType.ENUM)
.withEnumValues(List.of("IL", "MO"));
}
/*******************************************************************************
** Define the authentication used in standard tests - using 'mock' type.
**
*******************************************************************************/
public static QAuthenticationMetaData defineAuthentication()
{
return new QAuthenticationMetaData()
.withName("mock")
.withType(QAuthenticationType.MOCK);
}
/*******************************************************************************
** Define the backend used in standard tests - using 'mock' type.
*******************************************************************************/
public static QBackendMetaData defineBackend()
{
return new QBackendMetaData()
.withName(DEFAULT_BACKEND_NAME)
.withBackendType(MockBackendModule.class);
}
/*******************************************************************************
** Define the 'person' table used in standard tests.
*******************************************************************************/
public static QTableMetaData defineTablePerson()
{
return new QTableMetaData()
.withName("person")
.withLabel("Person")
.withBackendName(DEFAULT_BACKEND_NAME)
.withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withIsEditable(false))
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withIsEditable(false))
.withField(new QFieldMetaData("firstName", QFieldType.STRING))
.withField(new QFieldMetaData("lastName", QFieldType.STRING))
.withField(new QFieldMetaData("birthDate", QFieldType.DATE))
.withField(new QFieldMetaData("email", QFieldType.STRING))
.withField(new QFieldMetaData("homeState", QFieldType.STRING).withPossibleValueSourceName("state"));
}
/*******************************************************************************
** Define a 2nd version of the 'person' table for this test (pretend it's backed by a file)
*******************************************************************************/
public static QTableMetaData definePersonFileTable()
{
return (new QTableMetaData()
.withName("personFile")
.withLabel("Person File")
.withBackendName(DEFAULT_BACKEND_NAME)
.withPrimaryKeyField("id")
.withFields(TestUtils.defineTablePerson().getFields()));
}
/*******************************************************************************
** Define simple table with just an id and name
*******************************************************************************/
public static QTableMetaData defineTableIdAndNameOnly()
{
return new QTableMetaData()
.withName("idAndNameOnly")
.withLabel("Id and Name Only")
.withBackendName(DEFAULT_BACKEND_NAME)
.withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
.withField(new QFieldMetaData("name", QFieldType.STRING));
}
/*******************************************************************************
** Define the 'greet people' process
*******************************************************************************/
private static QProcessMetaData defineProcessGreetPeople()
{
return new QProcessMetaData()
.withName(PROCESS_NAME_GREET_PEOPLE)
.withTableName("person")
.addStep(new QBackendStepMetaData()
.withName("prepare")
.withCode(new QCodeReference()
.withName(MockBackendStep.class.getName())
.withCodeType(QCodeType.JAVA)
.withCodeUsage(QCodeUsage.BACKEND_STEP)) // todo - needed, or implied in this context?
.withInputData(new QFunctionInputMetaData()
.withRecordListMetaData(new QRecordListMetaData().withTableName("person"))
.withFieldList(List.of(
new QFieldMetaData("greetingPrefix", QFieldType.STRING),
new QFieldMetaData("greetingSuffix", QFieldType.STRING)
)))
.withOutputMetaData(new QFunctionOutputMetaData()
.withRecordListMetaData(new QRecordListMetaData()
.withTableName("person")
.addField(new QFieldMetaData("fullGreeting", QFieldType.STRING))
)
.withFieldList(List.of(new QFieldMetaData("outputMessage", QFieldType.STRING))))
);
}
/*******************************************************************************
** Define an interactive version of the 'greet people' process
*******************************************************************************/
private static QProcessMetaData defineProcessGreetPeopleInteractive()
{
return new QProcessMetaData()
.withName(PROCESS_NAME_GREET_PEOPLE_INTERACTIVE)
.withTableName("person")
.addStep(new QFrontendStepMetaData()
.withName("setup")
.withFormField(new QFieldMetaData("greetingPrefix", QFieldType.STRING))
.withFormField(new QFieldMetaData("greetingSuffix", QFieldType.STRING))
)
.addStep(new QBackendStepMetaData()
.withName("doWork")
.withCode(new QCodeReference()
.withName(MockBackendStep.class.getName())
.withCodeType(QCodeType.JAVA)
.withCodeUsage(QCodeUsage.BACKEND_STEP)) // todo - needed, or implied in this context?
.withInputData(new QFunctionInputMetaData()
.withRecordListMetaData(new QRecordListMetaData().withTableName("person"))
.withFieldList(List.of(
new QFieldMetaData("greetingPrefix", QFieldType.STRING),
new QFieldMetaData("greetingSuffix", QFieldType.STRING)
)))
.withOutputMetaData(new QFunctionOutputMetaData()
.withRecordListMetaData(new QRecordListMetaData()
.withTableName("person")
.addField(new QFieldMetaData("fullGreeting", QFieldType.STRING))
)
.withFieldList(List.of(new QFieldMetaData("outputMessage", QFieldType.STRING))))
)
.addStep(new QFrontendStepMetaData()
.withName("results")
.withFormField(new QFieldMetaData("outputMessage", QFieldType.STRING))
);
}
/*******************************************************************************
** Define the "add to people's age" process
**
** Works on a list of rows from the person table.
** - first function reports the current min & max age for all input rows.
** - user is then prompted for how much they want to add to everyone.
** - then the second function adds that value to their age, and shows the results.
*******************************************************************************/
private static QProcessMetaData defineProcessAddToPeoplesAge()
{
return new QProcessMetaData()
.withName("addToPeoplesAge")
.withTableName("person")
.addStep(new QBackendStepMetaData()
.withName("getAgeStatistics")
.withCode(new QCodeReference()
.withName(GetAgeStatistics.class.getName())
.withCodeType(QCodeType.JAVA)
.withCodeUsage(QCodeUsage.BACKEND_STEP))
.withInputData(new QFunctionInputMetaData()
.withRecordListMetaData(new QRecordListMetaData().withTableName("person")))
.withOutputMetaData(new QFunctionOutputMetaData()
.withRecordListMetaData(new QRecordListMetaData()
.withTableName("person")
.addField(new QFieldMetaData("age", QFieldType.INTEGER)))
.withFieldList(List.of(
new QFieldMetaData("minAge", QFieldType.INTEGER),
new QFieldMetaData("maxAge", QFieldType.INTEGER)))))
.addStep(new QBackendStepMetaData()
.withName("addAge")
.withCode(new QCodeReference()
.withName(AddAge.class.getName())
.withCodeType(QCodeType.JAVA)
.withCodeUsage(QCodeUsage.BACKEND_STEP))
.withInputData(new QFunctionInputMetaData()
.withFieldList(List.of(new QFieldMetaData("yearsToAdd", QFieldType.INTEGER))))
.withOutputMetaData(new QFunctionOutputMetaData()
.withRecordListMetaData(new QRecordListMetaData()
.withTableName("person")
.addField(new QFieldMetaData("newAge", QFieldType.INTEGER)))));
}
/*******************************************************************************
**
*******************************************************************************/
public static QSession getMockSession()
{
MockAuthenticationModule mockAuthenticationModule = new MockAuthenticationModule();
return (mockAuthenticationModule.createSession(null, null));
}
/*******************************************************************************
**
*******************************************************************************/
public static List<QRecord> queryTable(String tableName) throws QException
{
QueryInput queryInput = new QueryInput(TestUtils.defineInstance());
queryInput.setSession(TestUtils.getMockSession());
queryInput.setTableName(tableName);
QueryOutput queryOutput = new QueryAction().execute(queryInput);
return (queryOutput.getRecords());
}
/*******************************************************************************
**
*******************************************************************************/
public static String getPersonCsvHeader()
{
return ("""
"id","createDate","modifyDate","firstName","lastName","birthDate","email"
""");
}
/*******************************************************************************
**
*******************************************************************************/
public static String getPersonCsvHeaderUsingLabels()
{
return ("""
"Id","Create Date","Modify Date","First Name","Last Name","Birth Date","Email"
""");
}
/*******************************************************************************
**
*******************************************************************************/
public static String getPersonCsvRow1()
{
return ("""
"0","2021-10-26 14:39:37","2021-10-26 14:39:37","John","Doe","1980-01-01","john@doe.com"
""");
}
/*******************************************************************************
**
*******************************************************************************/
public static String getPersonCsvRow2()
{
return ("""
"0","2021-10-26 14:39:37","2021-10-26 14:39:37","Jane","Doe","1981-01-01","john@doe.com"
""");
}
}

View File

@ -0,0 +1,143 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. 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.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.*;
/*******************************************************************************
** Unit test for ValueUtils
*******************************************************************************/
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");
}
}));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGetValueAsInteger() throws QValueException
{
assertNull(ValueUtils.getValueAsInteger(null));
assertNull(ValueUtils.getValueAsInteger(""));
assertNull(ValueUtils.getValueAsInteger(" "));
assertEquals(1, ValueUtils.getValueAsInteger(1));
assertEquals(1, ValueUtils.getValueAsInteger("1"));
assertEquals(1_000, ValueUtils.getValueAsInteger("1,000"));
assertEquals(1_000_000, ValueUtils.getValueAsInteger("1,000,000"));
assertEquals(1, ValueUtils.getValueAsInteger(new BigDecimal(1)));
assertEquals(1, ValueUtils.getValueAsInteger(new BigDecimal("1.00")));
assertEquals(-1, ValueUtils.getValueAsInteger("-1.00"));
assertEquals(1_000, ValueUtils.getValueAsInteger("1,000.00"));
assertEquals(1_000, ValueUtils.getValueAsInteger(1000L));
assertEquals(1, ValueUtils.getValueAsInteger(1.0F));
assertEquals(1, ValueUtils.getValueAsInteger(1.0D));
assertThrows(QValueException.class, () -> ValueUtils.getValueAsInteger("a"));
assertThrows(QValueException.class, () -> ValueUtils.getValueAsInteger("a,b"));
assertThrows(QValueException.class, () -> ValueUtils.getValueAsInteger(new Object()));
assertThrows(QValueException.class, () -> ValueUtils.getValueAsInteger(1_000_000_000_000L));
assertThrows(QValueException.class, () -> ValueUtils.getValueAsInteger(1.1F));
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()));
}
}

View File

@ -0,0 +1,156 @@
{
"authentication": {
"name": "mock",
"type": "mock",
"values": null
},
"tables": {
"person": {
"name": "person",
"label": "Person",
"backendName": "default",
"primaryKeyField": "id",
"fields": {
"id": {
"name": "id",
"label": null,
"backendName": null,
"type": "INTEGER",
"possibleValueSourceName": null
},
"createDate": {
"name": "createDate",
"label": null,
"backendName": null,
"type": "DATE_TIME",
"possibleValueSourceName": null
},
"modifyDate": {
"name": "modifyDate",
"label": null,
"backendName": null,
"type": "DATE_TIME",
"possibleValueSourceName": null
},
"firstName": {
"name": "firstName",
"label": null,
"backendName": null,
"type": "STRING",
"possibleValueSourceName": null
},
"lastName": {
"name": "lastName",
"label": null,
"backendName": null,
"type": "STRING",
"possibleValueSourceName": null
},
"birthDate": {
"name": "birthDate",
"label": null,
"backendName": null,
"type": "DATE",
"possibleValueSourceName": null
},
"email": {
"name": "email",
"label": null,
"backendName": null,
"type": "STRING",
"possibleValueSourceName": null
},
"homeState": {
"name": "homeState",
"label": null,
"backendName": null,
"type": "STRING",
"possibleValueSourceName": "state"
}
}
}
},
"possibleValueSources": {
"state": {
"name": "state",
"type": "ENUM",
"enumValues": [
"IL",
"MO"
]
}
},
"processes": {
"greet": {
"name": "greet",
"tableName": "person",
"stepList": [
{
"name": "prepare",
"label": null,
"inputMetaData": {
"recordListMetaData": {
"tableName": "person",
"fields": null
},
"fieldList": [
{
"name": "greetingPrefix",
"label": null,
"backendName": null,
"type": "STRING",
"possibleValueSourceName": null
},
{
"name": "greetingSuffix",
"label": null,
"backendName": null,
"type": "STRING",
"possibleValueSourceName": null
}
]
},
"outputMetaData": {
"recordListMetaData": {
"tableName": "person",
"fields": {
"fullGreeting": {
"name": "fullGreeting",
"label": null,
"backendName": null,
"type": "STRING",
"possibleValueSourceName": null
}
}
},
"fieldList": [
{
"name": "outputMessage",
"label": null,
"backendName": null,
"type": "STRING",
"possibleValueSourceName": null
}
]
},
"code": {
"name": "com.kingsrook.qqq.backend.core.interfaces.mock.MockFunctionBody",
"codeType": "JAVA",
"codeUsage": "FUNCTION"
},
"outputView": {
"messageField": "outputMessage",
"recordListView": {
"fieldNames": [
"id",
"firstName",
"lastName",
"fullGreeting"
]
}
}
}
]
}
}
}

View File

@ -0,0 +1,162 @@
{
"tables": {
"person": {
"primaryKeyField": "id",
"name": "person",
"backendName": "default",
"label": "Person",
"fields": {
"firstName": {
"name": "firstName",
"backendName": null,
"label": null,
"type": "STRING",
"possibleValueSourceName": null
},
"lastName": {
"name": "lastName",
"backendName": null,
"label": null,
"type": "STRING",
"possibleValueSourceName": null
},
"modifyDate": {
"name": "modifyDate",
"backendName": null,
"label": null,
"type": "DATE_TIME",
"possibleValueSourceName": null
},
"homeState": {
"name": "homeState",
"backendName": null,
"label": null,
"type": "STRING",
"possibleValueSourceName": "state"
},
"id": {
"name": "id",
"backendName": null,
"label": null,
"type": "INTEGER",
"possibleValueSourceName": null
},
"birthDate": {
"name": "birthDate",
"backendName": null,
"label": null,
"type": "DATE",
"possibleValueSourceName": null
},
"email": {
"name": "email",
"backendName": null,
"label": null,
"type": "STRING",
"possibleValueSourceName": null
},
"createDate": {
"name": "createDate",
"backendName": null,
"label": null,
"type": "DATE_TIME",
"possibleValueSourceName": null
}
}
}
},
"processes": {
"greet": {
"functionList": [
{
"code": {
"codeUsage": "FUNCTION",
"codeType": "JAVA",
"name": "com.kingsrook.qqq.backend.core.interfaces.mock.MockFunctionBody"
},
"inputMetaData": {
"recordListMetaData": {
"fields": null,
"tableName": "person"
},
"fieldList": [
{
"name": "greetingPrefix",
"backendName": null,
"label": null,
"type": "STRING",
"possibleValueSourceName": null
},
{
"name": "greetingSuffix",
"backendName": null,
"label": null,
"type": "STRING",
"possibleValueSourceName": null
}
]
},
"outputMetaData": {
"recordListMetaData": {
"fields": {
"fullGreeting": {
"name": "fullGreeting",
"backendName": null,
"label": null,
"type": "STRING",
"possibleValueSourceName": null
}
},
"tableName": "person"
},
"fieldList": [
{
"name": "outputMessage",
"backendName": null,
"label": null,
"type": "STRING",
"possibleValueSourceName": null
}
]
},
"outputView": {
"messageField": "outputMessage",
"recordListView": {
"fieldNames": [
"id",
"firstName",
"lastName",
"fullGreeting"
]
}
},
"name": "prepare",
"label": null
}
],
"name": "greet",
"tableName": "person"
}
},
"possibleValueSources": {
"state": {
"name": "state",
"type": "ENUM",
"enumValues": [
"IL",
"MO"
]
}
},
"backends": {
"default": {
"name": "default",
"backendType": "mock"
}
},
"authentication": {
"values": null,
"name": "mock",
"type": "mock"
}
}