package jnpf.elnService.template;
|
|
import jnpf.audit.model.AuditEventDTO;
|
import jnpf.elnEntity.field.ElnFieldDefEntity;
|
import jnpf.elnEntity.field.ElnFieldForms;
|
import jnpf.elnEntity.field.ElnFieldVersionEntity;
|
import jnpf.elnEntity.field.ElnFieldVos;
|
import jnpf.elnEntity.template.ElnTemplateBindingEntity;
|
import jnpf.elnEntity.template.ElnTemplateForms;
|
import jnpf.elnEntity.template.ElnTemplateRepeatGroupEntity;
|
import jnpf.elnEntity.template.ElnTemplateVersionEntity;
|
import jnpf.elnEntity.template.ElnTemplateVos;
|
import jnpf.elnService.ElnFieldCatalogService;
|
import jnpf.elnService.template.impl.ElnTemplateBindingServiceImpl;
|
import jnpf.elnService.template.impl.ElnTemplateRepeatGroupServiceImpl;
|
import jnpf.exception.DataException;
|
import jnpf.util.JsonUtil;
|
import org.junit.Test;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.TransactionDefinition;
|
import org.springframework.transaction.TransactionStatus;
|
import org.springframework.transaction.support.SimpleTransactionStatus;
|
import org.springframework.transaction.support.TransactionTemplate;
|
|
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Method;
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.Collection;
|
import java.util.Collections;
|
import java.util.Date;
|
import java.util.LinkedHashMap;
|
import java.util.LinkedHashSet;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.Set;
|
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.function.Consumer;
|
import java.util.function.Supplier;
|
|
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertNotEquals;
|
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.fail;
|
|
public class ElnTemplateBindingServiceImplTest {
|
|
@Test
|
public void templateKeepsItsConfiguredFieldVersionWhenTheCurrentFieldVersionChanges() {
|
Fixture f = new Fixture();
|
f.versions.get("version-current").setBangdingPeizhi(
|
"{\"read\":{\"source\":\"LIMS_TASK\",\"property\":\"test.basis\",\"propertyRevision\":1},"
|
+ "\"write\":{\"target\":\"ELN_RECORD\"}}");
|
f.versions.get("version-next").setBangdingPeizhi(
|
"{\"read\":{\"source\":\"LIMS_TASK\",\"property\":\"test.basis\",\"propertyRevision\":2},"
|
+ "\"write\":{\"target\":\"ELN_RECORD\"}}");
|
ElnTemplateVos.Binding reserved = f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-current", "configured-source-reserve"));
|
f.service.activate(f.authorization(), reserved.getId(), "configured-source-activate");
|
|
f.selectNextVersion();
|
|
ElnTemplateVos.AnnotationContext context = f.service.context(f.authorization());
|
ElnTemplateVos.Binding bound = context.getBindings().stream()
|
.filter(binding -> reserved.getId().equals(binding.getId())).findFirst().get();
|
assertEquals("version-current", bound.getFieldVersionId());
|
ElnFieldVos.Version available = context.getFields().stream()
|
.filter(field -> "field-1".equals(field.getId())).findFirst().get().getVersions().stream()
|
.filter(version -> "version-next".equals(version.getId())).findFirst().get();
|
assertEquals(Integer.valueOf(2), available.getBinding().getRead().getPropertyRevision());
|
}
|
|
@Test
|
public void contextExposesTemplateCheckboxGroupRules() {
|
ElnTemplateVos.CheckboxGroup configured = new ElnTemplateVos.CheckboxGroup();
|
configured.setGroupCode("result_choice");
|
configured.setMinSelected(1);
|
configured.setMaxSelected(2);
|
configured.setVersion(3);
|
Fixture f = new Fixture(new StubCheckboxGroupService(
|
Collections.singletonList(configured)));
|
|
ElnTemplateVos.AnnotationContext context = f.service.context(f.authorization());
|
|
assertEquals(1, context.getCheckboxGroups().size());
|
assertEquals("result_choice", context.getCheckboxGroups().get(0).getGroupCode());
|
assertEquals(Integer.valueOf(1), context.getCheckboxGroups().get(0).getMinSelected());
|
assertEquals(Integer.valueOf(2), context.getCheckboxGroups().get(0).getMaxSelected());
|
assertEquals(Integer.valueOf(3), context.getCheckboxGroups().get(0).getVersion());
|
}
|
|
@Test
|
public void contextExposesGlobalFieldCheckboxGroupRecommendation() {
|
Fixture f = new Fixture();
|
f.addCheckboxFields();
|
ElnFieldVersionEntity version = f.versions.get("checkbox-version-1");
|
version.setRecommendedCheckboxGroupCode("result_choice");
|
version.setRecommendedCheckboxMinSelected(1);
|
version.setRecommendedCheckboxMaxSelected(2);
|
|
ElnTemplateVos.AnnotationContext context = f.service.context(f.authorization());
|
|
ElnFieldVos.Version exposed = context.getFields().stream()
|
.filter(field -> "checkbox-1".equals(field.getId()))
|
.findFirst().get().getVersions().get(0);
|
assertEquals("result_choice", exposed.getRecommendedCheckboxGroupCode());
|
assertEquals(Integer.valueOf(1), exposed.getRecommendedCheckboxMinSelected());
|
assertEquals(Integer.valueOf(2), exposed.getRecommendedCheckboxMaxSelected());
|
}
|
|
@Test
|
public void reserveCreatesTemplateCheckboxGroupWithRequestedBounds() {
|
StubCheckboxGroupService groups = new StubCheckboxGroupService(new ArrayList<>());
|
Fixture f = new Fixture(groups);
|
f.addCheckboxFields();
|
ElnTemplateForms.ReserveBinding form = reserve(
|
"checkbox-1", "checkbox-version-1", "create-selection-group");
|
form.setCheckboxGroupCode("result_choice");
|
form.setCheckboxGroupMinSelected(1);
|
form.setCheckboxGroupMaxSelected(2);
|
|
ElnTemplateVos.Binding binding = f.service.reservePrimary(f.authorization(), form);
|
|
assertEquals("result_choice", binding.getCheckboxGroupCode());
|
assertEquals(1, groups.groups.size());
|
assertEquals(Integer.valueOf(1), groups.groups.get(0).getMinSelected());
|
assertEquals(Integer.valueOf(2), groups.groups.get(0).getMaxSelected());
|
}
|
|
@Test
|
public void reserveCannotOverrideExistingTemplateCheckboxGroupRule() {
|
ElnTemplateVos.CheckboxGroup configured = checkboxGroup("result_choice", 1, 2, 0);
|
StubCheckboxGroupService groups = new StubCheckboxGroupService(
|
new ArrayList<>(Collections.singletonList(configured)));
|
Fixture f = new Fixture(groups);
|
f.addCheckboxFields();
|
ElnTemplateForms.ReserveBinding form = reserve(
|
"checkbox-1", "checkbox-version-1", "mismatch-selection-group");
|
form.setCheckboxGroupCode("result_choice");
|
form.setCheckboxGroupMinSelected(0);
|
form.setCheckboxGroupMaxSelected(1);
|
|
expectDataException(() -> f.service.reservePrimary(f.authorization(), form));
|
|
assertEquals(0, f.store.bindings.size());
|
assertEquals(Integer.valueOf(1), groups.groups.get(0).getMinSelected());
|
assertEquals(Integer.valueOf(2), groups.groups.get(0).getMaxSelected());
|
}
|
|
@Test
|
public void reserveRetryRejectsChangedCheckboxGroupBounds() {
|
StubCheckboxGroupService groups = new StubCheckboxGroupService(new ArrayList<>());
|
Fixture f = new Fixture(groups);
|
f.addCheckboxFields();
|
ElnTemplateForms.ReserveBinding original = reserve(
|
"checkbox-1", "checkbox-version-1", "retry-selection-group");
|
original.setCheckboxGroupCode("result_choice");
|
original.setCheckboxGroupMinSelected(1);
|
original.setCheckboxGroupMaxSelected(2);
|
f.service.reservePrimary(f.authorization(), original);
|
ElnTemplateForms.ReserveBinding changed = reserve(
|
"checkbox-1", "checkbox-version-1", "retry-selection-group");
|
changed.setCheckboxGroupCode("result_choice");
|
changed.setCheckboxGroupMinSelected(0);
|
changed.setCheckboxGroupMaxSelected(1);
|
|
expectDataException(() -> f.service.reservePrimary(f.authorization(), changed));
|
}
|
|
@Test
|
public void changesTemplateCheckboxGroupRuleWithCasAndAudit() {
|
StubCheckboxGroupService groups = new StubCheckboxGroupService(new ArrayList<>(
|
Collections.singletonList(checkboxGroup("result_choice", 0, 1, 3))));
|
Fixture f = new Fixture(groups);
|
ElnTemplateForms.ChangeCheckboxGroupRule form =
|
new ElnTemplateForms.ChangeCheckboxGroupRule();
|
form.setMinSelected(1);
|
form.setMaxSelected(4);
|
form.setExpectedVersion(3);
|
form.setOperationCode("change-selection-rule");
|
|
ElnTemplateVos.CheckboxGroup changed = f.service.changeCheckboxGroupRule(
|
f.authorization(), "result_choice", form);
|
|
assertEquals(Integer.valueOf(1), changed.getMinSelected());
|
assertEquals(Integer.valueOf(4), changed.getMaxSelected());
|
assertEquals(Integer.valueOf(4), changed.getVersion());
|
assertEquals(1, f.countAudits("ELN_TEMPLATE_CHECKBOX_GROUP_RULE_CHANGE"));
|
}
|
|
@Test
|
public void reservesPrimaryWithSingleStepCompatibilityAssignment() {
|
Fixture f = new Fixture();
|
|
ElnTemplateVos.Binding reserved = f.service.reservePrimary(
|
f.authorization(), reserve("field-1", "version-current", "single-step"));
|
|
assertEquals("test-single-step", reserved.getStepId());
|
assertEquals("test-single-step", f.store.binding(reserved.getId()).getStepId());
|
}
|
|
@Test
|
public void reservesCheckboxGroupAndRequiresRetryToKeepTheSameGroup() {
|
Fixture f = new Fixture();
|
f.addCheckboxFields();
|
ElnTemplateForms.ReserveBinding form = reserve(
|
"checkbox-1", "checkbox-version-1", "checkbox-group-reserve");
|
form.setCheckboxGroupCode("result_choice");
|
|
ElnTemplateVos.Binding reserved = f.service.reservePrimary(f.authorization(), form);
|
ElnTemplateVos.Binding retried = f.service.reservePrimary(f.authorization(), form);
|
|
assertEquals("result_choice", reserved.getCheckboxGroupCode());
|
assertEquals("result_choice", retried.getCheckboxGroupCode());
|
assertEquals("result_choice",
|
f.store.binding(reserved.getId()).getCheckboxGroupCode());
|
|
ElnTemplateForms.ReserveBinding changed = reserve(
|
"checkbox-1", "checkbox-version-1", "checkbox-group-reserve");
|
changed.setCheckboxGroupCode("another_group");
|
expectOperationStatus(409,
|
() -> f.service.reservePrimary(f.authorization(), changed));
|
}
|
|
@Test
|
public void rejectsInvalidOrIneligibleCheckboxGroupAssignments() {
|
Fixture invalidCode = new Fixture();
|
invalidCode.addCheckboxFields();
|
ElnTemplateForms.ReserveBinding invalid = reserve(
|
"checkbox-1", "checkbox-version-1", "invalid-checkbox-group");
|
invalid.setCheckboxGroupCode("含中文");
|
expectDataException(() -> invalidCode.service.reservePrimary(
|
invalidCode.authorization(), invalid));
|
|
Fixture text = new Fixture();
|
ElnTemplateForms.ReserveBinding textForm = reserve(
|
"field-1", "version-current", "text-checkbox-group");
|
textForm.setCheckboxGroupCode("result_choice");
|
expectDataException(() -> text.service.reservePrimary(text.authorization(), textForm));
|
|
Fixture readOnly = new Fixture();
|
readOnly.addCheckboxFields();
|
readOnly.versions.get("checkbox-version-1").setBangdingPeizhi(
|
"{\"read\":{\"source\":\"LIMS_TASK\"}}");
|
ElnTemplateForms.ReserveBinding readOnlyForm = reserve(
|
"checkbox-1", "checkbox-version-1", "readonly-checkbox-group");
|
readOnlyForm.setCheckboxGroupCode("result_choice");
|
expectDataException(() -> readOnly.service.reservePrimary(
|
readOnly.authorization(), readOnlyForm));
|
}
|
|
@Test
|
public void rejectsCheckboxGroupsAcrossStepsAndRepeatScopes() {
|
Fixture steps = new Fixture();
|
steps.addCheckboxFields();
|
ElnTemplateForms.ReserveBinding first = reserve(
|
"checkbox-1", "checkbox-version-1", "checkbox-step-a");
|
first.setStepId("step-a");
|
first.setCheckboxGroupCode("result_choice");
|
steps.service.reservePrimary(steps.authorization(), first);
|
ElnTemplateForms.ReserveBinding second = reserve(
|
"checkbox-2", "checkbox-version-2", "checkbox-step-b");
|
second.setStepId("step-b");
|
second.setCheckboxGroupCode("result_choice");
|
expectDataException(() -> steps.service.reservePrimary(steps.authorization(), second));
|
|
Fixture ordinaryAndRepeat = new Fixture();
|
ordinaryAndRepeat.addCheckboxFields();
|
ElnTemplateForms.ReserveBinding ordinary = reserve(
|
"checkbox-1", "checkbox-version-1", "checkbox-ordinary");
|
ordinary.setCheckboxGroupCode("result_choice");
|
ordinaryAndRepeat.service.reservePrimary(ordinaryAndRepeat.authorization(), ordinary);
|
ElnTemplateForms.ReserveBinding repeated = repeatReserve(
|
"checkbox-2", "checkbox-version-2", "checkbox-repeat", "results", 0, 5);
|
repeated.setCheckboxGroupCode("result_choice");
|
expectDataException(() -> ordinaryAndRepeat.service.reservePrimary(
|
ordinaryAndRepeat.authorization(), repeated));
|
|
Fixture repeatGroups = new Fixture();
|
repeatGroups.addCheckboxFields();
|
ElnTemplateForms.ReserveBinding resultGroup = repeatReserve(
|
"checkbox-1", "checkbox-version-1", "checkbox-results", "results", 0, 5);
|
resultGroup.setCheckboxGroupCode("result_choice");
|
repeatGroups.service.reservePrimary(repeatGroups.authorization(), resultGroup);
|
ElnTemplateForms.ReserveBinding sampleGroup = repeatReserve(
|
"checkbox-2", "checkbox-version-2", "checkbox-samples", "samples", 0, 5);
|
sampleGroup.setCheckboxGroupCode("result_choice");
|
expectDataException(() -> repeatGroups.service.reservePrimary(
|
repeatGroups.authorization(), sampleGroup));
|
}
|
|
@Test
|
public void enforcesConfiguredMaximumDefaultCheckedMembersDuringReserve() {
|
StubCheckboxGroupService groups = new StubCheckboxGroupService(new ArrayList<>(
|
Collections.singletonList(checkboxGroup("result_choice", 0, 2, 0))));
|
Fixture f = new Fixture(groups);
|
f.addCheckboxFields();
|
f.addField("checkbox-3", "result_review", "checkbox-version-3");
|
f.addVersion("checkbox-version-3", "checkbox-3",
|
"PUBLISHED", "CHECKBOX", "复核");
|
f.versions.get("checkbox-version-1").setKongjianPeizhi("{\"defaultChecked\":true}");
|
f.versions.get("checkbox-version-2").setKongjianPeizhi("{\"defaultChecked\":true}");
|
f.versions.get("checkbox-version-3").setKongjianPeizhi("{\"defaultChecked\":true}");
|
ElnTemplateForms.ReserveBinding first = reserve(
|
"checkbox-1", "checkbox-version-1", "checkbox-default-first");
|
first.setCheckboxGroupCode("result_choice");
|
first.setCheckboxGroupMinSelected(0);
|
first.setCheckboxGroupMaxSelected(2);
|
f.service.reservePrimary(f.authorization(), first);
|
ElnTemplateForms.ReserveBinding second = reserve(
|
"checkbox-2", "checkbox-version-2", "checkbox-default-second");
|
second.setCheckboxGroupCode("result_choice");
|
second.setCheckboxGroupMinSelected(0);
|
second.setCheckboxGroupMaxSelected(2);
|
f.service.reservePrimary(f.authorization(), second);
|
ElnTemplateForms.ReserveBinding third = reserve(
|
"checkbox-3", "checkbox-version-3", "checkbox-default-third");
|
third.setCheckboxGroupCode("result_choice");
|
third.setCheckboxGroupMinSelected(0);
|
third.setCheckboxGroupMaxSelected(2);
|
|
expectDataException(() -> f.service.reservePrimary(f.authorization(), third));
|
}
|
|
@Test
|
public void missingCheckboxGroupRuleKeepsLegacySingleDefaultMaximum() {
|
Fixture f = new Fixture();
|
f.addCheckboxFields();
|
f.versions.get("checkbox-version-1").setKongjianPeizhi("{\"defaultChecked\":true}");
|
f.versions.get("checkbox-version-2").setKongjianPeizhi("{\"defaultChecked\":true}");
|
ElnTemplateForms.ReserveBinding first = reserve(
|
"checkbox-1", "checkbox-version-1", "legacy-default-first");
|
first.setCheckboxGroupCode("legacy_result");
|
f.service.reservePrimary(f.authorization(), first);
|
ElnTemplateForms.ReserveBinding second = reserve(
|
"checkbox-2", "checkbox-version-2", "legacy-default-second");
|
second.setCheckboxGroupCode("legacy_result");
|
|
expectDataException(() -> f.service.reservePrimary(f.authorization(), second));
|
}
|
|
@Test
|
public void changesAndRemovesCheckboxGroupWithOptimisticLockAndIdempotentAudit() {
|
StubCheckboxGroupService checkboxGroups = new StubCheckboxGroupService(
|
new ArrayList<ElnTemplateVos.CheckboxGroup>());
|
Fixture f = new Fixture(checkboxGroups);
|
f.addCheckboxFields();
|
String bindingId = f.activePrimary(
|
"checkbox-binding", "checkbox-1", "checkbox-version-1");
|
ElnTemplateForms.ChangeCheckboxGroup change = checkboxGroupChange(
|
"result_choice", "change-checkbox-group", 3);
|
|
ElnTemplateVos.Binding changed = changeCheckboxGroup(
|
f.service, f.authorization(), bindingId, change);
|
ElnTemplateVos.Binding retried = changeCheckboxGroup(
|
f.service, f.authorization(), bindingId, change);
|
|
assertEquals("result_choice", changed.getCheckboxGroupCode());
|
assertEquals(Integer.valueOf(4), changed.getVersion());
|
assertEquals("result_choice", retried.getCheckboxGroupCode());
|
assertEquals(1, f.countAudits("ELN_TEMPLATE_BINDING_CHECKBOX_GROUP_CHANGE"));
|
assertTrue(f.audit("ELN_TEMPLATE_BINDING_CHECKBOX_GROUP_CHANGE")
|
.getDataSnapshot().contains("result_choice"));
|
|
ElnTemplateForms.ChangeCheckboxGroup reused = checkboxGroupChange(
|
"other_group", "change-checkbox-group", 4);
|
expectOperationStatus(409, () -> changeCheckboxGroup(
|
f.service, f.authorization(), bindingId, reused));
|
|
ElnTemplateForms.ChangeCheckboxGroup remove = checkboxGroupChange(
|
" ", "remove-checkbox-group", 4);
|
ElnTemplateVos.Binding removed = changeCheckboxGroup(
|
f.service, f.authorization(), bindingId, remove);
|
assertEquals(null, removed.getCheckboxGroupCode());
|
assertEquals(Integer.valueOf(5), removed.getVersion());
|
assertTrue(checkboxGroups.list("tenant-1", "template-1").isEmpty());
|
}
|
|
@Test
|
public void rejectsStaleOrConflictingCheckboxGroupChange() {
|
Fixture stale = new Fixture();
|
stale.addCheckboxFields();
|
String staleId = stale.activePrimary(
|
"checkbox-stale", "checkbox-1", "checkbox-version-1");
|
expectOperationStatus(409, () -> changeCheckboxGroup(stale.service,
|
stale.authorization(), staleId,
|
checkboxGroupChange("result_choice", "stale-checkbox-group", 2)));
|
|
Fixture conflict = new Fixture();
|
conflict.addCheckboxFields();
|
String firstId = conflict.activePrimary(
|
"checkbox-first", "checkbox-1", "checkbox-version-1");
|
conflict.store.binding(firstId).setCheckboxGroupCode("result_choice");
|
conflict.store.binding(firstId).setStepId("step-a");
|
String secondId = conflict.activePrimary(
|
"checkbox-second", "checkbox-2", "checkbox-version-2");
|
conflict.store.binding(secondId).setStepId("step-b");
|
expectDataException(() -> changeCheckboxGroup(conflict.service,
|
conflict.authorization(), secondId,
|
checkboxGroupChange("result_choice", "conflicting-checkbox-group", 3)));
|
assertEquals(0, conflict.countAudits(
|
"ELN_TEMPLATE_BINDING_CHECKBOX_GROUP_CHANGE"));
|
}
|
|
@Test
|
public void rejectsCheckboxGroupWhenDependencyValidatorMarksBindingReadOnly() {
|
Fixture f = new Fixture();
|
f.addCheckboxFields();
|
String bindingId = f.activePrimary(
|
"checkbox-readonly", "checkbox-1", "checkbox-version-1");
|
f.checkboxGroupDiagnostics = Collections.singletonList(
|
error("CHECKBOX_GROUP_BINDING_READ_ONLY"));
|
|
expectDataException(() -> changeCheckboxGroup(f.service, f.authorization(), bindingId,
|
checkboxGroupChange("result_choice", "readonly-checkbox-change", 3)));
|
}
|
|
@Test
|
public void allowsUpgradeWithinConfiguredMaximumDefaultCheckedMembers() {
|
StubCheckboxGroupService groups = new StubCheckboxGroupService(new ArrayList<>(
|
Collections.singletonList(checkboxGroup("result_choice", 0, 2, 0))));
|
Fixture f = new Fixture(groups);
|
f.addCheckboxFields();
|
f.versions.get("checkbox-version-1").setKongjianPeizhi(
|
"{\"defaultChecked\":true}");
|
f.versions.get("checkbox-version-2").setKongjianPeizhi(
|
"{\"defaultChecked\":false}");
|
f.addVersion("checkbox-version-2-default", "checkbox-2",
|
"PUBLISHED", "CHECKBOX", "不合格(默认)");
|
f.versions.get("checkbox-version-2-default").setKongjianPeizhi(
|
"{\"defaultChecked\":true}");
|
f.fields.get("checkbox-2").setDangqianZiduanBanbenId(
|
"checkbox-version-2-default");
|
String firstId = f.activePrimary(
|
"checkbox-default-first", "checkbox-1", "checkbox-version-1");
|
f.store.binding(firstId).setCheckboxGroupCode("result_choice");
|
String secondId = f.activePrimary(
|
"checkbox-default-second", "checkbox-2", "checkbox-version-2");
|
f.store.binding(secondId).setCheckboxGroupCode("result_choice");
|
ElnTemplateVos.ChangeCheck check = f.service.checkUpgrade(
|
f.authorization(), secondId, "checkbox-version-2-default");
|
|
f.service.confirmUpgrade(f.authorization(), secondId,
|
upgradeConfirm(check, "checkbox-version-2-default"));
|
assertEquals("checkbox-version-2-default",
|
f.store.binding(secondId).getZiduanBanbenId());
|
}
|
|
@Test
|
public void marksProductionConstructorForSpringInjection() {
|
Constructor<?> production = null;
|
for (Constructor<?> constructor : ElnTemplateBindingServiceImpl.class.getConstructors()) {
|
if (constructor.getAnnotation(Autowired.class) != null) {
|
production = constructor;
|
}
|
}
|
assertNotNull("未找到生产构造器", production);
|
assertNotNull("多 public 构造器时生产构造器必须显式 @Autowired",
|
production.getAnnotation(Autowired.class));
|
assertTrue("生产构造器必须注入 PENDING 对账服务",
|
Arrays.asList(production.getParameterTypes())
|
.contains(ElnPendingBindingCleanupService.class));
|
}
|
|
@Test
|
public void reservesActivatesCancelsAndRetriesSameOperationIdempotently() {
|
Fixture f = new Fixture();
|
ElnTemplateVos.Binding reserved = f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-current", "reserve-1"));
|
ElnTemplateVos.Binding retried = f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-current", "reserve-1"));
|
|
assertEquals(reserved.getId(), retried.getId());
|
assertEquals("eln.field.sample_code", reserved.getControlTag());
|
assertEquals("PENDING", reserved.getStatus());
|
f.service.activate(f.authorization(), reserved.getId(), "activate-1");
|
assertEquals("ACTIVE", f.store.binding(reserved.getId()).getBangdingZhuangtai());
|
|
ElnTemplateVos.Binding cancelled = f.service.reservePrimary(f.authorization(),
|
reserve("field-2", "version-2", "reserve-2"));
|
f.service.cancel(f.authorization(), cancelled.getId(), "cancel-1", "控件插入失败");
|
assertEquals("CANCELLED", f.store.binding(cancelled.getId()).getBangdingZhuangtai());
|
assertEquals(Collections.singletonList(cancelled.getId()), f.terminatedBindingIds);
|
assertTrue(f.hasAudit("ELN_TEMPLATE_BINDING_RESERVE"));
|
assertTrue(f.hasAudit("ELN_TEMPLATE_BINDING_ACTIVATE"));
|
assertTrue(f.hasAudit("ELN_TEMPLATE_BINDING_CANCEL"));
|
for (AuditEventDTO audit : f.audits) {
|
assertFalse(audit.getDataSnapshot().toLowerCase().contains("ticket"));
|
}
|
}
|
|
@Test
|
public void bindingMutationsAcquireTheTemplateWriteLock() {
|
Fixture f = new Fixture();
|
|
ElnTemplateVos.Binding reserved = f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-current", "reserve-with-lock"));
|
assertEquals(Collections.singletonList("template-1"), f.store.lockedTemplateIds);
|
|
f.store.lockedTemplateIds.clear();
|
f.service.activate(f.authorization(), reserved.getId(), "activate-with-lock");
|
assertEquals(Collections.singletonList("template-1"), f.store.lockedTemplateIds);
|
}
|
|
@Test
|
public void rejectsSecondLivePrimaryAndBuildsReferenceFromActivePrimary() {
|
Fixture f = new Fixture();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
expectDataException(() -> f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-current", "other-primary")));
|
|
ElnTemplateVos.Binding reference = f.service.reserveReference(
|
f.authorization(), primary, "reference-1");
|
|
assertEquals("version-current", reference.getFieldVersionId());
|
assertEquals(primary, reference.getPrimaryBindingId());
|
assertTrue(reference.getControlTag().matches("eln\\.ref\\.sample_code\\.[0-9a-f]{32}"));
|
assertNotEquals(f.store.binding(primary).getKongjianBiaoqian(), reference.getControlTag());
|
}
|
|
@Test
|
public void rejectsReferenceWithoutActivePrimary() {
|
Fixture f = new Fixture();
|
String pending = f.pendingPrimary("primary-1", "field-1", "version-current", "pending-op");
|
expectDataException(() -> f.service.reserveReference(f.authorization(), pending, "reference-1"));
|
}
|
|
@Test
|
public void contextReturnsOnlyAuthorizedTemplateLiveBindingsAndRejectsCrossTemplateBinding() {
|
Fixture f = new Fixture();
|
f.template.setShengbanCaozuoBianma("upgrade-template-1");
|
f.activePrimary("primary-1", "field-1", "version-current");
|
f.pendingPrimary("pending-1", "field-2", "version-2", "pending-context");
|
ElnTemplateBindingEntity removed = f.binding(
|
"removed-1", "field-2", "version-2", "PRIMARY", null, "REMOVED");
|
removed.setCaozuoBianma("removed-context");
|
f.store.bindings.put(removed.getId(), removed);
|
|
ElnTemplateVos.AnnotationContext context = f.service.context(f.authorization());
|
|
assertEquals("template-1", context.getTemplateId());
|
assertEquals("upgrade-template-1", context.getUpgradeOperationCode());
|
assertTrue(context.getStepConfiguration().isSingleStep());
|
assertEquals("test-single-step",
|
context.getStepConfiguration().getSteps().get(0).getId());
|
assertEquals(2, context.getFields().size());
|
assertEquals(2, context.getFields().get(0).getVersions().size());
|
assertEquals(2, context.getBindings().size());
|
ElnTemplateBindingEntity otherTemplate = f.binding(
|
"other-binding", "field-2", "version-2", "PRIMARY", null, "ACTIVE");
|
otherTemplate.setMubanId("template-2");
|
f.store.bindings.put(otherTemplate.getId(), otherTemplate);
|
expectDataException(() -> f.service.activate(
|
f.authorization(), otherTemplate.getId(), "cross-template"));
|
}
|
|
@Test
|
public void contextMarksOnlyCopiedPendingBindingsForVersionCopyConfirmation() {
|
Fixture f = new Fixture();
|
f.pendingPrimary("ordinary-pending", "field-2", "version-2", "ordinary-op");
|
f.pendingCopied("copied-pending", "source-binding-1", "copy-op");
|
f.activePrimary("copied-active", "field-1", "version-current");
|
f.store.binding("copied-active").setLaiyuanBangdingId("source-binding-2");
|
|
ElnTemplateVos.AnnotationContext context = f.service.context(f.authorization());
|
|
assertEquals(3, context.getBindings().size());
|
assertEquals(Collections.singletonList("copied-pending"),
|
context.getCopiedPendingBindingIds());
|
}
|
|
@Test
|
public void contextAndReserveDownloadFreshDocumentEvidenceOncePerCall() {
|
Fixture f = new Fixture();
|
|
f.service.context(f.authorization());
|
assertEquals(1, f.evidenceReads);
|
|
f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-current", "download-before-reserve"));
|
assertEquals(2, f.evidenceReads);
|
}
|
|
@Test
|
public void returnsAuthoritativeControlConfigAndIgnoresUnprovableTextDefaults() {
|
Fixture f = new Fixture();
|
f.selectNextVersion();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
|
ElnTemplateVos.AnnotationContext context = f.service.context(f.authorization());
|
assertEquals("version-current", controlConfig(context.getBindings().get(0))
|
.get("placeholder"));
|
ElnTemplateVos.RepairContext repair = f.service.repairContext(f.authorization(), primary);
|
assertEquals("version-current", controlConfig(repair.getBinding()).get("placeholder"));
|
|
ElnTemplateVos.ChangeCheck upgrade = f.service.checkUpgrade(
|
f.authorization(), primary, "version-next");
|
assertEquals("version-next", controlConfig(upgrade.getControls().get(0))
|
.get("placeholder"));
|
ElnTemplateForms.UpgradeConfirm confirmed = upgradeConfirm(upgrade, "version-next");
|
setControlConfig(confirmed.getControls().get(0), Collections.emptyMap());
|
|
f.service.confirmUpgrade(f.authorization(), primary, confirmed);
|
|
assertEquals("version-next", f.store.binding(primary).getZiduanBanbenId());
|
}
|
|
@Test
|
public void matchesDropdownOptionsSemanticallyAndRejectsChangedOption() {
|
Fixture accepted = new Fixture();
|
accepted.addDropdownVersion("version-select");
|
String acceptedPrimary = accepted.activePrimary(
|
"primary-1", "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck acceptedCheck = accepted.service.checkUpgrade(
|
accepted.authorization(), acceptedPrimary, "version-select");
|
ElnTemplateForms.UpgradeConfirm equivalent = upgradeConfirm(
|
acceptedCheck, "version-select");
|
Map<String, Object> docxOptions = new LinkedHashMap<>();
|
docxOptions.put("A", "选项 A");
|
docxOptions.put("B", "选项 B");
|
setControlConfig(equivalent.getControls().get(0), docxOptions);
|
|
accepted.service.confirmUpgrade(
|
accepted.authorization(), acceptedPrimary, equivalent);
|
assertEquals("version-select",
|
accepted.store.binding(acceptedPrimary).getZiduanBanbenId());
|
|
Fixture rejected = new Fixture();
|
rejected.addDropdownVersion("version-select");
|
String rejectedPrimary = rejected.activePrimary(
|
"primary-1", "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck rejectedCheck = rejected.service.checkUpgrade(
|
rejected.authorization(), rejectedPrimary, "version-select");
|
ElnTemplateForms.UpgradeConfirm changed = upgradeConfirm(
|
rejectedCheck, "version-select");
|
Map<String, Object> changedOptions = new LinkedHashMap<>();
|
changedOptions.put("A", "选项 A");
|
changedOptions.put("B", "已篡改");
|
setControlConfig(changed.getControls().get(0), changedOptions);
|
|
expectDataException(() -> rejected.service.confirmUpgrade(
|
rejected.authorization(), rejectedPrimary, changed));
|
}
|
|
@Test
|
public void acceptsOnlyEnabledPreReleaseOrCurrentPublishedFieldVersions() {
|
Fixture f = new Fixture();
|
f.fields.get("field-1").setBizEnabled("disabled");
|
expectDataException(() -> f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-current", "disabled-field")));
|
f.fields.get("field-1").setBizEnabled("enabled");
|
expectDataException(() -> f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-old", "old-version")));
|
expectDataException(() -> f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-draft", "draft-version")));
|
|
ElnTemplateVos.Binding preRelease = f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-pre", "pre-release"));
|
assertEquals("version-pre", preRelease.getFieldVersionId());
|
}
|
|
@Test
|
public void rejectsNonDraftDisabledAndRepeatRequests() {
|
Fixture f = new Fixture();
|
f.template.setBizStatus("PUBLISHED");
|
expectDataException(() -> f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-current", "published-template")));
|
f.template.setBizStatus("DRAFT");
|
f.template.setBizEnabled("disabled");
|
expectDataException(() -> f.service.reservePrimary(f.authorization(),
|
reserve("field-1", "version-current", "disabled-template")));
|
f.template.setBizEnabled("enabled");
|
ElnTemplateForms.ReserveBinding repeat = reserve("field-1", "version-current", "repeat");
|
repeat.setRepeatRequest(true);
|
expectDataException(() -> f.service.reservePrimary(f.authorization(), repeat));
|
}
|
|
@Test
|
public void reservesIndependentRepeatGroupsAndReturnsTheirConstraints() {
|
Fixture f = new Fixture();
|
ElnTemplateForms.ReserveBinding results = repeatReserve(
|
"field-1", "version-current", "repeat-results", "results", 2, 20);
|
ElnTemplateForms.ReserveBinding samples = repeatReserve(
|
"field-2", "version-2", "repeat-samples", "samples", 0, 5);
|
|
ElnTemplateVos.Binding first = f.service.reservePrimary(f.authorization(), results);
|
ElnTemplateVos.Binding second = f.service.reservePrimary(f.authorization(), samples);
|
ElnTemplateVos.AnnotationContext context = f.service.context(f.authorization());
|
|
assertEquals("results", first.getRepeatGroupCode());
|
assertEquals("samples", second.getRepeatGroupCode());
|
assertEquals(2, context.getRepeatGroups().size());
|
assertEquals("results", context.getRepeatGroups().get(0).getGroupCode());
|
assertEquals(Integer.valueOf(2), context.getRepeatGroups().get(0).getMinRows());
|
assertEquals(Integer.valueOf(20), context.getRepeatGroups().get(0).getMaxRows());
|
assertEquals("test-single-step", context.getRepeatGroups().get(0).getStepId());
|
assertEquals("samples", context.getRepeatGroups().get(1).getGroupCode());
|
assertEquals(null, first.getStepId());
|
assertEquals(null, second.getStepId());
|
|
expectDataException(() -> f.service.reservePrimary(f.authorization(), repeatReserve(
|
"field-1", "version-current", "repeat-results-duplicate", "results", 2, 20)));
|
expectDataException(() -> f.service.reservePrimary(f.authorization(), repeatReserve(
|
"field-2", "version-2", "repeat-results-conflict", "results", 1, 20)));
|
}
|
|
@Test
|
public void createsPrivatePreReleaseFieldThenReservesPrimaryWithoutDeletingItOnCancel() {
|
Fixture f = new Fixture();
|
ElnTemplateForms.PrivateField form = new ElnTemplateForms.PrivateField();
|
form.setOperationCode("private-1");
|
form.setZiduanMingcheng("模板备注");
|
form.setShujuLeixing("TEXT");
|
|
ElnTemplateVos.Binding binding = f.service.createPrivateFieldAndReserve(f.authorization(), form);
|
f.service.cancel(f.authorization(), binding.getId(), "cancel-private", "插件插入失败");
|
|
assertEquals("TEMPLATE", f.createdPrivate.getZuoyongFanwei());
|
assertEquals("TMP-001", f.createdPrivate.getMubanBianhao());
|
assertEquals("NONE", f.privateCommand.getBinding().getRead().getSource());
|
assertEquals("ELN_RECORD", f.privateCommand.getBinding().getWrite().getTarget());
|
assertEquals("RECORD_PRIVATE", f.privateCommand.getPiciMoshi());
|
assertEquals("CANCELLED", f.store.binding(binding.getId()).getBangdingZhuangtai());
|
assertTrue(f.fields.containsKey("private-field"));
|
}
|
|
@Test
|
public void privateFieldRetryRequiresSameBusinessSemantics() {
|
Fixture f = new Fixture();
|
ElnTemplateForms.PrivateField form = privateField("private-1", "模板备注", "TEXT");
|
form.setDanwei("mg");
|
form.setShifouBitian(1);
|
form.setBeizhu("原始备注");
|
form.setKongjianPeizhi(new LinkedHashMap<>(Collections.singletonMap(
|
"placeholder", "请输入")));
|
ElnTemplateVos.Binding first = f.service.createPrivateFieldAndReserve(
|
f.authorization(), form);
|
|
ElnTemplateVos.Binding retried = f.service.createPrivateFieldAndReserve(
|
f.authorization(), privateFieldCopy(form));
|
assertEquals(first.getId(), retried.getId());
|
assertEquals(1, f.privateCreateCalls.get());
|
|
ElnTemplateForms.PrivateField changed = privateFieldCopy(form);
|
changed.setZiduanMingcheng("另一个字段");
|
expectDataException(() -> f.service.createPrivateFieldAndReserve(
|
f.authorization(), changed));
|
|
f.createdPrivate.setZuoyongFanwei("GLOBAL");
|
expectDataException(() -> f.service.createPrivateFieldAndReserve(
|
f.authorization(), privateFieldCopy(form)));
|
}
|
|
@Test
|
public void privateCheckboxReservationCarriesGroupAndRetryRequiresSameGroup() {
|
StubCheckboxGroupService groups = new StubCheckboxGroupService(new ArrayList<>());
|
Fixture f = new Fixture(groups);
|
ElnTemplateForms.PrivateField form = privateField(
|
"private-checkbox-1", "合格", "CHECKBOX");
|
form.setCheckboxGroupCode("test_result");
|
form.setCheckboxGroupMinSelected(1);
|
form.setCheckboxGroupMaxSelected(3);
|
form.setKongjianPeizhi(new LinkedHashMap<>(Collections.singletonMap(
|
"defaultChecked", false)));
|
|
ElnTemplateVos.Binding first = f.service.createPrivateFieldAndReserve(
|
f.authorization(), form);
|
ElnTemplateVos.Binding retried = f.service.createPrivateFieldAndReserve(
|
f.authorization(), privateFieldCopy(form));
|
|
assertEquals("test_result", first.getCheckboxGroupCode());
|
assertEquals(Integer.valueOf(1), groups.groups.get(0).getMinSelected());
|
assertEquals(Integer.valueOf(3), groups.groups.get(0).getMaxSelected());
|
assertEquals(first.getId(), retried.getId());
|
assertEquals(1, f.privateCreateCalls.get());
|
|
ElnTemplateForms.PrivateField changed = privateFieldCopy(form);
|
changed.setCheckboxGroupCode("review_result");
|
expectDataException(() -> f.service.createPrivateFieldAndReserve(
|
f.authorization(), changed));
|
|
ElnTemplateForms.PrivateField changedBounds = privateFieldCopy(form);
|
changedBounds.setCheckboxGroupMaxSelected(4);
|
expectDataException(() -> f.service.createPrivateFieldAndReserve(
|
f.authorization(), changedBounds));
|
}
|
|
@Test
|
public void pendingReferenceBlocksPrimaryChangesAndActivationRevalidatesPrimary() {
|
Fixture f = new Fixture();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
f.pendingReference("pending-ref", primary, "field-1", "version-current");
|
|
expectDataException(() -> f.service.checkRemove(f.authorization(), primary));
|
f.selectNextVersion();
|
expectDataException(() -> f.service.checkUpgrade(
|
f.authorization(), primary, "version-next"));
|
f.activeReference("active-ref", primary, "field-1", "version-current");
|
expectDataException(() -> f.service.checkPromoteReference(
|
f.authorization(), "active-ref"));
|
|
f.store.binding(primary).setBangdingZhuangtai("REMOVED");
|
expectDataException(() -> f.service.activate(
|
f.authorization(), "pending-ref", "activate-pending-ref"));
|
assertEquals("PENDING", f.store.binding("pending-ref").getBangdingZhuangtai());
|
}
|
|
@Test
|
public void locksPrimaryForReferenceReserveActivationAndDestructiveChecks() {
|
Fixture f = new Fixture();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
f.service.reserveReference(f.authorization(), primary, "reserve-reference");
|
assertTrue(f.store.lockedBindingIds.contains(primary));
|
|
f.store.lockedBindingIds.clear();
|
f.pendingReference("pending-ref", primary, "field-1", "version-current");
|
f.service.activate(f.authorization(), "pending-ref", "activate-reference");
|
assertTrue(f.store.lockedBindingIds.contains(primary));
|
|
f.store.lockedBindingIds.clear();
|
expectDataException(() -> f.service.checkRemove(f.authorization(), primary));
|
assertTrue(f.store.lockedBindingIds.contains(primary));
|
|
Fixture upgrade = new Fixture();
|
String upgradePrimary = upgrade.activePrimary(
|
"upgrade-primary", "field-1", "version-current");
|
upgrade.selectNextVersion();
|
upgrade.service.checkUpgrade(
|
upgrade.authorization(), upgradePrimary, "version-next");
|
assertTrue(upgrade.store.lockedBindingIds.contains(upgradePrimary));
|
|
Fixture promote = new Fixture();
|
String promotePrimary = promote.activePrimary(
|
"promote-primary", "field-1", "version-current");
|
promote.activeReference(
|
"promote-reference", promotePrimary, "field-1", "version-current");
|
promote.service.checkPromoteReference(
|
promote.authorization(), "promote-reference");
|
assertTrue(promote.store.lockedBindingIds.contains(promotePrimary));
|
}
|
|
@Test
|
public void repairsWithoutChangingActiveStateAndRemovesPermanently() {
|
Fixture f = new Fixture();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
ElnTemplateVos.RepairContext repair = f.service.repairContext(f.authorization(), primary);
|
ElnTemplateForms.RepairConfirm repairForm = repairConfirm(repair);
|
int repairVersion = f.store.binding(primary).getFVersion();
|
f.service.confirmRepair(f.authorization(), primary, repairForm);
|
assertEquals("ACTIVE", f.store.binding(primary).getBangdingZhuangtai());
|
assertEquals(repairVersion + 1, (int) f.store.binding(primary).getFVersion());
|
f.service.confirmRepair(f.authorization(), primary, repairForm);
|
assertEquals(repairVersion + 1, (int) f.store.binding(primary).getFVersion());
|
assertTrue(f.hasAudit("ELN_TEMPLATE_BINDING_REPAIR"));
|
|
ElnTemplateVos.ChangeCheck remove = f.service.checkRemove(f.authorization(), primary);
|
f.service.confirmRemove(f.authorization(), primary, removeConfirm(remove));
|
assertEquals("REMOVED", f.store.binding(primary).getBangdingZhuangtai());
|
assertEquals(Collections.singletonList(primary), f.terminatedBindingIds);
|
expectDataException(() -> f.service.activate(f.authorization(), primary, "restore-forbidden"));
|
}
|
|
@Test
|
public void removeConfirmRejectsMissingStaleOrDuplicateDocumentTagEvidence() {
|
Fixture missing = new Fixture();
|
String missingId = missing.activePrimary(
|
"primary-1", "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck missingCheck = missing.service.checkRemove(
|
missing.authorization(), missingId);
|
ElnTemplateForms.RemoveConfirm missingForm = removeConfirm(missingCheck);
|
missingForm.setObservedControlTags(null);
|
expectDataException(() -> missing.service.confirmRemove(
|
missing.authorization(), missingId, missingForm));
|
assertEquals("ACTIVE", missing.store.binding(missingId).getBangdingZhuangtai());
|
|
Fixture stale = new Fixture();
|
String staleId = stale.activePrimary(
|
"primary-1", "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck staleCheck = stale.service.checkRemove(
|
stale.authorization(), staleId);
|
ElnTemplateForms.RemoveConfirm staleForm = removeConfirm(staleCheck);
|
staleForm.getObservedControlTags().add("eln.field.sample_code");
|
expectDataException(() -> stale.service.confirmRemove(
|
stale.authorization(), staleId, staleForm));
|
assertEquals("ACTIVE", stale.store.binding(staleId).getBangdingZhuangtai());
|
|
Fixture duplicate = new Fixture();
|
String duplicateId = duplicate.activePrimary(
|
"primary-1", "field-1", "version-current");
|
duplicate.activePrimary("primary-2", "field-2", "version-2");
|
ElnTemplateVos.ChangeCheck duplicateCheck = duplicate.service.checkRemove(
|
duplicate.authorization(), duplicateId);
|
ElnTemplateForms.RemoveConfirm duplicateForm = removeConfirm(duplicateCheck);
|
duplicateForm.getObservedControlTags().add(
|
duplicateForm.getObservedControlTags().get(0));
|
expectDataException(() -> duplicate.service.confirmRemove(
|
duplicate.authorization(), duplicateId, duplicateForm));
|
assertEquals("ACTIVE", duplicate.store.binding(duplicateId).getBangdingZhuangtai());
|
}
|
|
@Test
|
public void promoteConfirmRejectsMissingOrResidualReferenceTagEvidence() {
|
Fixture missing = new Fixture();
|
String missingPrimary = missing.activePrimary(
|
"primary-1", "field-1", "version-current");
|
missing.activeReference(
|
"reference-1", missingPrimary, "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck missingCheck = missing.service.checkPromoteReference(
|
missing.authorization(), "reference-1");
|
ElnTemplateForms.PromoteConfirm missingForm = promoteConfirm(missingCheck);
|
missingForm.setObservedControlTags(null);
|
expectDataException(() -> missing.service.confirmPromoteReference(
|
missing.authorization(), "reference-1", missingForm));
|
assertEquals("REFERENCE", missing.store.binding("reference-1").getKongjianJuese());
|
|
Fixture stale = new Fixture();
|
String stalePrimary = stale.activePrimary(
|
"primary-1", "field-1", "version-current");
|
stale.activeReference(
|
"reference-1", stalePrimary, "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck staleCheck = stale.service.checkPromoteReference(
|
stale.authorization(), "reference-1");
|
ElnTemplateForms.PromoteConfirm staleForm = promoteConfirm(staleCheck);
|
staleForm.getObservedControlTags().add("eln.ref.sample_code.reference-1");
|
expectDataException(() -> stale.service.confirmPromoteReference(
|
stale.authorization(), "reference-1", staleForm));
|
assertEquals("REFERENCE", stale.store.binding("reference-1").getKongjianJuese());
|
}
|
|
@Test
|
public void repairCasConflictRollsBackAndWritesIndependentFailureAudit() {
|
Fixture f = new Fixture();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
ElnTemplateVos.RepairContext repair = f.service.repairContext(f.authorization(), primary);
|
ElnTemplateForms.RepairConfirm form = repairConfirm(repair);
|
int beforeVersion = f.store.binding(primary).getFVersion();
|
f.store.failCasOnId = primary;
|
|
expectDataException(() -> f.service.confirmRepair(f.authorization(), primary, form));
|
|
assertEquals("ACTIVE", f.store.binding(primary).getBangdingZhuangtai());
|
assertEquals(beforeVersion, (int) f.store.binding(primary).getFVersion());
|
assertTrue(f.hasAudit("ELN_TEMPLATE_BINDING_CONFIRM_FAILED"));
|
}
|
|
@Test
|
public void successAuditsCapturePersistedCasVersions() {
|
Fixture activated = new Fixture();
|
ElnTemplateVos.Binding reserved = activated.service.reservePrimary(
|
activated.authorization(), reserve(
|
"field-1", "version-current", "reserve-for-activate"));
|
activated.service.activate(
|
activated.authorization(), reserved.getId(), "activate-for-audit");
|
assertEquals(1, numberProperty(
|
auditBinding(activated, "ELN_TEMPLATE_BINDING_ACTIVATE", "after"), "fVersion"));
|
|
Fixture cancelled = new Fixture();
|
String pending = cancelled.pendingPrimary(
|
"pending-1", "field-1", "version-current", "reserve-for-cancel");
|
cancelled.service.cancel(
|
cancelled.authorization(), pending, "cancel-for-audit", "插入失败");
|
assertEquals(4, numberProperty(
|
auditBinding(cancelled, "ELN_TEMPLATE_BINDING_CANCEL", "after"), "fVersion"));
|
|
Fixture repaired = new Fixture();
|
String repairId = repaired.activePrimary(
|
"repair-1", "field-1", "version-current");
|
ElnTemplateVos.RepairContext repair = repaired.service.repairContext(
|
repaired.authorization(), repairId);
|
repaired.service.confirmRepair(
|
repaired.authorization(), repairId, repairConfirm(repair));
|
assertEquals(4, numberProperty(
|
auditBinding(repaired, "ELN_TEMPLATE_BINDING_REPAIR", "after"), "fVersion"));
|
|
Fixture removed = new Fixture();
|
String removeId = removed.activePrimary(
|
"remove-1", "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck remove = removed.service.checkRemove(
|
removed.authorization(), removeId);
|
removed.service.confirmRemove(
|
removed.authorization(), removeId, removeConfirm(remove));
|
assertEquals(4, numberProperty(
|
auditBinding(removed, "ELN_TEMPLATE_BINDING_REMOVE", "after"), "fVersion"));
|
|
Fixture promoted = new Fixture();
|
String primary = promoted.activePrimary(
|
"primary-1", "field-1", "version-current");
|
promoted.activeReference(
|
"reference-1", primary, "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck promote = promoted.service.checkPromoteReference(
|
promoted.authorization(), "reference-1");
|
promoted.service.confirmPromoteReference(
|
promoted.authorization(), "reference-1", promoteConfirm(promote));
|
List<Map<String, Object>> after = bindingSnapshots(
|
promoted.auditSnapshot("ELN_TEMPLATE_BINDING_PROMOTE"), "after");
|
assertEquals(2, countProperty(after, "fVersion", 4));
|
}
|
|
@Test
|
public void upgradesPrimaryAndAllReferencesAtomically() {
|
Fixture f = new Fixture();
|
f.selectNextVersion();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
f.activeReference("reference-1", primary, "field-1", "version-current");
|
f.activeReference("reference-2", primary, "field-1", "version-current");
|
|
ElnTemplateVos.ChangeCheck check = f.service.checkUpgrade(
|
f.authorization(), primary, "version-next");
|
f.service.confirmUpgrade(f.authorization(), primary, upgradeConfirm(check, "version-next"));
|
|
assertEquals("version-next", f.store.binding(primary).getZiduanBanbenId());
|
assertEquals("version-next", f.store.binding("reference-1").getZiduanBanbenId());
|
assertEquals("version-next", f.store.binding("reference-2").getZiduanBanbenId());
|
assertEquals(3, new LinkedHashSet<>(Arrays.asList(
|
f.store.binding(primary).getCaozuoBianma(),
|
f.store.binding("reference-1").getCaozuoBianma(),
|
f.store.binding("reference-2").getCaozuoBianma())).size());
|
Map<String, Object> audit = f.auditSnapshot("ELN_TEMPLATE_BINDING_UPGRADE");
|
List<Map<String, Object>> beforeAudit = bindingSnapshots(audit, "before");
|
List<Map<String, Object>> afterAudit = bindingSnapshots(audit, "after");
|
assertEquals(3, beforeAudit.size());
|
assertEquals(3, afterAudit.size());
|
assertEquals(3, countProperty(beforeAudit, "ziduanBanbenId", "version-current"));
|
assertEquals(3, countProperty(afterAudit, "ziduanBanbenId", "version-next"));
|
assertEquals(3, countProperty(afterAudit, "fVersion", 4));
|
assertFalse(f.audit("ELN_TEMPLATE_BINDING_UPGRADE").getDataSnapshot()
|
.toLowerCase().contains("ticket"));
|
assertEquals(2, f.upgradeValidationCalls.get());
|
f.service.confirmUpgrade(f.authorization(), primary, upgradeConfirm(check, "version-next"));
|
assertEquals(3, f.store.bindings.size());
|
}
|
|
@Test
|
public void rejectsIncompleteVersionsStaleSnapshotAndDependencyErrors() {
|
Fixture f = new Fixture();
|
f.selectNextVersion();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
f.activeReference("reference-1", primary, "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck check = f.service.checkUpgrade(
|
f.authorization(), primary, "version-next");
|
|
ElnTemplateForms.UpgradeConfirm incomplete = upgradeConfirm(check, "version-next");
|
incomplete.getExpectedVersions().remove("reference-1");
|
expectDataException(() -> f.service.confirmUpgrade(f.authorization(), primary, incomplete));
|
|
ElnTemplateForms.UpgradeConfirm stale = upgradeConfirm(check, "version-next");
|
stale.getControls().get(0).setControlAlias("被客户端篡改");
|
expectDataException(() -> f.service.confirmUpgrade(f.authorization(), primary, stale));
|
|
f.validatorDiagnostics = Collections.singletonList(error("FORMULA_DEPENDENCY"));
|
expectDataException(() -> f.service.checkUpgrade(
|
f.authorization(), primary, "version-next"));
|
}
|
|
@Test
|
public void casFailureRollsBackStateAndWritesIndependentFailureAudit() {
|
Fixture f = new Fixture();
|
f.selectNextVersion();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
f.activeReference("reference-1", primary, "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck check = f.service.checkUpgrade(
|
f.authorization(), primary, "version-next");
|
f.store.failCasOnId = "reference-1";
|
|
expectDataException(() -> f.service.confirmUpgrade(
|
f.authorization(), primary, upgradeConfirm(check, "version-next")));
|
|
assertEquals("version-current", f.store.binding(primary).getZiduanBanbenId());
|
assertEquals("version-current", f.store.binding("reference-1").getZiduanBanbenId());
|
assertTrue(f.hasAudit("ELN_TEMPLATE_BINDING_CONFIRM_FAILED"));
|
assertEquals(Arrays.asList(TransactionDefinition.PROPAGATION_REQUIRES_NEW,
|
TransactionDefinition.PROPAGATION_REQUIRES_NEW),
|
f.transactions.completedPropagations());
|
}
|
|
@Test
|
public void failureAuditSurvivesCallingOuterTransactionRollback() {
|
Fixture f = new Fixture();
|
f.selectNextVersion();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck check = f.service.checkUpgrade(
|
f.authorization(), primary, "version-next");
|
f.store.failCasOnId = primary;
|
|
try {
|
new TransactionTemplate(f.transactions).execute(status -> {
|
try {
|
f.service.confirmUpgrade(f.authorization(), primary,
|
upgradeConfirm(check, "version-next"));
|
} finally {
|
status.setRollbackOnly();
|
}
|
return null;
|
});
|
fail("预期抛出 DataException");
|
} catch (DataException expected) {
|
assertTrue(f.hasAudit("ELN_TEMPLATE_BINDING_CONFIRM_FAILED"));
|
}
|
assertEquals("version-current", f.store.binding(primary).getZiduanBanbenId());
|
assertEquals(Arrays.asList(TransactionDefinition.PROPAGATION_REQUIRED,
|
TransactionDefinition.PROPAGATION_REQUIRES_NEW,
|
TransactionDefinition.PROPAGATION_REQUIRES_NEW),
|
f.transactions.completedPropagations());
|
}
|
|
@Test
|
public void removeMissingRequiresServerDocumentEvidenceAndIgnoresClientBoolean() {
|
Fixture f = new Fixture();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
ElnTemplateForms.RemoveMissingConfirm form = new ElnTemplateForms.RemoveMissingConfirm();
|
form.setOperationCode("remove-missing-1");
|
form.setExpectedVersion(f.store.binding(primary).getFVersion());
|
form.setClientReportedMissing(true);
|
expectDataException(() -> f.service.confirmRemoveMissing(primary, form));
|
form.setReason("服务端体检确认控件缺失");
|
f.documentTags.add("eln.field.sample_code");
|
expectDataException(() -> f.service.confirmRemoveMissing(primary, form));
|
assertEquals("ACTIVE", f.store.binding(primary).getBangdingZhuangtai());
|
|
f.documentTags.clear();
|
f.service.confirmRemoveMissing(primary, form);
|
assertEquals("REMOVED", f.store.binding(primary).getBangdingZhuangtai());
|
assertEquals(Collections.singletonList(primary), f.terminatedBindingIds);
|
assertEquals("服务端体检确认控件缺失",
|
f.store.binding(primary).getZhuangtaiYuanyin());
|
assertEquals("template-1.docx", f.lastEvidenceFileId);
|
AuditEventDTO audit = f.audit("ELN_TEMPLATE_BINDING_REMOVE");
|
assertEquals("ELN_TEMPLATE_BINDING_REMOVE", audit.getActionCode());
|
assertEquals("服务端体检确认控件缺失", audit.getReason());
|
Map<String, Object> snapshot = f.auditSnapshot("ELN_TEMPLATE_BINDING_REMOVE");
|
assertEquals("服务端体检确认控件缺失", snapshot.get("reason"));
|
assertEquals("SUCCESS", snapshot.get("result"));
|
assertEquals(1, countProperty(Collections.singletonList(
|
auditBinding(f, "ELN_TEMPLATE_BINDING_REMOVE", "before")),
|
"bangdingZhuangtai", "ACTIVE"));
|
assertEquals(1, countProperty(Collections.singletonList(
|
auditBinding(f, "ELN_TEMPLATE_BINDING_REMOVE", "after")),
|
"bangdingZhuangtai", "REMOVED"));
|
}
|
|
@Test
|
public void removeMissingRetryWithSameNormalizedReasonIsIdempotent() {
|
Fixture f = new Fixture();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
ElnTemplateForms.RemoveMissingConfirm form = new ElnTemplateForms.RemoveMissingConfirm();
|
form.setOperationCode("remove-missing-retry");
|
form.setExpectedVersion(f.store.binding(primary).getFVersion());
|
form.setReason(" 服务端确认控件缺失 ");
|
|
f.service.confirmRemoveMissing(primary, form);
|
int persistedVersion = f.store.binding(primary).getFVersion();
|
int successAudits = f.countAudits("ELN_TEMPLATE_BINDING_REMOVE");
|
f.template.setBizStatus("PUBLISHED");
|
|
form.setReason("\t服务端确认控件缺失\n");
|
f.service.confirmRemoveMissing(primary, form);
|
|
assertEquals(persistedVersion, (int) f.store.binding(primary).getFVersion());
|
assertEquals("服务端确认控件缺失",
|
f.store.binding(primary).getZhuangtaiYuanyin());
|
assertEquals(successAudits, f.countAudits("ELN_TEMPLATE_BINDING_REMOVE"));
|
}
|
|
@Test
|
public void removeMissingRetryWithDifferentReasonUsesConflictWithoutChangingRemoval() {
|
Fixture f = new Fixture();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
ElnTemplateForms.RemoveMissingConfirm form = new ElnTemplateForms.RemoveMissingConfirm();
|
form.setOperationCode("remove-missing-retry");
|
form.setExpectedVersion(f.store.binding(primary).getFVersion());
|
form.setReason("原因 A");
|
f.service.confirmRemoveMissing(primary, form);
|
int persistedVersion = f.store.binding(primary).getFVersion();
|
int successAudits = f.countAudits("ELN_TEMPLATE_BINDING_REMOVE");
|
|
form.setReason(" 原因 B ");
|
expectOperationStatus(409,
|
() -> f.service.confirmRemoveMissing(primary, form));
|
|
assertEquals("REMOVED", f.store.binding(primary).getBangdingZhuangtai());
|
assertEquals(persistedVersion, (int) f.store.binding(primary).getFVersion());
|
assertEquals("原因 A", f.store.binding(primary).getZhuangtaiYuanyin());
|
assertEquals(successAudits, f.countAudits("ELN_TEMPLATE_BINDING_REMOVE"));
|
assertEquals(1, f.countAudits("ELN_TEMPLATE_BINDING_CONFIRM_FAILED"));
|
assertEquals("FAILED",
|
f.auditSnapshot("ELN_TEMPLATE_BINDING_CONFIRM_FAILED").get("result"));
|
}
|
|
@Test
|
public void rejectsRemovingPrimaryWithReferencesAndPromotesReferenceAfterDependenciesClear() {
|
Fixture f = new Fixture();
|
String primary = f.activePrimary("primary-1", "field-1", "version-current");
|
f.activeReference("reference-1", primary, "field-1", "version-current");
|
f.activeReference("reference-2", primary, "field-1", "version-current");
|
expectDataException(() -> f.service.checkRemove(f.authorization(), primary));
|
|
ElnTemplateVos.ChangeCheck promote = f.service.checkPromoteReference(
|
f.authorization(), "reference-1");
|
assertEquals(3, promote.getExpectedVersions().size());
|
f.service.confirmPromoteReference(f.authorization(), "reference-1", promoteConfirm(promote));
|
|
assertEquals("REMOVED", f.store.binding(primary).getBangdingZhuangtai());
|
ElnTemplateBindingEntity promoted = f.store.binding("reference-1");
|
assertEquals("PRIMARY", promoted.getKongjianJuese());
|
assertEquals("eln.field.sample_code", promoted.getKongjianBiaoqian());
|
assertEquals(null, promoted.getYinyongShiliId());
|
assertEquals("test-single-step", promoted.getStepId());
|
assertEquals("reference-1", f.store.binding("reference-2").getYinyongShiliId());
|
assertNotEquals(f.store.binding(primary).getCaozuoBianma(), promoted.getCaozuoBianma());
|
assertNotEquals(f.store.binding("reference-2").getCaozuoBianma(), promoted.getCaozuoBianma());
|
f.service.confirmPromoteReference(f.authorization(), "reference-1", promoteConfirm(promote));
|
}
|
|
@Test
|
public void promotesReferenceAfterRemovingPrimaryRegardlessOfBindingIdOrder() {
|
Fixture f = new Fixture();
|
String primary = f.activePrimary("z-primary", "field-1", "version-current");
|
f.activeReference("a-reference", primary, "field-1", "version-current");
|
f.activeReference("b-reference", primary, "field-1", "version-current");
|
ElnTemplateVos.ChangeCheck promote = f.service.checkPromoteReference(
|
f.authorization(), "a-reference");
|
|
f.service.confirmPromoteReference(
|
f.authorization(), "a-reference", promoteConfirm(promote));
|
|
assertEquals("REMOVED", f.store.binding(primary).getBangdingZhuangtai());
|
assertEquals("PRIMARY", f.store.binding("a-reference").getKongjianJuese());
|
assertEquals("a-reference", f.store.binding("b-reference").getYinyongShiliId());
|
}
|
|
@Test
|
public void confirmsVersionCopyOnlyForCompletePendingSetAndRunsParticipantsOnce() {
|
Fixture f = new Fixture();
|
f.template.setLaiyuanMubanId("source-template");
|
f.template.setShengbanCaozuoBianma("upgrade-copy-1");
|
f.pendingCopied("copy-1", "source-binding-1", "upgrade-copy-1:source-binding-1");
|
f.pendingCopiedReference("copy-2", "source-binding-2", "copy-1",
|
"upgrade-copy-1:source-binding-2");
|
ElnTemplateForms.VersionCopyConfirm form = new ElnTemplateForms.VersionCopyConfirm();
|
form.setUpgradeOperationCode("upgrade-copy-1");
|
form.setExpectedVersions(f.expectedVersions("copy-1", "copy-2"));
|
form.setControls(f.snapshots("copy-1", "copy-2"));
|
|
ElnTemplateForms.VersionCopyConfirm incomplete = copyVersionForm(form);
|
incomplete.getExpectedVersions().remove("copy-2");
|
expectDataException(() -> f.service.confirmVersionCopy(f.authorization(), incomplete));
|
|
f.service.confirmVersionCopy(f.authorization(), form);
|
f.service.confirmVersionCopy(f.authorization(), form);
|
assertEquals("ACTIVE", f.store.binding("copy-1").getBangdingZhuangtai());
|
assertEquals("ACTIVE", f.store.binding("copy-2").getBangdingZhuangtai());
|
assertEquals(1, f.participantCalls.size());
|
assertEquals("copy-1", f.participantCalls.get(0).get("source-binding-1"));
|
assertEquals("copy-2", f.participantCalls.get(0).get("source-binding-2"));
|
Map<String, Object> audit =
|
f.auditSnapshot("ELN_TEMPLATE_BINDING_VERSION_COPY_CONFIRM");
|
List<Map<String, Object>> beforeAudit = bindingSnapshots(audit, "before");
|
List<Map<String, Object>> afterAudit = bindingSnapshots(audit, "after");
|
assertEquals(2, beforeAudit.size());
|
assertEquals(2, afterAudit.size());
|
assertEquals(2, countProperty(beforeAudit, "bangdingZhuangtai", "PENDING"));
|
assertEquals(2, countProperty(afterAudit, "bangdingZhuangtai", "ACTIVE"));
|
assertEquals(2, countProperty(afterAudit, "fVersion", 4));
|
assertFalse(f.audit("ELN_TEMPLATE_BINDING_VERSION_COPY_CONFIRM").getDataSnapshot()
|
.toLowerCase().contains("ticket"));
|
|
ElnTemplateForms.VersionCopyConfirm wrongVersion = copyVersionForm(form);
|
wrongVersion.getExpectedVersions().put("copy-1", 99);
|
expectDataException(() -> f.service.confirmVersionCopy(f.authorization(), wrongVersion));
|
ElnTemplateForms.VersionCopyConfirm wrongStructure = copyVersionForm(form);
|
wrongStructure.getControls().get(0).setControlTag("eln.field.tampered");
|
expectDataException(() -> f.service.confirmVersionCopy(f.authorization(), wrongStructure));
|
}
|
|
@Test
|
public void draftVersionCopyInheritsPreReleasePrivateField() {
|
Fixture f = new Fixture();
|
f.template.setLaiyuanMubanId("source-template");
|
f.template.setShengbanCaozuoBianma("upgrade-copy-1");
|
f.fields.get("field-1").setZuoyongFanwei("TEMPLATE");
|
f.fields.get("field-1").setMubanBianhao(f.template.getMubanBianhao());
|
f.pendingCopied("copy-1", "source-binding-1",
|
"upgrade-copy-1:source-binding-1");
|
f.store.binding("copy-1").setZiduanBanbenId("version-pre");
|
ElnTemplateForms.VersionCopyConfirm form = new ElnTemplateForms.VersionCopyConfirm();
|
form.setUpgradeOperationCode("upgrade-copy-1");
|
form.setExpectedVersions(f.expectedVersions("copy-1"));
|
form.setControls(f.snapshots("copy-1"));
|
|
f.service.confirmVersionCopy(f.authorization(), form);
|
|
assertEquals("ACTIVE", f.store.binding("copy-1").getBangdingZhuangtai());
|
assertEquals(1, f.participantCalls.size());
|
}
|
|
@Test
|
public void copiedBindingsRejectSingleActivationCancellationAndShrunkConfirmationSet() {
|
Fixture f = new Fixture();
|
f.template.setLaiyuanMubanId("source-template");
|
f.template.setShengbanCaozuoBianma("upgrade-copy-1");
|
f.pendingCopied("copy-1", "source-binding-1", "upgrade-copy-1:source-binding-1");
|
f.pendingCopied("copy-2", "source-binding-2", "upgrade-copy-1:source-binding-2");
|
|
expectDataException(() -> f.service.activate(f.authorization(), "copy-1", "single-activate"));
|
expectDataException(() -> f.service.cancel(
|
f.authorization(), "copy-1", "single-cancel", "不允许单条取消"));
|
|
f.store.binding("copy-2").setBangdingZhuangtai("CANCELLED");
|
ElnTemplateForms.VersionCopyConfirm shrunk = new ElnTemplateForms.VersionCopyConfirm();
|
shrunk.setUpgradeOperationCode("upgrade-copy-1");
|
shrunk.setExpectedVersions(f.expectedVersions("copy-1"));
|
shrunk.setControls(f.snapshots("copy-1"));
|
expectDataException(() -> f.service.confirmVersionCopy(f.authorization(), shrunk));
|
assertEquals("PENDING", f.store.binding("copy-1").getBangdingZhuangtai());
|
assertTrue(f.participantCalls.isEmpty());
|
}
|
|
@Test
|
public void versionCopyMixedStateUsesConflictException() {
|
Fixture f = versionCopyFixture();
|
f.store.binding("copy-2").setBangdingZhuangtai("ACTIVE");
|
ElnTemplateForms.VersionCopyConfirm form = versionCopyForm(f);
|
|
expectOperationStatus(409,
|
() -> f.service.confirmVersionCopy(f.authorization(), form));
|
}
|
|
@Test
|
public void versionCopyPendingExpiryUsesConflictException() {
|
Fixture f = versionCopyFixture();
|
f.store.binding("copy-1").setDaoqiShijian(new Date());
|
ElnTemplateForms.VersionCopyConfirm form = versionCopyForm(f);
|
|
expectOperationStatus(409,
|
() -> f.service.confirmVersionCopy(f.authorization(), form));
|
}
|
|
@Test
|
public void versionCopyRetryVersionDriftUsesConflictException() {
|
Fixture f = versionCopyFixture();
|
ElnTemplateForms.VersionCopyConfirm form = versionCopyForm(f);
|
f.service.confirmVersionCopy(f.authorization(), form);
|
|
ElnTemplateForms.VersionCopyConfirm incomplete = copyVersionForm(form);
|
incomplete.getExpectedVersions().remove("copy-2");
|
expectOperationStatus(409,
|
() -> f.service.confirmVersionCopy(f.authorization(), incomplete));
|
|
ElnTemplateForms.VersionCopyConfirm stale = copyVersionForm(form);
|
stale.getExpectedVersions().put("copy-1", 99);
|
expectOperationStatus(409,
|
() -> f.service.confirmVersionCopy(f.authorization(), stale));
|
}
|
|
@Test
|
public void copiedBindingSingleTransitionUsesConflictException() {
|
Fixture f = versionCopyFixture();
|
|
expectOperationStatus(409,
|
() -> f.service.activate(f.authorization(), "copy-1", "single-activate"));
|
}
|
|
private static ElnTemplateForms.ReserveBinding reserve(String fieldId, String versionId,
|
String operationCode) {
|
ElnTemplateForms.ReserveBinding form = new ElnTemplateForms.ReserveBinding();
|
form.setFieldId(fieldId);
|
form.setFieldVersionId(versionId);
|
form.setOperationCode(operationCode);
|
return form;
|
}
|
|
private static ElnTemplateForms.ReserveBinding repeatReserve(String fieldId, String versionId,
|
String operationCode, String groupCode, int minRows, int maxRows) {
|
ElnTemplateForms.ReserveBinding form = reserve(fieldId, versionId, operationCode);
|
form.setRepeatRequest(true);
|
form.setRepeatGroupCode(groupCode);
|
form.setRepeatPrototypeRowId("prototype");
|
form.setMinRows(minRows);
|
form.setMaxRows(maxRows);
|
return form;
|
}
|
|
private static ElnTemplateForms.ChangeCheckboxGroup checkboxGroupChange(
|
String groupCode, String operationCode, int expectedVersion) {
|
ElnTemplateForms.ChangeCheckboxGroup form = new ElnTemplateForms.ChangeCheckboxGroup();
|
form.setCheckboxGroupCode(groupCode);
|
form.setOperationCode(operationCode);
|
form.setExpectedVersion(expectedVersion);
|
return form;
|
}
|
|
private static ElnTemplateVos.CheckboxGroup checkboxGroup(
|
String code, Integer minSelected, Integer maxSelected, Integer version) {
|
ElnTemplateVos.CheckboxGroup group = new ElnTemplateVos.CheckboxGroup();
|
group.setGroupCode(code);
|
group.setMinSelected(minSelected);
|
group.setMaxSelected(maxSelected);
|
group.setVersion(version);
|
return group;
|
}
|
|
private static ElnTemplateVos.Binding changeCheckboxGroup(
|
ElnTemplateBindingService service,
|
TemplateAnnotationAuthorization authorization,
|
String bindingId,
|
ElnTemplateForms.ChangeCheckboxGroup form) {
|
return service.changeCheckboxGroup(authorization, bindingId, form);
|
}
|
|
private static Fixture versionCopyFixture() {
|
Fixture f = new Fixture();
|
f.template.setLaiyuanMubanId("source-template");
|
f.template.setShengbanCaozuoBianma("upgrade-copy-1");
|
f.pendingCopied("copy-1", "source-binding-1", "upgrade-copy-1:source-binding-1");
|
f.pendingCopiedReference("copy-2", "source-binding-2", "copy-1",
|
"upgrade-copy-1:source-binding-2");
|
return f;
|
}
|
|
private static ElnTemplateForms.VersionCopyConfirm versionCopyForm(Fixture f) {
|
ElnTemplateForms.VersionCopyConfirm form = new ElnTemplateForms.VersionCopyConfirm();
|
form.setUpgradeOperationCode("upgrade-copy-1");
|
form.setExpectedVersions(f.expectedVersions("copy-1", "copy-2"));
|
form.setControls(f.snapshots("copy-1", "copy-2"));
|
return form;
|
}
|
|
private static ElnTemplateForms.PrivateField privateField(
|
String operation, String name, String type) {
|
ElnTemplateForms.PrivateField form = new ElnTemplateForms.PrivateField();
|
form.setOperationCode(operation);
|
form.setZiduanMingcheng(name);
|
form.setShujuLeixing(type);
|
return form;
|
}
|
|
private static ElnTemplateForms.PrivateField privateFieldCopy(
|
ElnTemplateForms.PrivateField source) {
|
ElnTemplateForms.PrivateField copy = privateField(source.getOperationCode(),
|
source.getZiduanMingcheng(), source.getShujuLeixing());
|
copy.setDanwei(source.getDanwei());
|
copy.setShifouBitian(source.getShifouBitian());
|
copy.setBeizhu(source.getBeizhu());
|
copy.setCheckboxGroupCode(source.getCheckboxGroupCode());
|
copy.setCheckboxGroupMinSelected(source.getCheckboxGroupMinSelected());
|
copy.setCheckboxGroupMaxSelected(source.getCheckboxGroupMaxSelected());
|
copy.setStepId(source.getStepId());
|
copy.setKongjianPeizhi(source.getKongjianPeizhi() == null ? null
|
: new LinkedHashMap<>(source.getKongjianPeizhi()));
|
return copy;
|
}
|
|
private static ElnTemplateForms.RepairConfirm repairConfirm(ElnTemplateVos.ChangeCheck check) {
|
ElnTemplateForms.RepairConfirm form = new ElnTemplateForms.RepairConfirm();
|
copyCheck(check, form);
|
return form;
|
}
|
|
private static ElnTemplateForms.UpgradeConfirm upgradeConfirm(ElnTemplateVos.ChangeCheck check,
|
String targetVersionId) {
|
ElnTemplateForms.UpgradeConfirm form = new ElnTemplateForms.UpgradeConfirm();
|
copyCheck(check, form);
|
form.setTargetFieldVersionId(targetVersionId);
|
return form;
|
}
|
|
private static ElnTemplateForms.RemoveConfirm removeConfirm(ElnTemplateVos.ChangeCheck check) {
|
ElnTemplateForms.RemoveConfirm form = new ElnTemplateForms.RemoveConfirm();
|
copyCheck(check, form);
|
return form;
|
}
|
|
private static ElnTemplateForms.PromoteConfirm promoteConfirm(ElnTemplateVos.ChangeCheck check) {
|
ElnTemplateForms.PromoteConfirm form = new ElnTemplateForms.PromoteConfirm();
|
copyCheck(check, form);
|
return form;
|
}
|
|
private static void copyCheck(ElnTemplateVos.ChangeCheck check,
|
ElnTemplateForms.ChangeConfirm form) {
|
form.setOperationCode(check.getOperationCode());
|
form.setExpectedVersions(new LinkedHashMap<>(check.getExpectedVersions()));
|
form.setControls(copySnapshots(check.getControls()));
|
List<String> observedControlTags = new ArrayList<>();
|
for (ElnTemplateVos.ControlSnapshot control : check.getControls()) {
|
observedControlTags.add(control.getControlTag());
|
}
|
form.setObservedControlTags(observedControlTags);
|
}
|
|
private static List<ElnTemplateVos.ControlSnapshot> copySnapshots(
|
Collection<ElnTemplateVos.ControlSnapshot> source) {
|
List<ElnTemplateVos.ControlSnapshot> result = new ArrayList<>();
|
for (ElnTemplateVos.ControlSnapshot item : source) {
|
ElnTemplateVos.ControlSnapshot copy = new ElnTemplateVos.ControlSnapshot();
|
copy.setBindingId(item.getBindingId());
|
copy.setControlTag(item.getControlTag());
|
copy.setControlAlias(item.getControlAlias());
|
copy.setControlType(item.getControlType());
|
copy.setControlRole(item.getControlRole());
|
copy.setFieldVersionId(item.getFieldVersionId());
|
copy.setPrimaryBindingId(item.getPrimaryBindingId());
|
copy.setRepeatGroupCode(item.getRepeatGroupCode());
|
copy.setRepeatPrototypeRowId(item.getRepeatPrototypeRowId());
|
copy.setStepId(item.getStepId());
|
copy.setControlConfig(item.getControlConfig() == null ? null
|
: new LinkedHashMap<>(item.getControlConfig()));
|
result.add(copy);
|
}
|
return result;
|
}
|
|
private static ElnTemplateForms.VersionCopyConfirm copyVersionForm(
|
ElnTemplateForms.VersionCopyConfirm source) {
|
ElnTemplateForms.VersionCopyConfirm copy = new ElnTemplateForms.VersionCopyConfirm();
|
copy.setUpgradeOperationCode(source.getUpgradeOperationCode());
|
copy.setExpectedVersions(new LinkedHashMap<>(source.getExpectedVersions()));
|
copy.setControls(copySnapshots(source.getControls()));
|
return copy;
|
}
|
|
private static ElnTemplateVos.Diagnostic error(String code) {
|
ElnTemplateVos.Diagnostic diagnostic = new ElnTemplateVos.Diagnostic();
|
diagnostic.setSeverity("ERROR");
|
diagnostic.setCode(code);
|
diagnostic.setMessage(code);
|
return diagnostic;
|
}
|
|
private static void expectDataException(Runnable action) {
|
try {
|
action.run();
|
fail("预期抛出 DataException");
|
} catch (DataException expected) {
|
assertFalse(expected.getMessage().trim().isEmpty());
|
}
|
}
|
|
private static void expectOperationStatus(int status, Runnable action) {
|
try {
|
action.run();
|
fail("预期抛出 ElnTemplateOperationException");
|
} catch (ElnTemplateOperationException expected) {
|
assertEquals(status, expected.getStatus());
|
}
|
}
|
|
@SuppressWarnings("unchecked")
|
private static Map<String, Object> controlConfig(Object target) {
|
try {
|
Method method = target.getClass().getMethod("getControlConfig");
|
return (Map<String, Object>) method.invoke(target);
|
} catch (ReflectiveOperationException e) {
|
throw new AssertionError("绑定与控件快照必须暴露 controlConfig", e);
|
}
|
}
|
|
private static void setControlConfig(Object target, Map<String, Object> config) {
|
try {
|
Method method = target.getClass().getMethod("setControlConfig", Map.class);
|
method.invoke(target, config);
|
} catch (ReflectiveOperationException e) {
|
throw new AssertionError("控件快照必须允许提交 controlConfig", e);
|
}
|
}
|
|
@SuppressWarnings("unchecked")
|
private static List<Map<String, Object>> bindingSnapshots(
|
Map<String, Object> audit, String key) {
|
Object value = audit.get(key);
|
if (!(value instanceof List)) {
|
throw new AssertionError("审计 " + key + " 必须是绑定实体快照列表");
|
}
|
return (List<Map<String, Object>>) value;
|
}
|
|
private static int countProperty(List<Map<String, Object>> snapshots,
|
String property, Object expected) {
|
int count = 0;
|
for (Map<String, Object> snapshot : snapshots) {
|
Object actual = null;
|
for (Map.Entry<String, Object> item : snapshot.entrySet()) {
|
if (property.equalsIgnoreCase(item.getKey())) {
|
actual = item.getValue();
|
break;
|
}
|
}
|
if (expected instanceof Number && actual instanceof Number
|
? ((Number) expected).longValue() == ((Number) actual).longValue()
|
: Objects.equals(expected, actual)) {
|
count++;
|
}
|
}
|
return count;
|
}
|
|
@SuppressWarnings("unchecked")
|
private static Map<String, Object> auditBinding(
|
Fixture fixture, String action, String key) {
|
Object value = fixture.auditSnapshot(action).get(key);
|
if (!(value instanceof Map)) {
|
throw new AssertionError("审计 " + key + " 必须是绑定实体快照");
|
}
|
return (Map<String, Object>) value;
|
}
|
|
private static int numberProperty(Map<String, Object> snapshot, String property) {
|
for (Map.Entry<String, Object> item : snapshot.entrySet()) {
|
if (property.equalsIgnoreCase(item.getKey()) && item.getValue() instanceof Number) {
|
return ((Number) item.getValue()).intValue();
|
}
|
}
|
throw new AssertionError("审计快照缺少数字属性: " + property);
|
}
|
|
private static ElnTemplateBindingServiceImpl testService(
|
ElnTemplateBindingServiceImpl.BindingStore store,
|
ElnFieldCatalogService fieldCatalogService,
|
ElnTemplateRepeatGroupService repeatGroupService,
|
List<ElnTemplateBindingDependencyValidator> validators,
|
List<ElnTemplateVersionCopyParticipant> participants,
|
List<ElnTemplateBindingLifecycleParticipant> lifecycleParticipants,
|
Supplier<String> bindingIdSupplier, Supplier<String> referenceIdSupplier,
|
Consumer<AuditEventDTO> auditRecorder, ElnTemplateDocumentEvidence documentEvidence,
|
Supplier<ElnTemplateBindingServiceImpl.UserContext> userContextSupplier,
|
ElnTemplateBindingConfirmationFacade facade) {
|
return testService(store, fieldCatalogService, repeatGroupService, validators,
|
participants, lifecycleParticipants, bindingIdSupplier, referenceIdSupplier,
|
auditRecorder, documentEvidence, userContextSupplier, facade, null);
|
}
|
|
private static ElnTemplateBindingServiceImpl testService(
|
ElnTemplateBindingServiceImpl.BindingStore store,
|
ElnFieldCatalogService fieldCatalogService,
|
ElnTemplateRepeatGroupService repeatGroupService,
|
List<ElnTemplateBindingDependencyValidator> validators,
|
List<ElnTemplateVersionCopyParticipant> participants,
|
List<ElnTemplateBindingLifecycleParticipant> lifecycleParticipants,
|
Supplier<String> bindingIdSupplier, Supplier<String> referenceIdSupplier,
|
Consumer<AuditEventDTO> auditRecorder, ElnTemplateDocumentEvidence documentEvidence,
|
Supplier<ElnTemplateBindingServiceImpl.UserContext> userContextSupplier,
|
ElnTemplateBindingConfirmationFacade facade,
|
ElnTemplateCheckboxGroupService checkboxGroupService) {
|
try {
|
for (Constructor<?> constructor
|
: ElnTemplateBindingServiceImpl.class.getDeclaredConstructors()) {
|
if (constructor.getParameterCount() == 13
|
&& constructor.getParameterTypes()[0]
|
== ElnTemplateBindingServiceImpl.BindingStore.class
|
&& constructor.getParameterTypes()[12]
|
== ElnTemplateCheckboxGroupService.class) {
|
constructor.setAccessible(true);
|
return (ElnTemplateBindingServiceImpl) constructor.newInstance(
|
store, fieldCatalogService, repeatGroupService, validators, participants,
|
lifecycleParticipants,
|
bindingIdSupplier, referenceIdSupplier, auditRecorder,
|
documentEvidence, userContextSupplier, facade, checkboxGroupService);
|
}
|
}
|
throw new AssertionError("未找到测试构造器");
|
} catch (ReflectiveOperationException e) {
|
throw new AssertionError("创建绑定服务测试实例失败", e);
|
}
|
}
|
|
private static final class Fixture {
|
private final MemoryStore store = new MemoryStore();
|
private final Map<String, ElnFieldDefEntity> fields = new LinkedHashMap<>();
|
private final Map<String, ElnFieldVersionEntity> versions = new LinkedHashMap<>();
|
private final List<AuditEventDTO> audits = new ArrayList<>();
|
private final Set<String> documentTags = new LinkedHashSet<>();
|
private final List<Map<String, String>> participantCalls = new ArrayList<>();
|
private final List<String> terminatedBindingIds = new ArrayList<>();
|
private final SnapshotTransactionManager transactions = new SnapshotTransactionManager(store);
|
private final AtomicInteger ids = new AtomicInteger();
|
private final AtomicInteger upgradeValidationCalls = new AtomicInteger();
|
private final AtomicInteger privateCreateCalls = new AtomicInteger();
|
private final Map<String, ElnTemplateRepeatGroupEntity> repeatGroups =
|
new LinkedHashMap<>();
|
private ElnTemplateVersionEntity template;
|
private ElnFieldDefEntity createdPrivate;
|
private ElnFieldForms.TemplateFieldCreate privateCommand;
|
private List<ElnTemplateVos.Diagnostic> validatorDiagnostics = Collections.emptyList();
|
private List<ElnTemplateVos.Diagnostic> checkboxGroupDiagnostics =
|
Collections.emptyList();
|
private String lastEvidenceFileId;
|
private int evidenceReads;
|
private final ElnTemplateBindingService service;
|
|
private Fixture() {
|
this(null);
|
}
|
|
private Fixture(ElnTemplateCheckboxGroupService checkboxGroupService) {
|
template = template("template-1", "DRAFT", "enabled");
|
store.templates.put(template.getId(), template);
|
addField("field-1", "sample_code", "version-current");
|
addVersion("version-current", "field-1", "PUBLISHED", "TEXT", "样品编号");
|
addVersion("version-old", "field-1", "PUBLISHED", "TEXT", "旧样品编号");
|
addVersion("version-draft", "field-1", "DRAFT", "TEXT", "草稿字段");
|
addVersion("version-pre", "field-1", "PRE_RELEASE", "TEXT", "预发布字段");
|
addVersion("version-next", "field-1", "PUBLISHED", "TEXTAREA", "样品编号新版");
|
addField("field-2", "remark", "version-2");
|
addVersion("version-2", "field-2", "PUBLISHED", "TEXT", "备注");
|
|
ElnTemplateBindingDependencyValidator validator = new ElnTemplateBindingDependencyValidator() {
|
@Override
|
public List<ElnTemplateVos.Diagnostic> validateUpgrade(String tenantId,
|
String templateId, Set<String> bindingIds, String targetFieldVersionId) {
|
upgradeValidationCalls.incrementAndGet();
|
return validatorDiagnostics;
|
}
|
|
@Override
|
public List<ElnTemplateVos.Diagnostic> validateRemoval(String tenantId,
|
String templateId, Set<String> bindingIds) {
|
return validatorDiagnostics;
|
}
|
|
@Override
|
public List<ElnTemplateVos.Diagnostic> validatePromotion(String tenantId,
|
String templateId, String primaryBindingId, String referenceBindingId) {
|
return validatorDiagnostics;
|
}
|
|
@Override
|
public List<ElnTemplateVos.Diagnostic> validateCheckboxGroup(String tenantId,
|
String templateId, String bindingId) {
|
return checkboxGroupDiagnostics;
|
}
|
};
|
ElnTemplateVersionCopyParticipant participant = (tenantId, sourceTemplateId,
|
targetTemplateId, bindingIdMap) -> participantCalls.add(
|
new LinkedHashMap<>(bindingIdMap));
|
ElnTemplateBindingLifecycleParticipant lifecycleParticipant =
|
(tenantId, bindingId, userId) -> terminatedBindingIds.add(bindingId);
|
ElnFieldCatalogService fieldCatalog = privateFieldCatalog();
|
ElnTemplateRepeatGroupService repeatGroupService =
|
new ElnTemplateRepeatGroupServiceImpl(
|
new ElnTemplateRepeatGroupServiceImpl.Store() {
|
@Override
|
public ElnTemplateRepeatGroupEntity find(String tenantId,
|
String templateId, String groupCode) {
|
return repeatGroups.get(templateId + ":" + groupCode);
|
}
|
|
@Override
|
public List<ElnTemplateRepeatGroupEntity> list(
|
String tenantId, String templateId) {
|
List<ElnTemplateRepeatGroupEntity> result = new ArrayList<>();
|
for (ElnTemplateRepeatGroupEntity group : repeatGroups.values()) {
|
if (tenantId.equals(group.getFTenantId())
|
&& templateId.equals(group.getMubanId())) {
|
result.add(group);
|
}
|
}
|
return result;
|
}
|
|
@Override
|
public void insert(ElnTemplateRepeatGroupEntity entity) {
|
repeatGroups.put(entity.getMubanId() + ":"
|
+ entity.getRepeatGroupCode(), entity);
|
}
|
}, () -> "repeat-group-" + ids.incrementAndGet());
|
ElnTemplateBindingConfirmationFacade facade =
|
new ElnTemplateBindingConfirmationFacade(transactions);
|
service = testService(store, fieldCatalog, repeatGroupService,
|
Collections.singletonList(validator), Collections.singletonList(participant),
|
Collections.singletonList(lifecycleParticipant),
|
() -> "binding-new-" + ids.incrementAndGet(),
|
() -> String.format("%032x", ids.incrementAndGet()), audits::add,
|
(tenantId, fileId) -> {
|
evidenceReads++;
|
lastEvidenceFileId = fileId;
|
List<jnpf.elnService.template.docx.DocxControlSnapshot> controls =
|
new ArrayList<>();
|
for (String tag : documentTags) {
|
controls.add(new jnpf.elnService.template.docx.DocxControlSnapshot(
|
tag, null, null, "TEXT", "PRIMARY",
|
"word/document.xml", "OK", Collections.emptyMap()));
|
}
|
return controls;
|
}, () -> new ElnTemplateBindingServiceImpl.UserContext("tenant-1", "user-1"),
|
facade, checkboxGroupService);
|
}
|
|
private TemplateAnnotationAuthorization authorization() {
|
return new TemplateAnnotationAuthorization(
|
"tenant-1", "user-1", "template-1", "template-1.docx");
|
}
|
|
private void selectNextVersion() {
|
fields.get("field-1").setDangqianZiduanBanbenId("version-next");
|
}
|
|
private void addDropdownVersion(String id) {
|
addVersion(id, "field-1", "PUBLISHED", "SELECT", "样品类型");
|
versions.get(id).setKongjianPeizhi("{\"placeholder\":\"请选择\","
|
+ "\"options\":[{\"value\":\"A\",\"display\":\"选项 A\"},"
|
+ "{\"value\":\"B\",\"display\":\"选项 B\"}]}");
|
fields.get("field-1").setDangqianZiduanBanbenId(id);
|
}
|
|
private void addCheckboxFields() {
|
addField("checkbox-1", "result_passed", "checkbox-version-1");
|
addVersion("checkbox-version-1", "checkbox-1", "PUBLISHED", "CHECKBOX", "合格");
|
addField("checkbox-2", "result_failed", "checkbox-version-2");
|
addVersion("checkbox-version-2", "checkbox-2", "PUBLISHED", "CHECKBOX", "不合格");
|
}
|
|
private String pendingPrimary(String id, String fieldId, String versionId, String operation) {
|
ElnTemplateBindingEntity binding = binding(id, fieldId, versionId, "PRIMARY", null, "PENDING");
|
binding.setStepId("test-single-step");
|
binding.setCaozuoBianma(operation);
|
store.bindings.put(id, binding);
|
return id;
|
}
|
|
private String activePrimary(String id, String fieldId, String versionId) {
|
ElnTemplateBindingEntity binding = binding(id, fieldId, versionId, "PRIMARY", null, "ACTIVE");
|
binding.setStepId("test-single-step");
|
binding.setCaozuoBianma("seed-" + id);
|
store.bindings.put(id, binding);
|
return id;
|
}
|
|
private void activeReference(String id, String primaryId, String fieldId, String versionId) {
|
ElnTemplateBindingEntity binding = binding(id, fieldId, versionId,
|
"REFERENCE", primaryId, "ACTIVE");
|
binding.setKongjianBiaoqian("eln.ref." + binding.getZiduanBianma() + "." + id);
|
binding.setCaozuoBianma("seed-" + id);
|
store.bindings.put(id, binding);
|
}
|
|
private void pendingReference(String id, String primaryId,
|
String fieldId, String versionId) {
|
ElnTemplateBindingEntity binding = binding(id, fieldId, versionId,
|
"REFERENCE", primaryId, "PENDING");
|
binding.setKongjianBiaoqian("eln.ref." + binding.getZiduanBianma() + "." + id);
|
binding.setCaozuoBianma("seed-" + id);
|
store.bindings.put(id, binding);
|
}
|
|
private void pendingCopied(String id, String sourceId, String operation) {
|
ElnTemplateBindingEntity binding = binding(id, "field-1", "version-old",
|
"PRIMARY", null, "PENDING");
|
binding.setLaiyuanBangdingId(sourceId);
|
binding.setCaozuoBianma(operation);
|
binding.setDaoqiShijian(null);
|
binding.setKongjianBiaoqian("eln.field." + sourceId.replace("source-binding-", "copy_"));
|
store.bindings.put(id, binding);
|
}
|
|
private void pendingCopiedReference(String id, String sourceId,
|
String primaryId, String operation) {
|
ElnTemplateBindingEntity binding = binding(id, "field-1", "version-old",
|
"REFERENCE", primaryId, "PENDING");
|
binding.setLaiyuanBangdingId(sourceId);
|
binding.setCaozuoBianma(operation);
|
binding.setDaoqiShijian(null);
|
binding.setKongjianBiaoqian("eln.ref.sample_code." + id);
|
store.bindings.put(id, binding);
|
}
|
|
private ElnTemplateBindingEntity binding(String id, String fieldId, String versionId,
|
String role, String primaryId, String status) {
|
ElnFieldDefEntity field = fields.get(fieldId);
|
ElnFieldVersionEntity version = versions.get(versionId);
|
ElnTemplateBindingEntity binding = new ElnTemplateBindingEntity();
|
binding.setId(id);
|
binding.setFTenantId("tenant-1");
|
binding.setMubanId("template-1");
|
binding.setMubanBianhao("TMP-001");
|
binding.setZiduanDingyiId(fieldId);
|
binding.setZiduanBianma(field.getZiduanBianma());
|
binding.setZiduanBanbenId(versionId);
|
binding.setKongjianBiaoqian("eln.field." + field.getZiduanBianma());
|
binding.setKongjianBieming(version.getZiduanMingcheng());
|
binding.setKongjianLeixing(controlType(version.getShujuLeixing()));
|
binding.setKongjianJuese(role);
|
binding.setYinyongShiliId(primaryId);
|
binding.setBangdingZhuangtai(status);
|
binding.setFVersion(3);
|
binding.setFDeleteMark(0);
|
return binding;
|
}
|
|
private void addField(String id, String code, String currentVersion) {
|
ElnFieldDefEntity field = new ElnFieldDefEntity();
|
field.setId(id);
|
field.setFTenantId("tenant-1");
|
field.setZiduanBianma(code);
|
field.setZuoyongFanwei("GLOBAL");
|
field.setDangqianZiduanBanbenId(currentVersion);
|
field.setBizEnabled("enabled");
|
fields.put(id, field);
|
store.fields.put(id, field);
|
}
|
|
private void addVersion(String id, String fieldId, String status,
|
String dataType, String name) {
|
ElnFieldVersionEntity version = new ElnFieldVersionEntity();
|
version.setId(id);
|
version.setFTenantId("tenant-1");
|
version.setZiduanDingyiId(fieldId);
|
version.setBizStatus(status);
|
version.setShujuLeixing(dataType);
|
version.setZiduanMingcheng(name);
|
version.setKongjianPeizhi("{\"placeholder\":\"" + id
|
+ "\",\"options\":[\"A\",\"B\"]}");
|
versions.put(id, version);
|
store.versions.put(id, version);
|
}
|
|
private ElnFieldCatalogService privateFieldCatalog() {
|
return new UnsupportedFieldCatalogService() {
|
@Override
|
public ElnFieldVos.Detail createTemplateFieldAndPreRelease(
|
ElnFieldForms.TemplateFieldCreate command) {
|
privateCreateCalls.incrementAndGet();
|
privateCommand = command;
|
createdPrivate = new ElnFieldDefEntity();
|
createdPrivate.setId("private-field");
|
createdPrivate.setFTenantId(command.getTenantId());
|
createdPrivate.setZiduanBianma("private_note");
|
createdPrivate.setZuoyongFanwei("TEMPLATE");
|
createdPrivate.setMubanBianhao(command.getTemplateCode());
|
createdPrivate.setBizEnabled("enabled");
|
createdPrivate.setBeizhu(command.getBeizhu());
|
createdPrivate.setDangqianZiduanBanbenId(null);
|
fields.put(createdPrivate.getId(), createdPrivate);
|
store.fields.put(createdPrivate.getId(), createdPrivate);
|
addVersion("private-version", createdPrivate.getId(),
|
"PRE_RELEASE", command.getShujuLeixing(), command.getZiduanMingcheng());
|
ElnFieldVersionEntity storedVersion = versions.get("private-version");
|
storedVersion.setDanwei(command.getDanwei());
|
storedVersion.setShifouBitian(command.getShifouBitian() == null
|
? 0 : command.getShifouBitian());
|
storedVersion.setPiciMoshi(command.getPiciMoshi());
|
storedVersion.setBangdingPeizhi(JsonUtil.getObjectToString(command.getBinding()));
|
storedVersion.setKongjianPeizhi(command.getKongjianPeizhi() == null
|
? null : JsonUtil.getObjectToString(command.getKongjianPeizhi()));
|
ElnFieldVos.Version voVersion = new ElnFieldVos.Version();
|
voVersion.setId("private-version");
|
voVersion.setBizStatus("PRE_RELEASE");
|
ElnFieldVos.Detail detail = new ElnFieldVos.Detail();
|
detail.setId(createdPrivate.getId());
|
detail.setZiduanBianma(createdPrivate.getZiduanBianma());
|
detail.setZuoyongFanwei("TEMPLATE");
|
detail.setMubanBianhao(command.getTemplateCode());
|
detail.setBizEnabled("enabled");
|
detail.setVersions(Collections.singletonList(voVersion));
|
return detail;
|
}
|
};
|
}
|
|
private Map<String, Integer> expectedVersions(String... ids) {
|
Map<String, Integer> result = new LinkedHashMap<>();
|
for (String id : ids) {
|
result.put(id, store.binding(id).getFVersion());
|
}
|
return result;
|
}
|
|
private List<ElnTemplateVos.ControlSnapshot> snapshots(String... ids) {
|
List<ElnTemplateVos.ControlSnapshot> result = new ArrayList<>();
|
for (String id : ids) {
|
ElnTemplateVos.ControlSnapshot snapshot =
|
ElnTemplateBindingServiceImpl.snapshot(store.binding(id));
|
snapshot.setControlConfig(JsonUtil.getJsonToBean(
|
versions.get(store.binding(id).getZiduanBanbenId()).getKongjianPeizhi(),
|
Map.class));
|
result.add(snapshot);
|
}
|
return result;
|
}
|
|
private boolean hasAudit(String actionCode) {
|
for (AuditEventDTO audit : audits) {
|
if (actionCode.equals(audit.getActionCode())) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
private int countAudits(String actionCode) {
|
int count = 0;
|
for (AuditEventDTO audit : audits) {
|
if (actionCode.equals(audit.getActionCode())) {
|
count++;
|
}
|
}
|
return count;
|
}
|
|
private AuditEventDTO audit(String actionCode) {
|
for (AuditEventDTO audit : audits) {
|
if (actionCode.equals(audit.getActionCode())) {
|
return audit;
|
}
|
}
|
throw new AssertionError("未找到审计事件: " + actionCode);
|
}
|
|
@SuppressWarnings("unchecked")
|
private Map<String, Object> auditSnapshot(String actionCode) {
|
return JsonUtil.getJsonToBean(audit(actionCode).getDataSnapshot(), Map.class);
|
}
|
}
|
|
private static final class StubCheckboxGroupService
|
implements ElnTemplateCheckboxGroupService {
|
private final List<ElnTemplateVos.CheckboxGroup> groups;
|
|
private StubCheckboxGroupService(List<ElnTemplateVos.CheckboxGroup> groups) {
|
this.groups = groups;
|
}
|
|
@Override
|
public List<ElnTemplateVos.CheckboxGroup> list(String tenantId, String templateId) {
|
return groups;
|
}
|
|
@Override
|
public ElnTemplateVos.CheckboxGroup ensureConfigured(String tenantId, String templateId,
|
String groupCode, Integer minSelected, Integer maxSelected, String operatorId) {
|
for (ElnTemplateVos.CheckboxGroup group : groups) {
|
if (!groupCode.equals(group.getGroupCode())) continue;
|
if (!Objects.equals(minSelected, group.getMinSelected())
|
|| !Objects.equals(maxSelected, group.getMaxSelected())) {
|
throw new DataException("选择组配置与已有配置不一致");
|
}
|
return group;
|
}
|
ElnTemplateVos.CheckboxGroup created = checkboxGroup(
|
groupCode, minSelected, maxSelected, 0);
|
groups.add(created);
|
return created;
|
}
|
|
@Override
|
public ElnTemplateVos.CheckboxGroup change(String tenantId, String templateId,
|
String groupCode, Integer minSelected, Integer maxSelected,
|
Integer expectedVersion, String operatorId) {
|
for (ElnTemplateVos.CheckboxGroup group : groups) {
|
if (!groupCode.equals(group.getGroupCode())) continue;
|
if (!Objects.equals(expectedVersion, group.getVersion())) {
|
throw new DataException("选择组配置已被其他用户更新,请刷新后重试");
|
}
|
group.setMinSelected(minSelected);
|
group.setMaxSelected(maxSelected);
|
group.setVersion(expectedVersion + 1);
|
return group;
|
}
|
throw new DataException("选择组不存在");
|
}
|
|
@Override
|
public void removeUnused(String tenantId, String templateId,
|
Set<String> usedGroupCodes, String operatorId) {
|
groups.removeIf(group -> !usedGroupCodes.contains(group.getGroupCode()));
|
}
|
|
@Override
|
public void copyForVersion(String tenantId, String sourceTemplateId,
|
String targetTemplateId) {
|
}
|
|
@Override
|
public void afterBindingsActivated(String tenantId, String sourceTemplateId,
|
String targetTemplateId, Map<String, String> bindingIdMap) {
|
}
|
}
|
|
private static ElnTemplateVersionEntity template(String id, String status, String enabled) {
|
ElnTemplateVersionEntity template = new ElnTemplateVersionEntity();
|
template.setId(id);
|
template.setFTenantId("tenant-1");
|
template.setMubanBianhao("TMP-001");
|
template.setMubanFile("[{\"fileId\":\"template-1.docx\"}]");
|
template.setBizStatus(status);
|
template.setBizEnabled(enabled);
|
template.setFDeleteMark(0);
|
return template;
|
}
|
|
private static String controlType(String dataType) {
|
if ("TEXTAREA".equals(dataType)) {
|
return "MULTILINE";
|
}
|
if ("CHECKBOX".equals(dataType) || "BOOLEAN".equals(dataType)) {
|
return "CHECKBOX";
|
}
|
return "TEXT";
|
}
|
|
private static final class MemoryStore implements ElnTemplateBindingServiceImpl.BindingStore {
|
private final Map<String, ElnTemplateVersionEntity> templates = new LinkedHashMap<>();
|
private final Map<String, ElnFieldDefEntity> fields = new LinkedHashMap<>();
|
private final Map<String, ElnFieldVersionEntity> versions = new LinkedHashMap<>();
|
private final Map<String, ElnTemplateBindingEntity> bindings = new LinkedHashMap<>();
|
private final List<String> lockedTemplateIds = new ArrayList<>();
|
private final List<String> lockedBindingIds = new ArrayList<>();
|
private String failCasOnId;
|
|
@Override
|
public ElnTemplateVersionEntity findTemplate(String tenantId, String templateId) {
|
ElnTemplateVersionEntity item = templates.get(templateId);
|
return item != null && tenantId.equals(item.getFTenantId()) ? item : null;
|
}
|
|
@Override
|
public ElnTemplateVersionEntity findTemplateForUpdate(String tenantId, String templateId) {
|
lockedTemplateIds.add(templateId);
|
return findTemplate(tenantId, templateId);
|
}
|
|
@Override
|
public ElnFieldDefEntity findField(String tenantId, String fieldId) {
|
ElnFieldDefEntity item = fields.get(fieldId);
|
return item != null && tenantId.equals(item.getFTenantId()) ? item : null;
|
}
|
|
@Override
|
public List<ElnFieldDefEntity> listFields(String tenantId) {
|
List<ElnFieldDefEntity> result = new ArrayList<>();
|
for (ElnFieldDefEntity item : fields.values()) {
|
if (tenantId.equals(item.getFTenantId())) {
|
result.add(item);
|
}
|
}
|
return result;
|
}
|
|
@Override
|
public List<ElnFieldVersionEntity> listFieldVersions(String tenantId, String fieldId) {
|
List<ElnFieldVersionEntity> result = new ArrayList<>();
|
for (ElnFieldVersionEntity item : versions.values()) {
|
if (tenantId.equals(item.getFTenantId())
|
&& fieldId.equals(item.getZiduanDingyiId())) {
|
result.add(item);
|
}
|
}
|
return result;
|
}
|
|
@Override
|
public ElnFieldVersionEntity findFieldVersion(String tenantId, String versionId) {
|
ElnFieldVersionEntity item = versions.get(versionId);
|
return item != null && tenantId.equals(item.getFTenantId()) ? item : null;
|
}
|
|
@Override
|
public ElnTemplateBindingEntity findBinding(String tenantId, String bindingId) {
|
ElnTemplateBindingEntity item = bindings.get(bindingId);
|
return item != null && tenantId.equals(item.getFTenantId()) ? copy(item) : null;
|
}
|
|
@Override
|
public ElnTemplateBindingEntity findBindingForUpdate(String tenantId, String bindingId) {
|
lockedBindingIds.add(bindingId);
|
return findBinding(tenantId, bindingId);
|
}
|
|
@Override
|
public ElnTemplateBindingEntity findByOperation(String tenantId, String templateId,
|
String operationCode) {
|
for (ElnTemplateBindingEntity item : bindings.values()) {
|
if (tenantId.equals(item.getFTenantId()) && templateId.equals(item.getMubanId())
|
&& operationCode.equals(item.getCaozuoBianma())) {
|
return copy(item);
|
}
|
}
|
return null;
|
}
|
|
@Override
|
public List<ElnTemplateBindingEntity> listBindings(String tenantId, String templateId) {
|
List<ElnTemplateBindingEntity> result = new ArrayList<>();
|
for (ElnTemplateBindingEntity item : bindings.values()) {
|
if (tenantId.equals(item.getFTenantId()) && templateId.equals(item.getMubanId())) {
|
result.add(copy(item));
|
}
|
}
|
return result;
|
}
|
|
@Override
|
public void insert(ElnTemplateBindingEntity binding) {
|
bindings.put(binding.getId(), copy(binding));
|
}
|
|
@Override
|
public boolean compareAndSet(ElnTemplateBindingEntity replacement,
|
int expectedVersion, String expectedStatus) {
|
ElnTemplateBindingEntity current = bindings.get(replacement.getId());
|
if (replacement.getId().equals(failCasOnId) || current == null
|
|| current.getFVersion() == null || current.getFVersion() != expectedVersion
|
|| !expectedStatus.equals(current.getBangdingZhuangtai())) {
|
return false;
|
}
|
for (ElnTemplateBindingEntity item : bindings.values()) {
|
if (!replacement.getId().equals(item.getId())
|
&& replacement.getFTenantId().equals(item.getFTenantId())
|
&& replacement.getMubanId().equals(item.getMubanId())
|
&& replacement.getCaozuoBianma().equals(item.getCaozuoBianma())) {
|
return false;
|
}
|
if (!replacement.getId().equals(item.getId())
|
&& replacement.getFTenantId().equals(item.getFTenantId())
|
&& replacement.getMubanId().equals(item.getMubanId())
|
&& live(replacement) && live(item)
|
&& replacement.getKongjianBiaoqian().equals(item.getKongjianBiaoqian())) {
|
return false;
|
}
|
if (!replacement.getId().equals(item.getId())
|
&& replacement.getFTenantId().equals(item.getFTenantId())
|
&& replacement.getMubanId().equals(item.getMubanId())
|
&& live(replacement) && live(item)
|
&& "PRIMARY".equals(replacement.getKongjianJuese())
|
&& "PRIMARY".equals(item.getKongjianJuese())
|
&& replacement.getZiduanBianma().equals(item.getZiduanBianma())) {
|
return false;
|
}
|
}
|
ElnTemplateBindingEntity next = copy(replacement);
|
next.setFVersion(expectedVersion + 1);
|
bindings.put(next.getId(), next);
|
return true;
|
}
|
|
private static boolean live(ElnTemplateBindingEntity binding) {
|
return "PENDING".equals(binding.getBangdingZhuangtai())
|
|| "ACTIVE".equals(binding.getBangdingZhuangtai());
|
}
|
|
private ElnTemplateBindingEntity binding(String id) {
|
return bindings.get(id);
|
}
|
|
private Map<String, ElnTemplateBindingEntity> snapshot() {
|
Map<String, ElnTemplateBindingEntity> snapshot = new LinkedHashMap<>();
|
for (Map.Entry<String, ElnTemplateBindingEntity> item : bindings.entrySet()) {
|
snapshot.put(item.getKey(), copy(item.getValue()));
|
}
|
return snapshot;
|
}
|
|
private void restore(Map<String, ElnTemplateBindingEntity> snapshot) {
|
bindings.clear();
|
for (Map.Entry<String, ElnTemplateBindingEntity> item : snapshot.entrySet()) {
|
bindings.put(item.getKey(), copy(item.getValue()));
|
}
|
}
|
|
private static ElnTemplateBindingEntity copy(ElnTemplateBindingEntity source) {
|
ElnTemplateBindingEntity copy = new ElnTemplateBindingEntity();
|
copy.setId(source.getId());
|
copy.setMubanId(source.getMubanId());
|
copy.setMubanBianhao(source.getMubanBianhao());
|
copy.setZiduanDingyiId(source.getZiduanDingyiId());
|
copy.setZiduanBianma(source.getZiduanBianma());
|
copy.setZiduanBanbenId(source.getZiduanBanbenId());
|
copy.setKongjianBiaoqian(source.getKongjianBiaoqian());
|
copy.setKongjianBieming(source.getKongjianBieming());
|
copy.setKongjianLeixing(source.getKongjianLeixing());
|
copy.setKongjianJuese(source.getKongjianJuese());
|
copy.setYinyongShiliId(source.getYinyongShiliId());
|
copy.setRepeatGroupCode(source.getRepeatGroupCode());
|
copy.setRepeatPrototypeRowId(source.getRepeatPrototypeRowId());
|
copy.setCheckboxGroupCode(source.getCheckboxGroupCode());
|
copy.setStepId(source.getStepId());
|
copy.setBangdingZhuangtai(source.getBangdingZhuangtai());
|
copy.setCaozuoBianma(source.getCaozuoBianma());
|
copy.setLaiyuanBangdingId(source.getLaiyuanBangdingId());
|
copy.setDaoqiShijian(source.getDaoqiShijian());
|
copy.setZhuangtaiYuanyin(source.getZhuangtaiYuanyin());
|
copy.setFTenantId(source.getFTenantId());
|
copy.setFVersion(source.getFVersion());
|
copy.setFDeleteMark(source.getFDeleteMark());
|
return copy;
|
}
|
}
|
|
private static final class SnapshotTransactionManager implements PlatformTransactionManager {
|
private final MemoryStore store;
|
private final List<Integer> propagations = new ArrayList<>();
|
private final List<Map<String, ElnTemplateBindingEntity>> snapshots = new ArrayList<>();
|
private final List<Boolean> completed = new ArrayList<>();
|
|
private SnapshotTransactionManager(MemoryStore store) {
|
this.store = store;
|
}
|
|
@Override
|
public TransactionStatus getTransaction(TransactionDefinition definition) {
|
propagations.add(definition.getPropagationBehavior());
|
snapshots.add(store.snapshot());
|
return new SimpleTransactionStatus();
|
}
|
|
@Override
|
public void commit(TransactionStatus status) {
|
completed.add(Boolean.TRUE);
|
}
|
|
@Override
|
public void rollback(TransactionStatus status) {
|
store.restore(snapshots.get(completed.size()));
|
completed.add(Boolean.FALSE);
|
}
|
|
private List<Integer> completedPropagations() {
|
return new ArrayList<>(propagations);
|
}
|
}
|
|
private abstract static class UnsupportedFieldCatalogService implements ElnFieldCatalogService {
|
@Override public void ensureTestSubmissionFields() { }
|
@Override public ElnFieldVos.Detail create(ElnFieldForms.Create form) { throw unsupported(); }
|
@Override public ElnFieldVos.Detail detail(String id) { throw unsupported(); }
|
@Override public com.baomidou.mybatisplus.core.metadata.IPage<ElnFieldVos.ListItem> page(
|
ElnFieldForms.Query query) { throw unsupported(); }
|
@Override public ElnFieldVos.Version createVersion(String fieldId,
|
ElnFieldForms.VersionCreate form) { throw unsupported(); }
|
@Override public ElnFieldVos.Version updateDraft(String versionId,
|
ElnFieldForms.VersionUpdate form) { throw unsupported(); }
|
@Override public void preRelease(String versionId) { throw unsupported(); }
|
@Override public void publish(String versionId) { throw unsupported(); }
|
@Override public void cancel(String versionId, String reason) { throw unsupported(); }
|
@Override public void switchCurrent(String fieldId, String versionId, String reason) { throw unsupported(); }
|
@Override public void promoteToGlobal(String fieldId, String reason) { throw unsupported(); }
|
@Override public void enable(String fieldId, String reason) { throw unsupported(); }
|
@Override public void disable(String fieldId, String reason) { throw unsupported(); }
|
private UnsupportedOperationException unsupported() {
|
return new UnsupportedOperationException();
|
}
|
}
|
}
|