package jnpf.dmsService;
|
|
import jakarta.validation.Validation;
|
import jakarta.validation.ValidatorFactory;
|
import jnpf.base.UserInfo;
|
import jnpf.dmsEntity.storage.approval.*;
|
import jnpf.dmsEntity.storage.form.*;
|
import jnpf.dmsEntity.storage.form.DmsApprovedRegistrationCallback.StoredFile;
|
import jnpf.dmsEntity.storage.vo.DmsFileUploadVO;
|
import jnpf.dmsMapper.*;
|
import jnpf.dmsPermission.DmsCurrentIdentity;
|
import jnpf.dmsService.center.DmsCenterDirectoryService;
|
import jnpf.dmsService.center.DmsCenterQueryService;
|
import jnpf.dmsService.center.DmsCenterRegistrationService;
|
import jnpf.exception.DataException;
|
import jnpf.flowable.WorkFlowApi;
|
import jnpf.flowable.entity.TaskEntity;
|
import org.junit.jupiter.api.*;
|
import java.util.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
import static org.mockito.ArgumentMatchers.*;
|
import static org.mockito.Mockito.*;
|
|
class DmsApprovedFileAcceptanceServiceTest {
|
static ValidatorFactory validation;
|
@BeforeAll static void openValidation() { validation = Validation.buildDefaultValidatorFactory(); }
|
@AfterAll static void closeValidation() { validation.close(); }
|
DmsApprovedAuditSource source = mock(DmsApprovedAuditSource.class);
|
DmsFileUploadService uploads = mock(DmsFileUploadService.class);
|
DmsFileRevisionService revisions = mock(DmsFileRevisionService.class);
|
DmsApprovedBatchMapper receipts = mock(DmsApprovedBatchMapper.class);
|
DmsVersionStateMapper states = mock(DmsVersionStateMapper.class);
|
DmsCurrentIdentity identity = mock(DmsCurrentIdentity.class);
|
DmsCenterDirectoryService directories = mock(DmsCenterDirectoryService.class);
|
DmsCenterRegistrationService center = new DmsCenterRegistrationService(uploads, revisions, states);
|
DmsCenterQueryService centerQueries = mock(DmsCenterQueryService.class);
|
WorkFlowApi workflow = mock(WorkFlowApi.class);
|
DmsQianpiyeRenderSnapshotService qianpiyeSnapshots = mock(DmsQianpiyeRenderSnapshotService.class);
|
DmsApprovedVersionPropertiesService approvedProperties = mock(DmsApprovedVersionPropertiesService.class);
|
DmsVersionPropertiesMapper versionProperties = mock(DmsVersionPropertiesMapper.class);
|
DmsBusinessLifecycleGuard businessOccupancy = mock(DmsBusinessLifecycleGuard.class);
|
DmsApprovedFileAcceptanceService service = new DmsApprovedFileAcceptanceService(
|
source, directories, center, centerQueries, receipts, workflow,
|
validation.getValidator(), qianpiyeSnapshots, approvedProperties, versionProperties, businessOccupancy);
|
Date approved = new Date();
|
UUID batchId = UUID.randomUUID();
|
TaskEntity task = new TaskEntity();
|
DmsApprovedRegistrationForm command = new DmsApprovedRegistrationForm();
|
|
UUID v7() { UUID value=UUID.randomUUID(); return new UUID((value.getMostSignificantBits() & ~0xF000L) | 0x7000L,value.getLeastSignificantBits()); }
|
@BeforeEach void approval() {
|
when(receipts.nextUuidV7()).thenAnswer(i -> v7().toString());
|
command.setSourceId("flow"); command.setTaskId("task"); command.setAuditType("xinzeng_shenhe");
|
task.setId("task"); task.setFlowId("flow"); task.setStatus(2); task.setEndTime(approved);
|
when(workflow.getInfoSubmit("task")).thenReturn(task);
|
UserInfo user = new UserInfo(); user.setUserId("actor");
|
when(identity.requireUser()).thenReturn(user); when(identity.tenantId(user)).thenReturn("tenant");
|
}
|
DmsApprovedBatch arrange(DmsApprovedItem... items) {
|
when(approvedProperties.create(eq("author"), nullable(String.class)))
|
.thenReturn(new com.fasterxml.jackson.databind.ObjectMapper().createObjectNode()
|
.put("prepared_by_name", "制定人"));
|
DmsApprovedBatch b = new DmsApprovedBatch(); b.setId(batchId); b.setStatus("REGISTERING"); b.setTenantId("tenant"); b.setSourceDigest("digest"); b.setItems(Arrays.asList(items));
|
b.setLeaseToken("attempt"); b.setScheduledAt(approved); b.setApprovedAt(approved); b.setActorId("actor");
|
b.setAuditId("audit"); b.setFlowId("flow"); b.setTaskId("task"); b.setAuditType(command.getAuditType());
|
when(source.load("tenant", "flow", "task", command.getAuditType(), "actor", approved))
|
.thenReturn(new DmsApprovedAuditSource.Source(b, "author", approved, false));
|
when(receipts.lock("tenant", batchId)).thenReturn(b);
|
when(receipts.items("tenant", batchId)).thenReturn(Arrays.asList(items));
|
return b;
|
}
|
DmsApprovedItem item(String key, String type) {
|
DmsApprovedItem i = new DmsApprovedItem(); i.setTenantId("tenant");
|
i.setSourceKey("main".equals(key) ? "audit:zhuwenjian" : key); i.setChangeType(type);
|
i.setDocumentNo(key); i.setDocumentName(key); i.setDepartmentId("dept"); i.setFolderId(UUID.randomUUID().toString());
|
i.setVersionLabel("D(00)"); i.setFileJson("[{\"url\":\"/api/file/Image/annex/" + key + ".docx\"}]");
|
i.setShifouShoukong("main".equals(key) ? "yes" : "no");
|
i.setDocumentId(v7()); i.setFileVersionId(v7());
|
if (!"CREATE".equals(type)) i.setBaseFileVersionId(UUID.randomUUID());
|
if ("REUSE".equals(type)) i.setFileVersionId(i.getBaseFileVersionId());
|
return i;
|
}
|
DmsApprovedRegistrationCallback callback(DmsApprovedItem... items) {
|
DmsApprovedRegistrationCallback form = new DmsApprovedRegistrationCallback();
|
form.setTenantId("tenant"); form.setAttemptToken("attempt"); form.setBatchId(batchId); form.setSourceDigest("digest");
|
for (DmsApprovedItem item : items) {
|
StoredFile f = new StoredFile(); f.setSourceKey(item.getSourceKey()); f.setFileJson(item.getFileJson());
|
DmsFileUploadRegisterForm c = new DmsFileUploadRegisterForm();
|
c.setDocumentId(item.getDocumentId() == null ? UUID.randomUUID() : item.getDocumentId());
|
c.setFileVersionId(item.getFileVersionId()); c.setFileObjectId(UUID.randomUUID());
|
c.setChecksumSha256("a".repeat(64)); c.setSizeBytes(12L); c.setFileName("a.docx"); c.setDocumentType("DOCX");
|
c.setStoragePlatform("seaweedfs-1"); c.setBucketName("dms"); c.setObjectPath("dms/path/"); c.setObjectName("a.docx");
|
f.setContent(c); form.getFiles().add(f);
|
}
|
return form;
|
}
|
void successfulRegistration() {
|
when(receipts.registered("tenant", batchId)).thenReturn(1);
|
when(receipts.storeChecksum(eq("tenant"), eq(batchId), any(), any())).thenReturn(1);
|
when(uploads.registerApproved(any(), eq("jnpf-file"), eq("flow"), eq("task"), any(UserInfo.class))).thenAnswer(inv -> {
|
DmsFileUploadRegisterForm req = inv.getArgument(0); return result(req.getDocumentId(), req.getFileVersionId(), req.getChecksumSha256());
|
});
|
when(revisions.registerApproved(any(), eq("jnpf-file"), eq("flow"), eq("task"), any(UserInfo.class))).thenAnswer(inv -> {
|
DmsFileRevisionRegisterForm req = inv.getArgument(0); return result(req.getDocumentId(), req.getFileVersionId(), req.getChecksumSha256());
|
});
|
when(states.approvePending(eq("tenant"), any(), eq(approved), eq(approved), isNull(), eq("actor"))).thenReturn(1);
|
}
|
DmsFileUploadVO result(UUID documentId, UUID versionId, String checksum) {
|
DmsFileUploadVO r = new DmsFileUploadVO(); r.setDocumentId(documentId); r.setFileVersionId(versionId); r.setChecksumSha256(checksum); return r;
|
}
|
DmsApprovedBatch stage() { return service.stage("tenant", "flow", "task", command.getAuditType(), "actor", approved); }
|
@Test void occupancyFailureStopsPreparingBeforeReservedIdsOrItemsAreWritten() {
|
arrange(item("main","CREATE"));
|
doThrow(new DataException("occupancy changed")).when(businessOccupancy).requireAuditSource(any());
|
assertThrows(DataException.class,this::stage);
|
verify(receipts,never()).nextUuidV7();verify(receipts,never()).insertItem(any());
|
verifyNoInteractions(uploads,revisions,states,directories);
|
}
|
@Test void occupancyFailureStopsCallbackBeforeCenterRegistration() {
|
var main=item("main","CREATE");arrange(main);
|
doThrow(new DataException("occupancy changed")).when(businessOccupancy).requireAuditSource(any());
|
assertThrows(DataException.class,()->service.accept(callback(main),"jnpf-file"));
|
verifyNoInteractions(uploads,revisions,states,directories);
|
verify(receipts,never()).registered(any(),any());
|
}
|
@Test void recordsBothProgressTablesBeforeAnyCenterRegistration() {
|
DmsApprovedBatch b = arrange(item("main", "CREATE"), item("record", "CREATE"));
|
assertSame(b, stage());
|
assertEquals("REGISTERING", b.getStatus());
|
verify(receipts).insertBatch(b); verify(receipts, times(2)).insertItem(any());
|
verifyNoInteractions(uploads, revisions, states);
|
}
|
@Test void rejectsUnapprovedWrongTaskWrongFlowAndMissingApprovalDate() {
|
task.setStatus(1); assertThrows(DataException.class, () -> stage());
|
task.setStatus(2); task.setId("other"); assertThrows(DataException.class, () -> stage());
|
task.setId("task"); task.setFlowId("other"); assertThrows(DataException.class, () -> stage());
|
task.setFlowId("flow"); task.setEndTime(null); assertThrows(DataException.class, () -> stage());
|
verifyNoInteractions(source, uploads, revisions, states);
|
}
|
@Test void rejectsChangedControlFlagAgainstPersistedTaskBeforeRegistration() {
|
DmsApprovedItem nativeItem = item("main", "CREATE");
|
arrange(nativeItem);
|
DmsApprovedItem saved = item("main", "CREATE");
|
saved.setDocumentId(nativeItem.getDocumentId()); saved.setFileVersionId(nativeItem.getFileVersionId());
|
saved.setShifouShoukong("no");
|
when(receipts.items("tenant", batchId)).thenReturn(List.of(saved));
|
assertThrows(DataException.class, () -> service.accept(callback(nativeItem), "jnpf-file"));
|
verifyNoInteractions(uploads, revisions, states);
|
}
|
@Test void rejectsReviewAndUntrustedService() {
|
command.setAuditType("fushen"); assertThrows(DataException.class, () -> stage());
|
command.setAuditType("xinzeng_shenhe"); assertThrows(DataException.class, () -> service.accept(callback(), "jnpf-other"));
|
verifyNoInteractions(source);
|
}
|
@Test void confirmsCompleteSetAndUsesNativeAttributes() {
|
DmsApprovedItem main = item("main", "CREATE"), row = item("record", "CREATE"); arrange(main, row);
|
DmsApprovedRegistrationCallback form = callback(main, row);
|
form.getFiles().get(0).getContent().setDocumentName("untrusted name");
|
form.getFiles().get(0).getContent().setExtendedProperties(
|
new com.fasterxml.jackson.databind.ObjectMapper().createObjectNode().put("prepared_by_name", "伪造人员"));
|
form.getFiles().get(0).getContent().setShifouShoukong("no");
|
form.getFiles().get(1).getContent().setShifouShoukong("yes");
|
successfulRegistration();
|
DmsApprovedBatch receipt = service.accept(form, "jnpf-file");
|
assertEquals("WAITING", receipt.getStatus());
|
assertEquals("main", form.getFiles().get(0).getContent().getDocumentName());
|
assertEquals("制定人", form.getFiles().get(0).getContent().getExtendedProperties().path("prepared_by_name").asText());
|
verify(approvedProperties).create("author", null);
|
assertEquals("author", form.getFiles().get(0).getContent().getPreparedBy());
|
assertEquals("yes", form.getFiles().get(0).getContent().getShifouShoukong());
|
assertEquals("no", form.getFiles().get(1).getContent().getShifouShoukong());
|
verify(uploads, times(2)).registerApproved(any(), eq("jnpf-file"), eq("flow"), eq("task"), any(UserInfo.class));
|
verify(states, times(2)).approvePending(eq("tenant"), any(), eq(approved), eq(approved), isNull(), eq("actor"));
|
verify(receipts).registered("tenant", batchId);
|
verify(receipts, times(2)).storeChecksum(eq("tenant"), eq(batchId), any(), any());
|
verify(directories).bind(eq("tenant"), eq(main.getDocumentId()), eq(List.of(row.getDocumentId())));
|
verify(receipts, never()).insertBatch(any()); verify(receipts, never()).insertItem(any());
|
verifyNoInteractions(revisions);
|
}
|
@Test void revisionCanMixUpgradeNewRecordAndReuseWithoutRequiringANewDocumentNumberForUpgrade() {
|
command.setAuditType("xiuding_shenhe");
|
DmsApprovedItem main = item("main", "REVISE"), row = item("record", "CREATE"), reused = item("retained", "REUSE");
|
main.setDocumentNo(null); arrange(main, row, reused); successfulRegistration();
|
DmsApprovedBatch b = service.accept(callback(main, row), "jnpf-file");
|
assertEquals(3, b.getItems().size());
|
verify(revisions).registerApproved(any(), eq("jnpf-file"), eq("flow"), eq("task"), any(UserInfo.class));
|
verify(uploads).registerApproved(any(), eq("jnpf-file"), eq("flow"), eq("task"), any(UserInfo.class));
|
verify(receipts, never()).storeChecksum(eq("tenant"), eq(batchId), eq("retained"), any());
|
}
|
@Test void existingVersionRetryUsesFrozenSnapshotWithoutResolvingCurrentPersonnel() {
|
var main = item("main", "CREATE"); arrange(main); successfulRegistration();
|
var existing = new jnpf.dmsEntity.storage.vo.DmsVersionProperties();
|
existing.setId(main.getFileVersionId());
|
var frozen = new com.fasterxml.jackson.databind.ObjectMapper().createObjectNode().put("prepared_by_name", "登记时姓名");
|
existing.setExtendedProperties(frozen);
|
when(versionProperties.find("tenant", main.getFileVersionId())).thenReturn(existing);
|
var form = callback(main);
|
service.accept(form, "jnpf-file");
|
assertEquals(frozen, form.getFiles().get(0).getContent().getExtendedProperties());
|
verifyNoInteractions(approvedProperties);
|
}
|
|
@Test void allReusedBatchStillHasDurableDateCarrier() {
|
arrange(item("main", "REUSE"));
|
when(receipts.registered("tenant", batchId)).thenReturn(1);
|
DmsApprovedBatch receipt = service.accept(callback(), "jnpf-file");
|
assertNotNull(receipt.getId()); verify(receipts).registered("tenant", batchId);
|
verifyNoInteractions(approvedProperties, versionProperties);
|
verify(businessOccupancy).requireAuditSource(any());
|
verifyNoInteractions(uploads, revisions, states);
|
}
|
@Test void missingExtraDuplicateAndMismatchedFileFailBeforeAnyMetadataWrite() {
|
DmsApprovedItem main = item("main", "CREATE"), row = item("record", "CREATE"); arrange(main, row);
|
assertThrows(DataException.class, () -> service.accept(callback(main), "jnpf-file"));
|
assertThrows(DataException.class, () -> service.accept(callback(main, row, item("extra", "CREATE")), "jnpf-file"));
|
assertThrows(DataException.class, () -> service.accept(callback(main, row, row), "jnpf-file"));
|
DmsApprovedRegistrationCallback changed = callback(main, row); changed.getFiles().get(1).setFileJson("changed");
|
assertThrows(DataException.class, () -> service.accept(changed, "jnpf-file"));
|
verifyNoInteractions(uploads, revisions, states); verify(receipts, never()).insertBatch(any());
|
}
|
@Test void changedSourceOrApprovalDuringStorageCannotBeConfirmed() {
|
DmsApprovedItem main = item("main", "CREATE"); arrange(main);
|
DmsApprovedRegistrationCallback changed = callback(main); changed.setSourceDigest("stale");
|
assertThrows(DataException.class, () -> service.accept(changed, "jnpf-file"));
|
task.setStatus(1); assertThrows(DataException.class, () -> service.accept(callback(main), "jnpf-file"));
|
verifyNoInteractions(uploads, revisions, states);
|
}
|
@Test void validatesAllStorageFactsBeforeWritingFirstItem() {
|
DmsApprovedItem main = item("main", "CREATE"), row = item("record", "CREATE"); arrange(main, row);
|
DmsApprovedRegistrationCallback form = callback(main, row); form.getFiles().get(1).getContent().setChecksumSha256("bad");
|
assertThrows(DataException.class, () -> service.accept(form, "jnpf-file"));
|
verifyNoInteractions(uploads, revisions, states);
|
}
|
@Test void duplicateAcceptedCallbackDoesNotRegisterAgain() {
|
DmsApprovedBatch b = arrange(item("main", "CREATE")); b.setStatus("WAITING");
|
assertSame(b, service.accept(callback(), "jnpf-file"));
|
verifyNoInteractions(uploads, revisions, states); verify(receipts, never()).registered(any(), any());
|
}
|
@Test void registrationRetryReconstructsSourceWithTheSameReservedIds() {
|
DmsApprovedBatch b = arrange(item("main", "CREATE"));
|
when(receipts.find("tenant", "task", "xinzeng_shenhe")).thenReturn(b);
|
assertSame(b, stage());
|
assertEquals(b.getId(), b.getItems().get(0).getBatchId());
|
verifyNoInteractions(uploads, revisions, states); verify(receipts, never()).insertBatch(any());
|
}
|
@Test void appliedReceiptIsIdempotentWithoutReadingChangedBusinessOutput() {
|
DmsApprovedBatch b = arrange(item("main", "CREATE")); b.setStatus("APPLIED");
|
when(receipts.find("tenant", "task", "xinzeng_shenhe")).thenReturn(b);
|
assertSame(b, stage()); assertSame(b, service.accept(callback(), "jnpf-file"));
|
verifyNoInteractions(uploads, revisions, states);
|
DmsApprovedRegistrationCallback altered = callback(); altered.setSourceDigest("other");
|
assertThrows(DataException.class, () -> service.accept(altered, "jnpf-file"));
|
}
|
@Test void callbackCannotCreateRegistrationProgressOrChangeAssignedVersionIds() {
|
assertThrows(DataException.class, () -> service.accept(callback(), "jnpf-file"));
|
DmsApprovedItem i = item("main", "CREATE"); arrange(i);
|
DmsApprovedRegistrationCallback form = callback(i); form.getFiles().get(0).getContent().setFileVersionId(UUID.randomUUID());
|
assertThrows(DataException.class, () -> service.accept(form, "jnpf-file"));
|
verifyNoInteractions(uploads, revisions, states); verify(receipts, never()).insertBatch(any());
|
}
|
@Test void legacyReservationCanOnlyBeRepairedBeforeAnyMetadataExists() {
|
DmsApprovedItem saved = item("main", "CREATE"); saved.setDocumentId(UUID.randomUUID()); saved.setFileVersionId(UUID.randomUUID());
|
UUID oldDocument = saved.getDocumentId(), oldVersion = saved.getFileVersionId();
|
DmsApprovedBatch old = arrange(saved);
|
DmsApprovedItem fresh = item("main","CREATE");
|
DmsApprovedBatch nativeBatch = new DmsApprovedBatch(); nativeBatch.setSourceDigest("digest"); nativeBatch.setScheduledAt(approved); nativeBatch.setItems(List.of(fresh));
|
when(source.load("tenant", "flow", "task", "xinzeng_shenhe", "actor", approved))
|
.thenReturn(new DmsApprovedAuditSource.Source(nativeBatch,"author",approved,false));
|
when(receipts.find("tenant", "task", "xinzeng_shenhe")).thenReturn(old);
|
assertThrows(DataException.class,this::stage);
|
when(receipts.repairReservation(any(),eq(oldDocument),eq(oldVersion))).thenReturn(1);
|
assertEquals(7,stage().getItems().get(0).getDocumentId().version());
|
assertEquals(7,old.getItems().get(0).getFileVersionId().version());
|
verify(receipts,never()).insertBatch(any()); verify(receipts,never()).insertItem(any());
|
}
|
|
@Test void retryRepairsUnregisteredDocumentVersionIdCollision() {
|
DmsApprovedItem saved = item("main", "CREATE"); saved.setFileVersionId(saved.getDocumentId());
|
UUID oldId = saved.getDocumentId(); DmsApprovedBatch old = arrange(saved);
|
DmsApprovedItem fresh = item("main", "CREATE");
|
DmsApprovedBatch nativeBatch = new DmsApprovedBatch(); nativeBatch.setSourceDigest("digest");
|
nativeBatch.setScheduledAt(approved); nativeBatch.setItems(List.of(fresh));
|
when(source.load("tenant", "flow", "task", "xinzeng_shenhe", "actor", approved))
|
.thenReturn(new DmsApprovedAuditSource.Source(nativeBatch,"author",approved,false));
|
when(receipts.find("tenant", "task", "xinzeng_shenhe")).thenReturn(old);
|
when(receipts.repairReservation(any(),eq(oldId),eq(oldId))).thenReturn(1);
|
DmsApprovedItem repaired = stage().getItems().get(0);
|
assertEquals(oldId, repaired.getDocumentId());
|
assertNotEquals(oldId, repaired.getFileVersionId());
|
verify(receipts,never()).insertBatch(any());
|
}
|
|
@Test void staleExecutorOrWrongTenantCannotConfirmFiles() {
|
DmsApprovedItem main = item("main", "CREATE"); arrange(main);
|
DmsApprovedRegistrationCallback request = callback(main); request.setAttemptToken("expired-owner");
|
assertThrows(DataException.class, () -> service.accept(request, "jnpf-file"));
|
request.setAttemptToken("attempt"); request.setTenantId("other-tenant");
|
assertThrows(DataException.class, () -> service.accept(request, "jnpf-file"));
|
verifyNoInteractions(uploads,revisions,states);
|
}
|
@Test void retryRejectsChangedSourceWithoutReplacingReservedIds() {
|
DmsApprovedItem main = item("main", "CREATE"); DmsApprovedBatch old = arrange(main);
|
when(receipts.find("tenant", "task", "xinzeng_shenhe")).thenReturn(old);
|
DmsApprovedBatch changed = new DmsApprovedBatch(); changed.setSourceDigest("different"); changed.setItems(List.of(main));
|
when(source.load("tenant", "flow", "task", "xinzeng_shenhe", "actor", approved))
|
.thenReturn(new DmsApprovedAuditSource.Source(changed,"author",approved,false));
|
assertThrows(DataException.class,this::stage);
|
verify(receipts,never()).insertItem(any()); verify(receipts,never()).prepared(any());
|
}
|
|
@Test void springCommitsDmsProgressBeforePushAndRollsBackAFailedBatchCallback() {
|
class Transactions extends org.springframework.transaction.support.AbstractPlatformTransactionManager {
|
final List<String> events = new ArrayList<>();
|
protected Object doGetTransaction() { return new Object(); }
|
protected void doBegin(Object transaction, org.springframework.transaction.TransactionDefinition definition) { events.add("begin"); }
|
protected void doCommit(org.springframework.transaction.support.DefaultTransactionStatus status) { events.add("commit"); }
|
protected void doRollback(org.springframework.transaction.support.DefaultTransactionStatus status) { events.add("rollback"); }
|
}
|
Transactions transactions = new Transactions();
|
org.springframework.aop.framework.ProxyFactory proxy = new org.springframework.aop.framework.ProxyFactory(service);
|
proxy.addAdvice(new org.springframework.transaction.interceptor.TransactionInterceptor(transactions,
|
new org.springframework.transaction.annotation.AnnotationTransactionAttributeSource()));
|
DmsApprovedFileAcceptanceService transactional = (DmsApprovedFileAcceptanceService) proxy.getProxy();
|
DmsApprovedItem main = item("main", "CREATE"), row = item("record", "CREATE");
|
DmsApprovedBatch b = arrange(main, row);
|
when(source.load("tenant", "flow", "task", "xinzeng_shenhe", "actor", approved)).thenAnswer(inv -> {
|
assertTrue(org.springframework.transaction.support.TransactionSynchronizationManager.isActualTransactionActive());
|
return new DmsApprovedAuditSource.Source(b, "author", approved, false);
|
});
|
transactional.stage("tenant", "flow", "task", "xinzeng_shenhe", "actor", approved);
|
assertEquals(Arrays.asList("begin", "commit"), transactions.events);
|
assertFalse(org.springframework.transaction.support.TransactionSynchronizationManager.isActualTransactionActive(),
|
"DMS must commit registration progress and release source locks before pushing to file");
|
batchId = b.getId();
|
when(receipts.lock("tenant", batchId)).thenReturn(b);
|
when(receipts.items("tenant", batchId)).thenReturn(Arrays.asList(main, row));
|
successfulRegistration();
|
when(states.approvePending(eq("tenant"), any(), eq(approved), eq(approved), isNull(), eq("actor")))
|
.thenReturn(1, 0);
|
assertThrows(DataException.class, () -> transactional.accept(callback(main, row), "jnpf-file"));
|
assertEquals(Arrays.asList("begin", "commit", "begin", "rollback"), transactions.events);
|
verify(receipts).insertBatch(b);
|
verify(receipts, never()).registered(any(), any());
|
assertFalse(org.springframework.transaction.support.TransactionSynchronizationManager.isActualTransactionActive());
|
}
|
|
}
|