刘光辉
2 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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);
    }
}