From: David ‘Bombe’ Roden Date: Thu, 1 Aug 2019 14:06:29 +0000 (+0200) Subject: 🎨 Replace FreenetInterfaceTest with Kotlin version X-Git-Tag: v81^2~138 X-Git-Url: https://git.pterodactylus.net/?a=commitdiff_plain;h=7349d8b859f87a83913895fa6897a9a7c8557c4e;p=Sone.git 🎨 Replace FreenetInterfaceTest with Kotlin version --- diff --git a/src/test/java/net/pterodactylus/sone/core/FreenetInterfaceTest.java b/src/test/java/net/pterodactylus/sone/core/FreenetInterfaceTest.java deleted file mode 100644 index 9391bde..0000000 --- a/src/test/java/net/pterodactylus/sone/core/FreenetInterfaceTest.java +++ /dev/null @@ -1,509 +0,0 @@ -package net.pterodactylus.sone.core; - -import static freenet.client.FetchException.FetchExceptionMode.ALL_DATA_NOT_FOUND; -import static freenet.keys.InsertableClientSSK.createRandom; -import static freenet.node.RequestStarter.INTERACTIVE_PRIORITY_CLASS; -import static freenet.node.RequestStarter.PREFETCH_PRIORITY_CLASS; -import static net.pterodactylus.sone.test.Matchers.delivers; -import static net.pterodactylus.sone.test.TestUtil.setFinalField; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.mockito.ArgumentCaptor.forClass; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyShort; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.withSettings; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.util.HashMap; - -import net.pterodactylus.sone.core.FreenetInterface.BackgroundFetchCallback; -import net.pterodactylus.sone.core.FreenetInterface.Callback; -import net.pterodactylus.sone.core.FreenetInterface.InsertToken; -import net.pterodactylus.sone.core.FreenetInterface.InsertTokenSupplier; -import net.pterodactylus.sone.core.event.ImageInsertAbortedEvent; -import net.pterodactylus.sone.core.event.ImageInsertFailedEvent; -import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent; -import net.pterodactylus.sone.core.event.ImageInsertStartedEvent; -import net.pterodactylus.sone.data.Image; -import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.data.TemporaryImage; -import net.pterodactylus.sone.data.impl.ImageImpl; -import net.pterodactylus.sone.test.TestUtil; - -import freenet.client.ClientMetadata; -import freenet.client.FetchContext; -import freenet.client.FetchException; -import freenet.client.FetchException.FetchExceptionMode; -import freenet.client.FetchResult; -import freenet.client.HighLevelSimpleClient; -import freenet.client.InsertBlock; -import freenet.client.InsertContext; -import freenet.client.InsertException; -import freenet.client.InsertException.InsertExceptionMode; -import freenet.client.Metadata; -import freenet.client.async.ClientContext; -import freenet.client.async.ClientGetCallback; -import freenet.client.async.ClientGetter; -import freenet.client.async.ClientPutter; -import freenet.client.async.SnoopMetadata; -import freenet.client.async.USKCallback; -import freenet.client.async.USKManager; -import freenet.crypt.DummyRandomSource; -import freenet.crypt.RandomSource; -import freenet.keys.FreenetURI; -import freenet.keys.InsertableClientSSK; -import freenet.keys.USK; -import freenet.node.Node; -import freenet.node.NodeClientCore; -import freenet.node.RequestClient; -import freenet.support.Base64; -import freenet.support.api.Bucket; -import freenet.support.io.ArrayBucket; -import freenet.support.io.ResumeFailedException; - -import com.google.common.eventbus.EventBus; -import org.hamcrest.*; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatchers; - -/** - * Unit test for {@link FreenetInterface}. - */ -public class FreenetInterfaceTest { - - private final EventBus eventBus = mock(EventBus.class); - private final Node node = mock(Node.class); - private final NodeClientCore nodeClientCore = mock(NodeClientCore.class); - private final HighLevelSimpleClient highLevelSimpleClient = mock(HighLevelSimpleClient.class, withSettings().extraInterfaces(RequestClient.class)); - private final RandomSource randomSource = new DummyRandomSource(); - private final USKManager uskManager = mock(USKManager.class); - private FreenetInterface freenetInterface; - private final Sone sone = mock(Sone.class); - private final ArgumentCaptor callbackCaptor = forClass(USKCallback.class); - private final Image image = mock(Image.class); - private InsertToken insertToken; - private final Bucket bucket = mock(Bucket.class); - private final ArgumentCaptor clientGetCallback = forClass(ClientGetCallback.class); - private final FreenetURI uri = new FreenetURI("KSK@pgl.png"); - private final FetchResult fetchResult = mock(FetchResult.class); - private final BackgroundFetchCallback backgroundFetchCallback = mock(BackgroundFetchCallback.class); - private final ClientGetter clientGetter = mock(ClientGetter.class); - - public FreenetInterfaceTest() throws MalformedURLException { - } - - @Before - public void setupHighLevelSimpleClient() throws Exception { - when(highLevelSimpleClient.getFetchContext()).thenReturn(mock(FetchContext.class)); - when(highLevelSimpleClient.fetch(eq(uri), anyLong(), any(ClientGetCallback.class), any(FetchContext.class), anyShort())).thenReturn( clientGetter); - } - - @Before - public void setupFreenetInterface() { - when(nodeClientCore.makeClient(anyShort(), anyBoolean(), anyBoolean())).thenReturn(highLevelSimpleClient); - setFinalField(node, "clientCore", nodeClientCore); - setFinalField(node, "random", randomSource); - setFinalField(nodeClientCore, "uskManager", uskManager); - setFinalField(nodeClientCore, "clientContext", mock(ClientContext.class)); - freenetInterface = new FreenetInterface(eventBus, node); - insertToken = freenetInterface.new InsertToken(image); - insertToken.setBucket(bucket); - } - - @Before - public void setupSone() { - InsertableClientSSK insertSsk = createRandom(randomSource, "test-0"); - when(sone.getId()).thenReturn(Base64.encode(insertSsk.getURI().getRoutingKey())); - when(sone.getRequestUri()).thenReturn(insertSsk.getURI().uskForSSK()); - } - - @Before - public void setupCallbackCaptorAndUskManager() { - doNothing().when(uskManager).subscribe(any(USK.class), callbackCaptor.capture(), anyBoolean(), any(RequestClient.class)); - } - - @Test - public void canFetchUri() throws MalformedURLException, FetchException { - FreenetURI freenetUri = new FreenetURI("KSK@GPLv3.txt"); - FetchResult fetchResult = createFetchResult(); - when(highLevelSimpleClient.fetch(freenetUri)).thenReturn(fetchResult); - Fetched fetched = freenetInterface.fetchUri(freenetUri); - assertThat(fetched, notNullValue()); - assertThat(fetched.getFetchResult(), is(fetchResult)); - assertThat(fetched.getFreenetUri(), is(freenetUri)); - } - - @Test - public void fetchFollowsRedirect() throws MalformedURLException, FetchException { - FreenetURI freenetUri = new FreenetURI("KSK@GPLv2.txt"); - FreenetURI newFreenetUri = new FreenetURI("KSK@GPLv3.txt"); - FetchResult fetchResult = createFetchResult(); - FetchException fetchException = new FetchException(FetchExceptionMode.PERMANENT_REDIRECT, newFreenetUri); - when(highLevelSimpleClient.fetch(freenetUri)).thenThrow(fetchException); - when(highLevelSimpleClient.fetch(newFreenetUri)).thenReturn(fetchResult); - Fetched fetched = freenetInterface.fetchUri(freenetUri); - assertThat(fetched.getFetchResult(), is(fetchResult)); - assertThat(fetched.getFreenetUri(), is(newFreenetUri)); - } - - @Test - public void fetchReturnsNullOnFetchExceptions() throws MalformedURLException, FetchException { - FreenetURI freenetUri = new FreenetURI("KSK@GPLv2.txt"); - FetchException fetchException = new FetchException(ALL_DATA_NOT_FOUND); - when(highLevelSimpleClient.fetch(freenetUri)).thenThrow(fetchException); - Fetched fetched = freenetInterface.fetchUri(freenetUri); - assertThat(fetched, nullValue()); - } - - private FetchResult createFetchResult() { - ClientMetadata clientMetadata = new ClientMetadata("text/plain"); - Bucket bucket = new ArrayBucket("Some Data.".getBytes()); - return new FetchResult(clientMetadata, bucket); - } - - @Test - public void insertingAnImage() throws SoneException, InsertException, IOException { - TemporaryImage temporaryImage = new TemporaryImage("image-id"); - temporaryImage.setMimeType("image/png"); - byte[] imageData = new byte[] { 1, 2, 3, 4 }; - temporaryImage.setImageData(imageData); - Image image = new ImageImpl("image-id"); - InsertToken insertToken = freenetInterface.new InsertToken(image); - InsertContext insertContext = mock(InsertContext.class); - when(highLevelSimpleClient.getInsertContext(anyBoolean())).thenReturn(insertContext); - ClientPutter clientPutter = mock(ClientPutter.class); - ArgumentCaptor insertBlockCaptor = forClass(InsertBlock.class); - when(highLevelSimpleClient.insert(insertBlockCaptor.capture(), eq((String) null), eq(false), eq(insertContext), eq(insertToken), anyShort())).thenReturn(clientPutter); - freenetInterface.insertImage(temporaryImage, image, insertToken); - assertThat(insertBlockCaptor.getValue().getData().getInputStream(), delivers(new byte[] { 1, 2, 3, 4 })); - assertThat(TestUtil.getPrivateField(insertToken, "clientPutter"), is(clientPutter)); - verify(eventBus).post(any(ImageInsertStartedEvent.class)); - } - - @Test(expected = SoneInsertException.class) - public void insertExceptionCausesASoneException() throws InsertException, SoneException, IOException { - TemporaryImage temporaryImage = new TemporaryImage("image-id"); - temporaryImage.setMimeType("image/png"); - byte[] imageData = new byte[] { 1, 2, 3, 4 }; - temporaryImage.setImageData(imageData); - Image image = new ImageImpl("image-id"); - InsertToken insertToken = freenetInterface.new InsertToken(image); - InsertContext insertContext = mock(InsertContext.class); - when(highLevelSimpleClient.getInsertContext(anyBoolean())).thenReturn(insertContext); - ArgumentCaptor insertBlockCaptor = forClass(InsertBlock.class); - when(highLevelSimpleClient.insert(insertBlockCaptor.capture(), eq((String) null), eq(false), eq(insertContext), eq(insertToken), anyShort())).thenThrow(InsertException.class); - freenetInterface.insertImage(temporaryImage, image, insertToken); - } - - @Test - public void insertingADirectory() throws InsertException, SoneException { - FreenetURI freenetUri = mock(FreenetURI.class); - HashMap manifestEntries = new HashMap<>(); - String defaultFile = "index.html"; - FreenetURI resultingUri = mock(FreenetURI.class); - when(highLevelSimpleClient.insertManifest(eq(freenetUri), eq(manifestEntries), eq(defaultFile))).thenReturn(resultingUri); - assertThat(freenetInterface.insertDirectory(freenetUri, manifestEntries, defaultFile), is(resultingUri)); - } - - @Test(expected = SoneException.class) - public void insertExceptionIsForwardedAsSoneException() throws InsertException, SoneException { - when(highLevelSimpleClient.insertManifest(ArgumentMatchers.any(), ArgumentMatchers.>any(), ArgumentMatchers.any())).thenThrow(InsertException.class); - freenetInterface.insertDirectory(null, null, null); - } - - @Test - public void soneWithWrongRequestUriWillNotBeSubscribed() throws MalformedURLException { - when(sone.getRequestUri()).thenReturn(new FreenetURI("KSK@GPLv3.txt")); - freenetInterface.registerUsk(new FreenetURI("KSK@GPLv3.txt"), null); - verify(uskManager, never()).subscribe(any(USK.class), any(USKCallback.class), anyBoolean(), any(RequestClient.class)); - } - - @Test - public void registeringAUsk() { - FreenetURI freenetUri = createRandom(randomSource, "test-0").getURI().uskForSSK(); - Callback callback = mock(Callback.class); - freenetInterface.registerUsk(freenetUri, callback); - verify(uskManager).subscribe(any(USK.class), any(USKCallback.class), anyBoolean(), any(RequestClient.class)); - } - - @Test - public void registeringANonUskKeyWillNotBeSubscribed() throws MalformedURLException { - FreenetURI freenetUri = new FreenetURI("KSK@GPLv3.txt"); - Callback callback = mock(Callback.class); - freenetInterface.registerUsk(freenetUri, callback); - verify(uskManager, never()).subscribe(any(USK.class), any(USKCallback.class), anyBoolean(), any(RequestClient.class)); - } - - @Test - public void registeringAnActiveUskWillSubscribeToItCorrectly() { - FreenetURI freenetUri = createRandom(randomSource, "test-0").getURI().uskForSSK(); - final USKCallback uskCallback = mock(USKCallback.class); - freenetInterface.registerActiveUsk(freenetUri, uskCallback); - verify(uskManager).subscribe(any(USK.class), eq(uskCallback), eq(true), any(RequestClient.class)); - } - - @Test - public void registeringAnInactiveUskWillSubscribeToItCorrectly() { - FreenetURI freenetUri = createRandom(randomSource, "test-0").getURI().uskForSSK(); - final USKCallback uskCallback = mock(USKCallback.class); - freenetInterface.registerPassiveUsk(freenetUri, uskCallback); - verify(uskManager).subscribe(any(USK.class), eq(uskCallback), eq(false), any(RequestClient.class)); - } - - @Test - public void registeringAnActiveNonUskWillNotSubscribeToAUsk() - throws MalformedURLException { - FreenetURI freenetUri = createRandom(randomSource, "test-0").getURI(); - freenetInterface.registerActiveUsk(freenetUri, null); - verify(uskManager, never()).subscribe(any(USK.class), any(USKCallback.class), anyBoolean(), any(RequestClient.class)); - } - - @Test - public void registeringAnInactiveNonUskWillNotSubscribeToAUsk() - throws MalformedURLException { - FreenetURI freenetUri = createRandom(randomSource, "test-0").getURI(); - freenetInterface.registerPassiveUsk(freenetUri, null); - verify(uskManager, never()).subscribe(any(USK.class), any(USKCallback.class), anyBoolean(), any(RequestClient.class)); - } - - @Test - public void unregisteringANotRegisteredUskDoesNothing() { - FreenetURI freenetURI = createRandom(randomSource, "test-0").getURI().uskForSSK(); - freenetInterface.unregisterUsk(freenetURI); - verify(uskManager, never()).unsubscribe(any(USK.class), any(USKCallback.class)); - } - - @Test - public void unregisteringARegisteredUsk() { - FreenetURI freenetURI = createRandom(randomSource, "test-0").getURI().uskForSSK(); - Callback callback = mock(Callback.class); - freenetInterface.registerUsk(freenetURI, callback); - freenetInterface.unregisterUsk(freenetURI); - verify(uskManager).unsubscribe(any(USK.class), any(USKCallback.class)); - } - - @Test - public void unregisteringANotRegisteredSoneDoesNothing() { - freenetInterface.unregisterUsk(sone); - verify(uskManager, never()).unsubscribe(any(USK.class), any(USKCallback.class)); - } - - @Test - public void unregisteringARegisteredSoneUnregistersTheSone() - throws MalformedURLException { - freenetInterface.registerActiveUsk(sone.getRequestUri(), mock(USKCallback.class)); - freenetInterface.unregisterUsk(sone); - verify(uskManager).unsubscribe(any(USK.class), any(USKCallback.class)); - } - - @Test - public void unregisteringASoneWithAWrongRequestKeyWillNotUnsubscribe() throws MalformedURLException { - when(sone.getRequestUri()).thenReturn(new FreenetURI("KSK@GPLv3.txt")); - freenetInterface.registerUsk(sone.getRequestUri(), null); - freenetInterface.unregisterUsk(sone); - verify(uskManager, never()).unsubscribe(any(USK.class), any(USKCallback.class)); - } - - @Test - public void callbackForNormalUskUsesDifferentPriorities() { - Callback callback = mock(Callback.class); - FreenetURI soneUri = createRandom(randomSource, "test-0").getURI().uskForSSK(); - freenetInterface.registerUsk(soneUri, callback); - assertThat(callbackCaptor.getValue().getPollingPriorityNormal(), is(PREFETCH_PRIORITY_CLASS)); - assertThat(callbackCaptor.getValue().getPollingPriorityProgress(), is(INTERACTIVE_PRIORITY_CLASS)); - } - - @Test - public void callbackForNormalUskForwardsImportantParameters() throws MalformedURLException { - Callback callback = mock(Callback.class); - FreenetURI uri = createRandom(randomSource, "test-0").getURI().uskForSSK(); - freenetInterface.registerUsk(uri, callback); - USK key = mock(USK.class); - when(key.getURI()).thenReturn(uri); - callbackCaptor.getValue().onFoundEdition(3, key, null, false, (short) 0, null, true, true); - verify(callback).editionFound(eq(uri), eq(3L), eq(true), eq(true)); - } - - @Test - public void fetchedRetainsUriAndFetchResult() { - FreenetURI freenetUri = mock(FreenetURI.class); - FetchResult fetchResult = mock(FetchResult.class); - Fetched fetched = new Fetched(freenetUri, fetchResult); - assertThat(fetched.getFreenetUri(), is(freenetUri)); - assertThat(fetched.getFetchResult(), is(fetchResult)); - } - - @Test - public void cancellingAnInsertWillFireImageInsertAbortedEvent() { - ClientPutter clientPutter = mock(ClientPutter.class); - insertToken.setClientPutter(clientPutter); - ArgumentCaptor imageInsertStartedEvent = forClass(ImageInsertStartedEvent.class); - verify(eventBus).post(imageInsertStartedEvent.capture()); - assertThat(imageInsertStartedEvent.getValue().getImage(), is(image)); - insertToken.cancel(); - ArgumentCaptor imageInsertAbortedEvent = forClass(ImageInsertAbortedEvent.class); - verify(eventBus, times(2)).post(imageInsertAbortedEvent.capture()); - verify(bucket).free(); - assertThat(imageInsertAbortedEvent.getValue().getImage(), is(image)); - } - - @Test - public void failureWithoutExceptionSendsFailedEvent() { - InsertException insertException = new InsertException(mock(InsertException.class)); - insertToken.onFailure(insertException, null); - ArgumentCaptor imageInsertFailedEvent = forClass(ImageInsertFailedEvent.class); - verify(eventBus).post(imageInsertFailedEvent.capture()); - verify(bucket).free(); - assertThat(imageInsertFailedEvent.getValue().getImage(), is(image)); - assertThat(imageInsertFailedEvent.getValue().getCause(), Matchers.is(insertException)); - } - - @Test - public void failureSendsFailedEventWithException() { - InsertException insertException = new InsertException(InsertExceptionMode.INTERNAL_ERROR, "Internal error", null); - insertToken.onFailure(insertException, null); - ArgumentCaptor imageInsertFailedEvent = forClass(ImageInsertFailedEvent.class); - verify(eventBus).post(imageInsertFailedEvent.capture()); - verify(bucket).free(); - assertThat(imageInsertFailedEvent.getValue().getImage(), is(image)); - assertThat(imageInsertFailedEvent.getValue().getCause(), is((Throwable) insertException)); - } - - @Test - public void failureBecauseCancelledByUserSendsAbortedEvent() { - InsertException insertException = new InsertException(InsertExceptionMode.CANCELLED, null); - insertToken.onFailure(insertException, null); - ArgumentCaptor imageInsertAbortedEvent = forClass(ImageInsertAbortedEvent.class); - verify(eventBus).post(imageInsertAbortedEvent.capture()); - verify(bucket).free(); - assertThat(imageInsertAbortedEvent.getValue().getImage(), is(image)); - } - - @Test - public void ignoredMethodsDoNotThrowExceptions() throws ResumeFailedException { - insertToken.onResume(null); - insertToken.onFetchable(null); - insertToken.onGeneratedMetadata(null, null); - } - - @Test - public void generatedUriIsPostedOnSuccess() { - FreenetURI generatedUri = mock(FreenetURI.class); - insertToken.onGeneratedURI(generatedUri, null); - insertToken.onSuccess(null); - ArgumentCaptor imageInsertFinishedEvent = forClass(ImageInsertFinishedEvent.class); - verify(eventBus).post(imageInsertFinishedEvent.capture()); - verify(bucket).free(); - assertThat(imageInsertFinishedEvent.getValue().getImage(), is(image)); - assertThat(imageInsertFinishedEvent.getValue().getResultingUri(), is(generatedUri)); - } - - @Test - public void insertTokenSupplierSuppliesInsertTokens() { - InsertTokenSupplier insertTokenSupplier = new InsertTokenSupplier(freenetInterface); - assertThat(insertTokenSupplier.apply(image), notNullValue()); - } - - @Test - public void backgroundFetchCanBeStarted() throws Exception { - freenetInterface.startFetch(uri, backgroundFetchCallback); - verify(highLevelSimpleClient).fetch(eq(uri), anyLong(), any(ClientGetCallback.class), any(FetchContext.class), anyShort()); - } - - @Test - public void backgroundFetchRegistersSnoopAndRestartsTheRequest() throws Exception { - freenetInterface.startFetch(uri, backgroundFetchCallback); - verify(clientGetter).setMetaSnoop(any(SnoopMetadata.class)); - verify(clientGetter).restart(eq(uri), anyBoolean(), any(ClientContext.class)); - } - - @Test - public void requestIsNotCancelledForImageMimeType() { - verifySnoopCancelsRequestForMimeType("image/png", false); - verify(backgroundFetchCallback, never()).failed(uri); - } - - @Test - public void requestIsCancelledForNullMimeType() { - verifySnoopCancelsRequestForMimeType(null, true); - verify(backgroundFetchCallback, never()).shouldCancel(eq(uri), ArgumentMatchers.any(), anyLong()); - verify(backgroundFetchCallback).failed(uri); - } - - @Test - public void requestIsCancelledForVideoMimeType() { - verifySnoopCancelsRequestForMimeType("video/mkv", true); - verify(backgroundFetchCallback).failed(uri); - } - - @Test - public void requestIsCancelledForAudioMimeType() { - verifySnoopCancelsRequestForMimeType("audio/mpeg", true); - verify(backgroundFetchCallback).failed(uri); - } - - @Test - public void requestIsCancelledForTextMimeType() { - verifySnoopCancelsRequestForMimeType("text/plain", true); - verify(backgroundFetchCallback).failed(uri); - } - - private void verifySnoopCancelsRequestForMimeType(String mimeType, boolean cancel) { - when(backgroundFetchCallback.shouldCancel(eq(uri), eq(mimeType), anyLong())).thenReturn(cancel); - freenetInterface.startFetch(uri, backgroundFetchCallback); - ArgumentCaptor snoopMetadata = forClass(SnoopMetadata.class); - verify(clientGetter).setMetaSnoop(snoopMetadata.capture()); - Metadata metadata = mock(Metadata.class); - when(metadata.getMIMEType()).thenReturn(mimeType); - assertThat(snoopMetadata.getValue().snoopMetadata(metadata, mock(ClientContext.class)), is(cancel)); - } - - @Test - public void callbackOfBackgroundFetchIsNotifiedOnSuccess() throws Exception { - freenetInterface.startFetch(uri, backgroundFetchCallback); - verify(highLevelSimpleClient).fetch(eq(uri), anyLong(), clientGetCallback.capture(), any(FetchContext.class), anyShort()); - when(fetchResult.getMimeType()).thenReturn("image/png"); - when(fetchResult.asByteArray()).thenReturn(new byte[] { 1, 2, 3, 4, 5 }); - clientGetCallback.getValue().onSuccess(fetchResult, mock(ClientGetter.class)); - verify(backgroundFetchCallback).loaded(uri, "image/png", new byte[] { 1, 2, 3, 4, 5 }); - verifyNoMoreInteractions(backgroundFetchCallback); - } - - @Test - public void callbackOfBackgroundFetchIsNotifiedOnFailure() throws Exception { - freenetInterface.startFetch(uri, backgroundFetchCallback); - verify(highLevelSimpleClient).fetch(eq(uri), anyLong(), clientGetCallback.capture(), any(FetchContext.class), anyShort()); - when(fetchResult.getMimeType()).thenReturn("image/png"); - when(fetchResult.asByteArray()).thenReturn(new byte[] { 1, 2, 3, 4, 5 }); - clientGetCallback.getValue().onFailure(new FetchException(ALL_DATA_NOT_FOUND), mock(ClientGetter.class)); - verify(backgroundFetchCallback).failed(uri); - verifyNoMoreInteractions(backgroundFetchCallback); - } - - @Test - public void callbackOfBackgroundFetchIsNotifiedAsFailureIfBucketCanNotBeLoaded() throws Exception { - freenetInterface.startFetch(uri, backgroundFetchCallback); - verify(highLevelSimpleClient).fetch(eq(uri), anyLong(), clientGetCallback.capture(), any(FetchContext.class), anyShort()); - when(fetchResult.getMimeType()).thenReturn("image/png"); - when(fetchResult.asByteArray()).thenThrow(IOException.class); - clientGetCallback.getValue().onSuccess(fetchResult, mock(ClientGetter.class)); - verify(backgroundFetchCallback).failed(uri); - verifyNoMoreInteractions(backgroundFetchCallback); - } - -} diff --git a/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt new file mode 100644 index 0000000..1893acc --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt @@ -0,0 +1,461 @@ +package net.pterodactylus.sone.core + +import com.google.common.eventbus.* +import freenet.client.* +import freenet.client.FetchException.FetchExceptionMode.* +import freenet.client.InsertException.* +import freenet.client.async.* +import freenet.crypt.* +import freenet.keys.* +import freenet.keys.InsertableClientSSK.* +import freenet.node.* +import freenet.node.RequestStarter.* +import freenet.support.Base64 +import freenet.support.api.* +import freenet.support.io.* +import net.pterodactylus.sone.core.FreenetInterface.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.data.impl.* +import net.pterodactylus.sone.test.* +import net.pterodactylus.sone.test.Matchers.* +import net.pterodactylus.sone.test.TestUtil.* +import org.hamcrest.MatcherAssert.* +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.notNullValue +import org.hamcrest.Matchers.nullValue +import org.junit.* +import org.junit.rules.* +import org.mockito.* +import org.mockito.ArgumentCaptor.* +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mockito.* +import java.io.* +import java.util.* + +/** + * Unit test for [FreenetInterface]. + */ +class FreenetInterfaceTest { + + @Rule + @JvmField + val expectionException: ExpectedException = ExpectedException.none() + + @Suppress("UnstableApiUsage") + private val eventBus = mock() + private val node = mock() + private val nodeClientCore = mock() + private val highLevelSimpleClient: HighLevelSimpleClient = mock(HighLevelSimpleClient::class.java, withSettings().extraInterfaces(RequestClient::class.java)) + private val randomSource = DummyRandomSource() + private val uskManager = mock() + private val sone = mock() + private val callbackCaptor: ArgumentCaptor = forClass(USKCallback::class.java) + private val image = mock() + private val insertToken: InsertToken + private val bucket = mock() + private val clientGetCallback: ArgumentCaptor = forClass(ClientGetCallback::class.java) + private val uri = FreenetURI("KSK@pgl.png") + private val fetchResult = mock() + private val backgroundFetchCallback = mock() + private val clientGetter = mock() + private val freenetInterface: FreenetInterface + + init { + whenever(nodeClientCore.makeClient(anyShort(), anyBoolean(), anyBoolean())).thenReturn(highLevelSimpleClient) + setField(node, "clientCore", nodeClientCore) + setField(node, "random", randomSource) + setField(nodeClientCore, "uskManager", uskManager) + setField(nodeClientCore, "clientContext", mock()) + freenetInterface = FreenetInterface(eventBus, node) + insertToken = freenetInterface.InsertToken(image) + insertToken.setBucket(bucket) + } + + @Before + fun setupHighLevelSimpleClient() { + whenever(highLevelSimpleClient.fetchContext).thenReturn(mock()) + whenever(highLevelSimpleClient.fetch(eq(uri), anyLong(), any(ClientGetCallback::class.java), any(FetchContext::class.java), anyShort())).thenReturn(clientGetter) + } + + @Before + fun setupSone() { + val insertSsk = createRandom(randomSource, "test-0") + whenever(sone.id).thenReturn(Base64.encode(insertSsk.uri.routingKey)) + whenever(sone.requestUri).thenReturn(insertSsk.uri.uskForSSK()) + } + + @Before + fun setupCallbackCaptorAndUskManager() { + doNothing().`when`(uskManager).subscribe(any(USK::class.java), callbackCaptor.capture(), anyBoolean(), any(RequestClient::class.java)) + } + + @Test + fun `can fetch uri`() { + val freenetUri = FreenetURI("KSK@GPLv3.txt") + val fetchResult = createFetchResult() + whenever(highLevelSimpleClient.fetch(freenetUri)).thenReturn(fetchResult) + val fetched = freenetInterface.fetchUri(freenetUri) + assertThat(fetched, notNullValue()) + assertThat(fetched!!.fetchResult, equalTo(fetchResult)) + assertThat(fetched.freenetUri, equalTo(freenetUri)) + } + + @Test + fun `fetch follows redirect`() { + val freenetUri = FreenetURI("KSK@GPLv2.txt") + val newFreenetUri = FreenetURI("KSK@GPLv3.txt") + val fetchResult = createFetchResult() + val fetchException = FetchException(PERMANENT_REDIRECT, newFreenetUri) + whenever(highLevelSimpleClient.fetch(freenetUri)).thenThrow(fetchException) + whenever(highLevelSimpleClient.fetch(newFreenetUri)).thenReturn(fetchResult) + val fetched = freenetInterface.fetchUri(freenetUri) + assertThat(fetched!!.fetchResult, equalTo(fetchResult)) + assertThat(fetched.freenetUri, equalTo(newFreenetUri)) + } + + @Test + fun `fetch returns null on fetch exceptions`() { + val freenetUri = FreenetURI("KSK@GPLv2.txt") + val fetchException = FetchException(ALL_DATA_NOT_FOUND) + whenever(highLevelSimpleClient.fetch(freenetUri)).thenThrow(fetchException) + val fetched = freenetInterface.fetchUri(freenetUri) + assertThat(fetched, nullValue()) + } + + private fun createFetchResult(): FetchResult { + val clientMetadata = ClientMetadata("text/plain") + val bucket = ArrayBucket("Some Data.".toByteArray()) + return FetchResult(clientMetadata, bucket) + } + + @Test + fun `inserting an image`() { + val temporaryImage = TemporaryImage("image-id") + temporaryImage.mimeType = "image/png" + val imageData = byteArrayOf(1, 2, 3, 4) + temporaryImage.imageData = imageData + val image = ImageImpl("image-id") + val insertToken = freenetInterface.InsertToken(image) + val insertContext = mock() + whenever(highLevelSimpleClient.getInsertContext(anyBoolean())).thenReturn(insertContext) + val clientPutter = mock() + val insertBlockCaptor = forClass(InsertBlock::class.java) + whenever(highLevelSimpleClient.insert(insertBlockCaptor.capture(), eq(null as String?), eq(false), eq(insertContext), eq(insertToken), anyShort())).thenReturn(clientPutter) + freenetInterface.insertImage(temporaryImage, image, insertToken) + assertThat(insertBlockCaptor.value.data.inputStream, delivers(byteArrayOf(1, 2, 3, 4))) + assertThat(getPrivateField(insertToken, "clientPutter"), equalTo(clientPutter)) + verify(eventBus).post(any(ImageInsertStartedEvent::class.java)) + } + + @Test + fun `insert exception causes a sone exception`() { + val temporaryImage = TemporaryImage("image-id") + temporaryImage.mimeType = "image/png" + val imageData = byteArrayOf(1, 2, 3, 4) + temporaryImage.imageData = imageData + val image = ImageImpl("image-id") + val insertToken = freenetInterface.InsertToken(image) + val insertContext = mock() + whenever(highLevelSimpleClient.getInsertContext(anyBoolean())).thenReturn(insertContext) + val insertBlockCaptor = forClass(InsertBlock::class.java) + whenever(highLevelSimpleClient.insert(insertBlockCaptor.capture(), eq(null as String?), eq(false), eq(insertContext), eq(insertToken), anyShort())).thenThrow(InsertException::class.java) + expectionException.expect(SoneInsertException::class.java) + freenetInterface.insertImage(temporaryImage, image, insertToken) + } + + @Test + fun `inserting a directory`() { + val freenetUri = mock() + val manifestEntries = HashMap() + val defaultFile = "index.html" + val resultingUri = mock() + whenever(highLevelSimpleClient.insertManifest(eq(freenetUri), eq(manifestEntries), eq(defaultFile))).thenReturn(resultingUri) + assertThat(freenetInterface.insertDirectory(freenetUri, manifestEntries, defaultFile), equalTo(resultingUri)) + } + + @Test + fun `insert exception is forwarded as sone exception`() { + whenever(highLevelSimpleClient.insertManifest(any(), any(), any())).thenThrow(InsertException::class.java) + expectionException.expect(SoneException::class.java) + freenetInterface.insertDirectory(null, null, null) + } + + @Test + fun `sone with wrong request uri will not be subscribed`() { + whenever(sone.requestUri).thenReturn(FreenetURI("KSK@GPLv3.txt")) + freenetInterface.registerUsk(FreenetURI("KSK@GPLv3.txt"), null) + verify(uskManager, never()).subscribe(any(USK::class.java), any(USKCallback::class.java), anyBoolean(), any(RequestClient::class.java)) + } + + @Test + fun `registering a usk`() { + val freenetUri = createRandom(randomSource, "test-0").uri.uskForSSK() + val callback = mock() + freenetInterface.registerUsk(freenetUri, callback) + verify(uskManager).subscribe(any(USK::class.java), any(USKCallback::class.java), anyBoolean(), any(RequestClient::class.java)) + } + + @Test + fun `registering a non-usk key will not be subscribed`() { + val freenetUri = FreenetURI("KSK@GPLv3.txt") + val callback = mock() + freenetInterface.registerUsk(freenetUri, callback) + verify(uskManager, never()).subscribe(any(USK::class.java), any(USKCallback::class.java), anyBoolean(), any(RequestClient::class.java)) + } + + @Test + fun `registering an active usk will subscribe to it correctly`() { + val freenetUri = createRandom(randomSource, "test-0").uri.uskForSSK() + val uskCallback = mock() + freenetInterface.registerActiveUsk(freenetUri, uskCallback) + verify(uskManager).subscribe(any(USK::class.java), eq(uskCallback), eq(true), any(RequestClient::class.java)) + } + + @Test + fun `registering an inactive usk will subscribe to it correctly`() { + val freenetUri = createRandom(randomSource, "test-0").uri.uskForSSK() + val uskCallback = mock() + freenetInterface.registerPassiveUsk(freenetUri, uskCallback) + verify(uskManager).subscribe(any(USK::class.java), eq(uskCallback), eq(false), any(RequestClient::class.java)) + } + + @Test + fun `registering an active non-usk will not subscribe to a usk`() { + val freenetUri = createRandom(randomSource, "test-0").uri + freenetInterface.registerActiveUsk(freenetUri, null) + verify(uskManager, never()).subscribe(any(USK::class.java), any(USKCallback::class.java), anyBoolean(), any(RequestClient::class.java)) + } + + @Test + fun `registering an inactive non-usk will not subscribe to a usk`() { + val freenetUri = createRandom(randomSource, "test-0").uri + freenetInterface.registerPassiveUsk(freenetUri, null) + verify(uskManager, never()).subscribe(any(USK::class.java), any(USKCallback::class.java), anyBoolean(), any(RequestClient::class.java)) + } + + @Test + fun `unregistering a not registered usk does nothing`() { + val freenetURI = createRandom(randomSource, "test-0").uri.uskForSSK() + freenetInterface.unregisterUsk(freenetURI) + verify(uskManager, never()).unsubscribe(any(USK::class.java), any(USKCallback::class.java)) + } + + @Test + fun `unregistering a registered usk`() { + val freenetURI = createRandom(randomSource, "test-0").uri.uskForSSK() + val callback = mock() + freenetInterface.registerUsk(freenetURI, callback) + freenetInterface.unregisterUsk(freenetURI) + verify(uskManager).unsubscribe(any(USK::class.java), any(USKCallback::class.java)) + } + + @Test + fun `unregistering a not registered sone does nothing`() { + freenetInterface.unregisterUsk(sone) + verify(uskManager, never()).unsubscribe(any(USK::class.java), any(USKCallback::class.java)) + } + + @Test + fun `unregistering aregistered sone unregisters the sone`() { + freenetInterface.registerActiveUsk(sone.requestUri, mock()) + freenetInterface.unregisterUsk(sone) + verify(uskManager).unsubscribe(any(USK::class.java), any(USKCallback::class.java)) + } + + @Test + fun `unregistering asone with awrong request key will not unsubscribe`() { + whenever(sone.requestUri).thenReturn(FreenetURI("KSK@GPLv3.txt")) + freenetInterface.registerUsk(sone.requestUri, null) + freenetInterface.unregisterUsk(sone) + verify(uskManager, never()).unsubscribe(any(USK::class.java), any(USKCallback::class.java)) + } + + @Test + fun `callback for normal usk uses different priorities`() { + val callback = mock() + val soneUri = createRandom(randomSource, "test-0").uri.uskForSSK() + freenetInterface.registerUsk(soneUri, callback) + assertThat(callbackCaptor.value.pollingPriorityNormal, equalTo(PREFETCH_PRIORITY_CLASS)) + assertThat(callbackCaptor.value.pollingPriorityProgress, equalTo(INTERACTIVE_PRIORITY_CLASS)) + } + + @Test + fun `callback for normal usk forwards important parameters`() { + val callback = mock() + val uri = createRandom(randomSource, "test-0").uri.uskForSSK() + freenetInterface.registerUsk(uri, callback) + val key = mock() + whenever(key.uri).thenReturn(uri) + callbackCaptor.value.onFoundEdition(3, key, null, false, 0.toShort(), null, true, true) + verify(callback).editionFound(eq(uri), eq(3L), eq(true), eq(true)) + } + + @Test + fun `fetched retains uri and fetch result`() { + val freenetUri = mock() + val fetchResult = mock() + val (freenetUri1, fetchResult1) = Fetched(freenetUri, fetchResult) + assertThat(freenetUri1, equalTo(freenetUri)) + assertThat(fetchResult1, equalTo(fetchResult)) + } + + @Test + fun `cancelling an insert will fire image insert aborted event`() { + val clientPutter = mock() + insertToken.setClientPutter(clientPutter) + val imageInsertStartedEvent = forClass(ImageInsertStartedEvent::class.java) + verify(eventBus).post(imageInsertStartedEvent.capture()) + assertThat(imageInsertStartedEvent.value.image, equalTo(image)) + insertToken.cancel() + val imageInsertAbortedEvent = forClass(ImageInsertAbortedEvent::class.java) + verify(eventBus, times(2)).post(imageInsertAbortedEvent.capture()) + verify(bucket).free() + assertThat(imageInsertAbortedEvent.value.image, equalTo(image)) + } + + @Test + fun `failure without exception sends failed event`() { + val insertException = InsertException(mock()) + insertToken.onFailure(insertException, null) + val imageInsertFailedEvent = forClass(ImageInsertFailedEvent::class.java) + verify(eventBus).post(imageInsertFailedEvent.capture()) + verify(bucket).free() + assertThat(imageInsertFailedEvent.value.image, equalTo(image)) + assertThat(imageInsertFailedEvent.value.cause, equalTo(insertException)) + } + + @Test + fun `failure sends failed event with exception`() { + val insertException = InsertException(InsertExceptionMode.INTERNAL_ERROR, "Internal error", null) + insertToken.onFailure(insertException, null) + val imageInsertFailedEvent = forClass(ImageInsertFailedEvent::class.java) + verify(eventBus).post(imageInsertFailedEvent.capture()) + verify(bucket).free() + assertThat(imageInsertFailedEvent.value.image, equalTo(image)) + assertThat(imageInsertFailedEvent.value.cause, equalTo(insertException as Throwable)) + } + + @Test + fun `failure because cancelled by user sends aborted event`() { + val insertException = InsertException(InsertExceptionMode.CANCELLED, null) + insertToken.onFailure(insertException, null) + val imageInsertAbortedEvent = forClass(ImageInsertAbortedEvent::class.java) + verify(eventBus).post(imageInsertAbortedEvent.capture()) + verify(bucket).free() + assertThat(imageInsertAbortedEvent.value.image, equalTo(image)) + } + + @Test + fun `ignored methods do not throw exceptions`() { + insertToken.onResume(null) + insertToken.onFetchable(null) + insertToken.onGeneratedMetadata(null, null) + } + + @Test + fun `generated uri is posted on success`() { + val generatedUri = mock() + insertToken.onGeneratedURI(generatedUri, null) + insertToken.onSuccess(null) + val imageInsertFinishedEvent = forClass(ImageInsertFinishedEvent::class.java) + verify(eventBus).post(imageInsertFinishedEvent.capture()) + verify(bucket).free() + assertThat(imageInsertFinishedEvent.value.image, equalTo(image)) + assertThat(imageInsertFinishedEvent.value.resultingUri, equalTo(generatedUri)) + } + + @Test + fun `insert token supplier supplies insert tokens`() { + val insertTokenSupplier = InsertTokenSupplier(freenetInterface) + assertThat(insertTokenSupplier.apply(image), notNullValue()) + } + + @Test + fun `background fetch can be started`() { + freenetInterface.startFetch(uri, backgroundFetchCallback) + verify(highLevelSimpleClient).fetch(eq(uri), anyLong(), any(ClientGetCallback::class.java), any(FetchContext::class.java), anyShort()) + } + + @Test + fun `background fetch registers snoop and restarts the request`() { + freenetInterface.startFetch(uri, backgroundFetchCallback) + verify(clientGetter).metaSnoop = any(SnoopMetadata::class.java) + verify(clientGetter).restart(eq(uri), anyBoolean(), any(ClientContext::class.java)) + } + + @Test + fun `request is not cancelled for image mime type`() { + verifySnoopCancelsRequestForMimeType("image/png", false) + verify(backgroundFetchCallback, never()).failed(uri) + } + + @Test + fun `request is cancelled for null mime type`() { + verifySnoopCancelsRequestForMimeType(null, true) + verify(backgroundFetchCallback, never()).shouldCancel(eq(uri), any(), anyLong()) + verify(backgroundFetchCallback).failed(uri) + } + + @Test + fun `request is cancelled for video mime type`() { + verifySnoopCancelsRequestForMimeType("video/mkv", true) + verify(backgroundFetchCallback).failed(uri) + } + + @Test + fun `request is cancelled for audio mime type`() { + verifySnoopCancelsRequestForMimeType("audio/mpeg", true) + verify(backgroundFetchCallback).failed(uri) + } + + @Test + fun `request is cancelled for text mime type`() { + verifySnoopCancelsRequestForMimeType("text/plain", true) + verify(backgroundFetchCallback).failed(uri) + } + + private fun verifySnoopCancelsRequestForMimeType(mimeType: String?, cancel: Boolean) { + whenever(backgroundFetchCallback.shouldCancel(eq(uri), if (mimeType != null) eq(mimeType) else isNull(), anyLong())).thenReturn(cancel) + freenetInterface.startFetch(uri, backgroundFetchCallback) + val snoopMetadata = forClass(SnoopMetadata::class.java) + verify(clientGetter).metaSnoop = snoopMetadata.capture() + val metadata = mock() + whenever(metadata.mimeType).thenReturn(mimeType) + assertThat(snoopMetadata.value.snoopMetadata(metadata, mock()), equalTo(cancel)) + } + + @Test + fun `callback of background fetch is notified on success`() { + freenetInterface.startFetch(uri, backgroundFetchCallback) + verify(highLevelSimpleClient).fetch(eq(uri), anyLong(), clientGetCallback.capture(), any(FetchContext::class.java), anyShort()) + whenever(fetchResult.mimeType).thenReturn("image/png") + whenever(fetchResult.asByteArray()).thenReturn(byteArrayOf(1, 2, 3, 4, 5)) + clientGetCallback.value.onSuccess(fetchResult, mock()) + verify(backgroundFetchCallback).loaded(uri, "image/png", byteArrayOf(1, 2, 3, 4, 5)) + verifyNoMoreInteractions(backgroundFetchCallback) + } + + @Test + fun `callback of background fetch is notified on failure`() { + freenetInterface.startFetch(uri, backgroundFetchCallback) + verify(highLevelSimpleClient).fetch(eq(uri), anyLong(), clientGetCallback.capture(), any(FetchContext::class.java), anyShort()) + whenever(fetchResult.mimeType).thenReturn("image/png") + whenever(fetchResult.asByteArray()).thenReturn(byteArrayOf(1, 2, 3, 4, 5)) + clientGetCallback.value.onFailure(FetchException(ALL_DATA_NOT_FOUND), mock()) + verify(backgroundFetchCallback).failed(uri) + verifyNoMoreInteractions(backgroundFetchCallback) + } + + @Test + fun `callback of background fetch is notified as failure if bucket can not be loaded`() { + freenetInterface.startFetch(uri, backgroundFetchCallback) + verify(highLevelSimpleClient).fetch(eq(uri), anyLong(), clientGetCallback.capture(), any(FetchContext::class.java), anyShort()) + whenever(fetchResult.mimeType).thenReturn("image/png") + whenever(fetchResult.asByteArray()).thenThrow(IOException::class.java) + clientGetCallback.value.onSuccess(fetchResult, mock()) + verify(backgroundFetchCallback).failed(uri) + verifyNoMoreInteractions(backgroundFetchCallback) + } + +}