diff --git a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentConversionService.java b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentConversionService.java index 9c59593..811e541 100644 --- a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentConversionService.java +++ b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentConversionService.java @@ -7,6 +7,7 @@ import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.HexFormat; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -22,6 +23,8 @@ @Service public class DefaultDocumentConversionService implements DocumentConversionService { + private static final HexFormat HEX_FORMAT = HexFormat.of(); + private final ConversionJobRepository repository; private final DocumentValidationService validationService; private final ConversionWorker conversionWorker; @@ -121,12 +124,7 @@ private String contentHash(MultipartFile file) { } byte[] raw = digest.digest(); - StringBuilder hex = new StringBuilder(raw.length * 2); - for (byte b : raw) { - hex.append(String.format("%02x", b)); - } - - return hex.toString(); + return HEX_FORMAT.formatHex(raw); } catch (NoSuchAlgorithmException ex) { throw new IllegalStateException("SHA-256 digest unavailable", ex); } catch (IOException ex) { diff --git a/src/test/java/com/clearfolio/viewer/service/DefaultDocumentConversionServiceTest.java b/src/test/java/com/clearfolio/viewer/service/DefaultDocumentConversionServiceTest.java index 7d07d6b..6de6ca1 100644 --- a/src/test/java/com/clearfolio/viewer/service/DefaultDocumentConversionServiceTest.java +++ b/src/test/java/com/clearfolio/viewer/service/DefaultDocumentConversionServiceTest.java @@ -174,6 +174,34 @@ void returnsDifferentJobIdsForDifferentPayloads() { assertEquals(2, worker.enqueuedCount()); } + @Test + void storesContentHashAsLowercaseSha256Hex() { + ConversionJobRepository repository = new InMemoryConversionJobRepository(); + RecordingConversionWorker worker = new RecordingConversionWorker(); + DocumentConversionService service = new DefaultDocumentConversionService( + repository, + new DefaultDocumentValidationService(new ConversionProperties()), + worker, + new ConversionProperties() + ); + + byte[] payload = "hello-viewer".getBytes(StandardCharsets.UTF_8); + MockMultipartFile file = new MockMultipartFile( + "file", + "contract.docx", + "application/octet-stream", + payload + ); + + UUID jobId = service.submit(file); + + ConversionJob job = repository.findById(jobId).orElseThrow(); + assertEquals( + "6908f2d58e1a44cd5f7fcc1c76a2a14e801e9158779d569466b4471ae4aae75d", + job.getContentHash() + ); + } + @Test void returnsSameJobForConcurrentDuplicatePayloads() throws Exception { ConversionJobRepository repository = new InMemoryConversionJobRepository();