package jnpf.dmsService;
|
|
import jnpf.dmsEntity.permission.entity.DmsDocumentEntity;
|
import jnpf.dmsEntity.permission.entity.DmsFileVersionEntity;
|
import jnpf.dmsEntity.storage.form.DmsFileLifecycleUpdateForm;
|
import jnpf.dmsEntity.storage.vo.DmsFileLifecycleUpdateVO;
|
import jnpf.dmsIntegration.audit.DmsVersionStateAuditRow;
|
import jnpf.dmsMapper.DmsDocumentMapper;
|
import jnpf.dmsMapper.DmsFileVersionMapper;
|
import jnpf.dmsMapper.DmsVersionStateMapper;
|
import jnpf.exception.DataException;
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
|
import java.util.Date;
|
import java.util.UUID;
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verifyNoInteractions;
|
import static org.mockito.Mockito.when;
|
|
class DmsFileLifecycleServiceTest {
|
private static final String TENANT = "tenant-1";
|
private static final String SERVICE_ID = "jnpf-file";
|
private static final UUID VERSION = UUID.fromString("018f0000-0000-7000-8000-000000000901");
|
private static final UUID DOCUMENT = UUID.fromString("018f0000-0000-7000-8000-000000000902");
|
private static final Date EFFECTIVE_FROM = new Date(1726329600000L);
|
private static final Date EFFECTIVE_TO = new Date(1757865600000L);
|
|
private final DmsFileVersionMapper versions = mock(DmsFileVersionMapper.class);
|
private final DmsDocumentMapper documents = mock(DmsDocumentMapper.class);
|
private final DmsVersionStateMapper states = mock(DmsVersionStateMapper.class);
|
private final jnpf.dmsService.expiry.DmsFileExpiryProjection projection = mock(jnpf.dmsService.expiry.DmsFileExpiryProjection.class);
|
private final DmsBusinessLifecycleGuard businessOccupancy = mock(DmsBusinessLifecycleGuard.class);
|
private final DmsFileLifecycleService service = new DmsFileLifecycleService(
|
versions, documents, states, projection, businessOccupancy);
|
private DmsFileLifecycleUpdateForm form;
|
private DmsVersionStateAuditRow state;
|
|
@BeforeEach
|
void setUp() {
|
when(businessOccupancy.tryEnterExpiry(TENANT, VERSION)).thenReturn(true);
|
form = new DmsFileLifecycleUpdateForm();
|
form.setTenantId(" tenant-1 ");
|
form.setFileVersionId(VERSION);
|
form.setLifecycleStatus("EXPIRED");
|
form.setEffectiveFrom(EFFECTIVE_FROM);
|
form.setEffectiveTo(EFFECTIVE_TO);
|
form.setStatusReason(" 到期检查确认 ");
|
|
DmsFileVersionEntity version = new DmsFileVersionEntity();
|
version.setDocumentId(DOCUMENT);
|
when(versions.selectByTenantAndId(TENANT, VERSION)).thenReturn(version);
|
when(documents.selectByTenantAndIdForUpdate(TENANT, DOCUMENT))
|
.thenReturn(new DmsDocumentEntity());
|
state = new DmsVersionStateAuditRow();
|
state.setLifecycleStatus("EFFECTIVE");
|
state.setEffectiveFrom(EFFECTIVE_FROM);
|
state.setEffectiveTo(EFFECTIVE_TO);
|
when(states.selectAuditState(TENANT, VERSION)).thenReturn(state);
|
when(states.expire(TENANT, VERSION,
|
"到期检查确认", SERVICE_ID)).thenReturn(1);
|
}
|
|
@Test
|
void occupiedFileWaitsWithoutTakingDocumentLockOrChangingDates() {
|
when(businessOccupancy.tryEnterExpiry(TENANT, VERSION)).thenReturn(false);
|
assertThrows(DataException.class, () -> service.updateExpired(form, SERVICE_ID));
|
verifyNoInteractions(versions, documents, states, projection);
|
}
|
|
@Test
|
void expiresEffectiveFileAndReturnsPersistedValues() {
|
DmsFileLifecycleUpdateVO result = service.updateExpired(form, SERVICE_ID);
|
|
assertEquals(VERSION, result.getFileVersionId());
|
assertEquals("EXPIRED", result.getLifecycleStatus());
|
assertSame(EFFECTIVE_FROM, result.getEffectiveFrom());
|
assertSame(EFFECTIVE_TO, result.getEffectiveTo());
|
verify(documents).selectByTenantAndIdForUpdate(TENANT, DOCUMENT);
|
verify(states).expire(TENANT, VERSION,
|
"到期检查确认", SERVICE_ID);
|
verify(projection).apply(TENANT, VERSION, EFFECTIVE_TO, SERVICE_ID);
|
}
|
|
@Test
|
void sameExpiredRequestIsIdempotent() {
|
state.setLifecycleStatus("EXPIRED");
|
state.setEffectiveTo(EFFECTIVE_TO);
|
|
DmsFileLifecycleUpdateVO result = service.updateExpired(form, SERVICE_ID);
|
|
assertEquals("EXPIRED", result.getLifecycleStatus());
|
verify(projection).apply(TENANT, VERSION, EFFECTIVE_TO, SERVICE_ID);
|
verify(states, never()).expire(TENANT, VERSION,
|
"到期检查确认", SERVICE_ID);
|
}
|
|
@Test
|
void rejectsRetryWithDifferentLifecycleTimes() {
|
state.setLifecycleStatus("EXPIRED");
|
state.setEffectiveTo(new Date(EFFECTIVE_TO.getTime() - 1000L));
|
|
assertThrows(DataException.class, () -> service.updateExpired(form, SERVICE_ID));
|
verify(states, never()).expire(TENANT, VERSION,
|
"到期检查确认", SERVICE_ID);
|
}
|
|
@Test
|
void databaseControlsDueBoundaryAndPreventsProjectionWhenUpdateLosesRace() {
|
when(states.expire(TENANT, VERSION, "到期检查确认", SERVICE_ID)).thenReturn(0);
|
assertThrows(DataException.class, () -> service.updateExpired(form, SERVICE_ID));
|
verifyNoInteractions(projection);
|
}
|
|
@Test
|
void rejectsUnlimitedVersions() {
|
state.setEffectiveTo(null);
|
assertThrows(DataException.class, () -> service.updateExpired(form, SERVICE_ID));
|
verifyNoInteractions(projection);
|
}
|
|
@Test
|
void rejectsNonEffectiveLifecycleState() {
|
state.setLifecycleStatus("SUPERSEDED");
|
|
assertThrows(DataException.class, () -> service.updateExpired(form, SERVICE_ID));
|
verify(states, never()).expire(TENANT, VERSION,
|
"到期检查确认", SERVICE_ID);
|
}
|
|
@Test
|
void acceptsMinimalCommandAndNeverOverwritesStartFromLegacyCaller() {
|
form.setEffectiveFrom(new Date(0));
|
form.setEffectiveTo(null);
|
assertEquals(EFFECTIVE_FROM, service.updateExpired(form, SERVICE_ID).getEffectiveFrom());
|
verify(projection).apply(TENANT, VERSION, EFFECTIVE_TO, SERVICE_ID);
|
}
|
}
|