刘光辉
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
package jnpf.dmsService;
 
import jnpf.base.ActionResult;
import jnpf.dmsEntity.storage.approval.DmsApprovedBatch;
import jnpf.dmsMapper.DmsApprovedBatchMapper;
import jnpf.dmsService.impl.DmsWenjianBanbenServiceImpl;
import jnpf.exception.DataException;
import org.junit.jupiter.api.Test;
import java.util.Date;
import java.util.UUID;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
 
class DmsApprovedFileRetryServiceTest {
    DmsApprovedBatchMapper mapper = mock(DmsApprovedBatchMapper.class);
    DmsApprovedFileAcceptanceService acceptance = mock(DmsApprovedFileAcceptanceService.class);
    DmsApprovedContentService files = mock(DmsApprovedContentService.class);
    DmsApprovedFileActivationService activation = mock(DmsApprovedFileActivationService.class);
    DmsWenjianBanbenServiceImpl business = mock(DmsWenjianBanbenServiceImpl.class);
    DmsApprovedDisableProcessor disables = mock(DmsApprovedDisableProcessor.class);
    DmsInvalidationProperties invalidation = new DmsInvalidationProperties();
    DmsApprovedFileRetryService service = new DmsApprovedFileRetryService(mapper,acceptance,files,activation,business,disables,invalidation);
    DmsApprovedBatch batch(String status) {
        DmsApprovedBatch b = new DmsApprovedBatch(); b.setId(UUID.randomUUID()); b.setTenantId("tenant");
        b.setTaskId("task"); b.setFlowId("flow"); b.setAuditType("xinzeng_shenhe"); b.setActorId("actor");
        b.setStatus(status); b.setApprovedAt(new Date()); b.setAttempts(1);
        when(mapper.claim(eq("tenant"),eq(b.getId()),any(),anyBoolean())).thenAnswer(i -> { b.setLeaseToken(i.getArgument(2)); return 1; });
        when(mapper.find("tenant","task","xinzeng_shenhe")).thenReturn(b);
        return b;
    }
    @Test void unclaimedBatchHasNoSideEffects() {
        DmsApprovedBatch b = batch("REGISTERING"); when(mapper.claim(any(),any(),any(),anyBoolean())).thenReturn(0);
        service.process(b); verifyNoInteractions(acceptance,files,activation,business,disables);
    }
    @Test void sourceFailureLeavesApprovalQueuedAndSchedulesRetry() {
        DmsApprovedBatch b = batch("PREPARING");
        when(acceptance.stage(any(),any(),any(),any(),any(),any())).thenThrow(new DataException("source unavailable"));
        assertThrows(DataException.class, () -> service.process(b));
        verify(mapper).finish(eq("tenant"),eq(b.getId()),any(),eq("source unavailable"),eq(60));
        verifyNoInteractions(files,activation,business,disables);
    }
    @Test void pushFailureCanBeRetriedWithoutLosingReservedBatch() {
        DmsApprovedBatch b = batch("REGISTERING");
        when(acceptance.stage(any(),any(),any(),any(),any(),any())).thenReturn(b);
        when(files.register(any())).thenThrow(new DataException("storage unavailable"));
        assertThrows(DataException.class, () -> service.process(b));
        verify(mapper).finish(eq("tenant"),eq(b.getId()),any(),eq("storage unavailable"),eq(60));
        ActionResult<DmsApprovedBatch> ok = new ActionResult<>(); ok.setCode(200);
        DmsApprovedBatch result = new DmsApprovedBatch(); org.springframework.beans.BeanUtils.copyProperties(b,result); result.setStatus("APPLIED"); ok.setData(result);
        doReturn(result).when(files).register(any());
        when(activation.activate("tenant", b.getId())).thenReturn(result);
        service.process(b);
        verify(mapper).finish(eq("tenant"),eq(b.getId()),any(),isNull(),eq(0));
    }
    @Test void lostReplyAfterCommittedRegistrationResumesAtActivationWithoutResendingFiles() {
        DmsApprovedBatch b = batch("WAITING"); when(activation.activate("tenant",b.getId())).thenAnswer(i -> { b.setStatus("EFFECTIVE"); return b; });
        service.process(b); verify(business).applyApproved(b); verifyNoInteractions(files,acceptance);
    }
    @Test void failedBusinessWriteRetriesProjectionOnly() {
        DmsApprovedBatch b = batch("EFFECTIVE"); when(activation.activate("tenant",b.getId())).thenReturn(b);
        doThrow(new DataException("business unavailable")).doNothing().when(business).applyApproved(b);
        assertThrows(DataException.class, () -> service.process(b)); service.process(b);
        verify(business,times(2)).applyApproved(b); verifyNoInteractions(files,acceptance);
        verify(mapper,never()).fail(any(),any());
    }
    @Test void failedReceiptWriteStillPreservesOriginalFailureForLeaseRecovery() {
        DmsApprovedBatch b = batch("PREPARING");
        when(acceptance.stage(any(),any(),any(),any(),any(),any())).thenThrow(new DataException("source"));
        when(mapper.finish(any(),any(),any(),any(),anyInt())).thenThrow(new DataException("database"));
        RuntimeException error=assertThrows(DataException.class,()->service.process(b)); assertEquals(1,error.getSuppressed().length);
    }
    DmsApprovedBatch stopBatch() {
        DmsApprovedBatch b=batch("WAITING");b.setAuditType("shixiao_shenqing");invalidation.setEnabled(true);invalidation.setAllBatches(true);
        when(mapper.find("tenant","task","shixiao_shenqing")).thenReturn(b);
        return b;
    }
    @Test void stopTasksUseSharedLeaseAndProcessorOnly() {
        DmsApprovedBatch b=stopBatch();when(disables.isEnabled()).thenReturn(true);
        service.process(b);verify(disables).process(b);
        verify(mapper).finish(eq("tenant"),eq(b.getId()),anyString(),isNull(),eq(0));
        verifyNoInteractions(files,acceptance,activation,business);
    }
    @Test void stopFailureUsesSharedBackoffAndPreservesRowProgress() {
        DmsApprovedBatch b=stopBatch();when(disables.isEnabled()).thenReturn(true);
        doThrow(new DataException("row failed")).when(disables).process(b);
        assertThrows(DataException.class,()->service.process(b));
        verify(mapper).finish(eq("tenant"),eq(b.getId()),anyString(),eq("row failed"),eq(60));
        verify(mapper,never()).fail(any(),any());verifyNoInteractions(files,acceptance,activation,business);
    }
    @Test void disabledStopFeatureDoesNotClaimOrSendStopTask() {
        DmsApprovedBatch b=stopBatch();service.process(b);
        verify(mapper,never()).claim(any(),any(),any(),anyBoolean());verify(disables,never()).process(any());
    }
    @Test void disabledBackoffRetriesExistingDelayedTaskAndLeavesFailureReadyForNextScan() {
        org.springframework.test.util.ReflectionTestUtils.setField(service,"retryBackoffEnabled",false);
        DmsApprovedBatch b = batch("REGISTERING");
        b.setAttempts(7);
        b.setNextAttemptAt(new Date(System.currentTimeMillis() + 3600000));
        when(acceptance.stage(any(),any(),any(),any(),any(),any())).thenThrow(new DataException("source unavailable"));
        assertThrows(DataException.class, () -> service.process(b));
        verify(mapper).claim(eq("tenant"),eq(b.getId()),anyString(),eq(false));
        verify(mapper).finish(eq("tenant"),eq(b.getId()),anyString(),eq("source unavailable"),eq(0));
        verifyNoInteractions(files,activation,business,disables);
    }
    @Test void delayGrowsAndIsBounded() {
        assertEquals(60,DmsApprovedFileRetryService.retryDelay(1)); assertEquals(120,DmsApprovedFileRetryService.retryDelay(2));
        assertEquals(240,DmsApprovedFileRetryService.retryDelay(3)); assertEquals(3600,DmsApprovedFileRetryService.retryDelay(1000));
    }
    @Test void approvedRevocationUsesSharedProcessorWithoutRolloutConfiguration() {
        DmsApprovedBatch b=batch("WAITING");b.setAuditType("chexiao_shenqing");
        when(mapper.find("tenant","task","chexiao_shenqing")).thenReturn(b);
        when(disables.isEnabled()).thenReturn(true);
        service.process(b);
        verify(mapper).claim(eq("tenant"),eq(b.getId()),anyString(),anyBoolean());
        verify(disables).process(b);
        verify(mapper).finish(eq("tenant"),eq(b.getId()),anyString(),isNull(),eq(0));
        verifyNoInteractions(files,acceptance,activation,business);
    }
}