package jnpf.bizcommon.onlyoffice.service.impl;
|
|
import cn.hutool.crypto.digest.DigestUtil;
|
import cn.hutool.jwt.JWTUtil;
|
import jnpf.base.ActionResult;
|
import jnpf.base.model.uploadedit.UploadEditLease;
|
import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeCallbackParam;
|
import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeSessionEntity;
|
import jnpf.exception.DataException;
|
import jnpf.onlinedev.UploadEditApi;
|
import org.junit.Test;
|
import java.util.*;
|
import static org.mockito.ArgumentMatchers.*;
|
import static org.mockito.Mockito.*;
|
|
public class UploadEditSessionSupportTest {
|
private <T> ActionResult<T> success(T value) {
|
ActionResult<T> result = new ActionResult<>(); result.setCode(200); result.setData(value); return result;
|
}
|
private OnlyOfficeSessionEntity session() {
|
OnlyOfficeSessionEntity s = new OnlyOfficeSessionEntity();
|
s.setTenantId("tenant"); s.setLockUserId("owner"); s.setFileId("lease"); s.setFileName("source.docx"); return s;
|
}
|
private OnlyOfficeCallbackParam callback() {
|
OnlyOfficeCallbackParam c = new OnlyOfficeCallbackParam();
|
c.setKey("key"); c.setStatus(6); c.setUrl("https://office/result.docx"); return c;
|
}
|
@Test public void docCallbackUsesActualOutputNameInStorageAndAdoptsItsResult() {
|
for (String output : Arrays.asList("docx", "doc")) {
|
UploadEditApi api = mock(UploadEditApi.class); UploadEditStorage storage = mock(UploadEditStorage.class);
|
UploadEditLease lease = new UploadEditLease(); lease.setTracked(true); lease.setOwnerId("owner"); lease.setLeaseId("lease");
|
lease.setFile(Collections.singletonMap("fileId", "source"));
|
when(api.lease("tenant", "owner", "lease")).thenReturn(success(lease));
|
Map<String, Object> saved = Map.of("fileId", "saved", "name", "source." + output, "fileExtension", output);
|
when(storage.save(eq("tenant"), eq("lease"), eq("source." + output), any())).thenReturn(saved);
|
when(api.save(any())).thenReturn(success(true));
|
OnlyOfficeSessionEntity session = session(); session.setFileName("source.doc");
|
OnlyOfficeCallbackParam callback = callback(); callback.setFiletype(output);
|
new UploadEditSessionSupport(api, storage).save(session, callback, new byte[]{1});
|
verify(storage).save(eq("tenant"), eq("lease"), eq("source." + output), any());
|
verify(api).save(argThat(command -> saved.equals(command.getFile())));
|
}
|
}
|
@Test(expected = DataException.class) public void docCallbackWithoutOutputTypeCannotMislabelItsBytes() {
|
UploadEditSessionSupport.savedFileName("source.doc", null);
|
}
|
@Test(expected = DataException.class) public void unexpectedOutputTypeIsRejected() {
|
UploadEditSessionSupport.savedFileName("source.doc", "pdf");
|
}
|
@Test(expected = DataException.class) public void outputTypeMustMatchSignedCallback() {
|
OnlyOfficeCallbackParam c = callback(); c.setFiletype("doc");
|
Map<String, Object> claims = new HashMap<>(); claims.put("key", c.getKey()); claims.put("status", c.getStatus());
|
claims.put("url", c.getUrl()); claims.put("filetype", "docx");
|
c.setToken(JWTUtil.createToken(claims, new byte[]{1, 2, 3}));
|
UploadEditSessionSupport.requireBoundCallback(c, null);
|
}
|
@Test public void statusDoesNotFetchFilesOrWriteStorage() {
|
UploadEditApi api = mock(UploadEditApi.class); UploadEditStorage storage = mock(UploadEditStorage.class);
|
when(api.canReadStatus("tenant", "owner", "lease")).thenReturn(success(true));
|
new UploadEditSessionSupport(api, storage).requireStatusAccess("tenant", "owner", "lease");
|
verify(api, never()).lease(any(), any(), any());
|
verifyNoInteractions(storage);
|
}
|
@Test(expected = DataException.class) public void expiredOrForeignLeaseCannotReadStatus() {
|
UploadEditApi api = mock(UploadEditApi.class);
|
when(api.canReadStatus("tenant", "owner", "lease")).thenReturn(success(false));
|
new UploadEditSessionSupport(api, mock(UploadEditStorage.class)).requireStatusAccess("tenant", "owner", "lease");
|
}
|
@Test(expected = DataException.class) public void failedStatusProbeCannotBeTreatedAsSuccess() {
|
UploadEditApi api = mock(UploadEditApi.class);
|
ActionResult<Boolean> failed = success(true); failed.setCode(400);
|
when(api.canReadStatus("tenant", "owner", "lease")).thenReturn(failed);
|
new UploadEditSessionSupport(api, mock(UploadEditStorage.class)).requireStatusAccess("tenant", "owner", "lease");
|
}
|
@Test public void authorizedRevisionTemporaryCallbackStoresSeparateCopy() {
|
UploadEditApi api = mock(UploadEditApi.class); UploadEditStorage storage = mock(UploadEditStorage.class);
|
UploadEditLease lease = new UploadEditLease(); lease.setRevisionTemporary(true);
|
lease.setFile(Map.of("fileId", "original"));
|
when(api.lease("tenant", "owner", "lease")).thenReturn(success(lease));
|
Map<String,Object> copy = Map.of("fileId", "copy", "name", "source.docx");
|
when(storage.save(eq("tenant"),eq("lease"),eq("source.docx"),any())).thenReturn(copy);
|
when(api.save(any())).thenReturn(success(true));
|
new UploadEditSessionSupport(api,storage).save(session(),callback(),new byte[]{1});
|
verify(api).save(argThat(c -> copy.equals(c.getFile())));
|
}
|
@Test public void untrackedCallbackNeverWritesFileOrDraft() {
|
UploadEditApi api = mock(UploadEditApi.class); UploadEditStorage storage = mock(UploadEditStorage.class);
|
UploadEditLease lease = new UploadEditLease(); lease.setFile(Collections.singletonMap("fileId", "source"));
|
when(api.lease("tenant", "owner", "lease")).thenReturn(success(lease));
|
new UploadEditSessionSupport(api, storage).save(session(), callback(), new byte[]{1});
|
verifyNoInteractions(storage); verify(api, never()).save(any());
|
}
|
@Test public void repeatedCallbackDoesNotCreateAnotherPhysicalFile() {
|
UploadEditApi api = mock(UploadEditApi.class); UploadEditStorage storage = mock(UploadEditStorage.class);
|
UploadEditLease lease = new UploadEditLease(); lease.setTracked(true); lease.setFile(Collections.singletonMap("fileId", "source"));
|
OnlyOfficeCallbackParam c = callback();
|
lease.setLastOperationId(DigestUtil.sha256Hex(c.getKey() + "\n" + c.getStatus() + "\n" + c.getUrl()));
|
when(api.lease("tenant", "owner", "lease")).thenReturn(success(lease));
|
new UploadEditSessionSupport(api, storage).save(session(), c, new byte[]{1});
|
verifyNoInteractions(storage); verify(api, never()).save(any());
|
}
|
@Test public void acceptsMatchingRootAndWrappedSignedPayloads() {
|
for (String output : Arrays.asList(null, "doc", "docx")) {
|
OnlyOfficeCallbackParam c = callback(); c.setFiletype(output);
|
Map<String, Object> claims = new HashMap<>();
|
claims.put("key", c.getKey()); claims.put("status", c.getStatus()); claims.put("url", c.getUrl());
|
if (output != null) claims.put("filetype", output);
|
UploadEditSessionSupport.requireBoundCallback(c, "Bearer " + JWTUtil.createToken(claims, new byte[]{1, 2, 3}));
|
c.setToken(JWTUtil.createToken(Collections.singletonMap("payload", claims), new byte[]{1, 2, 3}));
|
UploadEditSessionSupport.requireBoundCallback(c, null);
|
}
|
}
|
@Test(expected = DataException.class) public void rejectsUnsignedRequestBodySubstitution() {
|
OnlyOfficeCallbackParam c = callback();
|
Map<String, Object> claims = new HashMap<>(); claims.put("key", c.getKey()); claims.put("status", c.getStatus()); claims.put("url", "https://different");
|
c.setToken(JWTUtil.createToken(claims, new byte[]{1, 2, 3}));
|
UploadEditSessionSupport.requireBoundCallback(c, null);
|
}
|
}
|