package com.constellio.data.utils.hashing;
import static com.constellio.data.conf.HashingEncoding.BASE32;
import static com.constellio.data.conf.HashingEncoding.BASE64;
import static com.constellio.data.conf.HashingEncoding.BASE64_URL_ENCODED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.io.input.ReaderInputStream;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import com.constellio.data.io.EncodingService;
import com.constellio.data.io.streamFactories.StreamFactory;
import com.constellio.data.utils.Factory;
import com.constellio.sdk.tests.ConstellioTest;
public class HashingServiceTest extends ConstellioTest {
String stringContent = "This a message";
byte[] bytesContent = stringContent.getBytes();
String expectedMD5Hash = "pjOAZQLkFvZxSS1u9mXnDQ==";
String expectedSHA1Hash = "NlWk4A0G3n9XSi43FGoWLwHXq50=";
String stringContentGeneratingSHA1WithSlash = "This a message 40";
String stringContentGeneratingSHA1WithPlus = "This a message 41";
EncodingService encodingService;
HashingService md5HashingService, md5HashingServiceUsingBase32, sha1HashingService, sha1HashingServiceUsingBase32;
HashingService md5HashingServiceUsingUrlEncodedBase64;
HashingService sha1HashingServiceUsingUrlEncodedBase64;
@Before
public void setUp() {
encodingService = new EncodingService();
md5HashingService = spy(HashingService.forMD5(encodingService, BASE64));
sha1HashingService = spy(HashingService.forSHA1(encodingService, BASE64));
md5HashingServiceUsingBase32 = spy(HashingService.forMD5(encodingService, BASE32));
sha1HashingServiceUsingBase32 = spy(HashingService.forSHA1(encodingService, BASE32));
md5HashingServiceUsingUrlEncodedBase64 = spy(HashingService.forMD5(encodingService, BASE64_URL_ENCODED));
sha1HashingServiceUsingUrlEncodedBase64 = spy(HashingService.forSHA1(encodingService, BASE64_URL_ENCODED));
}
@Test
public void whenHashingContentThenReceivedCorrectHash()
throws Exception {
assertThat(md5HashingService.getHashFromString(stringContent)).isEqualTo(expectedMD5Hash);
assertThat(sha1HashingService.getHashFromString(stringContent)).isEqualTo(expectedSHA1Hash);
}
@Test
public void givenBase64URLEnabledWhenHashingThenReplacePlusAndSlashesWithMinusAndUnderlines()
throws Exception {
assertThat(sha1HashingService.getHashFromString(stringContentGeneratingSHA1WithPlus))
.isEqualTo("9Pr0wDV0Dp6K7q+Q9yAPgozZ5Vg=");
assertThat(sha1HashingService.getHashFromString(stringContentGeneratingSHA1WithSlash))
.isEqualTo("8IPj/NxfvfS59bJO6yjLlU/AbSw=");
assertThat(sha1HashingServiceUsingUrlEncodedBase64.getHashFromString(stringContentGeneratingSHA1WithPlus))
.isEqualTo("9Pr0wDV0Dp6K7q-Q9yAPgozZ5Vg=");
assertThat(sha1HashingServiceUsingUrlEncodedBase64.getHashFromString(stringContentGeneratingSHA1WithSlash))
.isEqualTo("8IPj_NxfvfS59bJO6yjLlU_AbSw=");
assertThat(sha1HashingServiceUsingBase32.getHashFromString(stringContentGeneratingSHA1WithPlus))
.isEqualTo("6T5PJQBVOQHJ5CXOV6IPOIAPQKGNTZKY");
assertThat(sha1HashingServiceUsingBase32.getHashFromString(stringContentGeneratingSHA1WithSlash))
.isEqualTo("6CB6H7G4L667JOPVWJHOWKGLSVH4A3JM");
}
@Test
public void whenHashingBytesThenReceivedCorrectHash()
throws Exception {
assertThat(md5HashingService.getHashFromBytes(bytesContent)).isEqualTo(expectedMD5Hash);
assertThat(sha1HashingService.getHashFromBytes(bytesContent)).isEqualTo(expectedSHA1Hash);
}
@Test
public void whenHashingBytesFactoryThenReceivedCorrectHash()
throws Exception {
Factory<byte[]> bytesFactory = new Factory<byte[]>() {
@Override
public byte[] get() {
return bytesContent;
}
};
assertThat(md5HashingService.getHashFromBytes(bytesFactory)).isEqualTo(expectedMD5Hash);
assertThat(sha1HashingService.getHashFromBytes(bytesFactory)).isEqualTo(expectedSHA1Hash);
}
@Test
public void whenHashingReaderThenReceivedCorrectHashAndReaderNotClosed()
throws Exception {
StringReader reader1 = spy(new StringReader(stringContent));
StringReader reader2 = spy(new StringReader(stringContent));
assertThat(md5HashingService.getHashFromReader(reader1)).isEqualTo(expectedMD5Hash);
assertThat(sha1HashingService.getHashFromReader(reader2)).isEqualTo(expectedSHA1Hash);
verify(reader1, never()).close();
verify(reader2, never()).close();
}
@Test
public void whenHashingReaderFactoryThenReceivedCorrectHashAndOpenedReadersClosed()
throws Exception {
final List<Reader> returnReaders = new ArrayList<Reader>();
StreamFactory<Reader> readerFactory = new StreamFactory<Reader>() {
@Override
public Reader create(String name)
throws IOException {
Reader reader = spy(new StringReader(stringContent));
returnReaders.add(reader);
return reader;
}
};
assertThat(md5HashingService.getHashFromReader(readerFactory)).isEqualTo(expectedMD5Hash);
assertThat(sha1HashingService.getHashFromReader(readerFactory)).isEqualTo(expectedSHA1Hash);
verify(returnReaders.get(0)).close();
verify(returnReaders.get(1)).close();
}
@Test
public void givenReadExceptionWhenHashingReaderFactoryThenReceivedCorrectHashAndOpenedReadersClosed()
throws Exception {
final AtomicReference<Reader> readerRef = new AtomicReference<Reader>();
StreamFactory<Reader> readerFactory = new StreamFactory<Reader>() {
@Override
public Reader create(String name)
throws IOException {
Reader reader = spy(new StringReader(stringContent) {
@Override
public int read()
throws IOException {
throw new IOException();
}
@Override
public int read(char[] cbuf)
throws IOException {
throw new IOException();
}
@Override
public int read(char[] cbuf, int off, int len)
throws IOException {
throw new IOException();
}
@Override
public int read(CharBuffer target)
throws IOException {
throw new IOException();
}
});
readerRef.set(reader);
return reader;
}
};
try {
md5HashingService.getHashFromReader(readerFactory);
fail("Exception expected");
} catch (HashingServiceException.CannotReadContent e) {
}
verify(readerRef.get()).close();
}
@Test(expected = HashingServiceException.CannotReadContent.class)
public void givenStreamReaderWhenGetHashFromReaderAndIOExceptionThenHashingServiceExceptionThrown()
throws Exception {
final StreamFactory<Reader> readerFactory = Mockito.mock(StreamFactory.class, "IOExceptionReader");
when(readerFactory.create(SDK_STREAM)).thenThrow(new IOException());
md5HashingService.getHashFromReader(readerFactory);
}
@Test
public void givenHashExceptionWhenHashingReaderFactoryThenReceivedCorrectHashAndOpenedReadersClosed()
throws Exception {
final Reader reader = spy(new StringReader(stringContent));
StreamFactory<Reader> readerFactory = new StreamFactory<Reader>() {
@Override
public Reader create(String name)
throws IOException {
return reader;
}
};
when(md5HashingService.doHash(stringContent.getBytes())).thenThrow(new RuntimeException());
try {
md5HashingService.getHashFromReader(readerFactory);
fail("Exception expected");
} catch (HashingServiceException.CannotHashContent e) {
}
verify(reader).close();
}
@Test
public void whenHashingStreamThenReceivedCorrectHashAndStreamNotClosed()
throws Exception {
InputStream reader1 = spy(new ReaderInputStream(new StringReader(stringContent)));
InputStream reader2 = spy(new ReaderInputStream(new StringReader(stringContent)));
assertThat(md5HashingService.getHashFromStream(reader1)).isEqualTo(expectedMD5Hash);
assertThat(sha1HashingService.getHashFromStream(reader2)).isEqualTo(expectedSHA1Hash);
verify(reader1, never()).close();
verify(reader2, never()).close();
}
@Test
public void whenHashingStreamFactoryThenReceivedCorrectHashAndOpenedStreamsClosed()
throws Exception {
final List<InputStream> returnReaders = new ArrayList<>();
StreamFactory<InputStream> readerFactory = new StreamFactory<InputStream>() {
@Override
public InputStream create(String name)
throws IOException {
InputStream reader = spy(new ReaderInputStream(new StringReader(stringContent)));
returnReaders.add(reader);
return reader;
}
};
assertThat(md5HashingService.getHashFromStream(readerFactory)).isEqualTo(expectedMD5Hash);
assertThat(sha1HashingService.getHashFromStream(readerFactory)).isEqualTo(expectedSHA1Hash);
verify(returnReaders.get(0)).close();
verify(returnReaders.get(1)).close();
}
@Test
public void givenReadExceptionWhenHashingStreamFactoryThenReceivedCorrectHashAndOpenedStreamsClosed()
throws Exception {
final AtomicReference<InputStream> readerRef = new AtomicReference<InputStream>();
StreamFactory<InputStream> readerFactory = new StreamFactory<InputStream>() {
@Override
public InputStream create(String name)
throws IOException {
InputStream reader = spy(new ReaderInputStream(new StringReader(stringContent) {
@Override
public int read()
throws IOException {
throw new IOException();
}
@Override
public int read(char[] cbuf)
throws IOException {
throw new IOException();
}
@Override
public int read(char[] cbuf, int off, int len)
throws IOException {
throw new IOException();
}
@Override
public int read(CharBuffer target)
throws IOException {
throw new IOException();
}
}));
readerRef.set(reader);
return reader;
}
};
try {
md5HashingService.getHashFromStream(readerFactory);
fail("Exception expected");
} catch (HashingServiceException.CannotReadContent e) {
}
verify(readerRef.get()).close();
}
@Test(expected = HashingServiceException.CannotReadContent.class)
public void givenStreamFactoryWhenGetHashFromStreamAndIOExceptionThenHashingServiceExceptionThrown()
throws Exception {
final StreamFactory<InputStream> readerFactory = Mockito.mock(StreamFactory.class, "IOExceptionReader");
doThrow(new IOException()).when(readerFactory).create(any(String.class));
md5HashingService.getHashFromStream(readerFactory);
}
@Test
public void givenHashExceptionWhenHashingStreamFactoryThenReceivedCorrectHashAndOpenedStreamsClosed()
throws Exception {
final InputStream reader = spy(new ReaderInputStream(new StringReader(stringContent)));
StreamFactory<InputStream> readerFactory = new StreamFactory<InputStream>() {
@Override
public InputStream create(String name)
throws IOException {
return reader;
}
};
doThrow(new RuntimeException()).when(md5HashingService).encodeDigest(any(byte[].class));
try {
md5HashingService.getHashFromStream(readerFactory);
fail("Exception expected");
} catch (HashingServiceException.CannotHashContent e) {
}
verify(reader).close();
}
}