mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 21:20:45 +00:00
Merged dev into feature/CE-876-develop-missing-widget-types
This commit is contained in:
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.automation.polling;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.context.CapturedContext;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.automation.RecordAutomationInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TriggerEvent;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static com.kingsrook.qqq.backend.core.actions.automation.polling.PollingAutomationPerTableRunnerTest.runAllTableActions;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test for the case where:
|
||||
** - inserting into a main table and a child table, and the child table has a
|
||||
** post-insert customizer, which mo
|
||||
*******************************************************************************/
|
||||
public class PollingAutomationPerTableRunnerAutomtationUpdatingSelfAvoidInfiniteLoopTest extends BaseTest
|
||||
{
|
||||
private static boolean didFailInThread = false;
|
||||
|
||||
static
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// we can set this property to revert to the behavior that existed before this test was written. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// System.setProperty("qqq.recordAutomationStatusUpdater.skipPreUpdateFetch", "true");
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
void beforeEach()
|
||||
{
|
||||
didFailInThread = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
////////////////////////////////////
|
||||
// add automations to order table //
|
||||
////////////////////////////////////
|
||||
QContext.getQInstance().getTable(TestUtils.TABLE_NAME_ORDER)
|
||||
.withField(TestUtils.standardQqqAutomationStatusField())
|
||||
.withAutomationDetails(TestUtils.defineStandardAutomationDetails()
|
||||
.withAction(new TableAutomationAction()
|
||||
.withName("orderPostInsertAction")
|
||||
.withTriggerEvent(TriggerEvent.POST_INSERT)
|
||||
.withCodeReference(new QCodeReference(OrderPostInsertAndUpdateAction.class)))
|
||||
.withAction(new TableAutomationAction()
|
||||
.withName("orderPostUpdateAction")
|
||||
.withTriggerEvent(TriggerEvent.POST_UPDATE)
|
||||
.withCodeReference(new QCodeReference(OrderPostInsertAndUpdateAction.class))));
|
||||
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
|
||||
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("orderNo", "10101").withValue("total", new BigDecimal(1))));
|
||||
new InsertAction().execute(insertInput);
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// make sure the order is in pending-inserts status //
|
||||
//////////////////////////////////////////////////////
|
||||
{
|
||||
QRecord order = new GetAction().executeForRecord(new GetInput(TestUtils.TABLE_NAME_ORDER).withPrimaryKey(1));
|
||||
assertEquals(AutomationStatus.PENDING_INSERT_AUTOMATIONS.getId(), order.getValue(TestUtils.standardQqqAutomationStatusField().getName()));
|
||||
assertEquals(new BigDecimal(1), order.getValueBigDecimal("total"));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// run automations - that should update the order via the automation - but leave status as OK //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
runAllTableActions(QContext.getQInstance());
|
||||
assertFalse(didFailInThread, "A failure condition happened in the automation sub-thread. Check System.out for message.");
|
||||
|
||||
{
|
||||
QRecord order = new GetAction().executeForRecord(new GetInput(TestUtils.TABLE_NAME_ORDER).withPrimaryKey(1));
|
||||
assertEquals(AutomationStatus.OK.getId(), order.getValue(TestUtils.standardQqqAutomationStatusField().getName()));
|
||||
assertEquals(new BigDecimal(2), order.getValueBigDecimal("total"));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// now update the order, verify status moves to pending-updates //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
new UpdateAction().execute(new UpdateInput(TestUtils.TABLE_NAME_ORDER).withRecord(new QRecord()
|
||||
.withValue("id", 1)
|
||||
.withValue("storeId", "x")));
|
||||
|
||||
{
|
||||
QRecord order = new GetAction().executeForRecord(new GetInput(TestUtils.TABLE_NAME_ORDER).withPrimaryKey(1));
|
||||
assertEquals(AutomationStatus.PENDING_UPDATE_AUTOMATIONS.getId(), order.getValue(TestUtils.standardQqqAutomationStatusField().getName()));
|
||||
assertEquals(new BigDecimal(2), order.getValueBigDecimal("total"));
|
||||
assertEquals("x", order.getValueString("storeId"));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// run automations - that should update the order via the automation - but leave status as OK //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
runAllTableActions(QContext.getQInstance());
|
||||
assertFalse(didFailInThread, "A failure condition happened in the automation sub-thread. Check System.out for message.");
|
||||
|
||||
{
|
||||
QRecord order = new GetAction().executeForRecord(new GetInput(TestUtils.TABLE_NAME_ORDER).withPrimaryKey(1));
|
||||
assertEquals(AutomationStatus.OK.getId(), order.getValue(TestUtils.standardQqqAutomationStatusField().getName()));
|
||||
assertEquals(new BigDecimal(3), order.getValueBigDecimal("total"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class OrderPostInsertAndUpdateAction extends RecordAutomationHandler
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void execute(RecordAutomationInput recordAutomationInput) throws QException
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// launch a new thread, to make sure we avoid the "stack contains automations" check in RecordAutomationStatusUpdater //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
CapturedContext capturedContext = QContext.capture();
|
||||
for(QRecord record : recordAutomationInput.getRecordList())
|
||||
{
|
||||
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
|
||||
Future<?> submit = service.submit(() ->
|
||||
{
|
||||
QContext.init(capturedContext);
|
||||
try
|
||||
{
|
||||
new UpdateAction().execute(new UpdateInput(TestUtils.TABLE_NAME_ORDER).withRecord(new QRecord()
|
||||
.withValue("id", record.getValue("id"))
|
||||
.withValue("total", record.getValueBigDecimal("total").add(new BigDecimal(1)))
|
||||
));
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// make sure that update action didn't change the order's status //
|
||||
///////////////////////////////////////////////////////////////////
|
||||
QRecord order = new GetAction().executeForRecord(new GetInput(TestUtils.TABLE_NAME_ORDER).withPrimaryKey(1));
|
||||
if(Objects.equals(AutomationStatus.PENDING_UPDATE_AUTOMATIONS.getId(), order.getValue(TestUtils.standardQqqAutomationStatusField().getName())))
|
||||
{
|
||||
System.out.println("Failing test - expected status to not be [PENDING_UPDATE_AUTOMATIONS], but it was.");
|
||||
didFailInThread = true;
|
||||
}
|
||||
assertNotEquals(AutomationStatus.PENDING_UPDATE_AUTOMATIONS.getId(), order.getValue(TestUtils.standardQqqAutomationStatusField().getName()));
|
||||
}
|
||||
catch(QException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
QContext.clear();
|
||||
}
|
||||
});
|
||||
|
||||
while(!submit.isDone())
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.automation.polling;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPostInsertCustomizer;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.AggregateAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.Aggregate;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateResult;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.GroupBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
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.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.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.automation.RecordAutomationInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TriggerEvent;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static com.kingsrook.qqq.backend.core.actions.automation.polling.PollingAutomationPerTableRunnerTest.runAllTableActions;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test for the case where:
|
||||
** - inserting into a main table and a child table, and the child table has a
|
||||
** post-insert customizer, which mo
|
||||
*******************************************************************************/
|
||||
public class PollingAutomationPerTableRunnerChildPostInsertCustomizerTest extends BaseTest
|
||||
{
|
||||
|
||||
static
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// we can set this property to revert to the behavior that existed before this test was written. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// System.setProperty("qqq.recordAutomationStatusUpdater.skipPreUpdateFetch", "true");
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
////////////////////////////////////
|
||||
// add automations to order table //
|
||||
////////////////////////////////////
|
||||
QContext.getQInstance().getTable(TestUtils.TABLE_NAME_ORDER)
|
||||
.withField(TestUtils.standardQqqAutomationStatusField())
|
||||
.withAutomationDetails(TestUtils.defineStandardAutomationDetails()
|
||||
.withAction(new TableAutomationAction()
|
||||
.withName("orderPostInsertAction")
|
||||
.withTriggerEvent(TriggerEvent.POST_INSERT)
|
||||
.withCodeReference(new QCodeReference(OrderPostInsertAction.class))
|
||||
));
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// add a post-insert customizer to line-ite table (child of order table) //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
QContext.getQInstance().getTable(TestUtils.TABLE_NAME_LINE_ITEM)
|
||||
.withCustomizer(TableCustomizers.POST_INSERT_RECORD, new QCodeReference(LineItemPostInsertCustomizer.class));
|
||||
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
|
||||
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
insertInput.setRecords(List.of(new QRecord()
|
||||
.withValue("orderNo", "10101")
|
||||
.withAssociatedRecord("orderLine", new QRecord()
|
||||
.withValue("sku", "ABC")
|
||||
.withValue("quantity", 1))));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// make sure the order is in pending-inserts status (at one time, a bug meant that it wouldn't have been...) //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QRecord order = new GetAction().executeForRecord(new GetInput(TestUtils.TABLE_NAME_ORDER).withPrimaryKey(1));
|
||||
assertEquals(AutomationStatus.PENDING_INSERT_AUTOMATIONS.getId(), order.getValue(TestUtils.standardQqqAutomationStatusField().getName()));
|
||||
assertEquals(new BigDecimal(1), order.getValueBigDecimal("total"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// run automations - that should... insert a second line item, but should leave the order in //
|
||||
// automation-status = OK, to avoid perpetual re-running //
|
||||
// the line-item post-inserter should run a second time, making the order's total = 2 //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
runAllTableActions(QContext.getQInstance());
|
||||
|
||||
{
|
||||
QRecord order = new GetAction().executeForRecord(new GetInput(TestUtils.TABLE_NAME_ORDER).withPrimaryKey(1));
|
||||
assertEquals(AutomationStatus.OK.getId(), order.getValue(TestUtils.standardQqqAutomationStatusField().getName()));
|
||||
assertEquals(new BigDecimal(2), order.getValueBigDecimal("total"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class OrderPostInsertAction extends RecordAutomationHandler
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void execute(RecordAutomationInput recordAutomationInput) throws QException
|
||||
{
|
||||
///////////////////////////////////////
|
||||
// add a new line item to the orders //
|
||||
///////////////////////////////////////
|
||||
List<QRecord> lineItemsToInsert = new ArrayList<>();
|
||||
for(QRecord record : recordAutomationInput.getRecordList())
|
||||
{
|
||||
lineItemsToInsert.add(new QRecord()
|
||||
.withValue("orderId", record.getValue("id"))
|
||||
.withValue("sku", UUID.randomUUID())
|
||||
.withValue("quantity", 1)
|
||||
);
|
||||
}
|
||||
new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_LINE_ITEM).withRecords(lineItemsToInsert));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class LineItemPostInsertCustomizer extends AbstractPostInsertCustomizer
|
||||
{
|
||||
@Override
|
||||
public List<QRecord> apply(List<QRecord> records) throws QException
|
||||
{
|
||||
//////////////////////////////////
|
||||
// count line items by order id //
|
||||
//////////////////////////////////
|
||||
Set<Serializable> orderIds = records.stream().map(r -> r.getValue("orderId")).collect(Collectors.toSet());
|
||||
|
||||
GroupBy groupByOrderId = new GroupBy(QFieldType.STRING, "orderId");
|
||||
Aggregate countId = new Aggregate("id", AggregateOperator.COUNT);
|
||||
|
||||
AggregateInput aggregateInput = new AggregateInput();
|
||||
aggregateInput.setTableName(TestUtils.TABLE_NAME_LINE_ITEM);
|
||||
aggregateInput.setFilter(new QQueryFilter(new QFilterCriteria("orderId", QCriteriaOperator.IN, orderIds)));
|
||||
aggregateInput.withGroupBy(groupByOrderId);
|
||||
aggregateInput.withAggregate(countId);
|
||||
AggregateOutput aggregateOutput = new AggregateAction().execute(aggregateInput);
|
||||
Map<Integer, Integer> countByOrderId = new HashMap<>();
|
||||
for(AggregateResult result : aggregateOutput.getResults())
|
||||
{
|
||||
countByOrderId.put(ValueUtils.getValueAsInteger(result.getGroupByValue(groupByOrderId)), ValueUtils.getValueAsInteger(result.getAggregateValue(countId)));
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
// update the order total fields //
|
||||
// s/b in bulk, but, meh //
|
||||
///////////////////////////////////
|
||||
for(Integer orderId : countByOrderId.keySet())
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
updateInput.setRecords(List.of(new QRecord()
|
||||
.withValue("id", orderId)
|
||||
.withValue("total", new BigDecimal(countByOrderId.get(orderId)))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
}
|
||||
|
||||
return (records);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -46,6 +46,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.DynamicDefaultValueBehavior;
|
||||
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.processes.QProcessMetaData;
|
||||
@ -56,14 +57,11 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.QTableAut
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TriggerEvent;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.LoadViaInsertStep;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcessTest;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
@ -77,18 +75,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
class PollingAutomationPerTableRunnerTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void beforeAndAfterEach()
|
||||
{
|
||||
MemoryRecordStore.getInstance().reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test a cycle that does an insert, some automations, then and an update, and more automations.
|
||||
*******************************************************************************/
|
||||
@ -200,7 +186,7 @@ class PollingAutomationPerTableRunnerTest extends BaseTest
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void runAllTableActions(QInstance qInstance) throws QException
|
||||
static void runAllTableActions(QInstance qInstance) throws QException
|
||||
{
|
||||
List<PollingAutomationPerTableRunner.TableActionsInterface> tableActions = PollingAutomationPerTableRunner.getTableActions(qInstance, TestUtils.POLLING_AUTOMATION);
|
||||
for(PollingAutomationPerTableRunner.TableActionsInterface tableAction : tableActions)
|
||||
@ -210,7 +196,7 @@ class PollingAutomationPerTableRunnerTest extends BaseTest
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// note - don't call run - it is meant to be called async - e.g., it sets & clears thread context. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
pollingAutomationPerTableRunner.processTableInsertOrUpdate(qInstance.getTable(tableAction.tableName()), QContext.getQSession(), tableAction.status());
|
||||
pollingAutomationPerTableRunner.processTableInsertOrUpdate(qInstance.getTable(tableAction.tableName()), tableAction.status());
|
||||
}
|
||||
}
|
||||
|
||||
@ -512,7 +498,7 @@ class PollingAutomationPerTableRunnerTest extends BaseTest
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// note - don't call run - it is meant to be called async - e.g., it sets & clears thread context. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
pollingAutomationPerTableRunner.processTableInsertOrUpdate(qInstance.getTable(tableAction.tableName()), QContext.getQSession(), tableAction.status());
|
||||
pollingAutomationPerTableRunner.processTableInsertOrUpdate(qInstance.getTable(tableAction.tableName()), tableAction.status());
|
||||
}
|
||||
}).hasMessage(PollingAutomationPerTableRunnerThatShouldSimulateServerShutdownMidRun.EXCEPTION_MESSAGE);
|
||||
|
||||
@ -593,4 +579,70 @@ class PollingAutomationPerTableRunnerTest extends BaseTest
|
||||
new PollingAutomationPerTableRunner.ShardedTableActions(null, null, null, null, null).noopToFakeTestCoverage();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testAddOrderByToQueryFilter()
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// make a table we'll test with. just put a primary-key id on it first //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
QTableMetaData table = new QTableMetaData()
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER));
|
||||
{
|
||||
QQueryFilter filter = new QQueryFilter();
|
||||
PollingAutomationPerTableRunner.addOrderByToQueryFilter(table, AutomationStatus.PENDING_INSERT_AUTOMATIONS, filter);
|
||||
assertEquals("id", filter.getOrderBys().get(0).getFieldName());
|
||||
}
|
||||
|
||||
{
|
||||
QQueryFilter filter = new QQueryFilter();
|
||||
PollingAutomationPerTableRunner.addOrderByToQueryFilter(table, AutomationStatus.PENDING_UPDATE_AUTOMATIONS, filter);
|
||||
assertEquals("id", filter.getOrderBys().get(0).getFieldName());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// add createDate & modifyDate fields, but not with dynamic-default-behaviors //
|
||||
// so should still sort by id //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
QFieldMetaData createDate = new QFieldMetaData("createDate", QFieldType.DATE_TIME);
|
||||
QFieldMetaData modifyDate = new QFieldMetaData("modifyDate", QFieldType.DATE_TIME);
|
||||
table.addField(createDate);
|
||||
table.addField(modifyDate);
|
||||
{
|
||||
QQueryFilter filter = new QQueryFilter();
|
||||
PollingAutomationPerTableRunner.addOrderByToQueryFilter(table, AutomationStatus.PENDING_INSERT_AUTOMATIONS, filter);
|
||||
assertEquals("id", filter.getOrderBys().get(0).getFieldName());
|
||||
}
|
||||
|
||||
{
|
||||
QQueryFilter filter = new QQueryFilter();
|
||||
PollingAutomationPerTableRunner.addOrderByToQueryFilter(table, AutomationStatus.PENDING_UPDATE_AUTOMATIONS, filter);
|
||||
assertEquals("id", filter.getOrderBys().get(0).getFieldName());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// add dynamic default value behaviors, confirm create/modify date fields are used //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
createDate.withBehavior(DynamicDefaultValueBehavior.CREATE_DATE);
|
||||
modifyDate.withBehavior(DynamicDefaultValueBehavior.MODIFY_DATE);
|
||||
|
||||
{
|
||||
QQueryFilter filter = new QQueryFilter();
|
||||
PollingAutomationPerTableRunner.addOrderByToQueryFilter(table, AutomationStatus.PENDING_INSERT_AUTOMATIONS, filter);
|
||||
assertEquals("createDate", filter.getOrderBys().get(0).getFieldName());
|
||||
}
|
||||
|
||||
{
|
||||
QQueryFilter filter = new QQueryFilter();
|
||||
PollingAutomationPerTableRunner.addOrderByToQueryFilter(table, AutomationStatus.PENDING_UPDATE_AUTOMATIONS, filter);
|
||||
assertEquals("modifyDate", filter.getOrderBys().get(0).getFieldName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.dashboard;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for AbstractHTMLWidgetRenderer
|
||||
*******************************************************************************/
|
||||
class AbstractHTMLWidgetRendererTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
String link = AbstractHTMLWidgetRenderer.getCountLink(null, TestUtils.TABLE_NAME_PERSON, new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("a", QCriteriaOperator.EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("a", QCriteriaOperator.EQUALS, 1)), 2
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// assert that filter de-duplication is occurring //
|
||||
////////////////////////////////////////////////////
|
||||
assertThat(link).doesNotMatch(".*EQUALS.*EQUALS.*");
|
||||
}
|
||||
|
||||
}
|
@ -26,6 +26,7 @@ import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
@ -175,7 +176,7 @@ class QRecordTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testListAsValue()
|
||||
void testArrayListAsValue()
|
||||
{
|
||||
ArrayList<Integer> originalArrayList = new ArrayList<>(List.of(1, 2, 3));
|
||||
QRecord recordWithArrayListValue = new QRecord().withValue("myList", originalArrayList);
|
||||
@ -196,6 +197,31 @@ class QRecordTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testLinkedListAsValue()
|
||||
{
|
||||
LinkedList<Integer> originalLinkedList = new LinkedList<>(List.of(1, 2, 3));
|
||||
QRecord recordWithLinkedListValue = new QRecord().withValue("myList", originalLinkedList);
|
||||
QRecord cloneWithLinkedListValue = new QRecord(recordWithLinkedListValue);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// the clone list and original list should be equals (have contents that are equals), but not be the same (reference) //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
assertEquals(List.of(1, 2, 3), cloneWithLinkedListValue.getValue("myList"));
|
||||
assertNotSame(originalLinkedList, cloneWithLinkedListValue.getValue("myList"));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// make sure a change to the original list doesn't change the cloned list (as it was cloned deeply) //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
originalLinkedList.add(4);
|
||||
assertNotEquals(originalLinkedList, cloneWithLinkedListValue.getValue("myList"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.processes.implementations.automation;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationStatusUpdater;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
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.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.DynamicDefaultValueBehavior;
|
||||
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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for HealBadRecordAutomationStatusesProcessStep
|
||||
*******************************************************************************/
|
||||
class HealBadRecordAutomationStatusesProcessStepTest extends BaseTest
|
||||
{
|
||||
private static String tableName = TestUtils.TABLE_NAME_PERSON_MEMORY;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTwoFailedUpdates() throws QException
|
||||
{
|
||||
new InsertAction().execute(new InsertInput(tableName).withRecords(List.of(new QRecord(), new QRecord())));
|
||||
List<QRecord> records = queryAllRecords();
|
||||
RecordAutomationStatusUpdater.setAutomationStatusInRecordsAndUpdate(QContext.getQInstance().getTable(tableName), records, AutomationStatus.FAILED_UPDATE_AUTOMATIONS, null);
|
||||
|
||||
assertThat(queryAllRecords()).allMatch(r -> AutomationStatus.FAILED_UPDATE_AUTOMATIONS.getId().equals(getAutomationStatus(r)));
|
||||
|
||||
RunBackendStepOutput output = runProcessStep();
|
||||
|
||||
assertEquals(2, output.getValueInteger("totalRecordsUpdated"));
|
||||
assertThat(queryAllRecords()).allMatch(r -> AutomationStatus.PENDING_UPDATE_AUTOMATIONS.getId().equals(getAutomationStatus(r)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testOneFailedUpdateOneFailedInsert() throws QException
|
||||
{
|
||||
new InsertAction().execute(new InsertInput(tableName).withRecords(List.of(new QRecord(), new QRecord())));
|
||||
List<QRecord> records = queryAllRecords();
|
||||
RecordAutomationStatusUpdater.setAutomationStatusInRecordsAndUpdate(QContext.getQInstance().getTable(tableName), records.subList(0, 1), AutomationStatus.FAILED_UPDATE_AUTOMATIONS, null);
|
||||
RecordAutomationStatusUpdater.setAutomationStatusInRecordsAndUpdate(QContext.getQInstance().getTable(tableName), records.subList(1, 2), AutomationStatus.FAILED_INSERT_AUTOMATIONS, null);
|
||||
|
||||
assertThat(queryAllRecords())
|
||||
.anyMatch(r -> AutomationStatus.FAILED_UPDATE_AUTOMATIONS.getId().equals(getAutomationStatus(r)))
|
||||
.anyMatch(r -> AutomationStatus.FAILED_INSERT_AUTOMATIONS.getId().equals(getAutomationStatus(r)));
|
||||
|
||||
RunBackendStepOutput output = runProcessStep();
|
||||
|
||||
assertEquals(2, output.getValueInteger("totalRecordsUpdated"));
|
||||
assertThat(queryAllRecords())
|
||||
.anyMatch(r -> AutomationStatus.PENDING_UPDATE_AUTOMATIONS.getId().equals(getAutomationStatus(r)))
|
||||
.anyMatch(r -> AutomationStatus.PENDING_INSERT_AUTOMATIONS.getId().equals(getAutomationStatus(r)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testOldRunning() throws QException
|
||||
{
|
||||
/////////////////////////////////////////////////
|
||||
// temporarily remove the modify-date behavior //
|
||||
/////////////////////////////////////////////////
|
||||
QContext.getQInstance().getTable(tableName).getField("modifyDate").withBehavior(DynamicDefaultValueBehavior.NONE);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// insert 2 records, one with an old modifyDate, one with 6 minutes ago //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
new InsertAction().execute(new InsertInput(tableName).withRecords(List.of(
|
||||
new QRecord().withValue("firstName", "Darin").withValue("modifyDate", Instant.parse("2023-01-01T12:00:00Z")),
|
||||
new QRecord().withValue("firstName", "Tim").withValue("modifyDate", Instant.now().minus(6, ChronoUnit.MINUTES))
|
||||
)));
|
||||
List<QRecord> records = queryAllRecords();
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// put those records both in status: running-updates //
|
||||
///////////////////////////////////////////////////////
|
||||
RecordAutomationStatusUpdater.setAutomationStatusInRecordsAndUpdate(QContext.getQInstance().getTable(tableName), records, AutomationStatus.RUNNING_UPDATE_AUTOMATIONS, null);
|
||||
|
||||
assertThat(queryAllRecords())
|
||||
.allMatch(r -> AutomationStatus.RUNNING_UPDATE_AUTOMATIONS.getId().equals(getAutomationStatus(r)));
|
||||
|
||||
/////////////////////////////////////
|
||||
// restore the modifyDate behavior //
|
||||
/////////////////////////////////////
|
||||
QContext.getQInstance().getTable(tableName).getField("modifyDate").withBehavior(DynamicDefaultValueBehavior.MODIFY_DATE);
|
||||
|
||||
/////////////////////////
|
||||
// run code under test //
|
||||
/////////////////////////
|
||||
RunBackendStepOutput output = runProcessStep();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// assert we updated 1 (the old one) to pending-updates, the other left as running-updates //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
assertEquals(1, output.getValueInteger("totalRecordsUpdated"));
|
||||
assertThat(queryAllRecords())
|
||||
.anyMatch(r -> AutomationStatus.PENDING_UPDATE_AUTOMATIONS.getId().equals(getAutomationStatus(r)))
|
||||
.anyMatch(r -> AutomationStatus.RUNNING_UPDATE_AUTOMATIONS.getId().equals(getAutomationStatus(r)));
|
||||
|
||||
/////////////////////////////////
|
||||
// re-run, with 3-minute limit //
|
||||
/////////////////////////////////
|
||||
output = runProcessStep(new RunBackendStepInput().withValues(Map.of("minutesOldLimit", 3)));
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// assert that one updated too, and all are now pending-update //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
assertEquals(1, output.getValueInteger("totalRecordsUpdated"));
|
||||
assertThat(queryAllRecords())
|
||||
.allMatch(r -> AutomationStatus.PENDING_UPDATE_AUTOMATIONS.getId().equals(getAutomationStatus(r)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static Integer getAutomationStatus(QRecord r)
|
||||
{
|
||||
return r.getValueInteger(TestUtils.standardQqqAutomationStatusField().getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static List<QRecord> queryAllRecords() throws QException
|
||||
{
|
||||
return new QueryAction().execute(new QueryInput(tableName)).getRecords();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static RunBackendStepOutput runProcessStep() throws QException
|
||||
{
|
||||
RunBackendStepInput input = new RunBackendStepInput();
|
||||
return runProcessStep(input);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static RunBackendStepOutput runProcessStep(RunBackendStepInput input) throws QException
|
||||
{
|
||||
RunBackendStepOutput output = new RunBackendStepOutput();
|
||||
new HealBadRecordAutomationStatusesProcessStep().run(input, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.processes.implementations.automation;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
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.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeSupplier;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for RunTableAutomationsProcessStep
|
||||
*******************************************************************************/
|
||||
class RunTableAutomationsProcessStepTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws Exception
|
||||
{
|
||||
UnsafeSupplier<Integer, ?> getAutomationStatus = () -> new GetAction().executeForRecord(new GetInput(TestUtils.TABLE_NAME_PERSON_MEMORY).withPrimaryKey(1)).getValueInteger("qqqAutomationStatus");
|
||||
|
||||
new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_PERSON_MEMORY).withRecord(new QRecord()));
|
||||
assertEquals(AutomationStatus.PENDING_INSERT_AUTOMATIONS.getId(), getAutomationStatus.get());
|
||||
|
||||
RunBackendStepInput input = new RunBackendStepInput();
|
||||
input.addValue("tableName", TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
RunBackendStepOutput output = new RunBackendStepOutput();
|
||||
new RunTableAutomationsProcessStep().run(input, output);
|
||||
assertEquals("true", output.getValue("ok"));
|
||||
|
||||
assertEquals(AutomationStatus.OK.getId(), getAutomationStatus.get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testThrowsWithoutTableName() throws QException
|
||||
{
|
||||
RunBackendStepInput input = new RunBackendStepInput();
|
||||
RunBackendStepOutput output = new RunBackendStepOutput();
|
||||
assertThatThrownBy(() -> new RunTableAutomationsProcessStep().run(input, output))
|
||||
.hasMessageContaining("Missing required input value: tableName");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testThrowsWithInvalidTableName() throws QException
|
||||
{
|
||||
RunBackendStepInput input = new RunBackendStepInput();
|
||||
RunBackendStepOutput output = new RunBackendStepOutput();
|
||||
input.addValue("tableName", "asdf");
|
||||
assertThatThrownBy(() -> new RunTableAutomationsProcessStep().run(input, output))
|
||||
.hasMessageContaining("Unrecognized table name: asdf");
|
||||
}
|
||||
|
||||
}
|
@ -116,6 +116,19 @@ class ExceptionUtilsTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testGetTopAndBottomMessages()
|
||||
{
|
||||
assertEquals("foo", ExceptionUtils.getTopAndBottomMessages(new Exception("foo")));
|
||||
assertEquals("foo: bar", ExceptionUtils.getTopAndBottomMessages(new Exception("foo", new Exception("bar"))));
|
||||
assertEquals("foo: baz", ExceptionUtils.getTopAndBottomMessages(new Exception("foo", new Exception("bar", new Exception("baz")))));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test exception class - lets you set the cause, easier to create a loop.
|
||||
*******************************************************************************/
|
||||
|
@ -0,0 +1,355 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.utils;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator.EQUALS;
|
||||
import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator.GREATER_THAN;
|
||||
import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator.IN;
|
||||
import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator.NOT_EQUALS;
|
||||
import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator.NOT_IN;
|
||||
import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter.BooleanOperator.OR;
|
||||
import static com.kingsrook.qqq.backend.core.utils.QQueryFilterDeduper.dedupeFilter;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for QQueryFilterDeduper
|
||||
*******************************************************************************/
|
||||
class QQueryFilterDeduperTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testDegenerateCases()
|
||||
{
|
||||
assertNull(dedupeFilter(null));
|
||||
|
||||
QQueryFilter empty = new QQueryFilter();
|
||||
assertEquals(empty, dedupeFilter(empty));
|
||||
assertNotSame(empty, dedupeFilter(empty)); // method always clones, so, just assert that.
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testSimpleFiltersWithNoChanges()
|
||||
{
|
||||
QQueryFilter oneCriteria = new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1));
|
||||
assertEquals(oneCriteria, dedupeFilter(oneCriteria));
|
||||
assertNotSame(oneCriteria, dedupeFilter(oneCriteria));
|
||||
|
||||
QQueryFilter twoCriteriaDifferentFields = new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("b", GREATER_THAN, 2));
|
||||
assertEquals(twoCriteriaDifferentFields, dedupeFilter(twoCriteriaDifferentFields));
|
||||
assertNotSame(twoCriteriaDifferentFields, dedupeFilter(twoCriteriaDifferentFields));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testOrs()
|
||||
{
|
||||
///////////////////////////////////////////////////////
|
||||
// we've only written the simplest cases with ORs... //
|
||||
///////////////////////////////////////////////////////
|
||||
assertEquals(new QQueryFilter().withBooleanOperator(OR).withCriteria(new QFilterCriteria("a", EQUALS, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withBooleanOperator(OR)
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// just not built at this time - obviously, could become an IN list //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
QQueryFilter notSupportedOrTwoEquals = new QQueryFilter()
|
||||
.withBooleanOperator(OR)
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 2));
|
||||
assertEquals(notSupportedOrTwoEquals, dedupeFilter(notSupportedOrTwoEquals));
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// I think the logic would be, that the EQUALS 1 would be removed (is redundant) //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
QQueryFilter notSupportedOrEqualsNotEquals = new QQueryFilter()
|
||||
.withBooleanOperator(OR)
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 2));
|
||||
assertEquals(notSupportedOrEqualsNotEquals, dedupeFilter(notSupportedOrEqualsNotEquals));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testMoreOperators()
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// only simplest case (of criteria being .equals()) is supported... //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("a", GREATER_THAN, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("a", GREATER_THAN, 1))
|
||||
.withCriteria(new QFilterCriteria("a", GREATER_THAN, 1))
|
||||
));
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// in theory, we could do more, but we just haven't yet (e.g, this could be > 5) //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
QQueryFilter tooComplex = new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", GREATER_THAN, 1))
|
||||
.withCriteria(new QFilterCriteria("f", GREATER_THAN, 5));
|
||||
assertEquals(tooComplex, dedupeFilter(tooComplex));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testAllEquals()
|
||||
{
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("a", EQUALS, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("a", EQUALS, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("b", EQUALS, 2))
|
||||
.withCriteria(new QFilterCriteria("c", EQUALS, 3)),
|
||||
dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("b", EQUALS, 2))
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("b", EQUALS, 2))
|
||||
.withCriteria(new QFilterCriteria("b", EQUALS, 2))
|
||||
.withCriteria(new QFilterCriteria("a", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("c", EQUALS, 3))
|
||||
.withCriteria(new QFilterCriteria("c", EQUALS, 3))
|
||||
.withCriteria(new QFilterCriteria("c", EQUALS, 3))
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testEqualsAndNotEqualsAndNotIn()
|
||||
{
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", EQUALS, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 2))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", EQUALS, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 2))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 3))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", EQUALS, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 2))
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 3))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", EQUALS, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 2))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 3))
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", EQUALS, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_IN, 2, 3))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", EQUALS, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_IN, 2, 3))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 4))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", EQUALS, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_IN, 2, 3))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 4))
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", EQUALS, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_IN, 2, 3))
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 4))
|
||||
));
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// this is a contradiction, so we choose not to dedupe it //
|
||||
////////////////////////////////////////////////////////////
|
||||
QQueryFilter contradiction1 = new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 1));
|
||||
assertEquals(contradiction1, dedupeFilter(contradiction1));
|
||||
|
||||
QQueryFilter contradiction2 = new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_IN, 0, 1));
|
||||
assertEquals(contradiction2, dedupeFilter(contradiction2));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// this case can collapse the two not-equals, but then fails to merge the equals with them, because they are a contradiction! //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
assertEquals(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_IN, 2, 3))
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 2)),
|
||||
dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 2))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 3))
|
||||
.withCriteria(new QFilterCriteria("f", EQUALS, 2))
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNotEqualsAndNotIn()
|
||||
{
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", NOT_IN, 1, 2, 3)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 2))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 3))
|
||||
));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ideally, maybe, this would have the values ordered 1,2,3, but, is equivalent enough //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", NOT_IN, 2, 3, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_IN, 2, 3))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", NOT_IN, 2, 3, 1)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_IN, 2, 3))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 1))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", NOT_IN, 1, 2, 3)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_IN, 1, 2))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_IN, 2, 3))
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testInAndNotEquals()
|
||||
{
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", IN, 2, 3)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", IN, 2, 3))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", IN, 2, 3)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", IN, 2, 3))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 1))
|
||||
));
|
||||
|
||||
QQueryFilter contradiction1 = new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 1))
|
||||
.withCriteria(new QFilterCriteria("f", IN, 1));
|
||||
assertEquals(contradiction1, dedupeFilter(contradiction1));
|
||||
|
||||
QQueryFilter contradiction2 = new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", IN, 1))
|
||||
.withCriteria(new QFilterCriteria("f", NOT_EQUALS, 1));
|
||||
assertEquals(contradiction2, dedupeFilter(contradiction2));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testMultipleInLists()
|
||||
{
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", IN, 2)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", IN, 1, 2))
|
||||
.withCriteria(new QFilterCriteria("f", IN, 2, 3))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", IN, 3, 4)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", IN, 1, 2, 3, 4))
|
||||
.withCriteria(new QFilterCriteria("f", IN, 3, 4, 5, 6))
|
||||
));
|
||||
|
||||
assertEquals(new QQueryFilter().withCriteria(new QFilterCriteria("f", IN, 3)), dedupeFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", IN, 1, 2, 3, 4))
|
||||
.withCriteria(new QFilterCriteria("f", IN, 3, 4, 5, 6))
|
||||
.withCriteria(new QFilterCriteria("f", IN, 1, 3, 5, 7))
|
||||
));
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// contradicting in-lists - we give up and refuse to simplify it //
|
||||
///////////////////////////////////////////////////////////////////
|
||||
QQueryFilter contradiction = new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("f", IN, 1, 2))
|
||||
.withCriteria(new QFilterCriteria("f", IN, 3, 4));
|
||||
assertEquals(contradiction, dedupeFilter(contradiction));
|
||||
}
|
||||
|
||||
}
|
@ -720,7 +720,7 @@ public class TestUtils
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QTableAutomationDetails defineStandardAutomationDetails()
|
||||
public static QTableAutomationDetails defineStandardAutomationDetails()
|
||||
{
|
||||
return (new QTableAutomationDetails()
|
||||
.withProviderName(POLLING_AUTOMATION)
|
||||
|
@ -32,11 +32,14 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeFunction;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
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.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@ -122,6 +125,58 @@ class MemoizationTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testLookupFunction()
|
||||
{
|
||||
AtomicInteger lookupFunctionCallCounter = new AtomicInteger(0);
|
||||
|
||||
Memoization<String, Integer> memoization = new Memoization<>();
|
||||
|
||||
UnsafeFunction<String, Integer, Exception> lookupFunction = numberString ->
|
||||
{
|
||||
lookupFunctionCallCounter.getAndIncrement();
|
||||
|
||||
if(numberString.equals("null"))
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
return Integer.parseInt(numberString);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// get "1" twice - should return 1 each time, and call the lookup function exactly once //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
assertThat(memoization.getResult("1", lookupFunction)).isPresent().contains(1);
|
||||
assertEquals(1, lookupFunctionCallCounter.get());
|
||||
|
||||
assertThat(memoization.getResult("1", lookupFunction)).isPresent().contains(1);
|
||||
assertEquals(1, lookupFunctionCallCounter.get());
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// now get "null" twice - should return null each time, and call the lookup function exactly once more //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
assertThat(memoization.getResult("null", lookupFunction)).isEmpty();
|
||||
assertEquals(2, lookupFunctionCallCounter.get());
|
||||
|
||||
assertThat(memoization.getResult("null", lookupFunction)).isEmpty();
|
||||
assertEquals(2, lookupFunctionCallCounter.get());
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// now make a call that throws twice - again, should return null each time, and only do one more loookup call //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
assertThat(memoization.getResult(null, lookupFunction)).isEmpty();
|
||||
assertEquals(3, lookupFunctionCallCounter.get());
|
||||
|
||||
assertThat(memoization.getResult(null, lookupFunction)).isEmpty();
|
||||
assertEquals(3, lookupFunctionCallCounter.get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
Reference in New Issue
Block a user