Add business loop validation and coverage audit
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
package com.ruoyi.generator.service.front;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessActionDesign;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessActionEffectDesign;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopAuditResult;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopAction;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopQuantityEffect;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopRecordEffect;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopStateMachine;
|
||||
import com.ruoyi.generator.domain.front.dto.DatabaseColumnDesign;
|
||||
import com.ruoyi.generator.domain.front.dto.DatabaseDesignResponse;
|
||||
import com.ruoyi.generator.domain.front.dto.DatabaseTableDesign;
|
||||
|
||||
@Component
|
||||
public class BusinessLoopCoverageValidator
|
||||
{
|
||||
public BusinessLoopAuditResult auditDatabase(BusinessLoopPlan plan, DatabaseDesignResponse database)
|
||||
{
|
||||
BusinessLoopAuditResult result = newResult(plan);
|
||||
Map<String, Set<String>> columnsByTable = collectColumnsByTable(database);
|
||||
if (plan == null)
|
||||
{
|
||||
result.getErrors().add("Business loop plan is required");
|
||||
finish(result);
|
||||
return result;
|
||||
}
|
||||
for (BusinessLoopStateMachine stateMachine : safe(plan.getStateMachines()))
|
||||
{
|
||||
if (stateMachine == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!hasColumn(columnsByTable, stateMachine.getTableName(), stateMachine.getStatusField()))
|
||||
{
|
||||
result.getErrors().add("Missing state status field: "
|
||||
+ formatField(stateMachine.getTableName(), stateMachine.getStatusField()));
|
||||
}
|
||||
}
|
||||
for (BusinessLoopQuantityEffect quantityRule : safe(plan.getQuantityRules()))
|
||||
{
|
||||
if (quantityRule == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!hasColumn(columnsByTable, quantityRule.getTableName(), quantityRule.getQuantityField()))
|
||||
{
|
||||
result.getErrors().add("Missing quantity field: "
|
||||
+ formatField(quantityRule.getTableName(), quantityRule.getQuantityField()));
|
||||
}
|
||||
}
|
||||
Set<String> tableNames = columnsByTable.keySet();
|
||||
for (BusinessLoopRecordEffect recordRule : safe(plan.getRecordRules()))
|
||||
{
|
||||
if (recordRule == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!tableNames.contains(normalize(recordRule.getTableName())))
|
||||
{
|
||||
result.getErrors().add("Missing record table: " + StringUtils.defaultString(recordRule.getTableName()));
|
||||
}
|
||||
}
|
||||
finish(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public BusinessLoopAuditResult auditBusinessActions(BusinessLoopPlan plan, List<BusinessActionDesign> actions)
|
||||
{
|
||||
BusinessLoopAuditResult result = newResult(plan);
|
||||
Map<String, BusinessActionDesign> actionsByCode = collectActionsByCode(actions);
|
||||
if (plan == null)
|
||||
{
|
||||
result.getErrors().add("Business loop plan is required");
|
||||
finish(result);
|
||||
return result;
|
||||
}
|
||||
for (BusinessLoopAction planAction : safe(plan.getActions()))
|
||||
{
|
||||
if (planAction == null || !planAction.isRequired())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
BusinessActionDesign action = actionsByCode.get(planAction.getCode());
|
||||
if (action == null)
|
||||
{
|
||||
result.getMissingRequiredActions().add(planAction.getCode());
|
||||
continue;
|
||||
}
|
||||
validateRequiredEffects(result, planAction, action);
|
||||
}
|
||||
finish(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void validateRequiredEffects(BusinessLoopAuditResult result, BusinessLoopAction planAction,
|
||||
BusinessActionDesign action)
|
||||
{
|
||||
if (size(planAction.getStateTransitions()) > 0 && !hasAnyEffect(action, "SET_STATUS", "UPDATE_FIELDS", "UPDATE_FIELD"))
|
||||
{
|
||||
result.getErrors().add("Missing status effect for required action: " + planAction.getCode());
|
||||
}
|
||||
if (size(planAction.getQuantityEffects()) > 0 && !hasAnyEffect(action, "INCREASE_NUMBER", "DECREASE_NUMBER"))
|
||||
{
|
||||
result.getErrors().add("Missing quantity effect for required action: " + planAction.getCode());
|
||||
}
|
||||
if (size(planAction.getRecordEffects()) > 0 && !hasAnyEffect(action, "INSERT_ROW", "UPDATE_FIELDS"))
|
||||
{
|
||||
result.getErrors().add("Missing record effect for required action: " + planAction.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasAnyEffect(BusinessActionDesign action, String... types)
|
||||
{
|
||||
if (action == null || action.getEffects() == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Set<String> expectedTypes = new HashSet<String>();
|
||||
for (String type : types)
|
||||
{
|
||||
expectedTypes.add(type);
|
||||
}
|
||||
for (BusinessActionEffectDesign effect : action.getEffects())
|
||||
{
|
||||
if (effect == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String type = StringUtils.defaultString(effect.getType()).toUpperCase(Locale.ENGLISH);
|
||||
if (expectedTypes.contains(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Map<String, BusinessActionDesign> collectActionsByCode(List<BusinessActionDesign> actions)
|
||||
{
|
||||
Map<String, BusinessActionDesign> actionsByCode = new HashMap<String, BusinessActionDesign>();
|
||||
if (actions == null)
|
||||
{
|
||||
return actionsByCode;
|
||||
}
|
||||
for (BusinessActionDesign action : actions)
|
||||
{
|
||||
if (action != null && StringUtils.isNotBlank(action.getCode()))
|
||||
{
|
||||
actionsByCode.put(action.getCode(), action);
|
||||
}
|
||||
}
|
||||
return actionsByCode;
|
||||
}
|
||||
|
||||
private Map<String, Set<String>> collectColumnsByTable(DatabaseDesignResponse database)
|
||||
{
|
||||
Map<String, Set<String>> columnsByTable = new HashMap<String, Set<String>>();
|
||||
if (database == null || database.getTables() == null)
|
||||
{
|
||||
return columnsByTable;
|
||||
}
|
||||
for (DatabaseTableDesign table : database.getTables())
|
||||
{
|
||||
if (table == null || StringUtils.isBlank(table.getTableName()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Set<String> columns = new HashSet<String>();
|
||||
if (table.getColumns() != null)
|
||||
{
|
||||
for (DatabaseColumnDesign column : table.getColumns())
|
||||
{
|
||||
if (column != null && StringUtils.isNotBlank(column.getColumnName()))
|
||||
{
|
||||
columns.add(normalize(column.getColumnName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
columnsByTable.put(normalize(table.getTableName()), columns);
|
||||
}
|
||||
return columnsByTable;
|
||||
}
|
||||
|
||||
private boolean hasColumn(Map<String, Set<String>> columnsByTable, String tableName, String columnName)
|
||||
{
|
||||
Set<String> columns = columnsByTable.get(normalize(tableName));
|
||||
return columns != null && columns.contains(normalize(columnName));
|
||||
}
|
||||
|
||||
private String formatField(String tableName, String fieldName)
|
||||
{
|
||||
return StringUtils.defaultString(tableName) + "." + StringUtils.defaultString(fieldName);
|
||||
}
|
||||
|
||||
private BusinessLoopAuditResult newResult(BusinessLoopPlan plan)
|
||||
{
|
||||
BusinessLoopAuditResult result = new BusinessLoopAuditResult();
|
||||
result.setLoopActions(size(plan == null ? null : plan.getActions()));
|
||||
result.setStateTransitions(countStateTransitions(plan));
|
||||
result.setQuantityRules(size(plan == null ? null : plan.getQuantityRules()));
|
||||
result.setRecordRules(size(plan == null ? null : plan.getRecordRules()));
|
||||
return result;
|
||||
}
|
||||
|
||||
private void finish(BusinessLoopAuditResult result)
|
||||
{
|
||||
result.setComplete(result.getErrors().isEmpty() && result.getMissingRequiredActions().isEmpty());
|
||||
}
|
||||
|
||||
private int countStateTransitions(BusinessLoopPlan plan)
|
||||
{
|
||||
if (plan == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
for (BusinessLoopStateMachine stateMachine : safe(plan.getStateMachines()))
|
||||
{
|
||||
if (stateMachine != null)
|
||||
{
|
||||
count += size(stateMachine.getTransitions());
|
||||
}
|
||||
}
|
||||
for (BusinessLoopAction action : safe(plan.getActions()))
|
||||
{
|
||||
if (action != null)
|
||||
{
|
||||
count += size(action.getStateTransitions());
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private String normalize(String value)
|
||||
{
|
||||
return StringUtils.defaultString(value).toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
private <T> List<T> safe(List<T> items)
|
||||
{
|
||||
return items == null ? java.util.Collections.<T>emptyList() : items;
|
||||
}
|
||||
|
||||
private int size(List<?> items)
|
||||
{
|
||||
return items == null ? 0 : items.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.ruoyi.generator.service.front;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopAction;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopObject;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopStateMachine;
|
||||
|
||||
@Component
|
||||
public class BusinessLoopPlanValidator
|
||||
{
|
||||
private static final int MAX_CORE_OBJECTS = 20;
|
||||
private static final int MAX_ACTIONS = 30;
|
||||
private static final Pattern ACTION_CODE = Pattern.compile("^[a-z][a-z0-9_]{1,63}$");
|
||||
|
||||
public void validate(BusinessLoopPlan plan)
|
||||
{
|
||||
if (plan == null)
|
||||
{
|
||||
throw new ServiceException("Business loop plan is required");
|
||||
}
|
||||
validateCoreObjects(plan.getCoreObjects());
|
||||
validateActions(plan.getActions());
|
||||
if (countStateTransitions(plan) == 0)
|
||||
{
|
||||
throw new ServiceException("Business loop must contain at least one state transition");
|
||||
}
|
||||
if (size(plan.getQuantityRules()) == 0 && size(plan.getRecordRules()) == 0)
|
||||
{
|
||||
throw new ServiceException("Business loop must contain at least one quantity or record rule");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateCoreObjects(List<BusinessLoopObject> coreObjects)
|
||||
{
|
||||
if (size(coreObjects) == 0)
|
||||
{
|
||||
throw new ServiceException("Business loop core objects cannot be empty");
|
||||
}
|
||||
if (coreObjects.size() > MAX_CORE_OBJECTS)
|
||||
{
|
||||
throw new ServiceException("Business loop core object count exceeds limit");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateActions(List<BusinessLoopAction> actions)
|
||||
{
|
||||
if (size(actions) == 0)
|
||||
{
|
||||
throw new ServiceException("Business loop actions cannot be empty");
|
||||
}
|
||||
if (actions.size() > MAX_ACTIONS)
|
||||
{
|
||||
throw new ServiceException("Business loop action count exceeds limit");
|
||||
}
|
||||
Set<String> actionCodes = new HashSet<String>();
|
||||
boolean hasRequiredAction = false;
|
||||
for (BusinessLoopAction action : actions)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
throw new ServiceException("Business loop action cannot be empty");
|
||||
}
|
||||
if (StringUtils.isBlank(action.getCode()) || !ACTION_CODE.matcher(action.getCode()).matches())
|
||||
{
|
||||
throw new ServiceException("Business loop action code is invalid");
|
||||
}
|
||||
if (!actionCodes.add(action.getCode()))
|
||||
{
|
||||
throw new ServiceException("Business loop action code duplicated: " + action.getCode());
|
||||
}
|
||||
hasRequiredAction = hasRequiredAction || action.isRequired();
|
||||
}
|
||||
if (!hasRequiredAction)
|
||||
{
|
||||
throw new ServiceException("Business loop must contain at least one required action");
|
||||
}
|
||||
}
|
||||
|
||||
private int countStateTransitions(BusinessLoopPlan plan)
|
||||
{
|
||||
int count = 0;
|
||||
if (plan.getStateMachines() != null)
|
||||
{
|
||||
for (BusinessLoopStateMachine stateMachine : plan.getStateMachines())
|
||||
{
|
||||
if (stateMachine != null)
|
||||
{
|
||||
count += size(stateMachine.getTransitions());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (plan.getActions() != null)
|
||||
{
|
||||
for (BusinessLoopAction action : plan.getActions())
|
||||
{
|
||||
if (action != null)
|
||||
{
|
||||
count += size(action.getStateTransitions());
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int size(List<?> items)
|
||||
{
|
||||
return items == null ? 0 : items.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package com.ruoyi.generator.service.front;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import org.junit.Test;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessActionDesign;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessActionEffectDesign;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopAuditResult;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopAction;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopObject;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopQuantityEffect;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopRecordEffect;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopStateMachine;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopStateTransition;
|
||||
import com.ruoyi.generator.domain.front.dto.DatabaseColumnDesign;
|
||||
import com.ruoyi.generator.domain.front.dto.DatabaseDesignResponse;
|
||||
import com.ruoyi.generator.domain.front.dto.DatabaseTableDesign;
|
||||
|
||||
public class BusinessLoopCoverageValidatorTest
|
||||
{
|
||||
private final BusinessLoopCoverageValidator validator = new BusinessLoopCoverageValidator();
|
||||
|
||||
@Test
|
||||
public void databaseAuditPassesWhenTablesAndFieldsCoverPlan()
|
||||
{
|
||||
BusinessLoopAuditResult result = validator.auditDatabase(completePlan(), completeDatabase());
|
||||
|
||||
assertTrue(result.isComplete());
|
||||
assertTrue(result.getErrors().isEmpty());
|
||||
assertEquals(1, result.getLoopActions());
|
||||
assertEquals(2, result.getStateTransitions());
|
||||
assertEquals(1, result.getQuantityRules());
|
||||
assertEquals(1, result.getRecordRules());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void databaseAuditFailsWhenQuantityFieldMissing()
|
||||
{
|
||||
DatabaseDesignResponse database = completeDatabase();
|
||||
database.getTables().get(1).setColumns(Collections.<DatabaseColumnDesign>emptyList());
|
||||
|
||||
BusinessLoopAuditResult result = validator.auditDatabase(completePlan(), database);
|
||||
|
||||
assertFalse(result.isComplete());
|
||||
assertTrue(contains(result.getErrors().toString(), "available_count"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void businessActionAuditFailsWhenRequiredActionMissing()
|
||||
{
|
||||
BusinessLoopAuditResult result = validator.auditBusinessActions(completePlan(),
|
||||
Collections.<BusinessActionDesign>emptyList());
|
||||
|
||||
assertFalse(result.isComplete());
|
||||
assertTrue(result.getMissingRequiredActions().contains("confirm_borrow"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void businessActionAuditPassesWhenRequiredStatusAndQuantityEffectsExist()
|
||||
{
|
||||
BusinessActionDesign action = new BusinessActionDesign();
|
||||
action.setCode("confirm_borrow");
|
||||
action.setEffects(Arrays.asList(effect("SET_STATUS"), effect("DECREASE_NUMBER")));
|
||||
|
||||
BusinessLoopAuditResult result = validator.auditBusinessActions(completePlan(), Arrays.asList(action));
|
||||
|
||||
assertTrue(result.isComplete());
|
||||
assertTrue(result.getMissingRequiredActions().isEmpty());
|
||||
assertTrue(result.getErrors().isEmpty());
|
||||
}
|
||||
|
||||
private BusinessLoopPlan completePlan()
|
||||
{
|
||||
BusinessLoopPlan plan = new BusinessLoopPlan();
|
||||
plan.setDomainName("library");
|
||||
plan.setCoreObjects(Arrays.asList(coreObject()));
|
||||
plan.setStateMachines(Arrays.asList(stateMachine()));
|
||||
plan.setActions(Arrays.asList(requiredAction()));
|
||||
plan.setQuantityRules(Arrays.asList(quantityRule()));
|
||||
plan.setRecordRules(Arrays.asList(recordRule()));
|
||||
return plan;
|
||||
}
|
||||
|
||||
private BusinessLoopObject coreObject()
|
||||
{
|
||||
BusinessLoopObject object = new BusinessLoopObject();
|
||||
object.setCode("borrow_order");
|
||||
object.setName("Borrow order");
|
||||
object.setTableName("borrow_order");
|
||||
return object;
|
||||
}
|
||||
|
||||
private BusinessLoopStateMachine stateMachine()
|
||||
{
|
||||
BusinessLoopStateMachine stateMachine = new BusinessLoopStateMachine();
|
||||
stateMachine.setObjectCode("borrow_order");
|
||||
stateMachine.setTableName("borrow_order");
|
||||
stateMachine.setStatusField("borrow_status");
|
||||
stateMachine.setStates(Arrays.asList("submitted", "borrowed"));
|
||||
stateMachine.setTransitions(Arrays.asList(transition()));
|
||||
return stateMachine;
|
||||
}
|
||||
|
||||
private BusinessLoopAction requiredAction()
|
||||
{
|
||||
BusinessLoopAction action = new BusinessLoopAction();
|
||||
action.setCode("confirm_borrow");
|
||||
action.setName("Confirm borrow");
|
||||
action.setOwnerTable("borrow_order");
|
||||
action.setRequired(true);
|
||||
action.setStateTransitions(Arrays.asList(transition()));
|
||||
action.setQuantityEffects(Arrays.asList(quantityRule()));
|
||||
return action;
|
||||
}
|
||||
|
||||
private BusinessLoopStateTransition transition()
|
||||
{
|
||||
BusinessLoopStateTransition transition = new BusinessLoopStateTransition();
|
||||
transition.setActionCode("confirm_borrow");
|
||||
transition.setFromState("submitted");
|
||||
transition.setToState("borrowed");
|
||||
return transition;
|
||||
}
|
||||
|
||||
private BusinessLoopQuantityEffect quantityRule()
|
||||
{
|
||||
BusinessLoopQuantityEffect effect = new BusinessLoopQuantityEffect();
|
||||
effect.setActionCode("confirm_borrow");
|
||||
effect.setObjectCode("book_stock");
|
||||
effect.setTableName("book_stock");
|
||||
effect.setQuantityField("available_count");
|
||||
effect.setEffectType("decrease");
|
||||
effect.setAmount("1");
|
||||
return effect;
|
||||
}
|
||||
|
||||
private BusinessLoopRecordEffect recordRule()
|
||||
{
|
||||
BusinessLoopRecordEffect effect = new BusinessLoopRecordEffect();
|
||||
effect.setActionCode("confirm_borrow");
|
||||
effect.setObjectCode("borrow_record");
|
||||
effect.setTableName("borrow_record");
|
||||
effect.setOperation("insert");
|
||||
return effect;
|
||||
}
|
||||
|
||||
private DatabaseDesignResponse completeDatabase()
|
||||
{
|
||||
DatabaseDesignResponse response = new DatabaseDesignResponse();
|
||||
response.setTables(Arrays.asList(
|
||||
table("borrow_order", "id", "borrow_status"),
|
||||
table("book_stock", "id", "available_count"),
|
||||
table("borrow_record", "id")));
|
||||
return response;
|
||||
}
|
||||
|
||||
private DatabaseTableDesign table(String tableName, String... columnNames)
|
||||
{
|
||||
DatabaseTableDesign table = new DatabaseTableDesign();
|
||||
table.setTableName(tableName);
|
||||
DatabaseColumnDesign[] columns = new DatabaseColumnDesign[columnNames.length];
|
||||
for (int i = 0; i < columnNames.length; i++)
|
||||
{
|
||||
columns[i] = column(columnNames[i]);
|
||||
}
|
||||
table.setColumns(Arrays.asList(columns));
|
||||
return table;
|
||||
}
|
||||
|
||||
private DatabaseColumnDesign column(String columnName)
|
||||
{
|
||||
DatabaseColumnDesign column = new DatabaseColumnDesign();
|
||||
column.setColumnName(columnName);
|
||||
return column;
|
||||
}
|
||||
|
||||
private BusinessActionEffectDesign effect(String type)
|
||||
{
|
||||
BusinessActionEffectDesign effect = new BusinessActionEffectDesign();
|
||||
effect.setType(type);
|
||||
return effect;
|
||||
}
|
||||
|
||||
private boolean contains(String text, String needle)
|
||||
{
|
||||
return text != null && text.contains(needle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package com.ruoyi.generator.service.front;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import org.junit.Test;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopAction;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopObject;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopQuantityEffect;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopRecordEffect;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopStateMachine;
|
||||
import com.ruoyi.generator.domain.front.dto.BusinessLoopPlan.BusinessLoopStateTransition;
|
||||
|
||||
public class BusinessLoopPlanValidatorTest
|
||||
{
|
||||
private final BusinessLoopPlanValidator validator = new BusinessLoopPlanValidator();
|
||||
|
||||
@Test
|
||||
public void acceptsCompleteLoopPlan()
|
||||
{
|
||||
validator.validate(completePlan());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsPlanWithoutStateTransition()
|
||||
{
|
||||
BusinessLoopPlan plan = completePlan();
|
||||
plan.getStateMachines().get(0).setTransitions(Collections.<BusinessLoopStateTransition>emptyList());
|
||||
plan.getActions().get(0).setStateTransitions(Collections.<BusinessLoopStateTransition>emptyList());
|
||||
|
||||
ServiceException exception = expectServiceException(new ThrowingRunnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
validator.validate(plan);
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue(exception.getMessage().contains("state transition"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsPlanWithoutQuantityOrRecordRule()
|
||||
{
|
||||
BusinessLoopPlan plan = completePlan();
|
||||
plan.setQuantityRules(Collections.<BusinessLoopQuantityEffect>emptyList());
|
||||
plan.setRecordRules(Collections.<BusinessLoopRecordEffect>emptyList());
|
||||
|
||||
ServiceException exception = expectServiceException(new ThrowingRunnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
validator.validate(plan);
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue(exception.getMessage().contains("quantity or record"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsDuplicatedActionCode()
|
||||
{
|
||||
BusinessLoopPlan plan = completePlan();
|
||||
plan.setActions(Arrays.asList(requiredAction("confirm_borrow"), requiredAction("confirm_borrow")));
|
||||
|
||||
ServiceException exception = expectServiceException(new ThrowingRunnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
validator.validate(plan);
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue(exception.getMessage().contains("duplicated"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsPlanWithoutRequiredAction()
|
||||
{
|
||||
BusinessLoopPlan plan = completePlan();
|
||||
plan.getActions().get(0).setRequired(false);
|
||||
|
||||
ServiceException exception = expectServiceException(new ThrowingRunnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
validator.validate(plan);
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue(exception.getMessage().contains("required action"));
|
||||
}
|
||||
|
||||
private BusinessLoopPlan completePlan()
|
||||
{
|
||||
BusinessLoopPlan plan = new BusinessLoopPlan();
|
||||
plan.setDomainName("library");
|
||||
plan.setCoreObjects(Arrays.asList(coreObject()));
|
||||
plan.setStateMachines(Arrays.asList(stateMachine()));
|
||||
plan.setActions(Arrays.asList(requiredAction("confirm_borrow")));
|
||||
plan.setQuantityRules(Arrays.asList(quantityRule()));
|
||||
plan.setRecordRules(Arrays.asList(recordRule()));
|
||||
return plan;
|
||||
}
|
||||
|
||||
private BusinessLoopObject coreObject()
|
||||
{
|
||||
BusinessLoopObject object = new BusinessLoopObject();
|
||||
object.setCode("borrow_order");
|
||||
object.setName("Borrow order");
|
||||
object.setTableName("borrow_order");
|
||||
return object;
|
||||
}
|
||||
|
||||
private BusinessLoopStateMachine stateMachine()
|
||||
{
|
||||
BusinessLoopStateMachine stateMachine = new BusinessLoopStateMachine();
|
||||
stateMachine.setObjectCode("borrow_order");
|
||||
stateMachine.setTableName("borrow_order");
|
||||
stateMachine.setStatusField("borrow_status");
|
||||
stateMachine.setStates(Arrays.asList("submitted", "borrowed"));
|
||||
stateMachine.setTransitions(Arrays.asList(transition()));
|
||||
return stateMachine;
|
||||
}
|
||||
|
||||
private BusinessLoopAction requiredAction(String code)
|
||||
{
|
||||
BusinessLoopAction action = new BusinessLoopAction();
|
||||
action.setCode(code);
|
||||
action.setName("Confirm borrow");
|
||||
action.setActor("librarian");
|
||||
action.setOwnerObject("borrow_order");
|
||||
action.setOwnerTable("borrow_order");
|
||||
action.setRequired(true);
|
||||
action.setStateTransitions(Arrays.asList(transition()));
|
||||
action.setQuantityEffects(Arrays.asList(quantityRule()));
|
||||
return action;
|
||||
}
|
||||
|
||||
private BusinessLoopStateTransition transition()
|
||||
{
|
||||
BusinessLoopStateTransition transition = new BusinessLoopStateTransition();
|
||||
transition.setActionCode("confirm_borrow");
|
||||
transition.setFromState("submitted");
|
||||
transition.setToState("borrowed");
|
||||
return transition;
|
||||
}
|
||||
|
||||
private BusinessLoopQuantityEffect quantityRule()
|
||||
{
|
||||
BusinessLoopQuantityEffect effect = new BusinessLoopQuantityEffect();
|
||||
effect.setActionCode("confirm_borrow");
|
||||
effect.setObjectCode("book_stock");
|
||||
effect.setTableName("book_stock");
|
||||
effect.setQuantityField("available_count");
|
||||
effect.setEffectType("decrease");
|
||||
effect.setAmount("1");
|
||||
return effect;
|
||||
}
|
||||
|
||||
private BusinessLoopRecordEffect recordRule()
|
||||
{
|
||||
BusinessLoopRecordEffect effect = new BusinessLoopRecordEffect();
|
||||
effect.setActionCode("confirm_borrow");
|
||||
effect.setObjectCode("borrow_record");
|
||||
effect.setTableName("borrow_record");
|
||||
effect.setOperation("insert");
|
||||
return effect;
|
||||
}
|
||||
|
||||
private ServiceException expectServiceException(ThrowingRunnable runnable)
|
||||
{
|
||||
try
|
||||
{
|
||||
runnable.run();
|
||||
}
|
||||
catch (ServiceException e)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
throw new AssertionError("Expected ServiceException");
|
||||
}
|
||||
|
||||
private interface ThrowingRunnable
|
||||
{
|
||||
void run();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user