package jnpf.base.service;
|
|
import cn.dev33.satoken.stp.StpUtil;
|
import jnpf.base.ActionResult;
|
import jnpf.base.ModuleButtonApi;
|
import jnpf.base.entity.ModuleButtonEntity;
|
import jnpf.base.entity.VisualdevEntity;
|
import jnpf.base.model.button.ModuleButtonModel;
|
import jnpf.base.model.uploadedit.UploadEditContext;
|
import jnpf.base.util.FlowFormDataUtil;
|
import jnpf.exception.DataException;
|
import jnpf.flowable.WorkFlowApi;
|
import jnpf.flowable.entity.TaskEntity;
|
import jnpf.flowable.enums.TaskStatusEnum;
|
import jnpf.onlyoffice.UploadFileCapabilityApi;
|
import jnpf.flowable.entity.TemplateNodeEntity;
|
import jnpf.flowable.model.filepermission.FilePermissionView;
|
import jnpf.flowable.model.filepermission.FilePermissionPolicy.Action;
|
import jnpf.util.UserProvider;
|
import org.junit.Before;
|
import org.junit.Test;
|
import org.mockito.MockedStatic;
|
import org.springframework.beans.factory.ObjectProvider;
|
import java.util.*;
|
import static org.junit.Assert.*;
|
import static org.mockito.ArgumentMatchers.*;
|
import static org.mockito.Mockito.*;
|
|
public class UploadEditAccessTest {
|
private UploadEditAccess access;
|
private UploadEditContext context;
|
private WorkFlowApi workflow;
|
private VisualdevEntity model;
|
private UploadFileCapabilityApi capabilities;
|
private FlowFormDataUtil form;
|
private ModuleButtonApi buttons;
|
private Map<String, Object> data;
|
private VisualdevService modelService;
|
private UploadEditReadService sharedReads;
|
@Before @SuppressWarnings("unchecked") public void setup() {
|
jnpf.util.context.SpringContext.setApplicationContext(mock(org.springframework.context.ApplicationContext.class));
|
ObjectProvider<VisualdevService> models = mock(ObjectProvider.class);
|
modelService = mock(VisualdevService.class); when(models.getObject()).thenReturn(modelService);
|
ObjectProvider<FlowFormDataUtil> forms = mock(ObjectProvider.class);
|
form = mock(FlowFormDataUtil.class); when(forms.getObject()).thenReturn(form);
|
buttons = mock(ModuleButtonApi.class);
|
workflow = mock(WorkFlowApi.class);
|
when(workflow.filePermissions("alias", "model")).thenReturn(FilePermissionView.legacy("alias", "model"));
|
capabilities = mock(UploadFileCapabilityApi.class);
|
ActionResult<Map<String, Boolean>> options = new ActionResult<>(); options.setCode(200);
|
options.setData(Collections.singletonMap("editEnabled", true)); when(capabilities.options()).thenReturn(options);
|
model = new VisualdevEntity(); model.setFormData("{\"fields\":[{\"__vModel__\":\"files\",\"onlineEdit\":true,\"__config__\":{\"jnpfKey\":\"uploadFile\"}}]}");
|
model.setColumnData("{}"); when(modelService.getReleaseInfo("model")).thenReturn(model);
|
sharedReads = mock(UploadEditReadService.class); when(sharedReads.model("model")).thenReturn(model);
|
when(sharedReads.dmsAttachment(any(), any(), anyMap())).thenReturn(null);
|
data = new HashMap<>(); data.put("id", "real-record"); data.put("flowTaskId", "alias");
|
data.put("files", Collections.singletonList(Collections.singletonMap("fileId", "source")));
|
when(form.getEditDataInfo(eq(model), anyString(), any())).thenReturn(data);
|
access = new UploadEditAccess(models, forms, workflow, new UploadFilePermissionQuery(workflow, mock(ObjectProvider.class)), capabilities, buttons, sharedReads);
|
context = new UploadEditContext(); context.setModelId("model"); context.setRecordId("alias"); context.setField("files");
|
}
|
@Test public void copyLifecycleOnlyAppliesToUnsavedDmsAuditAttachmentFields() {
|
context.setRecordId(null); context.setField("wenjian");
|
for (String table : List.of("dms_wenjian_xinzeng_shenhe", "dms_wenjian_xiuding_shenhe")) {
|
model.setFormData("{\"tableName\":\""+table+"\"}");
|
assertTrue(access.isDmsAuditTemporary(context));
|
context.setTaskId("task"); assertFalse(access.isDmsAuditTemporary(context)); context.setTaskId(null);
|
context.setRecordId("saved"); assertFalse(access.isDmsAuditTemporary(context)); context.setRecordId(null);
|
}
|
model.setFormData("{\"tableName\":\"other_business\"}"); assertFalse(access.isDmsAuditTemporary(context));
|
model.setFormData("{}"); assertFalse(access.isDmsAuditTemporary(context));
|
}
|
@Test public void savedDmsAuditRejectsLegacyPermissionsForBothReadAndEdit() {
|
model.setFormData("{\"tableName\":\"dms_wenjian_xiuding_shenhe\",\"fields\":[{\"__vModel__\":\"wenjian\",\"onlineEdit\":true,\"__config__\":{\"jnpfKey\":\"uploadFile\"}}]}");
|
context.setField("wenjian");
|
for (boolean shared : List.of(false, true)) {
|
try {
|
if (shared) access.sharedBaseline(context, "owner"); else access.checkedBaseline(context, "owner");
|
fail("legacy DMS permissions must be rejected");
|
} catch (DataException expected) { assertEquals(UploadEditAccess.DMS_INCOMPATIBLE, expected.getMessage()); }
|
}
|
}
|
@Test public void newDmsAuditStillUsesCurrentTemporaryAuthorization() {
|
model.setFormData("{\"tableName\":\"dms_wenjian_xinzeng_shenhe\",\"fields\":[{\"__vModel__\":\"wenjian\",\"onlineEdit\":true,\"__config__\":{\"jnpfKey\":\"uploadFile\"}}]}");
|
context.setField("wenjian"); context.setRecordId(null);
|
try (MockedStatic<StpUtil> auth = mockStatic(StpUtil.class)) {
|
access.authorizeTemporary(context, "owner");
|
auth.verify(() -> StpUtil.checkPermission("model"));
|
}
|
}
|
@Test public void nonWorkflowRecordUsesNativeIdAndOrdinaryPermission() {
|
try (MockedStatic<StpUtil> auth = mockStatic(StpUtil.class)) {
|
assertEquals("source", access.baseline(context, "owner").get(0).get("fileId"));
|
assertEquals("real-record", context.getRecordId());
|
auth.verify(() -> StpUtil.checkPermission("model"));
|
}
|
}
|
@Test public void qianpiyePluginRequiresExplicitPublishedFieldSwitch() {
|
assertFalse(access.qianpiyeAnnotatorEnabled(context));
|
model.setFormData("{\"fields\":[{\"__vModel__\":\"files\",\"onlineEdit\":true,\"qianpiyeAnnotatorEnabled\":true,\"__config__\":{\"jnpfKey\":\"uploadFile\"}}]}");
|
assertTrue(access.qianpiyeAnnotatorEnabled(context));
|
model.setFormData("{\"fields\":[{\"__vModel__\":\"files\",\"onlineEdit\":false,\"qianpiyeAnnotatorEnabled\":true,\"__config__\":{\"jnpfKey\":\"uploadFile\"}}]}");
|
assertFalse(access.qianpiyeAnnotatorEnabled(context));
|
}
|
@Test public void unassociatedEditButtonDoesNotRestrictOnlineAttachmentEditing() {
|
model.setColumnData("{\"useBtnPermission\":true,\"onlyAssociatedBtnPermission\":true}");
|
when(buttons.getListByModuleIdsForPermission(any(ModuleButtonModel.class))).thenReturn(Collections.emptyList());
|
try (MockedStatic<StpUtil> auth = mockStatic(StpUtil.class)) {
|
access.baseline(context, "owner");
|
auth.verify(() -> StpUtil.checkPermission("model"));
|
auth.verify(() -> StpUtil.checkPermission("model::btn_edit"), never());
|
}
|
}
|
@Test public void associatedEditButtonStillRequiresPermission() {
|
model.setColumnData("{\"useBtnPermission\":true,\"onlyAssociatedBtnPermission\":true}");
|
ModuleButtonEntity resource = new ModuleButtonEntity(); resource.setEnCode("btn_edit");
|
when(buttons.getListByModuleIdsForPermission(any(ModuleButtonModel.class))).thenReturn(Collections.singletonList(resource));
|
try (MockedStatic<StpUtil> auth = mockStatic(StpUtil.class)) {
|
access.baseline(context, "owner");
|
auth.verify(() -> StpUtil.checkPermission("model::btn_edit"));
|
verify(buttons).getListByModuleIdsForPermission(argThat(query -> query.getIds().equals(Collections.singletonList("model"))));
|
}
|
}
|
@Test public void associatedAddButtonStillRequiresPermissionForNewRecords() {
|
model.setColumnData("{\"useBtnPermission\":true,\"onlyAssociatedBtnPermission\":true}");
|
context.setRecordId("");
|
ModuleButtonEntity resource = new ModuleButtonEntity(); resource.setEnCode("btn_add");
|
when(buttons.getListByModuleIdsForPermission(any(ModuleButtonModel.class))).thenReturn(Collections.singletonList(resource));
|
try (MockedStatic<StpUtil> auth = mockStatic(StpUtil.class)) {
|
access.baseline(context, "owner");
|
auth.verify(() -> StpUtil.checkPermission("model::btn_add"));
|
}
|
}
|
@Test public void defaultModeRetainsStrictButtonPermission() {
|
model.setColumnData("{\"useBtnPermission\":true}");
|
try (MockedStatic<StpUtil> auth = mockStatic(StpUtil.class)) {
|
access.baseline(context, "owner");
|
auth.verify(() -> StpUtil.checkPermission("model::btn_edit"));
|
verifyNoInteractions(buttons);
|
}
|
}
|
@Test public void missingAssociationResponseKeepsExistingPermissionCheck() {
|
model.setColumnData("{\"useBtnPermission\":true,\"onlyAssociatedBtnPermission\":true}");
|
when(buttons.getListByModuleIdsForPermission(any(ModuleButtonModel.class))).thenReturn(null);
|
try (MockedStatic<StpUtil> auth = mockStatic(StpUtil.class)) {
|
access.baseline(context, "owner");
|
auth.verify(() -> StpUtil.checkPermission("model::btn_edit"));
|
}
|
}
|
@Test public void nativeMenuDraftWithoutTaskStillChecksOrdinaryPermissions() {
|
data.put(FilePermissionNativeDraft.STATE_KEY, nativeState(0));
|
when(form.getFilePermissionDataInfo(eq(model), eq("real-record"), any())).thenReturn(data);
|
when(workflow.filePermissions("alias", "model")).thenReturn(FilePermissionView.unavailable("alias", "model", "TASK_NOT_FOUND"));
|
try (MockedStatic<StpUtil> auth = mockStatic(StpUtil.class)) {
|
assertFalse(access.checkedBaseline(context, "owner").isControlled());
|
assertTrue(access.allowDownload(context));
|
auth.verify(() -> StpUtil.checkPermission("model"));
|
}
|
}
|
@Test public void absentTaskCannotRescueRunningDeletedOrUnverifiedRecord() {
|
when(workflow.filePermissions("alias", "model")).thenReturn(FilePermissionView.unavailable("alias", "model", "TASK_NOT_FOUND"));
|
when(form.getFilePermissionDataInfo(eq(model), anyString(), any())).thenReturn(data);
|
rejects(() -> access.baseline(context, "owner"));
|
data.put(FilePermissionNativeDraft.STATE_KEY, nativeState(1));
|
rejects(() -> access.baseline(context, "owner"));
|
Map<String,Object> state = nativeState(0); state.put("deleteMark", 1);
|
data.put(FilePermissionNativeDraft.STATE_KEY, state);
|
rejects(() -> access.baseline(context, "owner"));
|
data.put(FilePermissionNativeDraft.STATE_KEY, nativeState(0));
|
context.setTaskId("alias");
|
rejects(() -> access.baseline(context, "owner"));
|
}
|
@Test public void nativeDraftCannotConvertNetworkFailureIntoLegacy() {
|
data.put(FilePermissionNativeDraft.STATE_KEY, nativeState(0));
|
when(workflow.filePermissions("alias", "model")).thenReturn(FilePermissionView.unavailable("alias", "model", "PERMISSION_UNAVAILABLE"));
|
rejects(() -> access.baseline(context, "owner"));
|
rejects(() -> access.allowDownload(context));
|
verify(form, never()).getFilePermissionDataInfo(any(), anyString(), any());
|
}
|
private Map<String,Object> nativeState(int status) {
|
Map<String,Object> state = new HashMap<>(); state.put("flowId", "flow"); state.put("flowState", status); state.put("deleteMark", null); return state;
|
}
|
@Test(expected = DataException.class) public void disabledCapabilityRejectsAccess() {
|
ActionResult<Map<String, Boolean>> options = new ActionResult<>(); options.setCode(200);
|
options.setData(Collections.singletonMap("editEnabled", false)); when(capabilities.options()).thenReturn(options);
|
access.baseline(context, "owner");
|
}
|
@Test(expected = DataException.class) public void workflowCannotBeEditedByAnotherUser() {
|
TaskEntity task = new TaskEntity(); task.setId("alias"); task.setCreatorUserId("someone-else"); task.setStatus(TaskStatusEnum.TO_BE_SUBMIT.getCode());
|
when(workflow.getInfoSubmit("alias")).thenReturn(task); access.baseline(context, "owner");
|
}
|
@Test(expected = DataException.class) public void nonDraftWorkflowCannotBeEditedEvenByCreator() {
|
TaskEntity task = new TaskEntity(); task.setId("alias"); task.setCreatorUserId("owner"); task.setStatus(-999);
|
when(workflow.getInfoSubmit("alias")).thenReturn(task); access.baseline(context, "owner");
|
}
|
@Test(expected = DataException.class) public void missingWorkflowIsNotTreatedAsOrdinaryRecord() {
|
context.setTaskId("task"); access.baseline(context, "owner");
|
}
|
@Test(expected = DataException.class) public void disabledParentCannotBeBypassedThroughChildField() {
|
model.setFormData("{\"fields\":[{\"__vModel__\":\"rows\",\"disabled\":true,\"__config__\":{\"jnpfKey\":\"table\",\"children\":[{\"__vModel__\":\"files\",\"onlineEdit\":true,\"__config__\":{\"jnpfKey\":\"uploadFile\"}}]}}]}");
|
context.setParentField("rows"); context.setRowId("row"); access.baseline(context, "owner");
|
}
|
@Test public void downloadUsesReleasedFieldAndDefaultsOnlyForOldSchemas() {
|
assertTrue(access.allowDownload(context));
|
model.setFormData("{\"fields\":[{\"__vModel__\":\"files\",\"allowDownload\":false,\"__config__\":{\"jnpfKey\":\"uploadFile\"}}]}");
|
assertFalse(access.allowDownload(context));
|
model.setFormData(model.getFormData().replace("false", "\"false\""));
|
assertFalse(access.allowDownload(context));
|
}
|
@Test public void downloadSeparatesMainAndChildFieldsWithSameName() {
|
model.setFormData("{\"fields\":[{\"__vModel__\":\"files\",\"allowDownload\":true,\"__config__\":{\"jnpfKey\":\"uploadFile\"}},{\"__vModel__\":\"rows\",\"__config__\":{\"jnpfKey\":\"table\",\"children\":[{\"__vModel__\":\"files\",\"allowDownload\":false,\"__config__\":{\"jnpfKey\":\"uploadFile\"}}]}}]}");
|
assertTrue(access.allowDownload(context));
|
context.setParentField("rows");
|
assertFalse(access.allowDownload(context));
|
}
|
@Test(expected = DataException.class) public void missingDownloadFieldDoesNotDefaultToAllowed() {
|
context.setField("missing"); access.allowDownload(context);
|
}
|
|
@Test public void oldDraftStillUsesNativeCreatorAndNodePermissions() {
|
TaskEntity task = task("owner", 0);
|
TemplateNodeEntity start = new TemplateNodeEntity(); start.setNodeType("start"); start.setFormId("model");
|
start.setNodeJson("{\"formOperates\":[{\"id\":\"files\",\"write\":true}]}");
|
when(workflow.getNodesByFlowId(task.getFlowId())).thenReturn(List.of(start));
|
assertEquals("source", access.baseline(context, "owner").get(0).get("fileId"));
|
start.setNodeJson("{\"formOperates\":[{\"id\":\"files\",\"write\":false}]}");
|
rejects(() -> access.baseline(context, "owner"));
|
}
|
@Test public void controlledCurrentHandlerCanEditWhileCreatorRuleNoLongerApplies() {
|
task("creator", 1);
|
when(workflow.filePermissions("alias", "model")).thenReturn(controlled(true, false));
|
try (MockedStatic<UserProvider> users = mockStatic(UserProvider.class)) {
|
users.when(UserProvider::getLoginUserId).thenReturn("handler");
|
assertEquals("source", access.baseline(context, "handler").get(0).get("fileId"));
|
assertEquals("alias", context.getTaskId());
|
verify(workflow, never()).getNodesByFlowId(any());
|
}
|
}
|
@Test public void controlledDenialAndUnavailableDoNotFallBackToCreator() {
|
task("owner", 0);
|
try (MockedStatic<UserProvider> users = mockStatic(UserProvider.class)) {
|
users.when(UserProvider::getLoginUserId).thenReturn("owner");
|
when(workflow.filePermissions("alias", "model")).thenReturn(controlled(false, true));
|
rejects(() -> access.baseline(context, "owner"));
|
when(workflow.filePermissions("alias", "model")).thenReturn(FilePermissionView.unavailable("alias", "model", "OFFLINE"));
|
rejects(() -> access.baseline(context, "owner"));
|
when(workflow.filePermissions("alias", "model")).thenReturn(null);
|
rejects(() -> access.baseline(context, "owner"));
|
}
|
}
|
@Test public void rejectsForeignPermissionContextAndOwner() {
|
task("creator", 1);
|
FilePermissionView view = controlled(true, true);
|
when(workflow.filePermissions("alias", "model")).thenReturn(view);
|
try (MockedStatic<UserProvider> users = mockStatic(UserProvider.class)) {
|
users.when(UserProvider::getLoginUserId).thenReturn("handler");
|
rejects(() -> access.baseline(context, "someone-else"));
|
view.setTaskId("other-task");
|
rejects(() -> access.baseline(context, "handler"));
|
}
|
}
|
@Test public void downloadRechecksActualRecordTaskAndDoesNotRequireEditing() {
|
context.setTaskId("forged-task");
|
when(workflow.filePermissions("alias", "model")).thenReturn(controlled(false, true));
|
assertTrue(access.allowDownload(context));
|
when(workflow.filePermissions("alias", "model")).thenReturn(controlled(true, false));
|
assertFalse(access.allowDownload(context));
|
verify(workflow, never()).filePermissions(eq("forged-task"), any());
|
}
|
@Test public void controlledMarkerComesFromRuntimeAndMissingTaskCannotDowngrade() {
|
when(workflow.filePermissions("alias", "model")).thenReturn(controlled(true, true));
|
rejects(() -> access.checkedBaseline(context, "handler"));
|
task("creator", 1);
|
try (MockedStatic<UserProvider> users = mockStatic(UserProvider.class)) {
|
users.when(UserProvider::getLoginUserId).thenReturn("handler");
|
assertTrue(access.checkedBaseline(context, "handler").isControlled());
|
assertEquals("alias", context.getTaskId());
|
}
|
}
|
@Test public void missingTaskWithUnavailableRuntimeCannotTakeOrdinaryPath() {
|
when(workflow.filePermissions("alias", "model")).thenReturn(null);
|
rejects(() -> access.baseline(context, "owner"));
|
when(workflow.filePermissions("alias", "model")).thenReturn(FilePermissionView.unavailable("alias", "model", "OFFLINE"));
|
rejects(() -> access.baseline(context, "owner"));
|
}
|
private TaskEntity task(String creator, int status) {
|
TaskEntity task = new TaskEntity(); task.setId("alias"); task.setCreatorUserId(creator);
|
task.setStatus(status); task.setFlowId("flow");
|
when(workflow.getInfoSubmit("alias")).thenReturn(task); return task;
|
}
|
private FilePermissionView controlled(boolean edit, boolean download) {
|
FilePermissionView view = new FilePermissionView(); view.setTaskId("alias"); view.setFormId("model");
|
view.setStatus("ready"); view.setRevision("state");
|
FilePermissionView.FieldAccess field = new FilePermissionView.FieldAccess(); field.setStatus("ready");
|
field.setActions(Map.of(Action.EDIT, edit, Action.VIEW, true, Action.DOWNLOAD, download));
|
view.getFields().put("files", field); return view;
|
}
|
|
private static void rejects(Runnable action) {
|
try { action.run(); } catch (DataException expected) { return; }
|
fail("expected access denial");
|
}
|
|
@Test public void sharedReadWorksForViewerWithoutGlobalEditingCapability() {
|
task("creator", 1); when(workflow.filePermissions("alias", "model")).thenReturn(controlled(false,false));
|
when(capabilities.options()).thenThrow(new IllegalStateException("edit disabled"));
|
try (MockedStatic<UserProvider> users = mockStatic(UserProvider.class)) {
|
users.when(UserProvider::getLoginUserId).thenReturn("viewer");
|
assertEquals("source",access.sharedBaseline(context,"viewer").getFiles().get(0).get("fileId"));
|
verifyNoInteractions(capabilities);
|
context.setTaskId("other"); rejects(() -> access.sharedBaseline(context,"viewer"));
|
}
|
}
|
|
@Test public void sharedReadUsesOneDefinitionAndRechecksItOnEachRequest() {
|
task("creator", 1);
|
context.setField("wenjian"); data.put("wenjian", data.get("files"));
|
FilePermissionView view = controlled(false, false);
|
view.getFields().put("wenjian", view.getFields().get("files"));
|
when(workflow.filePermissions("alias", "model")).thenReturn(view);
|
try (MockedStatic<UserProvider> users = mockStatic(UserProvider.class)) {
|
users.when(UserProvider::getLoginUserId).thenReturn("viewer");
|
for (String table : List.of("dms_wenjian_xinzeng_shenhe", "dms_wenjian_xiuding_shenhe", "other_business")) {
|
model.setFormData("{\"tableName\":\"" + table + "\",\"fields\":[{\"__vModel__\":\"wenjian\",\"onlineEdit\":true,\"__config__\":{\"jnpfKey\":\"uploadFile\"}}]}");
|
clearInvocations(sharedReads);
|
UploadEditAccess.CheckedBaseline result = access.sharedBaseline(context, "viewer");
|
assertEquals(!"other_business".equals(table), result.isDmsAudit());
|
assertEquals("source", result.getFiles().get(0).get("fileId"));
|
verify(sharedReads, times(1)).model("model");
|
verify(modelService, never()).getReleaseInfo("model");
|
}
|
when(workflow.filePermissions("alias", "model")).thenReturn(FilePermissionView.unavailable("alias", "model", "OFFLINE"));
|
rejects(() -> access.sharedBaseline(context, "viewer"));
|
}
|
}
|
|
@Test public void sharedDmsReadUsesRawProjectionAndNeverLoadsNativeDisplayData() {
|
task("creator", 1); context.setField("wenjian"); data.put("wenjian", data.get("files"));
|
model.setFormData("{\"tableName\":\"dms_wenjian_xinzeng_shenhe\",\"fields\":[{\"__vModel__\":\"wenjian\",\"onlineEdit\":true,\"__config__\":{\"jnpfKey\":\"uploadFile\"}}]}");
|
FilePermissionView view = controlled(false, false); view.getFields().put("wenjian", view.getFields().get("files"));
|
when(workflow.filePermissions("alias", "model")).thenReturn(view);
|
when(sharedReads.dmsAttachment(eq(model), eq(context), anyMap())).thenReturn(data);
|
try (MockedStatic<UserProvider> users = mockStatic(UserProvider.class)) {
|
users.when(UserProvider::getLoginUserId).thenReturn("viewer");
|
assertEquals("source", access.sharedBaseline(context, "viewer").getFiles().get(0).get("fileId"));
|
verifyNoInteractions(form, modelService);
|
when(sharedReads.dmsAttachment(eq(model), eq(context), anyMap())).thenThrow(new DataException("query failed"));
|
rejects(() -> access.sharedBaseline(context, "viewer"));
|
verifyNoInteractions(form, modelService);
|
}
|
}
|
|
@Test public void batchSharesDefinitionAndPermissionsButNotAcrossRequests() {
|
task("creator", 1); context.setField("wenjian"); data.put("wenjian", data.get("files"));
|
model.setFormData("{\"tableName\":\"dms_wenjian_xinzeng_shenhe\",\"fields\":[{\"__vModel__\":\"wenjian\",\"onlineEdit\":true,\"__config__\":{\"jnpfKey\":\"uploadFile\"}}]}");
|
FilePermissionView view = controlled(false, false); view.getFields().put("wenjian", view.getFields().get("files"));
|
when(workflow.filePermissions("alias", "model")).thenReturn(view);
|
when(sharedReads.dmsAttachment(eq(model), any(), anyMap())).thenReturn(data);
|
try (MockedStatic<UserProvider> users = mockStatic(UserProvider.class)) {
|
users.when(UserProvider::getLoginUserId).thenReturn("viewer");
|
assertEquals(2, access.sharedBaselines(List.of(context, context), "viewer").size());
|
verify(sharedReads, times(1)).model("model");
|
verify(workflow, times(1)).filePermissions("alias", "model");
|
verify(workflow, times(1)).getInfoSubmit("alias");
|
when(workflow.filePermissions("alias", "model")).thenReturn(FilePermissionView.unavailable("alias", "model", "OFFLINE"));
|
rejects(() -> access.sharedBaselines(List.of(context), "viewer"));
|
}
|
}
|
@Test public void batchRejectsCrossRecordBeforeAnyReads() {
|
UploadEditContext other = new UploadEditContext(); other.setModelId("model"); other.setRecordId("other"); other.setField("files");
|
rejects(() -> access.sharedBaselines(List.of(context, other), "viewer"));
|
verifyNoInteractions(sharedReads);
|
}
|
}
|