feat(audit): instrument DocumentService for METADATA_UPDATED, STATUS_CHANGED, FILE_UPLOADED
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m37s
CI / OCR Service Tests (push) Successful in 40s
CI / Backend Unit Tests (push) Failing after 2m53s
CI / Unit & Component Tests (pull_request) Failing after 2m32s
CI / OCR Service Tests (pull_request) Successful in 28s
CI / Backend Unit Tests (pull_request) Failing after 2m42s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-19 13:40:49 +02:00
parent 9887968236
commit 2deaaf167e
2 changed files with 144 additions and 3 deletions

View File

@@ -6,6 +6,8 @@ import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.raddatz.familienarchiv.audit.AuditKind;
import org.raddatz.familienarchiv.audit.AuditService;
import org.raddatz.familienarchiv.dto.DocumentSearchResult;
import org.raddatz.familienarchiv.dto.DocumentSort;
import org.raddatz.familienarchiv.dto.DocumentUpdateDTO;
@@ -13,6 +15,7 @@ import org.raddatz.familienarchiv.dto.IncompleteDocumentDTO;
import org.raddatz.familienarchiv.dto.MatchOffset;
import org.raddatz.familienarchiv.dto.SearchMatchData;
import org.raddatz.familienarchiv.exception.DomainException;
import org.raddatz.familienarchiv.model.AppUser;
import org.raddatz.familienarchiv.model.Document;
import org.raddatz.familienarchiv.model.DocumentStatus;
import org.raddatz.familienarchiv.model.Person;
@@ -36,6 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
@@ -47,6 +51,8 @@ class DocumentServiceTest {
@Mock TagService tagService;
@Mock DocumentVersionService documentVersionService;
@Mock AnnotationService annotationService;
@Mock AuditService auditService;
@Mock UserService userService;
@InjectMocks DocumentService documentService;
// ─── deleteDocument ───────────────────────────────────────────────────────
@@ -1499,4 +1505,86 @@ class DocumentServiceTest {
assertThatThrownBy(() -> documentService.attachFile(id, file))
.isInstanceOf(DomainException.class);
}
// ─── audit events ─────────────────────────────────────────────────────────
@Test
void updateDocument_logsMetadataUpdated_whenNoStatusChange() throws Exception {
UUID id = UUID.randomUUID();
Document doc = Document.builder().id(id).status(DocumentStatus.UPLOADED).build();
when(documentRepository.findById(id)).thenReturn(Optional.of(doc));
when(documentRepository.save(any())).thenReturn(doc);
documentService.updateDocument(id, new DocumentUpdateDTO(), null);
verify(auditService).log(eq(AuditKind.METADATA_UPDATED), isNull(), eq(id), isNull());
verify(auditService, never()).log(eq(AuditKind.STATUS_CHANGED), any(), any(), any());
}
@Test
void updateDocument_logsStatusChanged_whenFileCausesPlaceholderToUploadedTransition() throws Exception {
UUID id = UUID.randomUUID();
Document doc = Document.builder().id(id).status(DocumentStatus.PLACEHOLDER).build();
when(documentRepository.findById(id)).thenReturn(Optional.of(doc));
when(documentRepository.save(any())).thenAnswer(inv -> {
Document saved = inv.getArgument(0);
saved.setStatus(DocumentStatus.UPLOADED);
return saved;
});
MockMultipartFile file = new MockMultipartFile("file", "doc.pdf", "application/pdf", new byte[]{1});
when(fileService.uploadFile(any(), any())).thenReturn(new FileService.UploadResult("key", "hash"));
documentService.updateDocument(id, new DocumentUpdateDTO(), file);
verify(auditService).log(eq(AuditKind.STATUS_CHANGED), isNull(), eq(id), any());
verify(auditService, never()).log(eq(AuditKind.METADATA_UPDATED), any(), any(), any());
}
@Test
void storeDocument_logsFileUploaded_whenExistingDocumentWasPlaceholder() throws Exception {
String filename = "test.pdf";
Document existing = Document.builder()
.id(UUID.randomUUID()).originalFilename(filename).status(DocumentStatus.PLACEHOLDER).build();
when(documentRepository.findFirstByOriginalFilename(filename)).thenReturn(Optional.of(existing));
when(fileService.uploadFile(any(), any())).thenReturn(new FileService.UploadResult("s3key", "hash"));
when(documentRepository.save(any())).thenReturn(existing);
MockMultipartFile file = new MockMultipartFile("file", filename, "application/pdf", new byte[]{1});
documentService.storeDocument(file);
verify(auditService).log(eq(AuditKind.FILE_UPLOADED), isNull(), eq(existing.getId()), isNull());
}
@Test
void storeDocument_doesNotLogFileUploaded_whenDocumentIsAlreadyUploaded() throws Exception {
String filename = "test.pdf";
Document existing = Document.builder()
.id(UUID.randomUUID()).originalFilename(filename).status(DocumentStatus.UPLOADED).build();
when(documentRepository.findFirstByOriginalFilename(filename)).thenReturn(Optional.of(existing));
when(fileService.uploadFile(any(), any())).thenReturn(new FileService.UploadResult("s3key", "hash"));
when(documentRepository.save(any())).thenReturn(existing);
MockMultipartFile file = new MockMultipartFile("file", filename, "application/pdf", new byte[]{1});
documentService.storeDocument(file);
verify(auditService, never()).log(any(), any(), any(), any());
}
@Test
void attachFile_logsFileUploaded_whenDocumentWasPlaceholder() throws Exception {
UUID id = UUID.randomUUID();
Document doc = Document.builder().id(id).status(DocumentStatus.PLACEHOLDER).build();
when(documentRepository.findById(id)).thenReturn(Optional.of(doc));
when(fileService.uploadFile(any(), any())).thenReturn(new FileService.UploadResult("s3key", "hash"));
when(documentRepository.save(any())).thenAnswer(inv -> {
Document saved = inv.getArgument(0);
saved.setStatus(DocumentStatus.UPLOADED);
return saved;
});
MockMultipartFile file = new MockMultipartFile("file", "brief.pdf", "application/pdf", new byte[]{1});
documentService.attachFile(id, file);
verify(auditService).log(eq(AuditKind.FILE_UPLOADED), isNull(), eq(id), isNull());
}
}