import static java.util.concurrent.TimeUnit.DAYS;
import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.pterodactylus.sone.core.FreenetInterface.Fetched;
-import net.pterodactylus.sone.data.Album;
-import net.pterodactylus.sone.data.Client;
-import net.pterodactylus.sone.data.Image;
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Sone;
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;
import net.pterodactylus.util.service.AbstractService;
-import net.pterodactylus.util.xml.SimpleXML;
-import net.pterodactylus.util.xml.XML;
import freenet.client.FetchResult;
import freenet.client.async.ClientContext;
import freenet.keys.USK;
import freenet.node.RequestStarter;
import freenet.support.api.Bucket;
-
import com.db4o.ObjectContainer;
import com.google.common.annotations.VisibleForTesting;
-import org.w3c.dom.Document;
/**
* The Sone downloader is responsible for download Sones as they are updated.
/** The core. */
private final Core core;
+ private final SoneParser soneParser;
/** The Freenet interface. */
private final FreenetInterface freenetInterface;
* The Freenet interface
*/
public SoneDownloaderImpl(Core core, FreenetInterface freenetInterface) {
+ this(core, freenetInterface, new SoneParser(core));
+ }
+
+ /**
+ * Creates a new Sone downloader.
+ *
+ * @param core
+ * The core
+ * @param freenetInterface
+ * The Freenet interface
+ * @param soneParser
+ */
+ @VisibleForTesting
+ SoneDownloaderImpl(Core core, FreenetInterface freenetInterface, SoneParser soneParser) {
super("Sone Downloader", false);
this.core = core;
this.freenetInterface = freenetInterface;
+ this.soneParser = soneParser;
}
//
InputStream soneInputStream = null;
try {
soneInputStream = soneBucket.getInputStream();
- Sone parsedSone = parseSone(originalSone, soneInputStream);
+ Sone parsedSone = soneParser.parseSone(originalSone,
+ soneInputStream);
if (parsedSone != null) {
parsedSone.setLatestEdition(requestUri.getEdition());
}
return null;
}
- /**
- * Parses a Sone from the given input stream and creates a new Sone from the
- * parsed data.
- *
- * @param originalSone
- * The Sone to update
- * @param soneInputStream
- * The input stream to parse the Sone from
- * @return The parsed Sone
- * @throws SoneException
- * if a parse error occurs, or the protocol is invalid
- */
- @VisibleForTesting
- protected Sone parseSone(Sone originalSone, InputStream soneInputStream) throws SoneException {
- /* TODO - impose a size limit? */
-
- Document document;
- /* XML parsing is not thread-safe. */
- synchronized (this) {
- document = XML.transformToDocument(soneInputStream);
- }
- if (document == null) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Could not parse XML for Sone %s!", originalSone));
- return null;
- }
-
- SoneBuilder soneBuilder = core.soneBuilder().from(originalSone.getIdentity());
- if (originalSone.isLocal()) {
- soneBuilder = soneBuilder.local();
- }
- Sone sone = soneBuilder.build();
-
- SimpleXML soneXml;
- try {
- soneXml = SimpleXML.fromDocument(document);
- } catch (NullPointerException npe1) {
- /* for some reason, invalid XML can cause NPEs. */
- logger.log(Level.WARNING, String.format("XML for Sone %s can not be parsed!", sone), npe1);
- return null;
- }
-
- Integer protocolVersion = null;
- String soneProtocolVersion = soneXml.getValue("protocol-version", null);
- if (soneProtocolVersion != null) {
- protocolVersion = Numbers.safeParseInteger(soneProtocolVersion);
- }
- if (protocolVersion == null) {
- logger.log(Level.INFO, "No protocol version found, assuming 0.");
- protocolVersion = 0;
- }
-
- if (protocolVersion < 0) {
- logger.log(Level.WARNING, String.format("Invalid protocol version: %d! Not parsing Sone.", protocolVersion));
- return null;
- }
-
- /* check for valid versions. */
- if (protocolVersion > MAX_PROTOCOL_VERSION) {
- logger.log(Level.WARNING, String.format("Unknown protocol version: %d! Not parsing Sone.", protocolVersion));
- return null;
- }
-
- String soneTime = soneXml.getValue("time", null);
- if (soneTime == null) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Downloaded time for Sone %s was null!", sone));
- return null;
- }
- try {
- sone.setTime(Long.parseLong(soneTime));
- } catch (NumberFormatException nfe1) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Downloaded Sone %s with invalid time: %s", sone, soneTime));
- return null;
- }
-
- SimpleXML clientXml = soneXml.getNode("client");
- if (clientXml != null) {
- String clientName = clientXml.getValue("name", null);
- String clientVersion = clientXml.getValue("version", null);
- if ((clientName == null) || (clientVersion == null)) {
- logger.log(Level.WARNING, String.format("Download Sone %s with client XML but missing name or version!", sone));
- return null;
- }
- sone.setClient(new Client(clientName, clientVersion));
- }
-
- SimpleXML profileXml = soneXml.getNode("profile");
- if (profileXml == null) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Downloaded Sone %s has no profile!", sone));
- return null;
- }
-
- /* parse profile. */
- String profileFirstName = profileXml.getValue("first-name", null);
- String profileMiddleName = profileXml.getValue("middle-name", null);
- String profileLastName = profileXml.getValue("last-name", null);
- Integer profileBirthDay = Numbers.safeParseInteger(profileXml.getValue("birth-day", null));
- Integer profileBirthMonth = Numbers.safeParseInteger(profileXml.getValue("birth-month", null));
- Integer profileBirthYear = Numbers.safeParseInteger(profileXml.getValue("birth-year", null));
- Profile profile = new Profile(sone).setFirstName(profileFirstName).setMiddleName(profileMiddleName).setLastName(profileLastName);
- profile.setBirthDay(profileBirthDay).setBirthMonth(profileBirthMonth).setBirthYear(profileBirthYear);
- /* avatar is processed after images are loaded. */
- String avatarId = profileXml.getValue("avatar", null);
-
- /* parse profile fields. */
- SimpleXML profileFieldsXml = profileXml.getNode("fields");
- if (profileFieldsXml != null) {
- for (SimpleXML fieldXml : profileFieldsXml.getNodes("field")) {
- String fieldName = fieldXml.getValue("field-name", null);
- String fieldValue = fieldXml.getValue("field-value", "");
- if (fieldName == null) {
- logger.log(Level.WARNING, String.format("Downloaded profile field for Sone %s with missing data! Name: %s, Value: %s", sone, fieldName, fieldValue));
- return null;
- }
- try {
- profile.addField(fieldName.trim()).setValue(fieldValue);
- } catch (IllegalArgumentException iae1) {
- logger.log(Level.WARNING, String.format("Duplicate field: %s", fieldName), iae1);
- return null;
- }
- }
- }
-
- /* parse posts. */
- SimpleXML postsXml = soneXml.getNode("posts");
- Set<Post> posts = new HashSet<Post>();
- if (postsXml == null) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Downloaded Sone %s has no posts!", sone));
- } else {
- for (SimpleXML postXml : postsXml.getNodes("post")) {
- String postId = postXml.getValue("id", null);
- String postRecipientId = postXml.getValue("recipient", null);
- String postTime = postXml.getValue("time", null);
- String postText = postXml.getValue("text", null);
- if ((postId == null) || (postTime == null) || (postText == null)) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Downloaded post for Sone %s with missing data! ID: %s, Time: %s, Text: %s", sone, postId, postTime, postText));
- return null;
- }
- try {
- PostBuilder postBuilder = core.postBuilder();
- /* TODO - parse time correctly. */
- postBuilder.withId(postId).from(sone.getId()).withTime(Long.parseLong(postTime)).withText(postText);
- if ((postRecipientId != null) && (postRecipientId.length() == 43)) {
- postBuilder.to(postRecipientId);
- }
- posts.add(postBuilder.build());
- } catch (NumberFormatException nfe1) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Downloaded post for Sone %s with invalid time: %s", sone, postTime));
- return null;
- }
- }
- }
-
- /* parse replies. */
- SimpleXML repliesXml = soneXml.getNode("replies");
- Set<PostReply> replies = new HashSet<PostReply>();
- if (repliesXml == null) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Downloaded Sone %s has no replies!", sone));
- } else {
- for (SimpleXML replyXml : repliesXml.getNodes("reply")) {
- String replyId = replyXml.getValue("id", null);
- String replyPostId = replyXml.getValue("post-id", null);
- String replyTime = replyXml.getValue("time", null);
- String replyText = replyXml.getValue("text", null);
- if ((replyId == null) || (replyPostId == null) || (replyTime == null) || (replyText == null)) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Downloaded reply for Sone %s with missing data! ID: %s, Post: %s, Time: %s, Text: %s", sone, replyId, replyPostId, replyTime, replyText));
- return null;
- }
- try {
- PostReplyBuilder postReplyBuilder = core.postReplyBuilder();
- /* TODO - parse time correctly. */
- postReplyBuilder.withId(replyId).from(sone.getId()).to(replyPostId).withTime(Long.parseLong(replyTime)).withText(replyText);
- replies.add(postReplyBuilder.build());
- } catch (NumberFormatException nfe1) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Downloaded reply for Sone %s with invalid time: %s", sone, replyTime));
- return null;
- }
- }
- }
-
- /* parse liked post IDs. */
- SimpleXML likePostIdsXml = soneXml.getNode("post-likes");
- Set<String> likedPostIds = new HashSet<String>();
- if (likePostIdsXml == null) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Downloaded Sone %s has no post likes!", sone));
- } else {
- for (SimpleXML likedPostIdXml : likePostIdsXml.getNodes("post-like")) {
- String postId = likedPostIdXml.getValue();
- likedPostIds.add(postId);
- }
- }
-
- /* parse liked reply IDs. */
- SimpleXML likeReplyIdsXml = soneXml.getNode("reply-likes");
- Set<String> likedReplyIds = new HashSet<String>();
- if (likeReplyIdsXml == null) {
- /* TODO - mark Sone as bad. */
- logger.log(Level.WARNING, String.format("Downloaded Sone %s has no reply likes!", sone));
- } else {
- for (SimpleXML likedReplyIdXml : likeReplyIdsXml.getNodes("reply-like")) {
- String replyId = likedReplyIdXml.getValue();
- likedReplyIds.add(replyId);
- }
- }
-
- /* parse albums. */
- SimpleXML albumsXml = soneXml.getNode("albums");
- Map<String, Image> allImages = new HashMap<String, Image>();
- List<Album> topLevelAlbums = new ArrayList<Album>();
- if (albumsXml != null) {
- for (SimpleXML albumXml : albumsXml.getNodes("album")) {
- String id = albumXml.getValue("id", null);
- String parentId = albumXml.getValue("parent", null);
- String title = albumXml.getValue("title", null);
- String description = albumXml.getValue("description", "");
- String albumImageId = albumXml.getValue("album-image", null);
- if ((id == null) || (title == null)) {
- logger.log(Level.WARNING, String.format("Downloaded Sone %s contains invalid album!", sone));
- return null;
- }
- Album parent = null;
- if (parentId != null) {
- parent = core.getAlbum(parentId);
- if (parent == null) {
- logger.log(Level.WARNING, String.format("Downloaded Sone %s has album with invalid parent!", sone));
- return null;
- }
- }
- Album album = core.albumBuilder()
- .withId(id)
- .by(sone)
- .build()
- .modify()
- .setTitle(title)
- .setDescription(description)
- .update();
- if (parent != null) {
- parent.addAlbum(album);
- } else {
- topLevelAlbums.add(album);
- }
- SimpleXML imagesXml = albumXml.getNode("images");
- if (imagesXml != null) {
- for (SimpleXML imageXml : imagesXml.getNodes("image")) {
- String imageId = imageXml.getValue("id", null);
- String imageCreationTimeString = imageXml.getValue("creation-time", null);
- String imageKey = imageXml.getValue("key", null);
- String imageTitle = imageXml.getValue("title", null);
- String imageDescription = imageXml.getValue("description", "");
- String imageWidthString = imageXml.getValue("width", null);
- String imageHeightString = imageXml.getValue("height", null);
- if ((imageId == null) || (imageCreationTimeString == null) || (imageKey == null) || (imageTitle == null) || (imageWidthString == null) || (imageHeightString == null)) {
- logger.log(Level.WARNING, String.format("Downloaded Sone %s contains invalid images!", sone));
- return null;
- }
- long creationTime = Numbers.safeParseLong(imageCreationTimeString, 0L);
- int imageWidth = Numbers.safeParseInteger(imageWidthString, 0);
- int imageHeight = Numbers.safeParseInteger(imageHeightString, 0);
- if ((imageWidth < 1) || (imageHeight < 1)) {
- logger.log(Level.WARNING, String.format("Downloaded Sone %s contains image %s with invalid dimensions (%s, %s)!", sone, imageId, imageWidthString, imageHeightString));
- return null;
- }
- Image image = core.imageBuilder().withId(imageId).build().modify().setSone(sone).setKey(imageKey).setCreationTime(creationTime).update();
- image = image.modify().setTitle(imageTitle).setDescription(imageDescription).update();
- image = image.modify().setWidth(imageWidth).setHeight(imageHeight).update();
- album.addImage(image);
- allImages.put(imageId, image);
- }
- }
- album.modify().setAlbumImage(albumImageId).update();
- }
- }
-
- /* process avatar. */
- if (avatarId != null) {
- profile.setAvatar(allImages.get(avatarId));
- }
-
- /* okay, apparently everything was parsed correctly. Now import. */
- /* atomic setter operation on the Sone. */
- synchronized (sone) {
- sone.setProfile(profile);
- sone.setPosts(posts);
- sone.setReplies(replies);
- sone.setLikePostIds(likedPostIds);
- sone.setLikeReplyIds(likedReplyIds);
- for (Album album : topLevelAlbums) {
- sone.getRootAlbum().addAlbum(album);
- }
- }
-
- return sone;
- }
-
@Override
public Runnable fetchSoneWithUriAction(final Sone sone) {
return new Runnable() {
--- /dev/null
+package net.pterodactylus.sone.core;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.Client;
+import net.pterodactylus.sone.data.Image;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostReply;
+import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.database.PostBuilder;
+import net.pterodactylus.sone.database.PostReplyBuilder;
+import net.pterodactylus.sone.database.SoneBuilder;
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.number.Numbers;
+import net.pterodactylus.util.xml.SimpleXML;
+import net.pterodactylus.util.xml.XML;
+
+import org.w3c.dom.Document;
+
+/**
+ * Parses a {@link Sone} from an XML {@link InputStream}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class SoneParser {
+
+ private static final Logger logger = Logging.getLogger(SoneParser.class);
+ private static final int MAX_PROTOCOL_VERSION = 0;
+ private final Core core;
+
+ public SoneParser(Core core) {
+ this.core = core;
+ }
+
+ public Sone parseSone(Sone originalSone, InputStream soneInputStream) throws SoneException {
+ /* TODO - impose a size limit? */
+
+ Document document;
+ /* XML parsing is not thread-safe. */
+ synchronized (this) {
+ document = XML.transformToDocument(soneInputStream);
+ }
+ if (document == null) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Could not parse XML for Sone %s!", originalSone));
+ return null;
+ }
+
+ SoneBuilder soneBuilder = core.soneBuilder().from(originalSone.getIdentity());
+ if (originalSone.isLocal()) {
+ soneBuilder = soneBuilder.local();
+ }
+ Sone sone = soneBuilder.build();
+
+ SimpleXML soneXml;
+ try {
+ soneXml = SimpleXML.fromDocument(document);
+ } catch (NullPointerException npe1) {
+ /* for some reason, invalid XML can cause NPEs. */
+ logger.log(Level.WARNING, String.format("XML for Sone %s can not be parsed!", sone), npe1);
+ return null;
+ }
+
+ Integer protocolVersion = null;
+ String soneProtocolVersion = soneXml.getValue("protocol-version", null);
+ if (soneProtocolVersion != null) {
+ protocolVersion = Numbers.safeParseInteger(soneProtocolVersion);
+ }
+ if (protocolVersion == null) {
+ logger.log(Level.INFO, "No protocol version found, assuming 0.");
+ protocolVersion = 0;
+ }
+
+ if (protocolVersion < 0) {
+ logger.log(Level.WARNING, String.format("Invalid protocol version: %d! Not parsing Sone.", protocolVersion));
+ return null;
+ }
+
+ /* check for valid versions. */
+ if (protocolVersion > MAX_PROTOCOL_VERSION) {
+ logger.log(Level.WARNING, String.format("Unknown protocol version: %d! Not parsing Sone.", protocolVersion));
+ return null;
+ }
+
+ String soneTime = soneXml.getValue("time", null);
+ if (soneTime == null) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Downloaded time for Sone %s was null!", sone));
+ return null;
+ }
+ try {
+ sone.setTime(Long.parseLong(soneTime));
+ } catch (NumberFormatException nfe1) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Downloaded Sone %s with invalid time: %s", sone, soneTime));
+ return null;
+ }
+
+ SimpleXML clientXml = soneXml.getNode("client");
+ if (clientXml != null) {
+ String clientName = clientXml.getValue("name", null);
+ String clientVersion = clientXml.getValue("version", null);
+ if ((clientName == null) || (clientVersion == null)) {
+ logger.log(Level.WARNING, String.format("Download Sone %s with client XML but missing name or version!", sone));
+ return null;
+ }
+ sone.setClient(new Client(clientName, clientVersion));
+ }
+
+ SimpleXML profileXml = soneXml.getNode("profile");
+ if (profileXml == null) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Downloaded Sone %s has no profile!", sone));
+ return null;
+ }
+
+ /* parse profile. */
+ String profileFirstName = profileXml.getValue("first-name", null);
+ String profileMiddleName = profileXml.getValue("middle-name", null);
+ String profileLastName = profileXml.getValue("last-name", null);
+ Integer profileBirthDay = Numbers.safeParseInteger(profileXml.getValue("birth-day", null));
+ Integer profileBirthMonth = Numbers.safeParseInteger(profileXml.getValue("birth-month", null));
+ Integer profileBirthYear = Numbers.safeParseInteger(profileXml.getValue("birth-year", null));
+ Profile profile = new Profile(sone).setFirstName(profileFirstName).setMiddleName(profileMiddleName).setLastName(profileLastName);
+ profile.setBirthDay(profileBirthDay).setBirthMonth(profileBirthMonth).setBirthYear(profileBirthYear);
+ /* avatar is processed after images are loaded. */
+ String avatarId = profileXml.getValue("avatar", null);
+
+ /* parse profile fields. */
+ SimpleXML profileFieldsXml = profileXml.getNode("fields");
+ if (profileFieldsXml != null) {
+ for (SimpleXML fieldXml : profileFieldsXml.getNodes("field")) {
+ String fieldName = fieldXml.getValue("field-name", null);
+ String fieldValue = fieldXml.getValue("field-value", "");
+ if (fieldName == null) {
+ logger.log(Level.WARNING, String.format("Downloaded profile field for Sone %s with missing data! Name: %s, Value: %s", sone, fieldName, fieldValue));
+ return null;
+ }
+ try {
+ profile.addField(fieldName.trim()).setValue(fieldValue);
+ } catch (IllegalArgumentException iae1) {
+ logger.log(Level.WARNING, String.format("Duplicate field: %s", fieldName), iae1);
+ return null;
+ }
+ }
+ }
+
+ /* parse posts. */
+ SimpleXML postsXml = soneXml.getNode("posts");
+ Set<Post> posts = new HashSet<Post>();
+ if (postsXml == null) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Downloaded Sone %s has no posts!", sone));
+ } else {
+ for (SimpleXML postXml : postsXml.getNodes("post")) {
+ String postId = postXml.getValue("id", null);
+ String postRecipientId = postXml.getValue("recipient", null);
+ String postTime = postXml.getValue("time", null);
+ String postText = postXml.getValue("text", null);
+ if ((postId == null) || (postTime == null) || (postText == null)) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Downloaded post for Sone %s with missing data! ID: %s, Time: %s, Text: %s", sone, postId, postTime, postText));
+ return null;
+ }
+ try {
+ PostBuilder postBuilder = core.postBuilder();
+ /* TODO - parse time correctly. */
+ postBuilder.withId(postId).from(sone.getId()).withTime(Long.parseLong(postTime)).withText(postText);
+ if ((postRecipientId != null) && (postRecipientId.length() == 43)) {
+ postBuilder.to(postRecipientId);
+ }
+ posts.add(postBuilder.build());
+ } catch (NumberFormatException nfe1) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Downloaded post for Sone %s with invalid time: %s", sone, postTime));
+ return null;
+ }
+ }
+ }
+
+ /* parse replies. */
+ SimpleXML repliesXml = soneXml.getNode("replies");
+ Set<PostReply> replies = new HashSet<PostReply>();
+ if (repliesXml == null) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Downloaded Sone %s has no replies!", sone));
+ } else {
+ for (SimpleXML replyXml : repliesXml.getNodes("reply")) {
+ String replyId = replyXml.getValue("id", null);
+ String replyPostId = replyXml.getValue("post-id", null);
+ String replyTime = replyXml.getValue("time", null);
+ String replyText = replyXml.getValue("text", null);
+ if ((replyId == null) || (replyPostId == null) || (replyTime == null) || (replyText == null)) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Downloaded reply for Sone %s with missing data! ID: %s, Post: %s, Time: %s, Text: %s", sone, replyId, replyPostId, replyTime, replyText));
+ return null;
+ }
+ try {
+ PostReplyBuilder postReplyBuilder = core.postReplyBuilder();
+ /* TODO - parse time correctly. */
+ postReplyBuilder.withId(replyId).from(sone.getId()).to(replyPostId).withTime(Long.parseLong(replyTime)).withText(replyText);
+ replies.add(postReplyBuilder.build());
+ } catch (NumberFormatException nfe1) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Downloaded reply for Sone %s with invalid time: %s", sone, replyTime));
+ return null;
+ }
+ }
+ }
+
+ /* parse liked post IDs. */
+ SimpleXML likePostIdsXml = soneXml.getNode("post-likes");
+ Set<String> likedPostIds = new HashSet<String>();
+ if (likePostIdsXml == null) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Downloaded Sone %s has no post likes!", sone));
+ } else {
+ for (SimpleXML likedPostIdXml : likePostIdsXml.getNodes("post-like")) {
+ String postId = likedPostIdXml.getValue();
+ likedPostIds.add(postId);
+ }
+ }
+
+ /* parse liked reply IDs. */
+ SimpleXML likeReplyIdsXml = soneXml.getNode("reply-likes");
+ Set<String> likedReplyIds = new HashSet<String>();
+ if (likeReplyIdsXml == null) {
+ /* TODO - mark Sone as bad. */
+ logger.log(Level.WARNING, String.format("Downloaded Sone %s has no reply likes!", sone));
+ } else {
+ for (SimpleXML likedReplyIdXml : likeReplyIdsXml.getNodes("reply-like")) {
+ String replyId = likedReplyIdXml.getValue();
+ likedReplyIds.add(replyId);
+ }
+ }
+
+ /* parse albums. */
+ SimpleXML albumsXml = soneXml.getNode("albums");
+ Map<String, Image> allImages = new HashMap<String, Image>();
+ List<Album> topLevelAlbums = new ArrayList<Album>();
+ if (albumsXml != null) {
+ for (SimpleXML albumXml : albumsXml.getNodes("album")) {
+ String id = albumXml.getValue("id", null);
+ String parentId = albumXml.getValue("parent", null);
+ String title = albumXml.getValue("title", null);
+ String description = albumXml.getValue("description", "");
+ String albumImageId = albumXml.getValue("album-image", null);
+ if ((id == null) || (title == null)) {
+ logger.log(Level.WARNING, String.format("Downloaded Sone %s contains invalid album!", sone));
+ return null;
+ }
+ Album parent = null;
+ if (parentId != null) {
+ parent = core.getAlbum(parentId);
+ if (parent == null) {
+ logger.log(Level.WARNING, String.format("Downloaded Sone %s has album with invalid parent!", sone));
+ return null;
+ }
+ }
+ Album album = core.albumBuilder()
+ .withId(id)
+ .by(sone)
+ .build()
+ .modify()
+ .setTitle(title)
+ .setDescription(description)
+ .update();
+ if (parent != null) {
+ parent.addAlbum(album);
+ } else {
+ topLevelAlbums.add(album);
+ }
+ SimpleXML imagesXml = albumXml.getNode("images");
+ if (imagesXml != null) {
+ for (SimpleXML imageXml : imagesXml.getNodes("image")) {
+ String imageId = imageXml.getValue("id", null);
+ String imageCreationTimeString = imageXml.getValue("creation-time", null);
+ String imageKey = imageXml.getValue("key", null);
+ String imageTitle = imageXml.getValue("title", null);
+ String imageDescription = imageXml.getValue("description", "");
+ String imageWidthString = imageXml.getValue("width", null);
+ String imageHeightString = imageXml.getValue("height", null);
+ if ((imageId == null) || (imageCreationTimeString == null) || (imageKey == null) || (imageTitle == null) || (imageWidthString == null) || (imageHeightString == null)) {
+ logger.log(Level.WARNING, String.format("Downloaded Sone %s contains invalid images!", sone));
+ return null;
+ }
+ long creationTime = Numbers.safeParseLong(imageCreationTimeString, 0L);
+ int imageWidth = Numbers.safeParseInteger(imageWidthString, 0);
+ int imageHeight = Numbers.safeParseInteger(imageHeightString, 0);
+ if ((imageWidth < 1) || (imageHeight < 1)) {
+ logger.log(Level.WARNING, String.format("Downloaded Sone %s contains image %s with invalid dimensions (%s, %s)!", sone, imageId, imageWidthString, imageHeightString));
+ return null;
+ }
+ Image image = core.imageBuilder().withId(imageId).build().modify().setSone(sone).setKey(imageKey).setCreationTime(creationTime).update();
+ image = image.modify().setTitle(imageTitle).setDescription(imageDescription).update();
+ image = image.modify().setWidth(imageWidth).setHeight(imageHeight).update();
+ album.addImage(image);
+ allImages.put(imageId, image);
+ }
+ }
+ album.modify().setAlbumImage(albumImageId).update();
+ }
+ }
+
+ /* process avatar. */
+ if (avatarId != null) {
+ profile.setAvatar(allImages.get(avatarId));
+ }
+
+ /* okay, apparently everything was parsed correctly. Now import. */
+ /* atomic setter operation on the Sone. */
+ synchronized (sone) {
+ sone.setProfile(profile);
+ sone.setPosts(posts);
+ sone.setReplies(replies);
+ sone.setLikePostIds(likedPostIds);
+ sone.setLikeReplyIds(likedReplyIds);
+ for (Album album : topLevelAlbums) {
+ sone.getRootAlbum().addAlbum(album);
+ }
+ }
+
+ return sone;
+
+ }
+
+}
package net.pterodactylus.sone.core;
-import static com.google.common.base.Optional.of;
import static freenet.keys.InsertableClientSSK.createRandom;
import static java.lang.System.currentTimeMillis;
-import static java.util.UUID.randomUUID;
import static java.util.concurrent.TimeUnit.DAYS;
import static net.pterodactylus.sone.data.Sone.SoneStatus.downloading;
import static net.pterodactylus.sone.data.Sone.SoneStatus.idle;
import static net.pterodactylus.sone.data.Sone.SoneStatus.unknown;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.hamcrest.Matchers.nullValue;
import static org.mockito.ArgumentCaptor.forClass;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import java.io.IOException;
import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
import net.pterodactylus.sone.core.FreenetInterface.Fetched;
-import net.pterodactylus.sone.data.Album;
-import net.pterodactylus.sone.data.Album.Modifier;
-import net.pterodactylus.sone.data.Client;
-import net.pterodactylus.sone.data.Image;
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.data.Sone.SoneStatus;
-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;
import freenet.client.FetchResult;
import freenet.client.async.USKCallback;
import freenet.crypt.DummyRandomSource;
-import freenet.keys.ClientSSK;
import freenet.keys.FreenetURI;
import freenet.keys.InsertableClientSSK;
import freenet.support.api.Bucket;
-import com.google.common.base.Optional;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
private final Core core = mock(Core.class);
private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
- private final SoneDownloaderImpl soneDownloader = new SoneDownloaderImpl(core, freenetInterface);
+ private final SoneParser soneParser = mock(SoneParser.class);
+ private final SoneDownloaderImpl soneDownloader = new SoneDownloaderImpl(core, freenetInterface, soneParser);
private FreenetURI requestUri = mock(FreenetURI.class);
private Sone sone = mock(Sone.class);
- private final PostBuilder postBuilder = mock(PostBuilder.class);
- private final List<Post> createdPosts = new ArrayList<Post>();
- private Post post = mock(Post.class);
- private final PostReplyBuilder postReplyBuilder = mock(PostReplyBuilder.class);
- private final Set<PostReply> createdPostReplies = new HashSet<PostReply>();
- private PostReply postReply = mock(PostReply.class);
- private final AlbumBuilder albumBuilder = mock(AlbumBuilder.class);
- private final ListMultimap<Album, Album> nestedAlbums = ArrayListMultimap.create();
- private final ListMultimap<Album, Image> albumImages = ArrayListMultimap.create();
- private Album album = mock(Album.class);
- private final Map<String, Album> albums = new HashMap<String, Album>();
- private final ImageBuilder imageBuilder = mock(ImageBuilder.class);
- private Image image = mock(Image.class);
- private final Map<String, Image> images = new HashMap<String, Image>();
@Before
public void setupSone() {
when(sone.getTime()).thenReturn(0L);
}
- @Before
- public void setupSoneBuilder() {
- when(core.soneBuilder()).thenAnswer(new Answer<SoneBuilder>() {
- @Override
- public SoneBuilder answer(InvocationOnMock invocation) {
- return new MemorySoneBuilder();
- }
- });
- }
-
- @Before
- public void setupPost() {
- when(post.getRecipientId()).thenReturn(Optional.<String>absent());
- }
-
- @Before
- public void setupPostBuilder() {
- when(postBuilder.withId(anyString())).thenAnswer(new Answer<PostBuilder>() {
- @Override
- public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
- when(post.getId()).thenReturn((String) invocation.getArguments()[0]);
- return postBuilder;
- }
- });
- when(postBuilder.from(anyString())).thenAnswer(new Answer<PostBuilder>() {
- @Override
- public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
- final Sone sone = mock(Sone.class);
- when(sone.getId()).thenReturn((String) invocation.getArguments()[0]);
- when(post.getSone()).thenReturn(sone);
- return postBuilder;
- }
- });
- when(postBuilder.withTime(anyLong())).thenAnswer(new Answer<PostBuilder>() {
- @Override
- public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
- when(post.getTime()).thenReturn((Long) invocation.getArguments()[0]);
- return postBuilder;
- }
- });
- when(postBuilder.withText(anyString())).thenAnswer(new Answer<PostBuilder>() {
- @Override
- public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
- when(post.getText()).thenReturn((String) invocation.getArguments()[0]);
- return postBuilder;
- }
- });
- when(postBuilder.to(anyString())).thenAnswer(new Answer<PostBuilder>() {
- @Override
- public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
- when(post.getRecipientId()).thenReturn(of((String) invocation.getArguments()[0]));
- return postBuilder;
- }
- });
- when(postBuilder.build()).thenAnswer(new Answer<Post>() {
- @Override
- public Post answer(InvocationOnMock invocation) throws Throwable {
- Post post = SoneDownloaderTest.this.post;
- SoneDownloaderTest.this.post = mock(Post.class);
- setupPost();
- createdPosts.add(post);
- return post;
- }
- });
- when(core.postBuilder()).thenReturn(postBuilder);
- }
-
- @Before
- public void setupPostReplyBuilder() {
- when(postReplyBuilder.withId(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
- @Override
- public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
- when(postReply.getId()).thenReturn((String) invocation.getArguments()[0]);
- return postReplyBuilder;
- }
- });
- when(postReplyBuilder.from(anyString())).thenAnswer(
- new Answer<PostReplyBuilder>() {
- @Override
- public PostReplyBuilder answer(
- InvocationOnMock invocation) throws Throwable {
- Sone sone = when(mock(Sone.class).getId()).thenReturn(
- (String) invocation.getArguments()[0])
- .getMock();
- when(postReply.getSone()).thenReturn(sone);
- return postReplyBuilder;
- }
- });
- when(postReplyBuilder.to(anyString())).thenAnswer(
- new Answer<PostReplyBuilder>() {
- @Override
- public PostReplyBuilder answer(
- InvocationOnMock invocation) throws Throwable {
- when(postReply.getPostId()).thenReturn(
- (String) invocation.getArguments()[0]);
- Post post = when(mock(Post.class).getId()).thenReturn(
- (String) invocation.getArguments()[0])
- .getMock();
- when(postReply.getPost()).thenReturn(of(post));
- return postReplyBuilder;
- }
- });
- when(postReplyBuilder.withTime(anyLong())).thenAnswer(
- new Answer<PostReplyBuilder>() {
- @Override
- public PostReplyBuilder answer(
- InvocationOnMock invocation) throws Throwable {
- when(postReply.getTime()).thenReturn(
- (Long) invocation.getArguments()[0]);
- return postReplyBuilder;
- }
- });
- when(postReplyBuilder.withText(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
- @Override
- public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
- when(postReply.getText()).thenReturn((String) invocation.getArguments()[0]);
- return postReplyBuilder;
- }
- });
- when(postReplyBuilder.build()).thenAnswer(new Answer<PostReply>() {
- @Override
- public PostReply answer(InvocationOnMock invocation) throws Throwable {
- PostReply postReply = SoneDownloaderTest.this.postReply;
- createdPostReplies.add(postReply);
- SoneDownloaderTest.this.postReply = mock(PostReply.class);
- return postReply;
- }
- });
- when(core.postReplyBuilder()).thenReturn(postReplyBuilder);
- }
-
- @Before
- public void setupAlbum() {
- final Album album = SoneDownloaderTest.this.album;
- when(album.getAlbumImage()).thenReturn(mock(Image.class));
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- nestedAlbums.put(album, (Album) invocation.getArguments()[0]);
- return null;
- }
- }).when(album).addAlbum(any(Album.class));
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- albumImages.put(album, (Image) invocation.getArguments()[0]);
- return null;
- }
- }).when(album).addImage(any(Image.class));
- when(album.getAlbums()).thenAnswer(new Answer<List<Album>>() {
- @Override
- public List<Album> answer(InvocationOnMock invocation) {
- return nestedAlbums.get(album);
- }
- });
- when(album.getImages()).thenAnswer(new Answer<List<Image>>() {
- @Override
- public List<Image> answer(InvocationOnMock invocation) {
- return albumImages.get(album);
- }
- });
- final Modifier albumModifier = new Modifier() {
- private String title = album.getTitle();
- private String description = album.getDescription();
- private String imageId = album.getAlbumImage().getId();
-
- @Override
- public Modifier setTitle(String title) {
- this.title = title;
- return this;
- }
-
- @Override
- public Modifier setDescription(String description) {
- this.description = description;
- return this;
- }
-
- @Override
- public Modifier setAlbumImage(String imageId) {
- this.imageId = imageId;
- return this;
- }
-
- @Override
- public Album update() throws IllegalStateException {
- when(album.getTitle()).thenReturn(title);
- when(album.getDescription()).thenReturn(description);
- Image image = mock(Image.class);
- when(image.getId()).thenReturn(imageId);
- when(album.getAlbumImage()).thenReturn(image);
- return album;
- }
- };
- when(album.modify()).thenReturn(albumModifier);
- }
-
- @Before
- public void setupAlbumBuilder() {
- when(albumBuilder.withId(anyString())).thenAnswer(new Answer<AlbumBuilder>() {
- @Override
- public AlbumBuilder answer(InvocationOnMock invocation) {
- when(album.getId()).thenReturn((String) invocation.getArguments()[0]);
- return albumBuilder;
- }
- });
- when(albumBuilder.randomId()).thenAnswer(new Answer<AlbumBuilder>() {
- @Override
- public AlbumBuilder answer(InvocationOnMock invocation) {
- when(album.getId()).thenReturn(randomUUID().toString());
- return albumBuilder;
- }
- });
- when(albumBuilder.by(any(Sone.class))).thenAnswer(new Answer<AlbumBuilder>() {
- @Override
- public AlbumBuilder answer(InvocationOnMock invocation) {
- when(album.getSone()).thenReturn((Sone) invocation.getArguments()[0]);
- return albumBuilder;
- }
- });
- when(albumBuilder.build()).thenAnswer(new Answer<Album>() {
- @Override
- public Album answer(InvocationOnMock invocation) {
- Album album = SoneDownloaderTest.this.album;
- albums.put(album.getId(), album);
- SoneDownloaderTest.this.album = mock(Album.class);
- setupAlbum();
- return album;
- }
- });
- when(core.albumBuilder()).thenReturn(albumBuilder);
- }
-
- @Before
- public void setupAlbums() {
- when(core.getAlbum(anyString())).thenAnswer(new Answer<Album>() {
- @Override
- public Album answer(InvocationOnMock invocation)
- throws Throwable {
- return albums.get(invocation.getArguments()[0]);
- }
- });
- }
-
- @Before
- public void setupImage() {
- final Image image = SoneDownloaderTest.this.image;
- Image.Modifier modifier = new Image.Modifier() {
- private Sone sone = image.getSone();
- private long creationTime = image.getCreationTime();
- private String key = image.getKey();
- private String title = image.getTitle();
- private String description = image.getDescription();
- private int width = image.getWidth();
- private int height = image.getHeight();
-
- @Override
- public Image.Modifier setSone(Sone sone) {
- this.sone = sone;
- return this;
- }
-
- @Override
- public Image.Modifier setCreationTime(long creationTime) {
- this.creationTime = creationTime;
- return this;
- }
-
- @Override
- public Image.Modifier setKey(String key) {
- this.key = key;
- return this;
- }
-
- @Override
- public Image.Modifier setTitle(String title) {
- this.title = title;
- return this;
- }
-
- @Override
- public Image.Modifier setDescription(String description) {
- this.description = description;
- return this;
- }
-
- @Override
- public Image.Modifier setWidth(int width) {
- this.width = width;
- return this;
- }
-
- @Override
- public Image.Modifier setHeight(int height) {
- this.height = height;
- return this;
- }
-
- @Override
- public Image update() throws IllegalStateException {
- when(image.getSone()).thenReturn(sone);
- when(image.getCreationTime()).thenReturn(creationTime);
- when(image.getKey()).thenReturn(key);
- when(image.getTitle()).thenReturn(title);
- when(image.getDescription()).thenReturn(description);
- when(image.getWidth()).thenReturn(width);
- when(image.getHeight()).thenReturn(height);
- return image;
- }
- };
- when(image.getSone()).thenReturn(sone);
- when(image.modify()).thenReturn(modifier);
- }
-
- @Before
- public void setupImageBuilder() {
- when(imageBuilder.randomId()).thenAnswer(new Answer<ImageBuilder>() {
- @Override
- public ImageBuilder answer(InvocationOnMock invocation) {
- when(image.getId()).thenReturn(randomUUID().toString());
- return imageBuilder;
- }
- });
- when(imageBuilder.withId(anyString())).thenAnswer(new Answer<ImageBuilder>() {
- @Override
- public ImageBuilder answer(InvocationOnMock invocation) {
- when(image.getId()).thenReturn(
- (String) invocation.getArguments()[0]);
- return imageBuilder;
- }
- });
- when(imageBuilder.build()).thenAnswer(new Answer<Image>() {
- @Override
- public Image answer(InvocationOnMock invocation) {
- Image image = SoneDownloaderTest.this.image;
- images.put(image.getId(), image);
- SoneDownloaderTest.this.image = mock(Image.class);
- setupImage();
- return image;
- }
- });
- when(core.imageBuilder()).thenReturn(imageBuilder);
- }
-
- @Before
- public void setupImages() {
- when(core.getImage(anyString())).thenAnswer(new Answer<Image>() {
- @Override
- public Image answer(InvocationOnMock invocation)
- throws Throwable {
- return images.get(invocation.getArguments()[0]);
- }
- });
- }
-
@Test
public void addingASoneWillRegisterItsKey() {
soneDownloader.addSone(sone);
verify(freenetInterface).unregisterUsk(sone);
}
- @Test
- public void parsingASoneFailsWhenDocumentIsNotXml() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-not-xml.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWhenDocumentHasNegativeProtocolVersion() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-negative-protocol-version.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWhenProtocolVersionIsTooLarge() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-too-large-protocol-version.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWhenThereIsNoTime() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-time.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWhenTimeIsNotNumeric() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-time-not-numeric.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWhenProfileIsMissing() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-profile.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWhenProfileFieldIsMissingAFieldName() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-missing-field-name.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWhenProfileFieldNameIsEmpty() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-empty-field-name.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWhenProfileFieldNameIsNotUnique() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-duplicate-field-name.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneSucceedsWithoutPayload() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream).getTime(), is(
- 1407197508000L));
- }
-
- @Test
- public void parsingASoneSucceedsWithoutProtocolVersion() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-protocol-version.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), not(
- nullValue()));
- }
-
- @Test
- public void parsingASoneFailsWithMissingClientName() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-name.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithMissingClientVersion() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-version.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneSucceedsWithClientInfo() throws SoneException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-client-info.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream).getClient(), is(new Client("some-client", "some-version")));
- }
-
- @Test
- public void parsingASoneSucceedsWithProfile() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-profile.xml");
- final Profile profile = soneDownloader.parseSone(sone, inputStream).getProfile();
- assertThat(profile.getFirstName(), is("first"));
- assertThat(profile.getMiddleName(), is("middle"));
- assertThat(profile.getLastName(), is("last"));
- assertThat(profile.getBirthDay(), is(18));
- assertThat(profile.getBirthMonth(), is(12));
- assertThat(profile.getBirthYear(), is(1976));
- }
-
- @Test
- public void parsingASoneSucceedsWithoutProfileFields() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-fields.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), notNullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutPostId() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-id.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutPostTime() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-time.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutPostText() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-text.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithInvalidPostTime() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-time.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneSucceedsWithValidPostTime() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-time.xml");
- final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
- assertThat(posts, is(createdPosts));
- assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
- assertThat(posts.get(0).getId(), is("post-id"));
- assertThat(posts.get(0).getTime(), is(1407197508000L));
- assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
- assertThat(posts.get(0).getText(), is("text"));
- }
-
- @Test
- public void parsingASoneSucceedsWithRecipient() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-recipient.xml");
- final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
- assertThat(posts, is(createdPosts));
- assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
- assertThat(posts.get(0).getId(), is("post-id"));
- assertThat(posts.get(0).getTime(), is(1407197508000L));
- assertThat(posts.get(0).getRecipientId(), is(of(
- "1234567890123456789012345678901234567890123")));
- assertThat(posts.get(0).getText(), is("text"));
- }
-
- @Test
- public void parsingASoneSucceedsWithInvalidRecipient() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-recipient.xml");
- final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
- assertThat(posts, is(createdPosts));
- assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
- assertThat(posts.get(0).getId(), is("post-id"));
- assertThat(posts.get(0).getTime(), is(1407197508000L));
- assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
- assertThat(posts.get(0).getText(), is("text"));
- }
-
- @Test
- public void parsingASoneFailsWithoutPostReplyId() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-id.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutPostReplyPostId() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-post-id.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutPostReplyTime() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-time.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutPostReplyText() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-text.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithInvalidPostReplyTime() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-reply-time.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneSucceedsWithValidPostReplyTime() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-reply-time.xml");
- final Set<PostReply> postReplies = soneDownloader.parseSone(sone, inputStream).getReplies();
- assertThat(postReplies, is(createdPostReplies));
- PostReply postReply = createdPostReplies.iterator().next();
- assertThat(postReply.getId(), is("reply-id"));
- assertThat(postReply.getPostId(), is("post-id"));
- assertThat(postReply.getPost().get().getId(), is("post-id"));
- assertThat(postReply.getSone().getId(), is("identity"));
- assertThat(postReply.getTime(), is(1407197508000L));
- assertThat(postReply.getText(), is("reply-text"));
- }
-
- @Test
- public void parsingASoneSucceedsWithoutLikedPostIds() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-ids.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), not(
- nullValue()));
- }
-
- @Test
- public void parsingASoneSucceedsWithLikedPostIds() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-ids.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream).getLikedPostIds(), is(
- (Set<String>) ImmutableSet.of("liked-post-id")));
- }
-
- @Test
- public void parsingASoneSucceedsWithoutLikedPostReplyIds() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-reply-ids.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), not(
- nullValue()));
- }
-
- @Test
- public void parsingASoneSucceedsWithLikedPostReplyIds() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-reply-ids.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream).getLikedReplyIds(), is(
- (Set<String>) ImmutableSet.of("liked-post-reply-id")));
- }
-
- @Test
- public void parsingASoneSucceedsWithoutAlbums() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-albums.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), not(
- nullValue()));
- }
-
- @Test
- public void parsingASoneFailsWithoutAlbumId() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-id.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutAlbumTitle() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-title.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneSucceedsWithNestedAlbums() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-multiple-albums.xml");
- final Sone parsedSone = soneDownloader.parseSone(sone, inputStream);
- assertThat(parsedSone, not(nullValue()));
- assertThat(parsedSone.getRootAlbum().getAlbums(), hasSize(1));
- Album album = parsedSone.getRootAlbum().getAlbums().get(0);
- assertThat(album.getId(), is("album-id-1"));
- assertThat(album.getTitle(), is("album-title"));
- assertThat(album.getDescription(), is("album-description"));
- assertThat(album.getAlbums(), hasSize(1));
- Album nestedAlbum = album.getAlbums().get(0);
- assertThat(nestedAlbum.getId(), is("album-id-2"));
- assertThat(nestedAlbum.getTitle(), is("album-title-2"));
- assertThat(nestedAlbum.getDescription(), is("album-description-2"));
- assertThat(nestedAlbum.getAlbums(), hasSize(0));
- }
-
- @Test
- public void parsingASoneFailsWithInvalidParentAlbumId() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-parent-album-id.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneSucceedsWithoutImages() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-images.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), not(
- nullValue()));
- }
-
- @Test
- public void parsingASoneFailsWithoutImageId() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-id.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutImageTime() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-time.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutImageKey() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-key.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutImageTitle() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-title.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutImageWidth() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-width.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithoutImageHeight() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-height.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithInvalidImageWidth() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-width.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneFailsWithInvalidImageHeight() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-height.xml");
- assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
- }
-
- @Test
- public void parsingASoneSucceedsWithImage() throws SoneException, MalformedURLException {
- InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-image.xml");
- final Sone sone = soneDownloader.parseSone(this.sone, inputStream);
- assertThat(sone, not(nullValue()));
- assertThat(sone.getRootAlbum().getAlbums(), hasSize(1));
- assertThat(sone.getRootAlbum().getAlbums().get(0).getImages(), hasSize(1));
- Image image = sone.getRootAlbum().getAlbums().get(0).getImages().get(0);
- assertThat(image.getId(), is("image-id"));
- assertThat(image.getCreationTime(), is(1407197508000L));
- assertThat(image.getKey(), is("KSK@GPLv3.txt"));
- assertThat(image.getTitle(), is("image-title"));
- assertThat(image.getDescription(), is("image-description"));
- assertThat(image.getWidth(), is(1920));
- assertThat(image.getHeight(), is(1080));
- assertThat(sone.getProfile().getAvatar(), is("image-id"));
- }
@Test
public void stoppingTheSoneDownloaderUnregistersTheSone() {
}
@Test
- public void successfulFetchingOfSoneWithUskRequestUriUpdatesTheCoreWithASone() throws IOException {
- FreenetURI finalRequestUri = requestUri.sskForUSK()
- .setMetaString(new String[] { "sone.xml" });
- final Fetched fetchResult = createFetchResult(finalRequestUri,
- getClass().getResourceAsStream("sone-parser-no-payload.xml"));
- when(freenetInterface.fetchUri(finalRequestUri)).thenReturn(fetchResult);
- soneDownloader.fetchSoneAction(sone).run();
- verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
- }
-
- private void verifyThatParsedSoneHasTheSameIdAsTheOriginalSone() {
- ArgumentCaptor<Sone> soneCaptor = forClass(Sone.class);
- verify(core).updateSone(soneCaptor.capture());
- assertThat(soneCaptor.getValue().getId(), is(sone.getId()));
- }
-
- @Test
public void fetchingSoneWithInvalidXmlWillNotUpdateTheCore() throws IOException {
final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-not-xml.xml"));
when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
--- /dev/null
+package net.pterodactylus.sone.core;
+
+import static com.google.common.base.Optional.of;
+import static freenet.keys.InsertableClientSSK.createRandom;
+import static java.lang.System.currentTimeMillis;
+import static java.util.UUID.randomUUID;
+import static java.util.concurrent.TimeUnit.DAYS;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.Album.Modifier;
+import net.pterodactylus.sone.data.Client;
+import net.pterodactylus.sone.data.Image;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostReply;
+import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Sone;
+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 net.pterodactylus.sone.freenet.wot.OwnIdentity;
+
+import freenet.crypt.DummyRandomSource;
+import freenet.keys.FreenetURI;
+import freenet.keys.InsertableClientSSK;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ListMultimap;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Unit test for {@link SoneParser}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class SoneParserTest {
+
+ private final Core core = mock(Core.class);
+ private final SoneParser soneParser = new SoneParser(core);
+ private final Sone sone = mock(Sone.class);
+ private FreenetURI requestUri = mock(FreenetURI.class);
+ private final PostBuilder postBuilder = mock(PostBuilder.class);
+ private final List<Post> createdPosts = new ArrayList<Post>();
+ private Post post = mock(Post.class);
+ private final PostReplyBuilder postReplyBuilder = mock(PostReplyBuilder.class);
+ private final Set<PostReply> createdPostReplies = new HashSet<PostReply>();
+ private PostReply postReply = mock(PostReply.class);
+ private final AlbumBuilder albumBuilder = mock(AlbumBuilder.class);
+ private final ListMultimap<Album, Album>
+ nestedAlbums = ArrayListMultimap.create();
+ private final ListMultimap<Album, Image> albumImages = ArrayListMultimap.create();
+ private Album album = mock(Album.class);
+ private final Map<String, Album> albums = new HashMap<String, Album>();
+ private final ImageBuilder imageBuilder = mock(ImageBuilder.class);
+ private Image image = mock(Image.class);
+ private final Map<String, Image> images = new HashMap<String, Image>();
+
+ @Before
+ public void setupSone() {
+ setupSone(this.sone, Identity.class);
+ }
+
+ private void setupSone(Sone sone, Class<? extends Identity> identityClass) {
+ Identity identity = mock(identityClass);
+ InsertableClientSSK clientSSK =
+ createRandom(new DummyRandomSource(), "WoT");
+ when(identity.getRequestUri()).thenReturn(clientSSK.getURI().toString());
+ when(identity.getId()).thenReturn("identity");
+ when(sone.getId()).thenReturn("identity");
+ when(sone.getIdentity()).thenReturn(identity);
+ requestUri = clientSSK.getURI().setKeyType("USK").setDocName("Sone");
+ when(sone.getRequestUri()).thenAnswer(new Answer<FreenetURI>() {
+ @Override
+ public FreenetURI answer(InvocationOnMock invocation)
+ throws Throwable {
+ return requestUri;
+ }
+ });
+ when(sone.getTime())
+ .thenReturn(currentTimeMillis() - DAYS.toMillis(1));
+ }
+
+ @Before
+ public void setupSoneBuilder() {
+ when(core.soneBuilder()).thenAnswer(new Answer<SoneBuilder>() {
+ @Override
+ public SoneBuilder answer(InvocationOnMock invocation) {
+ return new MemorySoneBuilder();
+ }
+ });
+ }
+
+ @Before
+ public void setupPost() {
+ when(post.getRecipientId()).thenReturn(Optional.<String>absent());
+ }
+
+ @Before
+ public void setupPostBuilder() {
+ when(postBuilder.withId(anyString())).thenAnswer(new Answer<PostBuilder>() {
+ @Override
+ public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
+ when(post.getId()).thenReturn((String) invocation.getArguments()[0]);
+ return postBuilder;
+ }
+ });
+ when(postBuilder.from(anyString())).thenAnswer(new Answer<PostBuilder>() {
+ @Override
+ public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
+ final Sone sone = mock(Sone.class);
+ when(sone.getId()).thenReturn((String) invocation.getArguments()[0]);
+ when(post.getSone()).thenReturn(sone);
+ return postBuilder;
+ }
+ });
+ when(postBuilder.withTime(anyLong())).thenAnswer(new Answer<PostBuilder>() {
+ @Override
+ public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
+ when(post.getTime()).thenReturn((Long) invocation.getArguments()[0]);
+ return postBuilder;
+ }
+ });
+ when(postBuilder.withText(anyString())).thenAnswer(new Answer<PostBuilder>() {
+ @Override
+ public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
+ when(post.getText()).thenReturn((String) invocation.getArguments()[0]);
+ return postBuilder;
+ }
+ });
+ when(postBuilder.to(anyString())).thenAnswer(new Answer<PostBuilder>() {
+ @Override
+ public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
+ when(post.getRecipientId()).thenReturn(of((String) invocation.getArguments()[0]));
+ return postBuilder;
+ }
+ });
+ when(postBuilder.build()).thenAnswer(new Answer<Post>() {
+ @Override
+ public Post answer(InvocationOnMock invocation) throws Throwable {
+ Post post = SoneParserTest.this.post;
+ SoneParserTest.this.post = mock(Post.class);
+ setupPost();
+ createdPosts.add(post);
+ return post;
+ }
+ });
+ when(core.postBuilder()).thenReturn(postBuilder);
+ }
+
+ @Before
+ public void setupPostReplyBuilder() {
+ when(postReplyBuilder.withId(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
+ @Override
+ public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
+ when(postReply.getId()).thenReturn((String) invocation.getArguments()[0]);
+ return postReplyBuilder;
+ }
+ });
+ when(postReplyBuilder.from(anyString())).thenAnswer(
+ new Answer<PostReplyBuilder>() {
+ @Override
+ public PostReplyBuilder answer(
+ InvocationOnMock invocation) throws Throwable {
+ Sone sone = when(mock(Sone.class).getId()).thenReturn(
+ (String) invocation.getArguments()[0])
+ .getMock();
+ when(postReply.getSone()).thenReturn(sone);
+ return postReplyBuilder;
+ }
+ });
+ when(postReplyBuilder.to(anyString())).thenAnswer(
+ new Answer<PostReplyBuilder>() {
+ @Override
+ public PostReplyBuilder answer(
+ InvocationOnMock invocation) throws Throwable {
+ when(postReply.getPostId()).thenReturn(
+ (String) invocation.getArguments()[0]);
+ Post post = when(mock(Post.class).getId()).thenReturn(
+ (String) invocation.getArguments()[0])
+ .getMock();
+ when(postReply.getPost()).thenReturn(of(post));
+ return postReplyBuilder;
+ }
+ });
+ when(postReplyBuilder.withTime(anyLong())).thenAnswer(
+ new Answer<PostReplyBuilder>() {
+ @Override
+ public PostReplyBuilder answer(
+ InvocationOnMock invocation) throws Throwable {
+ when(postReply.getTime()).thenReturn(
+ (Long) invocation.getArguments()[0]);
+ return postReplyBuilder;
+ }
+ });
+ when(postReplyBuilder.withText(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
+ @Override
+ public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
+ when(postReply.getText()).thenReturn((String) invocation.getArguments()[0]);
+ return postReplyBuilder;
+ }
+ });
+ when(postReplyBuilder.build()).thenAnswer(new Answer<PostReply>() {
+ @Override
+ public PostReply answer(InvocationOnMock invocation) throws Throwable {
+ PostReply postReply = SoneParserTest.this.postReply;
+ createdPostReplies.add(postReply);
+ SoneParserTest.this.postReply = mock(PostReply.class);
+ return postReply;
+ }
+ });
+ when(core.postReplyBuilder()).thenReturn(postReplyBuilder);
+ }
+
+ @Before
+ public void setupAlbum() {
+ final Album album = SoneParserTest.this.album;
+ when(album.getAlbumImage()).thenReturn(mock(Image.class));
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ nestedAlbums.put(album, (Album) invocation.getArguments()[0]);
+ return null;
+ }
+ }).when(album).addAlbum(any(Album.class));
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ albumImages.put(album, (Image) invocation.getArguments()[0]);
+ return null;
+ }
+ }).when(album).addImage(any(Image.class));
+ when(album.getAlbums()).thenAnswer(new Answer<List<Album>>() {
+ @Override
+ public List<Album> answer(InvocationOnMock invocation) {
+ return nestedAlbums.get(album);
+ }
+ });
+ when(album.getImages()).thenAnswer(new Answer<List<Image>>() {
+ @Override
+ public List<Image> answer(InvocationOnMock invocation) {
+ return albumImages.get(album);
+ }
+ });
+ final Modifier albumModifier = new Modifier() {
+ private String title = album.getTitle();
+ private String description = album.getDescription();
+ private String imageId = album.getAlbumImage().getId();
+
+ @Override
+ public Modifier setTitle(String title) {
+ this.title = title;
+ return this;
+ }
+
+ @Override
+ public Modifier setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ @Override
+ public Modifier setAlbumImage(String imageId) {
+ this.imageId = imageId;
+ return this;
+ }
+
+ @Override
+ public Album update() throws IllegalStateException {
+ when(album.getTitle()).thenReturn(title);
+ when(album.getDescription()).thenReturn(description);
+ Image image = mock(Image.class);
+ when(image.getId()).thenReturn(imageId);
+ when(album.getAlbumImage()).thenReturn(image);
+ return album;
+ }
+ };
+ when(album.modify()).thenReturn(albumModifier);
+ }
+
+ @Before
+ public void setupAlbumBuilder() {
+ when(albumBuilder.withId(anyString())).thenAnswer(new Answer<AlbumBuilder>() {
+ @Override
+ public AlbumBuilder answer(InvocationOnMock invocation) {
+ when(album.getId()).thenReturn((String) invocation.getArguments()[0]);
+ return albumBuilder;
+ }
+ });
+ when(albumBuilder.randomId()).thenAnswer(new Answer<AlbumBuilder>() {
+ @Override
+ public AlbumBuilder answer(InvocationOnMock invocation) {
+ when(album.getId()).thenReturn(randomUUID().toString());
+ return albumBuilder;
+ }
+ });
+ when(albumBuilder.by(any(Sone.class))).thenAnswer(new Answer<AlbumBuilder>() {
+ @Override
+ public AlbumBuilder answer(InvocationOnMock invocation) {
+ when(album.getSone()).thenReturn((Sone) invocation.getArguments()[0]);
+ return albumBuilder;
+ }
+ });
+ when(albumBuilder.build()).thenAnswer(new Answer<Album>() {
+ @Override
+ public Album answer(InvocationOnMock invocation) {
+ Album album = SoneParserTest.this.album;
+ albums.put(album.getId(), album);
+ SoneParserTest.this.album = mock(Album.class);
+ setupAlbum();
+ return album;
+ }
+ });
+ when(core.albumBuilder()).thenReturn(albumBuilder);
+ }
+
+ @Before
+ public void setupAlbums() {
+ when(core.getAlbum(anyString())).thenAnswer(new Answer<Album>() {
+ @Override
+ public Album answer(InvocationOnMock invocation)
+ throws Throwable {
+ return albums.get(invocation.getArguments()[0]);
+ }
+ });
+ }
+
+ @Before
+ public void setupImage() {
+ final Image image = SoneParserTest.this.image;
+ Image.Modifier modifier = new Image.Modifier() {
+ private Sone sone = image.getSone();
+ private long creationTime = image.getCreationTime();
+ private String key = image.getKey();
+ private String title = image.getTitle();
+ private String description = image.getDescription();
+ private int width = image.getWidth();
+ private int height = image.getHeight();
+
+ @Override
+ public Image.Modifier setSone(Sone sone) {
+ this.sone = sone;
+ return this;
+ }
+
+ @Override
+ public Image.Modifier setCreationTime(long creationTime) {
+ this.creationTime = creationTime;
+ return this;
+ }
+
+ @Override
+ public Image.Modifier setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ @Override
+ public Image.Modifier setTitle(String title) {
+ this.title = title;
+ return this;
+ }
+
+ @Override
+ public Image.Modifier setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ @Override
+ public Image.Modifier setWidth(int width) {
+ this.width = width;
+ return this;
+ }
+
+ @Override
+ public Image.Modifier setHeight(int height) {
+ this.height = height;
+ return this;
+ }
+
+ @Override
+ public Image update() throws IllegalStateException {
+ when(image.getSone()).thenReturn(sone);
+ when(image.getCreationTime()).thenReturn(creationTime);
+ when(image.getKey()).thenReturn(key);
+ when(image.getTitle()).thenReturn(title);
+ when(image.getDescription()).thenReturn(description);
+ when(image.getWidth()).thenReturn(width);
+ when(image.getHeight()).thenReturn(height);
+ return image;
+ }
+ };
+ when(image.getSone()).thenReturn(sone);
+ when(image.modify()).thenReturn(modifier);
+ }
+
+ @Before
+ public void setupImageBuilder() {
+ when(imageBuilder.randomId()).thenAnswer(new Answer<ImageBuilder>() {
+ @Override
+ public ImageBuilder answer(InvocationOnMock invocation) {
+ when(image.getId()).thenReturn(randomUUID().toString());
+ return imageBuilder;
+ }
+ });
+ when(imageBuilder.withId(anyString())).thenAnswer(new Answer<ImageBuilder>() {
+ @Override
+ public ImageBuilder answer(InvocationOnMock invocation) {
+ when(image.getId()).thenReturn(
+ (String) invocation.getArguments()[0]);
+ return imageBuilder;
+ }
+ });
+ when(imageBuilder.build()).thenAnswer(new Answer<Image>() {
+ @Override
+ public Image answer(InvocationOnMock invocation) {
+ Image image = SoneParserTest.this.image;
+ images.put(image.getId(), image);
+ SoneParserTest.this.image = mock(Image.class);
+ setupImage();
+ return image;
+ }
+ });
+ when(core.imageBuilder()).thenReturn(imageBuilder);
+ }
+
+ @Before
+ public void setupImages() {
+ when(core.getImage(anyString())).thenAnswer(new Answer<Image>() {
+ @Override
+ public Image answer(InvocationOnMock invocation)
+ throws Throwable {
+ return images.get(invocation.getArguments()[0]);
+ }
+ });
+ }
+ @Test
+ public void parsingASoneFailsWhenDocumentIsNotXml() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-not-xml.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWhenDocumentHasNegativeProtocolVersion() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-negative-protocol-version.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWhenProtocolVersionIsTooLarge() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-too-large-protocol-version.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWhenThereIsNoTime() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-time.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWhenTimeIsNotNumeric() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-time-not-numeric.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWhenProfileIsMissing() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-profile.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWhenProfileFieldIsMissingAFieldName() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-missing-field-name.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWhenProfileFieldNameIsEmpty() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-empty-field-name.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWhenProfileFieldNameIsNotUnique() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-duplicate-field-name.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithoutPayload() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
+ assertThat(soneParser.parseSone(sone, inputStream).getTime(), is(
+ 1407197508000L));
+ }
+
+ @Test
+ public void parsingALocalSoneSucceedsWithoutPayload() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
+ Sone localSone = mock(Sone.class);
+ setupSone(localSone, OwnIdentity.class);
+ when(localSone.isLocal()).thenReturn(true);
+ Sone parsedSone = soneParser.parseSone(localSone, inputStream);
+ assertThat(parsedSone.getTime(), is(1407197508000L));
+ assertThat(parsedSone.isLocal(), is(true));
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithoutProtocolVersion() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-protocol-version.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), not(
+ nullValue()));
+ }
+
+ @Test
+ public void parsingASoneFailsWithMissingClientName() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-name.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithMissingClientVersion() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-version.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithClientInfo() throws SoneException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-client-info.xml");
+ assertThat(soneParser.parseSone(sone, inputStream).getClient(), is(new Client("some-client", "some-version")));
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithProfile() throws SoneException,
+ MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-profile.xml");
+ final Profile profile = soneParser.parseSone(sone, inputStream).getProfile();
+ assertThat(profile.getFirstName(), is("first"));
+ assertThat(profile.getMiddleName(), is("middle"));
+ assertThat(profile.getLastName(), is("last"));
+ assertThat(profile.getBirthDay(), is(18));
+ assertThat(profile.getBirthMonth(), is(12));
+ assertThat(profile.getBirthYear(), is(1976));
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithoutProfileFields() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-fields.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), notNullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutPostId() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-id.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutPostTime() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-time.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutPostText() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-text.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithInvalidPostTime() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-time.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithValidPostTime() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-time.xml");
+ final List<Post> posts = soneParser.parseSone(sone, inputStream).getPosts();
+ assertThat(posts, is(createdPosts));
+ assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
+ assertThat(posts.get(0).getId(), is("post-id"));
+ assertThat(posts.get(0).getTime(), is(1407197508000L));
+ assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
+ assertThat(posts.get(0).getText(), is("text"));
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithRecipient() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-recipient.xml");
+ final List<Post> posts = soneParser.parseSone(sone, inputStream).getPosts();
+ assertThat(posts, is(createdPosts));
+ assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
+ assertThat(posts.get(0).getId(), is("post-id"));
+ assertThat(posts.get(0).getTime(), is(1407197508000L));
+ assertThat(posts.get(0).getRecipientId(), is(of(
+ "1234567890123456789012345678901234567890123")));
+ assertThat(posts.get(0).getText(), is("text"));
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithInvalidRecipient() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-recipient.xml");
+ final List<Post> posts = soneParser.parseSone(sone, inputStream).getPosts();
+ assertThat(posts, is(createdPosts));
+ assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
+ assertThat(posts.get(0).getId(), is("post-id"));
+ assertThat(posts.get(0).getTime(), is(1407197508000L));
+ assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
+ assertThat(posts.get(0).getText(), is("text"));
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutPostReplyId() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-id.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutPostReplyPostId() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-post-id.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutPostReplyTime() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-time.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutPostReplyText() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-text.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithInvalidPostReplyTime() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-reply-time.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithValidPostReplyTime() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-reply-time.xml");
+ final Set<PostReply> postReplies = soneParser.parseSone(sone, inputStream).getReplies();
+ assertThat(postReplies, is(createdPostReplies));
+ PostReply postReply = createdPostReplies.iterator().next();
+ assertThat(postReply.getId(), is("reply-id"));
+ assertThat(postReply.getPostId(), is("post-id"));
+ assertThat(postReply.getPost().get().getId(), is("post-id"));
+ assertThat(postReply.getSone().getId(), is("identity"));
+ assertThat(postReply.getTime(), is(1407197508000L));
+ assertThat(postReply.getText(), is("reply-text"));
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithoutLikedPostIds() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-ids.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), not(
+ nullValue()));
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithLikedPostIds() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-ids.xml");
+ assertThat(soneParser.parseSone(sone, inputStream).getLikedPostIds(), is(
+ (Set<String>) ImmutableSet.of("liked-post-id")));
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithoutLikedPostReplyIds() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-reply-ids.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), not(
+ nullValue()));
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithLikedPostReplyIds() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-reply-ids.xml");
+ assertThat(soneParser.parseSone(sone, inputStream).getLikedReplyIds(), is(
+ (Set<String>) ImmutableSet.of("liked-post-reply-id")));
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithoutAlbums() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-albums.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), not(
+ nullValue()));
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutAlbumId() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-id.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutAlbumTitle() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-title.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithNestedAlbums() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-multiple-albums.xml");
+ final Sone parsedSone = soneParser.parseSone(sone, inputStream);
+ assertThat(parsedSone, not(nullValue()));
+ assertThat(parsedSone.getRootAlbum().getAlbums(), hasSize(1));
+ Album album = parsedSone.getRootAlbum().getAlbums().get(0);
+ assertThat(album.getId(), is("album-id-1"));
+ assertThat(album.getTitle(), is("album-title"));
+ assertThat(album.getDescription(), is("album-description"));
+ assertThat(album.getAlbums(), hasSize(1));
+ Album nestedAlbum = album.getAlbums().get(0);
+ assertThat(nestedAlbum.getId(), is("album-id-2"));
+ assertThat(nestedAlbum.getTitle(), is("album-title-2"));
+ assertThat(nestedAlbum.getDescription(), is("album-description-2"));
+ assertThat(nestedAlbum.getAlbums(), hasSize(0));
+ }
+
+ @Test
+ public void parsingASoneFailsWithInvalidParentAlbumId() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-parent-album-id.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithoutImages() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-images.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), not(
+ nullValue()));
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutImageId() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-id.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutImageTime() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-time.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutImageKey() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-key.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutImageTitle() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-title.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutImageWidth() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-width.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithoutImageHeight() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-height.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithInvalidImageWidth() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-width.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneFailsWithInvalidImageHeight() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-height.xml");
+ assertThat(soneParser.parseSone(sone, inputStream), nullValue());
+ }
+
+ @Test
+ public void parsingASoneSucceedsWithImage() throws SoneException, MalformedURLException {
+ InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-image.xml");
+ final Sone sone = soneParser.parseSone(this.sone, inputStream);
+ assertThat(sone, not(nullValue()));
+ assertThat(sone.getRootAlbum().getAlbums(), hasSize(1));
+ assertThat(sone.getRootAlbum().getAlbums().get(0).getImages(), hasSize(1));
+ Image image = sone.getRootAlbum().getAlbums().get(0).getImages().get(0);
+ assertThat(image.getId(), is("image-id"));
+ assertThat(image.getCreationTime(), is(1407197508000L));
+ assertThat(image.getKey(), is("KSK@GPLv3.txt"));
+ assertThat(image.getTitle(), is("image-title"));
+ assertThat(image.getDescription(), is("image-description"));
+ assertThat(image.getWidth(), is(1920));
+ assertThat(image.getHeight(), is(1080));
+ assertThat(sone.getProfile().getAvatar(), is("image-id"));
+ }
+
+
+}