package net.pterodactylus.sone.core;
-import static com.google.common.base.Optional.absent;
-import static com.google.common.base.Optional.fromNullable;
-import static com.google.common.base.Optional.of;
+import static com.google.common.base.Objects.equal;
import static java.lang.String.format;
import static java.util.logging.Level.OFF;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.logging.Logger;
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.Field;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.database.Database;
import net.pterodactylus.sone.database.SoneBuilder.SoneCreated;
import net.pterodactylus.sone.database.memory.MemoryDatabase;
-import com.google.common.base.Joiner;
import com.google.common.base.Optional;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
import org.junit.Test;
/**
private final Core core = mock(Core.class);
private final Database database = new MemoryDatabase(null);
private final Sone originalSone = database.newSoneBuilder().by("test").using(new Client("TestClient", "1.0")).build(Optional.<SoneCreated>absent());
- private final SoneXmlBuilder soneXmlBuilder = new SoneXmlBuilder();
private final SoneParser soneParser = new SoneParser();
public SoneParserTest() {
@Test
public void verifyThatANegativeProtocolVersionCausesAnError() {
- Optional<Sone> sone = soneParser.parseSone(database, originalSone, soneXmlBuilder.setProtocolVersion("-1").get());
+ Optional<Sone> sone = soneParser.parseSone(database, originalSone, getXml("negative-protocol-version"));
assertThat(sone, notNullValue());
assertThat(sone.isPresent(), is(false));
}
@Test
public void verifyThatATooLargeProtocolVersionCausesAnError() {
- Optional<Sone> sone = soneParser.parseSone(database, originalSone, soneXmlBuilder.setProtocolVersion("1").get());
+ Optional<Sone> sone = soneParser.parseSone(database, originalSone, getXml("too-large-protocol-version"));
assertThat(sone, notNullValue());
assertThat(sone.isPresent(), is(false));
}
@Test
public void verifyThatAMissingTimeCausesAnError() {
- Optional<Sone> sone = soneParser.parseSone(database, originalSone, soneXmlBuilder.removeTime().get());
+ Optional<Sone> sone = soneParser.parseSone(database, originalSone, getXml("missing-time"));
assertThat(sone, notNullValue());
assertThat(sone.isPresent(), is(false));
}
@Test
public void verifyThatAMissingClientCausesTheOriginalClientToBeUsed() {
- Optional<Sone> sone = soneParser.parseSone(database, originalSone, soneXmlBuilder.removeClientInformation().get());
+ Optional<Sone> sone = soneParser.parseSone(database, originalSone, getXml("missing-client"));
assertThat(sone, notNullValue());
assertThat(sone.isPresent(), is(true));
assertThat(sone.get().getClient(), notNullValue());
assertThat(sone.get().getProfile().getBirthMonth(), is(10));
assertThat(sone.get().getProfile().getBirthDay(), is(22));
assertThat(sone.get().getProfile().getAvatar(), is("96431abe-3add-11e3-8a46-67047503bf6d"));
- }
+ assertThat(sone.get().getProfile().getFields(), contains(
+ fieldMatcher("Field1", "Value1"),
+ fieldMatcher("Field2", "Value2")
+ ));
+ assertThat(sone.get().getPosts(), contains(
+ postMatcher("d8c9586e-3adb-11e3-bb31-171fc040e645", "0rpD4gL8mszav2trndhIdKIxvKUCNAe2kjA3dLV8CVU", 1382420181000L, "Hello, User!"),
+ postMatcher("bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3", null, 1382420140000L, "Hello, World!")
+ ));
+ assertThat(sone.get().getReplies(), containsInAnyOrder(
+ postReplyMatcher("f09fa448-3adb-11e3-a783-ab54a11aacc4", "bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3", 1382420224000L, "Talking to myself."),
+ postReplyMatcher("0a376440-3adc-11e3-8f45-c7cc157436a5", "11ebe86e-3adc-11e3-b7b9-7f2c88018a33", 1382420271000L, "Talking to somebody I can't see.")
+ ));
+ assertThat(sone.get().getLikedPostIds(), containsInAnyOrder(
+ "bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3",
+ "305d85e6-3adc-11e3-be45-8b53dd91f0af"
+ ));
+ assertThat(sone.get().getLikedReplyIds(), containsInAnyOrder(
+ "f09fa448-3adb-11e3-a783-ab54a11aacc4",
+ "3ba28960-3adc-11e3-93c7-6713d170f44c"
+ ));
- public InputStream getInputStream(String content) throws UnsupportedEncodingException {
- return new ByteArrayInputStream(content.getBytes("UTF-8"));
}
- private InputStream getXml(String name) {
- return getClass().getResourceAsStream(format("/sone-parser/%s.xml", name));
- }
+ private Matcher<PostReply> postReplyMatcher(final String id, final String postId, final long time, final String text) {
+ return new TypeSafeMatcher<PostReply>() {
+ @Override
+ protected boolean matchesSafely(PostReply postReply) {
+ return postReply.getId().equals(id) && postReply.getPostId().equals(postId) && (postReply.getTime() == time) && postReply.getText().equals(text);
+ }
- private static class SoneXmlBuilder {
-
- private Optional<Long> time = of(1000L);
- private Optional<String> protocolVersion = of("0");
- private Optional<String> clientInformation = of("<name>Test-Client</name><version>1.0</version>");
- private Optional<String> profile = of(Joiner.on("").join(
- "<first-name>First</first-name>",
- "<middle-name>M.</middle-name>",
- "<last-name>Last</last-name>",
- "<birth-year>2000</birth-year>",
- "<birth-month>9</birth-month>",
- "<birth-day>13</birth-day>",
- "<avatar>avatar-id</avatar>",
- "<fields>",
- "<field><field-name>Custom Field</field-name><field-value>Custom Value</field-value></field>",
- "</fields>"
- ));
- private Optional<String> posts = of("<post><id>post-id</id><time>1</time><recipient>recipient</recipient><text>Hello!</text></post>");
- private Optional<String> replies = of("<reply><id>reply-id</id><post-id>post-id</post-id><time>2</time><text>Reply!</text></reply>");
-
- public SoneXmlBuilder removeTime() {
- time = absent();
- return this;
- }
-
- public SoneXmlBuilder setProtocolVersion(String protocolVersion) {
- this.protocolVersion = fromNullable(protocolVersion);
- return this;
- }
-
- public SoneXmlBuilder removeProtocolVersion() {
- this.protocolVersion = absent();
- return this;
- }
-
- public SoneXmlBuilder setClientInformation(String name, String version) {
- clientInformation = of("<name>" + name + "</name><version>" + version + "</version>");
- return this;
- }
-
- public SoneXmlBuilder removeClientInformation() {
- clientInformation = absent();
- return this;
- }
-
- public SoneXmlBuilder removeProfile() {
- profile = absent();
- return this;
- }
-
- public SoneXmlBuilder removePost() {
- posts = absent();
- return this;
- }
-
- public SoneXmlBuilder removeReply() {
- replies = absent();
- return this;
- }
-
- public InputStream get() {
- StringBuilder content = new StringBuilder();
- content.append("<sone>");
- if (time.isPresent()) {
- content.append(createXmlElement("time", String.valueOf(time.get())));
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("PostReply(")
+ .appendValue(id).appendText(", ")
+ .appendValue(postId).appendText(", ")
+ .appendValue(time).appendText(", ")
+ .appendValue(text).appendText(")");
+ }
+
+ @Override
+ protected void describeMismatchSafely(PostReply postReply, Description mismatchDescription) {
+ mismatchDescription.appendText("PostReply(")
+ .appendValue(postReply.getId()).appendText(", ")
+ .appendValue(postReply.getPostId()).appendText(", ")
+ .appendValue(postReply.getTime()).appendText(", ")
+ .appendValue(postReply.getText()).appendText(")");
}
- if (protocolVersion.isPresent()) {
- content.append(createXmlElement("protocol-version", protocolVersion.get()));
+ };
+ }
+
+ private Matcher<Post> postMatcher(final String id, final String recipient, final long time, final String text) {
+ return new TypeSafeMatcher<Post>() {
+ @Override
+ protected boolean matchesSafely(Post post) {
+ return post.getId().equals(id) && equal(post.getRecipientId().orNull(), recipient) && (post.getTime() == time) && post.getText().equals(text);
}
- if (clientInformation.isPresent()) {
- content.append(createXmlElement("client", clientInformation.get()));
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Post(")
+ .appendValue(id).appendText(", ")
+ .appendValue(recipient).appendText(", ")
+ .appendValue(time).appendText(", ")
+ .appendValue(text).appendText(")");
}
- if (profile.isPresent()) {
- content.append(createXmlElement("profile", profile.get()));
+
+ @Override
+ protected void describeMismatchSafely(Post post, Description mismatchDescription) {
+ mismatchDescription.appendText("Post(")
+ .appendValue(post.getId()).appendText(", ")
+ .appendValue(post.getRecipientId().orNull()).appendText(", ")
+ .appendValue(post.getTime()).appendText(", ")
+ .appendValue(post.getText()).appendText(")");
}
- if (posts.isPresent()) {
- content.append(createXmlElement("posts", posts.get()));
+ };
+ }
+
+ private Matcher<Field> fieldMatcher(final String name, final String value) {
+ return new TypeSafeMatcher<Field>() {
+ @Override
+ protected boolean matchesSafely(Field field) {
+ return field.getName().equals(name) && field.getValue().equals(value);
}
- content.append("</sone>");
- try {
- String xmlString = content.toString();
- return new ByteArrayInputStream(xmlString.getBytes("UTF-8"));
- } catch (UnsupportedEncodingException e) {
- /* ignore. */
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("field named ").appendValue(name).appendText(" with value ").appendValue(value);
}
- return null;
- }
- private String createXmlElement(String xmlElement, String content) {
- return format("<%s>%s</%1$s>", xmlElement, content);
- }
+ @Override
+ protected void describeMismatchSafely(Field field, Description mismatchDescription) {
+ mismatchDescription.appendText("field named ").appendValue(field.getName()).appendText(" with value ").appendValue(field.getValue());
+ }
+ };
+ }
+ private InputStream getXml(String name) {
+ return getClass().getResourceAsStream(format("/sone-parser/%s.xml", name));
}
}
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<sone>
+
+ <time>1382419919000</time>
+ <protocol-version>0</protocol-version>
+
+ <profile>
+ <first-name>First</first-name>
+ <middle-name>M.</middle-name>
+ <last-name>Last</last-name>
+ <birth-day>22</birth-day>
+ <birth-month>10</birth-month>
+ <birth-year>2013</birth-year>
+ <avatar>96431abe-3add-11e3-8a46-67047503bf6d</avatar>
+ <fields>
+ <field>
+ <field-name>Field1</field-name>
+ <field-value>Value1</field-value>
+ </field>
+ <field>
+ <field-name>Field2</field-name>
+ <field-value>Value2</field-value>
+ </field>
+ </fields>
+ </profile>
+
+ <posts>
+ <post>
+ <id>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</id>
+ <recipient></recipient>
+ <time>1382420140000</time>
+ <text>Hello, World!</text>
+ </post>
+ <post>
+ <id>d8c9586e-3adb-11e3-bb31-171fc040e645</id>
+ <recipient>0rpD4gL8mszav2trndhIdKIxvKUCNAe2kjA3dLV8CVU</recipient>
+ <time>1382420181000</time>
+ <text>Hello, User!</text>
+ </post>
+ </posts>
+
+ <replies>
+ <reply>
+ <id>f09fa448-3adb-11e3-a783-ab54a11aacc4</id>
+ <post-id>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</post-id>
+ <time>1382420224000</time>
+ <text>Talking to myself.</text>
+ </reply>
+ <reply>
+ <id>0a376440-3adc-11e3-8f45-c7cc157436a5</id>
+ <post-id>11ebe86e-3adc-11e3-b7b9-7f2c88018a33</post-id>
+ <time>1382420271000</time>
+ <text>Talking to somebody I can't see.</text>
+ </reply>
+ </replies>
+
+ <post-likes>
+ <post-like>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</post-like>
+ <post-like>305d85e6-3adc-11e3-be45-8b53dd91f0af</post-like>
+ </post-likes>
+
+ <reply-likes>
+ <reply-like>f09fa448-3adb-11e3-a783-ab54a11aacc4</reply-like>
+ <reply-like>3ba28960-3adc-11e3-93c7-6713d170f44c</reply-like>
+ </reply-likes>
+
+ <albums>
+ <album>
+ <id>6a73c6e6-3adc-11e3-b091-577b10a725ad</id>
+ <title>Album1</title>
+ <description>First album with stuff.</description>
+ <album-image>e3707102-3adc-11e3-b828-9f4de99f0bc4</album-image>
+ <images>
+ <image>
+ <id>8966f69a-3adc-11e3-802d-0f57b63c8809</id>
+ <creation-time>1382420473000</creation-time>
+ <key>SSK@JvW9oZ8AriNGbwBosJD1gyMdiMHc5AgSmhNBU1CmAb8,na1IPpMcYiZLaWSv9EWq9NcDPPGplQBw8kJsLobXIG4,AQACAAE/8966f69a-3adc-11e3-802d-0f57b63c8809.jpg</key>
+ <title>Stuff</title>
+ <description>Yes, it's stuff!</description>
+ <width>640</width>
+ <height>480</height>
+ </image>
+ <image>
+ <id>e3707102-3adc-11e3-b828-9f4de99f0bc4</id>
+ <creation-time>1382420624000</creation-time>
+ <key>SSK@dM1er7A9tG242bG-Xxy0kmprD7YkqEDE9mrZ98C~L3E,Np3iWiaGyd~er86edP9ndpH3pPKk2owDmAT2TQZNBA0,AQACAAE/e3707102-3adc-11e3-b828-9f4de99f0bc4.jpg</key>
+ <title>More Stuff</title>
+ <description>Yes, it's more stuff!</description>
+ <width>640</width>
+ <height>360</height>
+ </image>
+ </images>
+ </album>
+ <album>
+ <id>1e73b52a-3add-11e3-ba45-cb0c28da9c2a</id>
+ <parent>6a73c6e6-3adc-11e3-b091-577b10a725ad</parent>
+ <title>Nested Album</title>
+ <description>Nested album with stuff.</description>
+ <album-image>312182ba-3add-11e3-bd76-13e7278f0161</album-image>
+ <images>
+ <image>
+ <id>312182ba-3add-11e3-bd76-13e7278f0161</id>
+ <creation-time>1382420756000</creation-time>
+ <key>SSK@0rpD4gL8mszav2trndhIdKIxvKUCNAe2kjA3dLV8CVU,2KP8WDSaQvyJ16pXBHQrE1vj~fAaYPIsk35lM-ec4B0,AQACAAE/312182ba-3add-11e3-bd76-13e7278f0161.jpg</key>
+ <title>Other Stuff</title>
+ <description>Yes, it's other stuff!</description>
+ <width>640</width>
+ <height>272</height>
+ </image>
+ </images>
+ </album>
+ <album>
+ <id>8a04b5fa-3add-11e3-afe2-676e721c04bd</id>
+ <title>Avatars</title>
+ <description>The avatar collection.</description>
+ <album-image>96431abe-3add-11e3-8a46-67047503bf6d</album-image>
+ <images>
+ <image>
+ <id>96431abe-3add-11e3-8a46-67047503bf6d</id>
+ <creation-time>1382420923000</creation-time>
+ <key>SSK@v6Js3ZJBMB23IB0JvdgiPynHQBYEvkFSi3~D8RDdj3s,FfLhjRC4bqYLHVexT33Pv6agXl6Ft~TDhX1jIM5w2n4,AQACAAE/96431abe-3add-11e3-8a46-67047503bf6d.png</key>
+ <title>Avatar 1</title>
+ <description>The first avatar.</description>
+ <width>64</width>
+ <height>64</height>
+ </image>
+ </images>
+ </album>
+ </albums>
+
+</sone>
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<sone>
+
+ <protocol-version>0</protocol-version>
+
+ <client>
+ <name>Sone</name>
+ <version>0.8.7</version>
+ </client>
+
+ <profile>
+ <first-name>First</first-name>
+ <middle-name>M.</middle-name>
+ <last-name>Last</last-name>
+ <birth-day>22</birth-day>
+ <birth-month>10</birth-month>
+ <birth-year>2013</birth-year>
+ <avatar>96431abe-3add-11e3-8a46-67047503bf6d</avatar>
+ <fields>
+ <field>
+ <field-name>Field1</field-name>
+ <field-value>Value1</field-value>
+ </field>
+ <field>
+ <field-name>Field2</field-name>
+ <field-value>Value2</field-value>
+ </field>
+ </fields>
+ </profile>
+
+ <posts>
+ <post>
+ <id>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</id>
+ <recipient></recipient>
+ <time>1382420140000</time>
+ <text>Hello, World!</text>
+ </post>
+ <post>
+ <id>d8c9586e-3adb-11e3-bb31-171fc040e645</id>
+ <recipient>0rpD4gL8mszav2trndhIdKIxvKUCNAe2kjA3dLV8CVU</recipient>
+ <time>1382420181000</time>
+ <text>Hello, User!</text>
+ </post>
+ </posts>
+
+ <replies>
+ <reply>
+ <id>f09fa448-3adb-11e3-a783-ab54a11aacc4</id>
+ <post-id>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</post-id>
+ <time>1382420224000</time>
+ <text>Talking to myself.</text>
+ </reply>
+ <reply>
+ <id>0a376440-3adc-11e3-8f45-c7cc157436a5</id>
+ <post-id>11ebe86e-3adc-11e3-b7b9-7f2c88018a33</post-id>
+ <time>1382420271000</time>
+ <text>Talking to somebody I can't see.</text>
+ </reply>
+ </replies>
+
+ <post-likes>
+ <post-like>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</post-like>
+ <post-like>305d85e6-3adc-11e3-be45-8b53dd91f0af</post-like>
+ </post-likes>
+
+ <reply-likes>
+ <reply-like>f09fa448-3adb-11e3-a783-ab54a11aacc4</reply-like>
+ <reply-like>3ba28960-3adc-11e3-93c7-6713d170f44c</reply-like>
+ </reply-likes>
+
+ <albums>
+ <album>
+ <id>6a73c6e6-3adc-11e3-b091-577b10a725ad</id>
+ <title>Album1</title>
+ <description>First album with stuff.</description>
+ <album-image>e3707102-3adc-11e3-b828-9f4de99f0bc4</album-image>
+ <images>
+ <image>
+ <id>8966f69a-3adc-11e3-802d-0f57b63c8809</id>
+ <creation-time>1382420473000</creation-time>
+ <key>SSK@JvW9oZ8AriNGbwBosJD1gyMdiMHc5AgSmhNBU1CmAb8,na1IPpMcYiZLaWSv9EWq9NcDPPGplQBw8kJsLobXIG4,AQACAAE/8966f69a-3adc-11e3-802d-0f57b63c8809.jpg</key>
+ <title>Stuff</title>
+ <description>Yes, it's stuff!</description>
+ <width>640</width>
+ <height>480</height>
+ </image>
+ <image>
+ <id>e3707102-3adc-11e3-b828-9f4de99f0bc4</id>
+ <creation-time>1382420624000</creation-time>
+ <key>SSK@dM1er7A9tG242bG-Xxy0kmprD7YkqEDE9mrZ98C~L3E,Np3iWiaGyd~er86edP9ndpH3pPKk2owDmAT2TQZNBA0,AQACAAE/e3707102-3adc-11e3-b828-9f4de99f0bc4.jpg</key>
+ <title>More Stuff</title>
+ <description>Yes, it's more stuff!</description>
+ <width>640</width>
+ <height>360</height>
+ </image>
+ </images>
+ </album>
+ <album>
+ <id>1e73b52a-3add-11e3-ba45-cb0c28da9c2a</id>
+ <parent>6a73c6e6-3adc-11e3-b091-577b10a725ad</parent>
+ <title>Nested Album</title>
+ <description>Nested album with stuff.</description>
+ <album-image>312182ba-3add-11e3-bd76-13e7278f0161</album-image>
+ <images>
+ <image>
+ <id>312182ba-3add-11e3-bd76-13e7278f0161</id>
+ <creation-time>1382420756000</creation-time>
+ <key>SSK@0rpD4gL8mszav2trndhIdKIxvKUCNAe2kjA3dLV8CVU,2KP8WDSaQvyJ16pXBHQrE1vj~fAaYPIsk35lM-ec4B0,AQACAAE/312182ba-3add-11e3-bd76-13e7278f0161.jpg</key>
+ <title>Other Stuff</title>
+ <description>Yes, it's other stuff!</description>
+ <width>640</width>
+ <height>272</height>
+ </image>
+ </images>
+ </album>
+ <album>
+ <id>8a04b5fa-3add-11e3-afe2-676e721c04bd</id>
+ <title>Avatars</title>
+ <description>The avatar collection.</description>
+ <album-image>96431abe-3add-11e3-8a46-67047503bf6d</album-image>
+ <images>
+ <image>
+ <id>96431abe-3add-11e3-8a46-67047503bf6d</id>
+ <creation-time>1382420923000</creation-time>
+ <key>SSK@v6Js3ZJBMB23IB0JvdgiPynHQBYEvkFSi3~D8RDdj3s,FfLhjRC4bqYLHVexT33Pv6agXl6Ft~TDhX1jIM5w2n4,AQACAAE/96431abe-3add-11e3-8a46-67047503bf6d.png</key>
+ <title>Avatar 1</title>
+ <description>The first avatar.</description>
+ <width>64</width>
+ <height>64</height>
+ </image>
+ </images>
+ </album>
+ </albums>
+
+</sone>
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<sone>
+
+ <time>1382419919000</time>
+ <protocol-version>-1</protocol-version>
+
+ <client>
+ <name>Sone</name>
+ <version>0.8.7</version>
+ </client>
+
+ <profile>
+ <first-name>First</first-name>
+ <middle-name>M.</middle-name>
+ <last-name>Last</last-name>
+ <birth-day>22</birth-day>
+ <birth-month>10</birth-month>
+ <birth-year>2013</birth-year>
+ <avatar>96431abe-3add-11e3-8a46-67047503bf6d</avatar>
+ <fields>
+ <field>
+ <field-name>Field1</field-name>
+ <field-value>Value1</field-value>
+ </field>
+ <field>
+ <field-name>Field2</field-name>
+ <field-value>Value2</field-value>
+ </field>
+ </fields>
+ </profile>
+
+ <posts>
+ <post>
+ <id>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</id>
+ <recipient></recipient>
+ <time>1382420140000</time>
+ <text>Hello, World!</text>
+ </post>
+ <post>
+ <id>d8c9586e-3adb-11e3-bb31-171fc040e645</id>
+ <recipient>0rpD4gL8mszav2trndhIdKIxvKUCNAe2kjA3dLV8CVU</recipient>
+ <time>1382420181000</time>
+ <text>Hello, User!</text>
+ </post>
+ </posts>
+
+ <replies>
+ <reply>
+ <id>f09fa448-3adb-11e3-a783-ab54a11aacc4</id>
+ <post-id>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</post-id>
+ <time>1382420224000</time>
+ <text>Talking to myself.</text>
+ </reply>
+ <reply>
+ <id>0a376440-3adc-11e3-8f45-c7cc157436a5</id>
+ <post-id>11ebe86e-3adc-11e3-b7b9-7f2c88018a33</post-id>
+ <time>1382420271000</time>
+ <text>Talking to somebody I can't see.</text>
+ </reply>
+ </replies>
+
+ <post-likes>
+ <post-like>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</post-like>
+ <post-like>305d85e6-3adc-11e3-be45-8b53dd91f0af</post-like>
+ </post-likes>
+
+ <reply-likes>
+ <reply-like>f09fa448-3adb-11e3-a783-ab54a11aacc4</reply-like>
+ <reply-like>3ba28960-3adc-11e3-93c7-6713d170f44c</reply-like>
+ </reply-likes>
+
+ <albums>
+ <album>
+ <id>6a73c6e6-3adc-11e3-b091-577b10a725ad</id>
+ <title>Album1</title>
+ <description>First album with stuff.</description>
+ <album-image>e3707102-3adc-11e3-b828-9f4de99f0bc4</album-image>
+ <images>
+ <image>
+ <id>8966f69a-3adc-11e3-802d-0f57b63c8809</id>
+ <creation-time>1382420473000</creation-time>
+ <key>SSK@JvW9oZ8AriNGbwBosJD1gyMdiMHc5AgSmhNBU1CmAb8,na1IPpMcYiZLaWSv9EWq9NcDPPGplQBw8kJsLobXIG4,AQACAAE/8966f69a-3adc-11e3-802d-0f57b63c8809.jpg</key>
+ <title>Stuff</title>
+ <description>Yes, it's stuff!</description>
+ <width>640</width>
+ <height>480</height>
+ </image>
+ <image>
+ <id>e3707102-3adc-11e3-b828-9f4de99f0bc4</id>
+ <creation-time>1382420624000</creation-time>
+ <key>SSK@dM1er7A9tG242bG-Xxy0kmprD7YkqEDE9mrZ98C~L3E,Np3iWiaGyd~er86edP9ndpH3pPKk2owDmAT2TQZNBA0,AQACAAE/e3707102-3adc-11e3-b828-9f4de99f0bc4.jpg</key>
+ <title>More Stuff</title>
+ <description>Yes, it's more stuff!</description>
+ <width>640</width>
+ <height>360</height>
+ </image>
+ </images>
+ </album>
+ <album>
+ <id>1e73b52a-3add-11e3-ba45-cb0c28da9c2a</id>
+ <parent>6a73c6e6-3adc-11e3-b091-577b10a725ad</parent>
+ <title>Nested Album</title>
+ <description>Nested album with stuff.</description>
+ <album-image>312182ba-3add-11e3-bd76-13e7278f0161</album-image>
+ <images>
+ <image>
+ <id>312182ba-3add-11e3-bd76-13e7278f0161</id>
+ <creation-time>1382420756000</creation-time>
+ <key>SSK@0rpD4gL8mszav2trndhIdKIxvKUCNAe2kjA3dLV8CVU,2KP8WDSaQvyJ16pXBHQrE1vj~fAaYPIsk35lM-ec4B0,AQACAAE/312182ba-3add-11e3-bd76-13e7278f0161.jpg</key>
+ <title>Other Stuff</title>
+ <description>Yes, it's other stuff!</description>
+ <width>640</width>
+ <height>272</height>
+ </image>
+ </images>
+ </album>
+ <album>
+ <id>8a04b5fa-3add-11e3-afe2-676e721c04bd</id>
+ <title>Avatars</title>
+ <description>The avatar collection.</description>
+ <album-image>96431abe-3add-11e3-8a46-67047503bf6d</album-image>
+ <images>
+ <image>
+ <id>96431abe-3add-11e3-8a46-67047503bf6d</id>
+ <creation-time>1382420923000</creation-time>
+ <key>SSK@v6Js3ZJBMB23IB0JvdgiPynHQBYEvkFSi3~D8RDdj3s,FfLhjRC4bqYLHVexT33Pv6agXl6Ft~TDhX1jIM5w2n4,AQACAAE/96431abe-3add-11e3-8a46-67047503bf6d.png</key>
+ <title>Avatar 1</title>
+ <description>The first avatar.</description>
+ <width>64</width>
+ <height>64</height>
+ </image>
+ </images>
+ </album>
+ </albums>
+
+</sone>
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<sone>
+
+ <time>1382419919000</time>
+ <protocol-version>1</protocol-version>
+
+ <client>
+ <name>Sone</name>
+ <version>0.8.7</version>
+ </client>
+
+ <profile>
+ <first-name>First</first-name>
+ <middle-name>M.</middle-name>
+ <last-name>Last</last-name>
+ <birth-day>22</birth-day>
+ <birth-month>10</birth-month>
+ <birth-year>2013</birth-year>
+ <avatar>96431abe-3add-11e3-8a46-67047503bf6d</avatar>
+ <fields>
+ <field>
+ <field-name>Field1</field-name>
+ <field-value>Value1</field-value>
+ </field>
+ <field>
+ <field-name>Field2</field-name>
+ <field-value>Value2</field-value>
+ </field>
+ </fields>
+ </profile>
+
+ <posts>
+ <post>
+ <id>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</id>
+ <recipient></recipient>
+ <time>1382420140000</time>
+ <text>Hello, World!</text>
+ </post>
+ <post>
+ <id>d8c9586e-3adb-11e3-bb31-171fc040e645</id>
+ <recipient>0rpD4gL8mszav2trndhIdKIxvKUCNAe2kjA3dLV8CVU</recipient>
+ <time>1382420181000</time>
+ <text>Hello, User!</text>
+ </post>
+ </posts>
+
+ <replies>
+ <reply>
+ <id>f09fa448-3adb-11e3-a783-ab54a11aacc4</id>
+ <post-id>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</post-id>
+ <time>1382420224000</time>
+ <text>Talking to myself.</text>
+ </reply>
+ <reply>
+ <id>0a376440-3adc-11e3-8f45-c7cc157436a5</id>
+ <post-id>11ebe86e-3adc-11e3-b7b9-7f2c88018a33</post-id>
+ <time>1382420271000</time>
+ <text>Talking to somebody I can't see.</text>
+ </reply>
+ </replies>
+
+ <post-likes>
+ <post-like>bbb7ebf0-3adb-11e3-8a0b-630cd8f21cf3</post-like>
+ <post-like>305d85e6-3adc-11e3-be45-8b53dd91f0af</post-like>
+ </post-likes>
+
+ <reply-likes>
+ <reply-like>f09fa448-3adb-11e3-a783-ab54a11aacc4</reply-like>
+ <reply-like>3ba28960-3adc-11e3-93c7-6713d170f44c</reply-like>
+ </reply-likes>
+
+ <albums>
+ <album>
+ <id>6a73c6e6-3adc-11e3-b091-577b10a725ad</id>
+ <title>Album1</title>
+ <description>First album with stuff.</description>
+ <album-image>e3707102-3adc-11e3-b828-9f4de99f0bc4</album-image>
+ <images>
+ <image>
+ <id>8966f69a-3adc-11e3-802d-0f57b63c8809</id>
+ <creation-time>1382420473000</creation-time>
+ <key>SSK@JvW9oZ8AriNGbwBosJD1gyMdiMHc5AgSmhNBU1CmAb8,na1IPpMcYiZLaWSv9EWq9NcDPPGplQBw8kJsLobXIG4,AQACAAE/8966f69a-3adc-11e3-802d-0f57b63c8809.jpg</key>
+ <title>Stuff</title>
+ <description>Yes, it's stuff!</description>
+ <width>640</width>
+ <height>480</height>
+ </image>
+ <image>
+ <id>e3707102-3adc-11e3-b828-9f4de99f0bc4</id>
+ <creation-time>1382420624000</creation-time>
+ <key>SSK@dM1er7A9tG242bG-Xxy0kmprD7YkqEDE9mrZ98C~L3E,Np3iWiaGyd~er86edP9ndpH3pPKk2owDmAT2TQZNBA0,AQACAAE/e3707102-3adc-11e3-b828-9f4de99f0bc4.jpg</key>
+ <title>More Stuff</title>
+ <description>Yes, it's more stuff!</description>
+ <width>640</width>
+ <height>360</height>
+ </image>
+ </images>
+ </album>
+ <album>
+ <id>1e73b52a-3add-11e3-ba45-cb0c28da9c2a</id>
+ <parent>6a73c6e6-3adc-11e3-b091-577b10a725ad</parent>
+ <title>Nested Album</title>
+ <description>Nested album with stuff.</description>
+ <album-image>312182ba-3add-11e3-bd76-13e7278f0161</album-image>
+ <images>
+ <image>
+ <id>312182ba-3add-11e3-bd76-13e7278f0161</id>
+ <creation-time>1382420756000</creation-time>
+ <key>SSK@0rpD4gL8mszav2trndhIdKIxvKUCNAe2kjA3dLV8CVU,2KP8WDSaQvyJ16pXBHQrE1vj~fAaYPIsk35lM-ec4B0,AQACAAE/312182ba-3add-11e3-bd76-13e7278f0161.jpg</key>
+ <title>Other Stuff</title>
+ <description>Yes, it's other stuff!</description>
+ <width>640</width>
+ <height>272</height>
+ </image>
+ </images>
+ </album>
+ <album>
+ <id>8a04b5fa-3add-11e3-afe2-676e721c04bd</id>
+ <title>Avatars</title>
+ <description>The avatar collection.</description>
+ <album-image>96431abe-3add-11e3-8a46-67047503bf6d</album-image>
+ <images>
+ <image>
+ <id>96431abe-3add-11e3-8a46-67047503bf6d</id>
+ <creation-time>1382420923000</creation-time>
+ <key>SSK@v6Js3ZJBMB23IB0JvdgiPynHQBYEvkFSi3~D8RDdj3s,FfLhjRC4bqYLHVexT33Pv6agXl6Ft~TDhX1jIM5w2n4,AQACAAE/96431abe-3add-11e3-8a46-67047503bf6d.png</key>
+ <title>Avatar 1</title>
+ <description>The first avatar.</description>
+ <width>64</width>
+ <height>64</height>
+ </image>
+ </images>
+ </album>
+ </albums>
+
+</sone>