From 210684b4bc499e298a0d0abeddd4008cdeb406bc Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 27 Sep 2014 20:40:14 +0200 Subject: [PATCH] Store Sones in database only. --- .../java/net/pterodactylus/sone/core/Core.java | 174 ++++++++------------- .../sone/core/SoneDownloaderImpl.java | 7 +- .../java/net/pterodactylus/sone/data/Sone.java | 1 - .../java/net/pterodactylus/sone/data/SoneImpl.java | 11 +- .../sone/data/impl/AbstractSoneBuilder.java | 37 +++++ .../pterodactylus/sone/database/SoneBuilder.java | 18 +++ .../sone/database/SoneBuilderFactory.java | 12 ++ .../pterodactylus/sone/database/SoneDatabase.java | 2 +- .../sone/database/memory/MemoryDatabase.java | 6 + .../sone/database/memory/MemorySoneBuilder.java | 20 +++ .../sone/core/SoneDownloaderTest.java | 17 +- .../sone/data/impl/AbstractSoneBuilderTest.java | 54 +++++++ 12 files changed, 240 insertions(+), 119 deletions(-) create mode 100644 src/main/java/net/pterodactylus/sone/data/impl/AbstractSoneBuilder.java create mode 100644 src/main/java/net/pterodactylus/sone/database/SoneBuilder.java create mode 100644 src/main/java/net/pterodactylus/sone/database/SoneBuilderFactory.java create mode 100644 src/main/java/net/pterodactylus/sone/database/memory/MemorySoneBuilder.java create mode 100644 src/test/java/net/pterodactylus/sone/data/impl/AbstractSoneBuilderTest.java diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 9beeac4..ef05eba 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -20,15 +20,11 @@ package net.pterodactylus.sone.core; import static com.google.common.base.Optional.fromNullable; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Predicates.not; import static com.google.common.primitives.Longs.tryParse; import static java.lang.String.format; import static java.util.logging.Level.WARNING; -import static net.pterodactylus.sone.data.Sone.LOCAL_SONE_FILTER; import static net.pterodactylus.sone.data.Sone.toAllAlbums; -import java.net.MalformedURLException; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -85,6 +81,7 @@ import net.pterodactylus.sone.database.PostBuilder; import net.pterodactylus.sone.database.PostProvider; import net.pterodactylus.sone.database.PostReplyBuilder; import net.pterodactylus.sone.database.PostReplyProvider; +import net.pterodactylus.sone.database.SoneBuilder; import net.pterodactylus.sone.database.SoneProvider; import net.pterodactylus.sone.fcp.FcpInterface; import net.pterodactylus.sone.freenet.wot.Identity; @@ -109,7 +106,6 @@ import com.google.common.base.Optional; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.eventbus.EventBus; @@ -117,8 +113,6 @@ import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import com.google.inject.Singleton; -import freenet.keys.FreenetURI; - /** * The Sone core. * @@ -187,10 +181,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, /* synchronize access on this on sones. */ private final Map soneRescuers = new HashMap(); - /** All Sones. */ - /* synchronize access on this on itself. */ - private final Map sones = new HashMap(); - /** All known Sones. */ private final Set knownSones = new HashSet(); @@ -317,7 +307,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, public SoneRescuer getSoneRescuer(Sone sone) { checkNotNull(sone, "sone must not be null"); checkArgument(sone.isLocal(), "sone must be local"); - synchronized (sones) { + synchronized (soneRescuers) { SoneRescuer soneRescuer = soneRescuers.get(sone); if (soneRescuer == null) { soneRescuer = new SoneRescuer(this, soneDownloader, sone); @@ -341,6 +331,10 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } } + public SoneBuilder soneBuilder() { + return database.newSoneBuilder(); + } + /** * {@inheritDocs} */ @@ -376,24 +370,14 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * * @param id * The ID of the Sone - * @param create - * {@code true} to create a new Sone if none exists, - * {@code false} to return null if none exists * @return The Sone with the given ID, or {@code null} */ - public Sone getLocalSone(String id, boolean create) { - synchronized (sones) { - Sone sone = sones.get(id); - if ((sone == null) && create) { - sone = new SoneImpl(id, true); - sones.put(id, sone); - } - if ((sone != null) && !sone.isLocal()) { - sone = new SoneImpl(id, true); - sones.put(id, sone); - } - return sone; + public Sone getLocalSone(String id) { + Optional sone = database.getSone(id); + if (sone.isPresent() && sone.get().isLocal()) { + return sone.get(); } + return null; } /** @@ -407,22 +391,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, /** * Returns the remote Sone with the given ID. * + * * @param id * The ID of the remote Sone to get - * @param create - * {@code true} to always create a Sone, {@code false} to return - * {@code null} if no Sone with the given ID exists * @return The Sone with the given ID */ - public Sone getRemoteSone(String id, boolean create) { - synchronized (sones) { - Sone sone = sones.get(id); - if ((sone == null) && create && (id != null) && (id.length() == 43)) { - sone = new SoneImpl(id, false); - sones.put(id, sone); - } - return sone; - } + public Sone getRemoteSone(String id) { + return database.getSone(id).orNull(); } /** @@ -707,26 +682,19 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, return null; } logger.info(String.format("Adding Sone from OwnIdentity: %s", ownIdentity)); - synchronized (sones) { - final Sone sone; - try { - sone = getLocalSone(ownIdentity.getId(), true).setIdentity(ownIdentity).setInsertUri(new FreenetURI(ownIdentity.getInsertUri())).setRequestUri(new FreenetURI(ownIdentity.getRequestUri())); - } catch (MalformedURLException mue1) { - logger.log(Level.SEVERE, String.format("Could not convert the Identity’s URIs to Freenet URIs: %s, %s", ownIdentity.getInsertUri(), ownIdentity.getRequestUri()), mue1); - return null; - } - sone.setLatestEdition(Numbers.safeParseLong(ownIdentity.getProperty("Sone.LatestEdition"), (long) 0)); - sone.setClient(new Client("Sone", SonePlugin.VERSION.toString())); - sone.setKnown(true); - /* TODO - load posts ’n stuff */ - sones.put(ownIdentity.getId(), sone); - SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, ownIdentity.getId()); + Sone sone = database.newSoneBuilder().local().from(ownIdentity).build(); + sone.setLatestEdition(Numbers.safeParseLong(ownIdentity.getProperty("Sone.LatestEdition"), 0L)); + sone.setClient(new Client("Sone", SonePlugin.VERSION.toString())); + sone.setKnown(true); + /* TODO - load posts ’n stuff */ + SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, ownIdentity.getId()); + synchronized (soneInserters) { soneInserters.put(sone, soneInserter); - sone.setStatus(SoneStatus.idle); - loadSone(sone); - soneInserter.start(); - return sone; } + loadSone(sone); + sone.setStatus(SoneStatus.idle); + soneInserter.start(); + return sone; } /** @@ -762,33 +730,32 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } final Long latestEdition = tryParse(fromNullable( identity.getProperty("Sone.LatestEdition")).or("0")); - synchronized (sones) { - final Sone sone = getRemoteSone(identity.getId(), true); - if (sone.isLocal()) { - return sone; + Optional existingSone = getSone(identity.getId()); + if (existingSone.isPresent() && existingSone.get().isLocal()) { + return existingSone.get(); + } + boolean newSone = !existingSone.isPresent(); + Sone sone = !newSone ? existingSone.get() : database.newSoneBuilder().from(identity).build(); + sone.setRequestUri(SoneUri.create(identity.getRequestUri())); + sone.setLatestEdition(latestEdition); + if (newSone) { + synchronized (knownSones) { + newSone = !knownSones.contains(sone.getId()); } - sone.setIdentity(identity); - boolean newSone = sone.getRequestUri() == null; - sone.setRequestUri(SoneUri.create(identity.getRequestUri())); - sone.setLatestEdition(latestEdition); + sone.setKnown(!newSone); if (newSone) { - synchronized (knownSones) { - newSone = !knownSones.contains(sone.getId()); - } - sone.setKnown(!newSone); - if (newSone) { - eventBus.post(new NewSoneFoundEvent(sone)); - for (Sone localSone : getLocalSones()) { - if (localSone.getOptions().isAutoFollow()) { - followSone(localSone, sone.getId()); - } + eventBus.post(new NewSoneFoundEvent(sone)); + for (Sone localSone : getLocalSones()) { + if (localSone.getOptions().isAutoFollow()) { + followSone(localSone, sone.getId()); } } } - soneDownloader.addSone(sone); - soneDownloaders.execute(soneDownloader.fetchSoneWithUriAction(sone)); - return sone; } + database.storeSone(sone); + soneDownloader.addSone(sone); + soneDownloaders.execute(soneDownloader.fetchSoneWithUriAction(sone)); + return sone; } /** @@ -983,14 +950,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, }); soneChangeDetector.detectChanges(sone); database.storeSone(sone); - synchronized (sones) { - sone.setOptions(storedSone.get().getOptions()); - sone.setKnown(storedSone.get().isKnown()); - sone.setStatus((sone.getTime() == 0) ? SoneStatus.unknown : SoneStatus.idle); - if (sone.isLocal()) { - touchConfiguration(); - } - sones.put(sone.getId(), sone); + sone.setOptions(storedSone.get().getOptions()); + sone.setKnown(storedSone.get().isKnown()); + sone.setStatus((sone.getTime() == 0) ? SoneStatus.unknown : SoneStatus.idle); + if (sone.isLocal()) { + touchConfiguration(); } } } @@ -1008,15 +972,14 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, logger.log(Level.WARNING, String.format("Tried to delete Sone of non-own identity: %s", sone)); return; } - synchronized (sones) { - if (!getLocalSones().contains(sone)) { - logger.log(Level.WARNING, String.format("Tried to delete non-local Sone: %s", sone)); - return; - } - sones.remove(sone.getId()); - SoneInserter soneInserter = soneInserters.remove(sone); - soneInserter.stop(); + if (!getLocalSones().contains(sone)) { + logger.log(Level.WARNING, String.format("Tried to delete non-local Sone: %s", sone)); + return; } + // FIXME – implement in database +// sones.remove(sone.getId()); + SoneInserter soneInserter = soneInserters.remove(sone); + soneInserter.stop(); webOfTrustUpdater.removeContext((OwnIdentity) sone.getIdentity(), "Sone"); webOfTrustUpdater.removeProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition"); try { @@ -1160,12 +1123,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, for (Album album : topLevelAlbums) { sone.getRootAlbum().addAlbum(album); } - soneInserters.get(sone).setLastInsertFingerprint(lastInsertFingerprint); - for (Album album : toAllAlbums.apply(sone)) { - database.storeAlbum(album); - for (Image image : album.getImages()) { - database.storeImage(image); - } + database.storeSone(sone); + synchronized (soneInserters) { + soneInserters.get(sone).setLastInsertFingerprint(lastInsertFingerprint); } } synchronized (knownSones) { @@ -1173,11 +1133,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, knownSones.add(friend); } } - database.storePosts(sone, posts); for (Post post : posts) { post.setKnown(true); } - database.storePostReplies(sone, replies); for (PostReply reply : replies) { reply.setKnown(true); } @@ -1528,10 +1486,10 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, @Override public void serviceStop() { localElementTicker.shutdownNow(); - synchronized (sones) { + synchronized (soneInserters) { for (Entry soneInserter : soneInserters.entrySet()) { soneInserter.getValue().stop(); - saveSone(getLocalSone(soneInserter.getKey().getId(), false)); + saveSone(soneInserter.getKey()); } } saveConfiguration(); @@ -1893,11 +1851,10 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, @Subscribe public void identityUpdated(IdentityUpdatedEvent identityUpdatedEvent) { Identity identity = identityUpdatedEvent.identity(); - final Sone sone = getRemoteSone(identity.getId(), false); + final Sone sone = getRemoteSone(identity.getId()); if (sone.isLocal()) { return; } - sone.setIdentity(identity); sone.setLatestEdition(Numbers.safeParseLong(identity.getProperty("Sone.LatestEdition"), sone.getLatestEdition())); soneDownloader.addSone(sone); soneDownloaders.execute(soneDownloader.fetchSoneAction(sone)); @@ -1935,9 +1892,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, for (PostReply reply : sone.get().getReplies()) { eventBus.post(new PostReplyRemovedEvent(reply)); } - synchronized (sones) { - sones.remove(identity.getId()); - } +// TODO – implement in database +// sones.remove(identity.getId()); eventBus.post(new SoneRemovedEvent(sone.get())); } diff --git a/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java b/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java index 1521eaf..28d4521 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java @@ -45,6 +45,7 @@ import net.pterodactylus.sone.data.Sone.SoneStatus; import net.pterodactylus.sone.data.SoneImpl; import net.pterodactylus.sone.database.PostBuilder; import net.pterodactylus.sone.database.PostReplyBuilder; +import net.pterodactylus.sone.database.SoneBuilder; import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.number.Numbers; @@ -278,7 +279,11 @@ public class SoneDownloaderImpl extends AbstractService implements SoneDownloade return null; } - Sone sone = new SoneImpl(originalSone.getId(), originalSone.isLocal()).setIdentity(originalSone.getIdentity()); + SoneBuilder soneBuilder = core.soneBuilder().from(originalSone.getIdentity()); + if (originalSone.isLocal()) { + soneBuilder = soneBuilder.local(); + } + Sone sone = soneBuilder.build(); SimpleXML soneXml; try { diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index c27e8cc..3b36f46 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -28,7 +28,6 @@ import java.util.Comparator; import java.util.List; import java.util.Set; -import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/src/main/java/net/pterodactylus/sone/data/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/SoneImpl.java index 86fe3d9..08c104f 100644 --- a/src/main/java/net/pterodactylus/sone/data/SoneImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/SoneImpl.java @@ -59,7 +59,7 @@ public class SoneImpl implements Sone { private final boolean local; /** The identity of this Sone. */ - private Identity identity; + private final Identity identity; /** The URI under which the Sone is stored in Freenet. */ private volatile FreenetURI requestUri; @@ -110,13 +110,14 @@ public class SoneImpl implements Sone { /** * Creates a new Sone. * - * @param id - * The ID of the Sone + * @param identity + * The identity of the Sone * @param local * {@code true} if the Sone is a local Sone, {@code false} otherwise */ - public SoneImpl(String id, boolean local) { - this.id = id; + public SoneImpl(Identity identity, boolean local) { + this.id = identity.getId(); + this.identity = identity; this.local = local; } diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AbstractSoneBuilder.java b/src/main/java/net/pterodactylus/sone/data/impl/AbstractSoneBuilder.java new file mode 100644 index 0000000..a214677 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/data/impl/AbstractSoneBuilder.java @@ -0,0 +1,37 @@ +package net.pterodactylus.sone.data.impl; + +import static com.google.common.base.Preconditions.checkState; + +import net.pterodactylus.sone.database.SoneBuilder; +import net.pterodactylus.sone.freenet.wot.Identity; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; + +/** + * Abstract {@link SoneBuilder} implementation. + * + * @author David ‘Bombe’ Roden + */ +public abstract class AbstractSoneBuilder implements SoneBuilder { + + protected Identity identity; + protected boolean local; + + @Override + public SoneBuilder from(Identity identity) { + this.identity = identity; + return this; + } + + @Override + public SoneBuilder local() { + this.local = true; + return this; + } + + protected void validate() throws IllegalStateException { + checkState(identity != null, "identity must not be null"); + checkState(!local || (identity instanceof OwnIdentity), + "can not create local Sone from remote identity"); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/database/SoneBuilder.java b/src/main/java/net/pterodactylus/sone/database/SoneBuilder.java new file mode 100644 index 0000000..d2047af --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/SoneBuilder.java @@ -0,0 +1,18 @@ +package net.pterodactylus.sone.database; + +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.freenet.wot.Identity; + +/** + * Builder for {@link Sone} objects. + * + * @author David ‘Bombe’ Roden + */ +public interface SoneBuilder { + + SoneBuilder from(Identity identity); + SoneBuilder local(); + + Sone build() throws IllegalStateException; + +} diff --git a/src/main/java/net/pterodactylus/sone/database/SoneBuilderFactory.java b/src/main/java/net/pterodactylus/sone/database/SoneBuilderFactory.java new file mode 100644 index 0000000..c95251f --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/SoneBuilderFactory.java @@ -0,0 +1,12 @@ +package net.pterodactylus.sone.database; + +/** + * Factory for {@link SoneBuilder}s. + * + * @author David ‘Bombe’ Roden + */ +public interface SoneBuilderFactory { + + SoneBuilder newSoneBuilder(); + +} diff --git a/src/main/java/net/pterodactylus/sone/database/SoneDatabase.java b/src/main/java/net/pterodactylus/sone/database/SoneDatabase.java index 0f97103..f5c5cda 100644 --- a/src/main/java/net/pterodactylus/sone/database/SoneDatabase.java +++ b/src/main/java/net/pterodactylus/sone/database/SoneDatabase.java @@ -6,6 +6,6 @@ package net.pterodactylus.sone.database; * * @author David ‘Bombe’ Roden */ -public interface SoneDatabase extends SoneProvider, SoneStore { +public interface SoneDatabase extends SoneProvider, SoneBuilderFactory, SoneStore { } diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java index 00bb5c9..6113d26 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.java @@ -51,6 +51,7 @@ import net.pterodactylus.sone.database.ImageBuilder; import net.pterodactylus.sone.database.PostBuilder; import net.pterodactylus.sone.database.PostDatabase; import net.pterodactylus.sone.database.PostReplyBuilder; +import net.pterodactylus.sone.database.SoneBuilder; import net.pterodactylus.sone.database.SoneProvider; import net.pterodactylus.util.config.Configuration; import net.pterodactylus.util.config.ConfigurationException; @@ -168,6 +169,11 @@ public class MemoryDatabase extends AbstractService implements Database { } @Override + public SoneBuilder newSoneBuilder() { + return new MemorySoneBuilder(); + } + + @Override public void storeSone(Sone sone) { lock.writeLock().lock(); try { diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemorySoneBuilder.java b/src/main/java/net/pterodactylus/sone/database/memory/MemorySoneBuilder.java new file mode 100644 index 0000000..8778fb5 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemorySoneBuilder.java @@ -0,0 +1,20 @@ +package net.pterodactylus.sone.database.memory; + +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.data.SoneImpl; +import net.pterodactylus.sone.data.impl.AbstractSoneBuilder; + +/** + * Memory-based {@link AbstractSoneBuilder} implementation. + * + * @author David ‘Bombe’ Roden + */ +public class MemorySoneBuilder extends AbstractSoneBuilder { + + @Override + public Sone build() throws IllegalStateException { + validate(); + return new SoneImpl(identity, local); + } + +} diff --git a/src/test/java/net/pterodactylus/sone/core/SoneDownloaderTest.java b/src/test/java/net/pterodactylus/sone/core/SoneDownloaderTest.java index c910233..28879bf 100644 --- a/src/test/java/net/pterodactylus/sone/core/SoneDownloaderTest.java +++ b/src/test/java/net/pterodactylus/sone/core/SoneDownloaderTest.java @@ -49,6 +49,8 @@ import net.pterodactylus.sone.database.AlbumBuilder; import net.pterodactylus.sone.database.ImageBuilder; import net.pterodactylus.sone.database.PostBuilder; import net.pterodactylus.sone.database.PostReplyBuilder; +import net.pterodactylus.sone.database.SoneBuilder; +import net.pterodactylus.sone.database.memory.MemorySoneBuilder; import net.pterodactylus.sone.freenet.wot.Identity; import freenet.client.ClientMetadata; @@ -78,7 +80,7 @@ public class SoneDownloaderTest { private final FreenetInterface freenetInterface = mock(FreenetInterface.class); private final SoneDownloaderImpl soneDownloader = new SoneDownloaderImpl(core, freenetInterface); private final FreenetURI requestUri = mock(FreenetURI.class); - private final Sone sone = mock(Sone.class); + private Sone sone = mock(Sone.class); private final PostBuilder postBuilder = mock(PostBuilder.class); private final List createdPosts = new ArrayList(); private Post post = mock(Post.class); @@ -96,6 +98,7 @@ public class SoneDownloaderTest { @Before public void setupSone() { + Sone sone = SoneDownloaderTest.this.sone; Identity identity = mock(Identity.class); when(identity.getId()).thenReturn("identity"); when(sone.getId()).thenReturn("identity"); @@ -118,6 +121,16 @@ public class SoneDownloaderTest { } @Before + public void setupSoneBuilder() { + when(core.soneBuilder()).thenAnswer(new Answer() { + @Override + public SoneBuilder answer(InvocationOnMock invocation) { + return new MemorySoneBuilder(); + } + }); + } + + @Before public void setupPost() { when(post.getRecipientId()).thenReturn(Optional.absent()); } @@ -921,7 +934,7 @@ public class SoneDownloaderTest { @Test public void exceptionWhileFetchingSoneWillNotUpdateTheCore() throws IOException { final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml")); - when(sone.getId()).thenThrow(NullPointerException.class); + when(core.soneBuilder()).thenReturn(null); when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult); soneDownloader.fetchSoneAction(sone).run(); verify(core, never()).updateSone(any(Sone.class)); diff --git a/src/test/java/net/pterodactylus/sone/data/impl/AbstractSoneBuilderTest.java b/src/test/java/net/pterodactylus/sone/data/impl/AbstractSoneBuilderTest.java new file mode 100644 index 0000000..b2d86dd --- /dev/null +++ b/src/test/java/net/pterodactylus/sone/data/impl/AbstractSoneBuilderTest.java @@ -0,0 +1,54 @@ +package net.pterodactylus.sone.data.impl; + +import static org.mockito.Mockito.mock; + +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.freenet.wot.Identity; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; + +import org.junit.Test; + +/** + * Unit test for {@link AbstractSoneBuilder}. + * + * @author David ‘Bombe’ Roden + */ +public class AbstractSoneBuilderTest { + + private final AbstractSoneBuilder soneBuilder = new AbstractSoneBuilder() { + @Override + public Sone build() throws IllegalStateException { + validate(); + return null; + } + }; + + @Test + public void localSoneIsValidated() { + Identity ownIdentity = mock(OwnIdentity.class); + soneBuilder.local().from(ownIdentity).build(); + } + + @Test(expected = IllegalStateException.class) + public void localSoneIsNotValidatedIfIdentityIsNotAnOwnIdentity() { + Identity identity = mock(Identity.class); + soneBuilder.local().from(identity).build(); + } + + @Test(expected = IllegalStateException.class) + public void localSoneIsNotValidatedIfIdentityIsNull() { + soneBuilder.local().build(); + } + + @Test + public void removeSoneIsValidate() { + Identity identity = mock(Identity.class); + soneBuilder.from(identity).build(); + } + + @Test(expected = IllegalStateException.class) + public void remoteSoneIsNotValidatedIfIdentityIsNull() { + soneBuilder.build(); + } + +} -- 2.7.4