package jnpf.base.service;
|
|
import jnpf.base.UserInfo;
|
import jnpf.base.entity.VisualdevEntity;
|
import jnpf.base.model.flow.DataModel;
|
import jnpf.base.model.uploadedit.UploadEditDraft;
|
import jnpf.base.util.FlowFormDataUtil;
|
import jnpf.exception.DataException;
|
import jnpf.flowable.WorkFlowApi;
|
import jnpf.flowable.model.filepermission.FilePermissionPolicy.Action;
|
import jnpf.flowable.model.filepermission.FilePermissionView;
|
import jnpf.model.visualjson.FieLdsModel;
|
import jnpf.model.visualjson.TableModel;
|
import jnpf.model.visualjson.analysis.*;
|
import jnpf.util.JsonUtil;
|
import jnpf.util.TicketUtil;
|
import jnpf.util.UserProvider;
|
import org.junit.*;
|
import org.mockito.MockedStatic;
|
import org.springframework.beans.factory.ObjectProvider;
|
import java.util.*;
|
import static org.junit.Assert.*;
|
import static org.mockito.Mockito.*;
|
|
public class UploadFilePermissionServiceTest {
|
private UploadFilePermissionService service;
|
private DataModel model;
|
private Map<String, Object> baseline;
|
private FilePermissionView permissions;
|
private UploadFilePermissionService.SaveContext context;
|
private MockedStatic<UserProvider> users;
|
private FlowFormDataUtil forms;
|
private WorkFlowApi workflow;
|
private VisualdevService models;
|
|
@Before public void setup() {
|
jnpf.util.context.SpringContext.setApplicationContext(mock(org.springframework.context.ApplicationContext.class));
|
UserInfo user = new UserInfo(); user.setUserId("owner"); user.setTenantId("tenant");
|
users = mockStatic(UserProvider.class); users.when(UserProvider::getUser).thenReturn(user);
|
ObjectProvider<VisualdevService> modelProvider = mock(ObjectProvider.class);
|
ObjectProvider<FlowFormDataUtil> formProvider = mock(ObjectProvider.class);
|
models = mock(VisualdevService.class); forms = mock(FlowFormDataUtil.class); workflow = mock(WorkFlowApi.class);
|
when(modelProvider.getObject()).thenReturn(models); when(formProvider.getObject()).thenReturn(forms);
|
service = new UploadFilePermissionService(modelProvider, formProvider, new UploadFilePermissionQuery(workflow, mock(ObjectProvider.class)));
|
model = new DataModel(); model.setVisualId("model"); model.setMainId("record");
|
model.setDataNewMap(new HashMap<>()); model.setTableModelList(new ArrayList<>());
|
FormAllModel field = new FormAllModel(); FormColumnModel column = new FormColumnModel();
|
column.setFieLdsModel(field()); field.setFormColumnModel(column);
|
model.setFormAllModel(Collections.singletonList(field));
|
baseline = new HashMap<>(); baseline.put("id", "record"); baseline.put("files", files("A")); baseline.put("flowTaskId", "actual-task");
|
model.getDataNewMap().put("files", files("A"));
|
permissions = new FilePermissionView(); permissions.setTaskId("actual-task"); permissions.setFormId("model");
|
permissions.setStatus("ready"); permissions.setRevision("revision");
|
allow("files");
|
context = new UploadFilePermissionService.SaveContext(baseline, permissions);
|
}
|
@Test public void persistedMenuDraftWithoutTaskUsesNativeSaveButMissingTaskInRunningRecordFails() {
|
VisualdevEntity released = new VisualdevEntity(); when(models.getReleaseInfo("model")).thenReturn(released);
|
when(forms.getFilePermissionDataInfo(eq(released), eq("record"), any())).thenReturn(baseline);
|
when(workflow.filePermissions("actual-task", "model")).thenReturn(FilePermissionView.unavailable("actual-task", "model", "TASK_NOT_FOUND"));
|
Map<String,Object> state = new HashMap<>(); state.put("flowId", "flow"); state.put("flowState", 0); state.put("deleteMark", null);
|
baseline.put(FilePermissionNativeDraft.STATE_KEY, state);
|
assertNull(service.prepare(model));
|
state.put("flowState", 1);
|
model.getDataNewMap().put("flowState", 0); model.getDataNewMap().put("isNew", true);
|
denied("权限暂不可用", () -> service.prepare(model));
|
state.put("flowState", 0); state.put("deleteMark", 1);
|
denied("权限暂不可用", () -> service.prepare(model));
|
state.put("deleteMark", null);
|
when(workflow.filePermissions("actual-task", "model")).thenReturn(FilePermissionView.unavailable("actual-task", "model", "PERMISSION_UNAVAILABLE"));
|
denied("权限暂不可用", () -> service.prepare(model));
|
}
|
@After public void cleanup() { users.close(); }
|
private FieLdsModel field() {
|
return JsonUtil.getJsonToBean("{\"__vModel__\":\"files\",\"__config__\":{\"jnpfKey\":\"uploadFile\"}}", FieLdsModel.class);
|
}
|
private List<Map<String, Object>> files(String... ids) {
|
List<Map<String, Object>> result = new ArrayList<>();
|
for (String id : ids) { Map<String, Object> f = new HashMap<>(); f.put("fileId", id); result.add(f); }
|
return result;
|
}
|
private void allow(String field, Action... actions) {
|
FilePermissionView.FieldAccess access = new FilePermissionView.FieldAccess(); access.setStatus("ready");
|
Map<Action, Boolean> values = new EnumMap<>(Action.class); for (Action action : actions) values.put(action, true);
|
access.setActions(values); permissions.getFields().put(field, access);
|
}
|
private void validate(UploadEditDraft... drafts) { service.validate(context, model, Arrays.asList(drafts), f -> false); }
|
private void denied(String message, Runnable call) {
|
try { call.run(); fail("expected rejection: " + message); }
|
catch (DataException expected) { assertTrue(expected.getMessage(), expected.getMessage().contains(message)); }
|
}
|
private UploadEditDraft draft(String origin, String result) {
|
UploadEditDraft d = new UploadEditDraft(); d.setId("draft"); d.setOwnerId("owner"); d.setRecordId("record");
|
d.setModelId("model"); d.setField("files"); d.setBaselineJson(JsonUtil.getObjectToString(files("A")));
|
d.setCurrentJson(JsonUtil.getObjectToString(files(result)));
|
d.setEditOriginsJson(JsonUtil.getObjectToString(Collections.singletonMap(result, origin))); return d;
|
}
|
private void children() {
|
FormAllModel item = new FormAllModel(); FormColumnTableModel child = new FormColumnTableModel();
|
child.setTableModel("rows"); child.setTableName("child_table");
|
FormColumnModel column = new FormColumnModel(); column.setFieLdsModel(field());
|
child.setChildList(Collections.singletonList(column)); item.setChildList(child);
|
model.setFormAllModel(Collections.singletonList(item));
|
TableModel table = new TableModel(); table.setTable("child_table"); model.setTableModelList(Collections.singletonList(table));
|
when(forms.getKey(eq(table), any())).thenReturn("custom_id");
|
Map<String, Object> row = new HashMap<>(); row.put("custom_id", "row-1"); row.put("files", files("A"));
|
baseline.put("rows", Collections.singletonList(row));
|
model.getDataNewMap().put("rows", new ArrayList<>(Collections.singletonList(new HashMap<>(row))));
|
allow("rows-files");
|
}
|
@Test public void usesRealRecordTaskAndPreservesLegacy() {
|
VisualdevEntity released = new VisualdevEntity(); when(models.getReleaseInfo("model")).thenReturn(released);
|
when(forms.getFilePermissionDataInfo(eq(released), eq("record"), any())).thenReturn(baseline);
|
model.getDataNewMap().put("flowTaskId", "forged-task");
|
when(workflow.filePermissions("actual-task", "model")).thenReturn(FilePermissionView.legacy("actual-task", "model"));
|
assertNull(service.prepare(model)); verify(workflow).filePermissions("actual-task", "model");
|
}
|
@Test public void mismatchedPermissionResponseFailsClosed() {
|
VisualdevEntity released = new VisualdevEntity(); when(models.getReleaseInfo("model")).thenReturn(released);
|
when(forms.getFilePermissionDataInfo(eq(released), anyString(), any())).thenReturn(baseline);
|
when(workflow.filePermissions("actual-task", "model")).thenReturn(FilePermissionView.legacy("other-task", "model"));
|
denied("权限暂不可用", () -> service.prepare(model));
|
}
|
@Test public void savedEmptyBaselineStillRequiresAddPermission() {
|
VisualdevEntity released = new VisualdevEntity(); when(models.getReleaseInfo("model")).thenReturn(released);
|
baseline.put("files", Collections.emptyList());
|
when(forms.getFilePermissionDataInfo(eq(released), eq("record"), any())).thenReturn(baseline);
|
permissions.setEnabled(true);
|
when(workflow.filePermissions("actual-task", "model")).thenReturn(permissions);
|
model.getDataNewMap().put("flowTaskId", "");
|
model.getDataNewMap().put("id", "");
|
model.getDataNewMap().put("isNew", true);
|
context = service.prepare(model);
|
assertNotNull(context);
|
Map<String, Object> uploaded = files("A").get(0); uploaded.put("uploadReceipt", "receipt");
|
model.getDataNewMap().put("files", Collections.singletonList(uploaded));
|
try (MockedStatic<TicketUtil> tickets = mockStatic(TicketUtil.class)) {
|
tickets.when(() -> TicketUtil.parseTicket("receipt")).thenReturn(
|
"{\"purpose\":\"upload-edit-source\",\"tenantId\":\"tenant\",\"ownerId\":\"owner\",\"fileId\":\"A\"}");
|
denied("ADD", () -> validate());
|
}
|
}
|
@Test public void missingSavedBaselineCannotFallBackToInitialCreate() {
|
VisualdevEntity released = new VisualdevEntity(); when(models.getReleaseInfo("model")).thenReturn(released);
|
when(forms.getFilePermissionDataInfo(eq(released), eq("record"), any())).thenReturn(Collections.emptyMap());
|
denied("记录不存在", () -> service.prepare(model));
|
verifyNoInteractions(workflow);
|
}
|
@Test public void unchangedFilesNeedNoMutationPermissionAndCannotForgeUrl() {
|
Map<String, Object> forged = files("A").get(0); forged.put("url", "other-file");
|
model.getDataNewMap().put("files", Collections.singletonList(forged)); validate();
|
assertEquals(files("A"), model.getDataNewMap().get("files"));
|
}
|
@Test public void omittedMainColumnIsRemovalNotPatch() {
|
model.getDataNewMap().remove("files"); denied("REMOVE", () -> validate());
|
}
|
@Test public void wrongCaseMainColumnCannotHideNativeNullWrite() {
|
model.getDataNewMap().remove("files"); model.getDataNewMap().put("FILES", files("A")); denied("REMOVE", () -> validate());
|
}
|
@Test public void ignoredNativeColumnDoesNotRequireActions() {
|
model.getDataNewMap().remove("files"); service.validate(context, model, Collections.emptyList(), f -> true);
|
}
|
@Test public void wholeChildRowDeletionRequiresRemoveEvenWhenColumnIsReadOnly() {
|
children(); model.getDataNewMap().put("rows", Collections.emptyList());
|
denied("REMOVE", () -> service.validate(context, model, Collections.emptyList(), f -> true));
|
}
|
@Test public void omittedChildTableAlsoRequiresRemove() {
|
children(); model.getDataNewMap().remove("rows"); denied("REMOVE", () -> validate());
|
}
|
@Test public void onlyUpdatePreservesMissingRows() {
|
children(); model.setOnlyUpdate(true); model.getDataNewMap().remove("rows"); validate();
|
}
|
@Test public void omittedChildFileColumnRequiresRemove() {
|
children(); Map<String, Object> row = new HashMap<>(); row.put("CUSTOM_ID", "row-1");
|
model.getDataNewMap().put("rows", Collections.singletonList(row)); denied("REMOVE", () -> validate());
|
}
|
@Test public void disabledChildTableIsNotMutatedByNativeSave() {
|
children(); model.getFormAllModel().get(0).getChildList().setDisabled(true);
|
model.getDataNewMap().remove("rows"); validate();
|
}
|
@Test public void trustedEditIsEditInsteadOfReplacement() {
|
allow("files", Action.EDIT, Action.VIEW); model.getDataNewMap().put("files", files("B")); validate(draft("A", "B"));
|
}
|
@Test public void revokedEditCannotBeAdoptedUsingReplacePermission() {
|
allow("files", Action.REPLACE); model.getDataNewMap().put("files", files("B"));
|
denied("EDIT", () -> validate(draft("A", "B")));
|
}
|
@Test public void uploadedThenEditedFileStillRequiresReplacement() {
|
allow("files", Action.EDIT, Action.VIEW); model.getDataNewMap().put("files", files("D"));
|
denied("REPLACE", () -> validate(draft("C", "D")));
|
allow("files", Action.EDIT, Action.VIEW, Action.REPLACE); validate(draft("C", "D"));
|
}
|
@Test public void otherRowOrOwnerDraftIsNotProof() {
|
allow("files", Action.EDIT, Action.REPLACE); model.getDataNewMap().put("files", files("B"));
|
UploadEditDraft other = draft("A", "B"); other.setOwnerId("other");
|
denied("上传来源", () -> validate(other));
|
}
|
@Test public void nativeOverrideCannotUseOldDraftAsUploadProof() {
|
allow("files", Action.REPLACE); model.getDataNewMap().put("files", files("B"));
|
context.nativeDraftIds.add("draft"); denied("上传来源", () -> validate(draft("A", "B")));
|
}
|
@Test public void verifiedUploadMayReplaceAndCannotForgeSourceMetadata() {
|
allow("files", Action.REPLACE);
|
Map<String, Object> uploaded = files("C").get(0); uploaded.put("uploadReceipt", "receipt"); uploaded.put("url", "forged");
|
model.getDataNewMap().put("files", Collections.singletonList(uploaded));
|
Map<String, Object> proof = new HashMap<>(); proof.put("purpose", "upload-edit-source"); proof.put("tenantId", "tenant");
|
proof.put("ownerId", "owner"); proof.put("fileId", "C"); proof.put("url", "actual");
|
try (MockedStatic<TicketUtil> tickets = mockStatic(TicketUtil.class)) {
|
tickets.when(() -> TicketUtil.parseTicket("receipt")).thenReturn(JsonUtil.getObjectToString(proof));
|
validate();
|
}
|
List<?> accepted = (List<?>) model.getDataNewMap().get("files"); assertEquals("actual", ((Map<?, ?>) accepted.get(0)).get("url"));
|
}
|
@Test public void anotherTenantsUploadReceiptCannotBeUsed() {
|
allow("files", Action.REPLACE);
|
Map<String, Object> uploaded = files("C").get(0); uploaded.put("uploadReceipt", "receipt");
|
model.getDataNewMap().put("files", Collections.singletonList(uploaded));
|
try (MockedStatic<TicketUtil> tickets = mockStatic(TicketUtil.class)) {
|
tickets.when(() -> TicketUtil.parseTicket("receipt")).thenReturn("{\"purpose\":\"upload-edit-source\",\"tenantId\":\"other\",\"ownerId\":\"owner\",\"fileId\":\"C\"}");
|
denied("上传来源", () -> validate());
|
}
|
}
|
private void currentDraftContext() {
|
VisualdevEntity released = new VisualdevEntity(); when(models.getReleaseInfo("model")).thenReturn(released);
|
when(forms.getFilePermissionDataInfo(eq(released), eq("record"), any())).thenReturn(baseline);
|
when(workflow.filePermissions("actual-task", "model")).thenReturn(permissions);
|
}
|
private Map<String, Object> nativeDraftContext() {
|
currentDraftContext();
|
when(workflow.filePermissions("actual-task", "model"))
|
.thenReturn(FilePermissionView.unavailable("actual-task", "model", "TASK_NOT_FOUND"));
|
Map<String, Object> state = new HashMap<>();
|
state.put("flowId", "flow"); state.put("flowState", 0); state.put("deleteMark", null);
|
baseline.put(FilePermissionNativeDraft.STATE_KEY, state);
|
return state;
|
}
|
@Test public void nativeMenuDraftCanDiscardEditedMainAndChildAttachments() {
|
nativeDraftContext();
|
UploadEditDraft main = draft("A", "B");
|
assertTrue(service.canDiscardDraft(main));
|
service.validateDraftDiscard(main);
|
UploadEditDraft child = draft("A", "C"); child.setParentField("rows"); child.setRowId("row-1");
|
assertTrue(service.canDiscardDraft(child));
|
service.validateDraftDiscard(child);
|
assertFalse(service.canDiscardDraft(draft("A", "A")));
|
service.validateDraftChange(main, files("D"));
|
}
|
@Test public void missingTaskDoesNotAllowDiscardOfRunningDeletedOrUnverifiedRecord() {
|
Map<String, Object> state = nativeDraftContext();
|
UploadEditDraft edited = draft("A", "B");
|
state.put("flowState", 1);
|
assertFalse(service.canDiscardDraft(edited));
|
denied("权限暂不可用", () -> service.validateDraftDiscard(edited));
|
denied("权限暂不可用", () -> service.validateDraftChange(edited, files("D")));
|
state.put("flowState", 0); state.put("deleteMark", 1);
|
assertFalse(service.canDiscardDraft(edited));
|
denied("权限暂不可用", () -> service.validateDraftDiscard(edited));
|
state.put("deleteMark", null); baseline.put("id", "other-record");
|
assertFalse(service.canDiscardDraft(edited));
|
baseline.put("id", "record"); baseline.remove(FilePermissionNativeDraft.STATE_KEY);
|
assertFalse(service.canDiscardDraft(edited));
|
}
|
@Test public void nativeDraftDiscardDoesNotDowngradePermissionErrorsOrMismatchedResponses() {
|
nativeDraftContext();
|
UploadEditDraft edited = draft("A", "B");
|
when(workflow.filePermissions("actual-task", "model"))
|
.thenReturn(FilePermissionView.unavailable("actual-task", "model", "PERMISSION_UNAVAILABLE"));
|
assertFalse(service.canDiscardDraft(edited));
|
denied("权限暂不可用", () -> service.validateDraftDiscard(edited));
|
when(workflow.filePermissions("actual-task", "model"))
|
.thenReturn(FilePermissionView.unavailable("other-task", "model", "TASK_NOT_FOUND"));
|
assertFalse(service.canDiscardDraft(edited));
|
when(workflow.filePermissions("actual-task", "model")).thenThrow(new IllegalStateException("offline"));
|
assertFalse(service.canDiscardDraft(edited));
|
}
|
@Test public void nativeDraftDiscardUsesInstanceRulesOnceTaskExists() {
|
nativeDraftContext();
|
UploadEditDraft edited = draft("A", "B");
|
when(workflow.filePermissions("actual-task", "model")).thenReturn(permissions);
|
assertFalse(service.canDiscardDraft(edited));
|
denied("EDIT", () -> service.validateDraftDiscard(edited));
|
allow("files", Action.EDIT);
|
assertTrue(service.canDiscardDraft(edited));
|
service.validateDraftDiscard(edited);
|
}
|
@Test public void discardDecisionUsesActualChangeAndCurrentPermissions() {
|
currentDraftContext(); allow("files", Action.EDIT);
|
UploadEditDraft edited = draft("A", "B");
|
assertTrue(service.canDiscardDraft(edited));
|
allow("files", Action.REPLACE);
|
assertFalse(service.canDiscardDraft(edited));
|
UploadEditDraft replacement = draft("C", "C");
|
assertTrue(service.canDiscardDraft(replacement));
|
replacement.setCurrentJson("[]");
|
assertFalse(service.canDiscardDraft(replacement));
|
allow("files", Action.ADD);
|
assertTrue(service.canDiscardDraft(replacement));
|
}
|
@Test public void discardDecisionIsFalseForUnchangedOrUnavailableDraft() {
|
UploadEditDraft unchanged = draft("A", "A");
|
assertFalse(service.canDiscardDraft(unchanged));
|
verifyNoInteractions(workflow);
|
currentDraftContext();
|
when(workflow.filePermissions("actual-task", "model")).thenThrow(new IllegalStateException("offline"));
|
assertFalse(service.canDiscardDraft(draft("A", "B")));
|
}
|
@Test public void discardDecisionKeepsLegacyButRejectsMissingEditProvenance() {
|
currentDraftContext();
|
when(workflow.filePermissions("actual-task", "model")).thenReturn(FilePermissionView.legacy("actual-task", "model"));
|
assertTrue(service.canDiscardDraft(draft("A", "B")));
|
when(workflow.filePermissions("actual-task", "model")).thenReturn(permissions);
|
allow("files", Action.REPLACE);
|
UploadEditDraft old = draft("A", "onlyoffice-edit-temp,tenant,lease,result.docx");
|
old.setEditOriginsJson(null);
|
assertFalse(service.canDiscardDraft(old));
|
}
|
@Test public void discardDecisionUsesChildFieldPermissionsIndependently() {
|
currentDraftContext(); allow("files", Action.EDIT); allow("rows-files");
|
UploadEditDraft child = draft("A", "B"); child.setParentField("rows"); child.setRowId("row-1");
|
assertFalse(service.canDiscardDraft(child));
|
allow("rows-files", Action.EDIT);
|
assertTrue(service.canDiscardDraft(child));
|
}
|
@Test public void directDraftRemovalCannotBypassRemovePermission() {
|
currentDraftContext(); allow("files", Action.EDIT);
|
denied("REMOVE", () -> service.validateDraftChange(draft("A", "B"), Collections.emptyList()));
|
}
|
@Test public void restoringEditedContentRequiresEditWithoutRequiringReplace() {
|
currentDraftContext(); allow("files", Action.EDIT);
|
service.validateDraftDiscard(draft("A", "B"));
|
allow("files", Action.REPLACE);
|
denied("EDIT", () -> service.validateDraftDiscard(draft("A", "B")));
|
}
|
@Test public void discardOfUploadReplacementRequiresReplace() {
|
currentDraftContext(); allow("files", Action.EDIT);
|
UploadEditDraft d = draft("C", "D");
|
denied("REPLACE", () -> service.validateDraftDiscard(d));
|
allow("files", Action.REPLACE); service.validateDraftDiscard(d);
|
}
|
@Test public void discardOfRemovedAttachmentRequiresAdd() {
|
currentDraftContext(); allow("files", Action.EDIT);
|
UploadEditDraft d = draft("A", "B"); d.setCurrentJson("[]"); d.setEditOriginsJson(null);
|
denied("ADD", () -> service.validateDraftDiscard(d));
|
}
|
@Test public void missingProvenanceForOldEditedDraftCannotMasqueradeAsReplacement() {
|
allow("files", Action.REPLACE);
|
String id = "onlyoffice-edit-temp,tenant,lease,result.docx";
|
UploadEditDraft d = draft("A", id); d.setEditOriginsJson(null);
|
model.getDataNewMap().put("files", files(id));
|
denied("缺少编辑来源", () -> validate(d));
|
}
|
@Test public void anotherRecordDraftCannotBeUsedForEditAdoption() {
|
allow("files", Action.EDIT, Action.REPLACE);
|
UploadEditDraft d = draft("A", "B"); d.setRecordId("other-record");
|
model.getDataNewMap().put("files", files("B")); denied("上传来源", () -> validate(d));
|
}
|
|
@Test public void incompleteReadCannotBeTreatedAsNonWorkflowRecord() {
|
VisualdevEntity released = new VisualdevEntity(); when(models.getReleaseInfo("model")).thenReturn(released);
|
baseline.remove("flowTaskId");
|
when(forms.getFilePermissionDataInfo(eq(released), anyString(), any())).thenReturn(baseline);
|
denied("记录不存在", () -> service.prepare(model)); verifyNoInteractions(workflow);
|
}
|
@Test public void conflictingCaseChildKeysCannotHideRemoval() {
|
children();
|
Map<String, Object> row = new LinkedHashMap<>(); row.put("custom_id", "row-1");
|
row.put("files", files("A")); row.put("FILES", Collections.emptyList());
|
model.getDataNewMap().put("rows", Collections.singletonList(row));
|
denied("大小写冲突", () -> validate());
|
}
|
@Test public void permittedNullRetainsNativeNullRepresentation() {
|
allow("files", Action.REMOVE); model.getDataNewMap().put("files", null); validate();
|
assertNull(model.getDataNewMap().get("files"));
|
}
|
|
@Test public void revokedViewAlsoPreventsAdoptionOfEditedContent() {
|
allow("files", Action.EDIT); model.getDataNewMap().put("files", files("B"));
|
denied("VIEW", () -> validate(draft("A", "B")));
|
}
|
|
@Test public void templateSourceReadsNativeBaselineWithoutOnlineEditorAndRejectsRecordMismatch() {
|
VisualdevEntity released = new VisualdevEntity(); when(models.getReleaseInfo("model")).thenReturn(released);
|
when(forms.getFilePermissionDataInfo(eq(released), eq("record"), any())).thenReturn(baseline);
|
baseline.put("wenjian", "[{\"fileId\":\"A\",\"laiyuan_muban_id\":\"templateA\"}]");
|
assertEquals("templateA", service.dmsMainAttachmentBaseline(model).get(0).get("laiyuan_muban_id"));
|
verifyNoInteractions(workflow);
|
baseline.put("id", "other");
|
denied("记录不匹配", () -> service.dmsMainAttachmentBaseline(model));
|
}
|
|
@Test public void sameFileTemplateRestoreStillRequiresReplaceAndUsesBatchPermissions() {
|
permissions.setEnabled(true); allow("wenjian", Action.VIEW, Action.EDIT);
|
UploadEditDraft d = draft("A", "A"); d.setField("wenjian"); d.setEditOriginsJson("{}");
|
d.setBaselineJson("[{\"fileId\":\"A\",\"laiyuan_muban_id\":\"templateA\"}]");
|
d.setCurrentJson("[{\"fileId\":\"A\",\"laiyuan_muban_id\":\"templateB\"}]");
|
assertFalse(service.canDiscardDraft(d, permissions));
|
allow("wenjian", Action.VIEW, Action.EDIT, Action.REPLACE);
|
assertTrue(service.canDiscardDraft(d, permissions));
|
verifyNoInteractions(models, forms, workflow);
|
}
|
}
|