刘光辉
4 小时以前 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package jnpf.dmsService;
 
import jnpf.dmsEntity.permission.entity.*;
import jnpf.dmsEntity.storage.approval.*;
import jnpf.dmsMapper.*;
import jnpf.dmsService.search.DmsSearchIndexJobService;
import jnpf.exception.DataException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
 
@ExtendWith(MockitoExtension.class)
class DmsApprovedFileActivationServiceTest {
    @Mock DmsApprovedBatchMapper receipts;
    @Mock DmsDocumentMapper documents;
    @Mock DmsFileVersionMapper versions;
    @Mock DmsVersionStateMapper states;
    @Mock DmsSearchIndexJobService search;
    @Mock DmsApprovedAuditSource source;
    @Mock DmsApprovedReadAccessService readAccess;
    DmsQianpiyeRenderSnapshotService qianpiyeSnapshots = new DmsQianpiyeRenderSnapshotService(
            null, null, null, null, new com.fasterxml.jackson.databind.ObjectMapper());
    DmsApprovedFileActivationService service;
    @org.junit.jupiter.api.BeforeEach void setupCenter() {
        service = new DmsApprovedFileActivationService(receipts,
                new jnpf.dmsService.center.DmsCenterActivationService(org.mockito.Mockito.mock(jnpf.dmsIntegration.audit.DmsBusinessChangeAudit.class), documents, versions, states, search), source, qianpiyeSnapshots, readAccess,new DmsBusinessLifecycleGuard(null,false));
    }
    private jnpf.dmsIntegration.audit.DmsVersionStateAuditRow auditState(String status) {
        var row = new jnpf.dmsIntegration.audit.DmsVersionStateAuditRow(); row.setLifecycleStatus(status); return row;
    }
    DmsApprovedBatch batch() {
        DmsApprovedBatch b = new DmsApprovedBatch(); b.setId(UUID.randomUUID()); b.setTenantId("tenant"); b.setStatus("WAITING");
        b.setFlowId("flow"); b.setTaskId("task"); b.setAuditType("xiuding_shenhe"); b.setActorId("actor"); b.setSourceDigest("digest");
        b.setApprovedAt(new Date()); return b;
    }
    DmsApprovedItem item(String type) {
        DmsApprovedItem i = new DmsApprovedItem(); i.setChangeType(type); i.setDocumentId(UUID.randomUUID());
        i.setFileVersionId(UUID.randomUUID()); i.setBaseFileVersionId(UUID.randomUUID()); i.setChecksumSha256("hash"); return i;
    }
    void arrange(DmsApprovedBatch b, List<DmsApprovedItem> items) {
        when(receipts.lock("tenant", b.getId())).thenReturn(b);
        when(receipts.activate("tenant", b.getId())).thenReturn(1);
        when(receipts.items("tenant", b.getId())).thenReturn(new ArrayList<>(items));
        b.setActivatedAt(new Date()); b.setItems(items);
        when(source.load("tenant", "flow", "task", "xiuding_shenhe", "actor", b.getApprovedAt()))
                .thenReturn(new DmsApprovedAuditSource.Source(b, "author", new Date(), false));
    }
    @Test void futureBatchDoesNotTouchVersions() {
        DmsApprovedBatch b = batch(); when(receipts.lock("tenant", b.getId())).thenReturn(b);
        assertEquals("WAITING", service.activate("tenant", b.getId()).getStatus());
        verifyNoInteractions(versions, states, documents, source, search, readAccess);
    }
    @Test void effectiveRetryDoesNotGrantAgain() {
        DmsApprovedBatch b = batch(); b.setStatus("EFFECTIVE");
        when(receipts.lock("tenant", b.getId())).thenReturn(b);
        assertSame(b, service.activate("tenant", b.getId()));
        verifyNoInteractions(versions, states, documents, source, search, readAccess);
        verify(receipts, never()).activate(any(), any());
    }
    @Test void authorizationFailurePropagatesFromTheSameActivationTransaction() {
        DmsApprovedBatch b = batch(); DmsApprovedItem i = item("REUSE"); arrange(b, List.of(i));
        when(documents.selectByTenantAndIdForUpdate("tenant", i.getDocumentId())).thenReturn(new DmsDocumentEntity());
        DmsFileVersionEntity current = new DmsFileVersionEntity(); current.setId(i.getFileVersionId());
        when(versions.selectCurrentEffective("tenant", i.getDocumentId())).thenReturn(current);
        doThrow(new DataException("invalid department")).when(readAccess).apply(eq(b), anyList(), anyList());
        assertThrows(DataException.class, () -> service.activate("tenant", b.getId()));
    }
    @Test void reusedItemsNeverCreateOrSwitchVersions() {
        DmsApprovedBatch b = batch(); DmsApprovedItem i = item("REUSE"); arrange(b, List.of(i));
        when(documents.selectByTenantAndIdForUpdate("tenant", i.getDocumentId())).thenReturn(new DmsDocumentEntity());
        DmsFileVersionEntity current = new DmsFileVersionEntity(); current.setId(i.getFileVersionId());
        when(versions.selectCurrentEffective("tenant", i.getDocumentId())).thenReturn(current);
        service.activate("tenant", b.getId());
        verifyNoInteractions(states, search);
        verify(readAccess).apply(b, List.of(i), List.of(i));
    }
    @Test void revisionChecksExactBaseBeforeSwitching() {
        DmsApprovedBatch b = batch(); DmsApprovedItem i = item("REVISE"); arrange(b, List.of(i));
        when(documents.selectByTenantAndIdForUpdate("tenant", i.getDocumentId())).thenReturn(new DmsDocumentEntity());
        DmsFileVersionEntity wrong = new DmsFileVersionEntity(); wrong.setId(UUID.randomUUID());
        when(versions.selectCurrentEffective("tenant", i.getDocumentId())).thenReturn(wrong);
        assertThrows(DataException.class, () -> service.activate("tenant", b.getId()));
        verifyNoInteractions(states, search);
    }
    @Test void completeBatchActivatesMainAndRecordAfterValidation() {
        DmsApprovedBatch b = batch(); DmsApprovedItem main = item("CREATE"); DmsApprovedItem record = item("CREATE"); arrange(b, List.of(main, record));
        // Historical registered DOCX files have no render snapshot; activation must keep their digest.
        main.setSourceKey("xiuding_shenhe:a:zhuwenjian");
        main.setFileJson("[{\"name\":\"main.docx\",\"url\":\"main.docx\"}]");
        for (DmsApprovedItem i : List.of(main, record)) {
            when(documents.selectByTenantAndIdForUpdate("tenant", i.getDocumentId())).thenReturn(new DmsDocumentEntity());
            DmsFileVersionEntity registered = new DmsFileVersionEntity();
            registered.setId(i.getFileVersionId()); registered.setDocumentId(i.getDocumentId());
            when(versions.selectByTenantAndId("tenant", i.getFileVersionId())).thenReturn(registered);
            when(states.activateApproved("tenant", i.getFileVersionId(), "actor")).thenReturn(1);
            when(states.selectAuditState("tenant", i.getFileVersionId())).thenReturn(auditState("PENDING_EFFECTIVE"), auditState("EFFECTIVE"));
        }
        service.activate("tenant", b.getId());
        for (DmsApprovedItem i : List.of(main, record)) {
            verify(states).activateApproved("tenant", i.getFileVersionId(), "actor");
            verify(search).enqueueContentUpsert("tenant", i.getDocumentId(), i.getFileVersionId(), "hash");
        }
    }
 
    @Test void approvalPageSnapshotIsIncludedWhenActivatingRegisteredFiles() {
        DmsApprovedBatch b = batch();
        DmsApprovedItem main = item("CREATE");
        main.setSourceKey("xiuding_shenhe:a:zhuwenjian");
        main.setApprovalPageFileJson("[{\"fileId\":\"page.docx\"}]");
        arrange(b, List.of(main));
        String snapshot = "{\"protocolVersion\":1,\"taskId\":\"task\",\"flowId\":\"flow\","
                + "\"valuesByTag\":{\"dms.qianpiye.flow.approve-1.approver\":\"审核人\"}}";
        qianpiyeSnapshots.attach(b, snapshot, "author", new Date());
        DmsApprovedBatch currentSource = batch();
        currentSource.setItems(List.of(main));
        when(source.load("tenant", "flow", "task", "xiuding_shenhe", "actor", b.getApprovedAt()))
                .thenReturn(new DmsApprovedAuditSource.Source(currentSource, "author", new Date(), false));
        when(documents.selectByTenantAndIdForUpdate("tenant", main.getDocumentId())).thenReturn(new DmsDocumentEntity());
        DmsFileVersionEntity registered = new DmsFileVersionEntity();
        registered.setId(main.getFileVersionId()); registered.setDocumentId(main.getDocumentId());
        when(versions.selectByTenantAndId("tenant", main.getFileVersionId())).thenReturn(registered);
        when(states.activateApproved("tenant", main.getFileVersionId(), "actor")).thenReturn(1);
        when(states.selectAuditState("tenant", main.getFileVersionId())).thenReturn(auditState("PENDING_EFFECTIVE"), auditState("EFFECTIVE"));
 
        service.activate("tenant", b.getId());
 
        verify(states).activateApproved("tenant", main.getFileVersionId(), "actor");
    }
 
    @Test void changedApprovedSourceIsRejectedBeforeTouchingCenterVersions() {
        DmsApprovedBatch b = batch();
        when(receipts.lock("tenant", b.getId())).thenReturn(b);
        when(receipts.activate("tenant", b.getId())).thenReturn(1);
        DmsApprovedBatch changed = batch();
        changed.setSourceDigest("changed-content");
        when(source.load("tenant", "flow", "task", "xiuding_shenhe", "actor", b.getApprovedAt()))
                .thenReturn(new DmsApprovedAuditSource.Source(changed, "author", new Date(), false));
        assertThrows(DataException.class, () -> service.activate("tenant", b.getId()));
        verifyNoInteractions(documents, versions, states, search);
    }
}