Move post reply parsing to new configuration parser.
[Sone.git] / src / test / java / net / pterodactylus / sone / core / ConfigurationSoneParserTest.java
1 package net.pterodactylus.sone.core;
2
3 import static com.google.common.base.Optional.fromNullable;
4 import static com.google.common.base.Optional.of;
5 import static java.lang.System.currentTimeMillis;
6 import static java.util.UUID.randomUUID;
7 import static net.pterodactylus.sone.Matchers.isPost;
8 import static net.pterodactylus.sone.Matchers.isPostReply;
9 import static org.hamcrest.MatcherAssert.assertThat;
10 import static org.hamcrest.Matchers.contains;
11 import static org.hamcrest.Matchers.containsInAnyOrder;
12 import static org.hamcrest.Matchers.emptyIterable;
13 import static org.hamcrest.Matchers.hasSize;
14 import static org.hamcrest.Matchers.is;
15 import static org.hamcrest.Matchers.notNullValue;
16 import static org.hamcrest.Matchers.nullValue;
17 import static org.mockito.Matchers.anyString;
18 import static org.mockito.Matchers.eq;
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.Mockito.when;
21
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.concurrent.atomic.AtomicReference;
25
26 import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostFound;
27 import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostReplyFound;
28 import net.pterodactylus.sone.data.Post;
29 import net.pterodactylus.sone.data.PostReply;
30 import net.pterodactylus.sone.data.Profile;
31 import net.pterodactylus.sone.data.Profile.Field;
32 import net.pterodactylus.sone.data.Sone;
33 import net.pterodactylus.sone.database.PostBuilder;
34 import net.pterodactylus.sone.database.PostBuilderFactory;
35 import net.pterodactylus.sone.database.PostReplyBuilder;
36 import net.pterodactylus.sone.database.PostReplyBuilderFactory;
37 import net.pterodactylus.util.config.Configuration;
38 import net.pterodactylus.util.config.ConfigurationException;
39 import net.pterodactylus.util.config.Value;
40
41 import com.google.common.base.Optional;
42 import org.hamcrest.Matchers;
43 import org.junit.Test;
44 import org.mockito.invocation.InvocationOnMock;
45 import org.mockito.stubbing.Answer;
46
47 /**
48  * Unit test for {@link ConfigurationSoneParser}.
49  *
50  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
51  */
52 public class ConfigurationSoneParserTest {
53
54         private final Configuration configuration = mock(Configuration.class);
55         private final Sone sone = mock(Sone.class);
56         private final ConfigurationSoneParser configurationSoneParser;
57
58         public ConfigurationSoneParserTest() {
59                 when(sone.getId()).thenReturn("1");
60                 configurationSoneParser =
61                                 new ConfigurationSoneParser(configuration, sone);
62         }
63
64         @Test
65         public void emptyProfileIsLoadedCorrectly() {
66                 setupEmptyProfile();
67                 Profile profile = configurationSoneParser.parseProfile();
68                 assertThat(profile, notNullValue());
69                 assertThat(profile.getFirstName(), nullValue());
70                 assertThat(profile.getMiddleName(), nullValue());
71                 assertThat(profile.getLastName(), nullValue());
72                 assertThat(profile.getBirthDay(), nullValue());
73                 assertThat(profile.getBirthMonth(), nullValue());
74                 assertThat(profile.getBirthYear(), nullValue());
75                 assertThat(profile.getFields(), emptyIterable());
76         }
77
78         private void setupEmptyProfile() {
79                 when(configuration.getStringValue(anyString())).thenReturn(
80                                 new TestValue<String>(null));
81                 when(configuration.getIntValue(anyString())).thenReturn(
82                                 new TestValue<Integer>(null));
83         }
84
85         @Test
86         public void filledProfileWithFieldsIsParsedCorrectly() {
87                 setupFilledProfile();
88                 Profile profile = configurationSoneParser.parseProfile();
89                 assertThat(profile, notNullValue());
90                 assertThat(profile.getFirstName(), is("First"));
91                 assertThat(profile.getMiddleName(), is("M."));
92                 assertThat(profile.getLastName(), is("Last"));
93                 assertThat(profile.getBirthDay(), is(18));
94                 assertThat(profile.getBirthMonth(), is(12));
95                 assertThat(profile.getBirthYear(), is(1976));
96                 final List<Field> fields = profile.getFields();
97                 assertThat(fields, hasSize(2));
98                 assertThat(fields.get(0).getName(), is("Field1"));
99                 assertThat(fields.get(0).getValue(), is("Value1"));
100                 assertThat(fields.get(1).getName(), is("Field2"));
101                 assertThat(fields.get(1).getValue(), is("Value2"));
102         }
103
104         private void setupFilledProfile() {
105                 setupString("Sone/1/Profile/FirstName", "First");
106                 setupString("Sone/1/Profile/MiddleName", "M.");
107                 setupString("Sone/1/Profile/LastName", "Last");
108                 setupInteger("Sone/1/Profile/BirthDay", 18);
109                 setupInteger("Sone/1/Profile/BirthMonth", 12);
110                 setupInteger("Sone/1/Profile/BirthYear", 1976);
111                 setupString("Sone/1/Profile/Fields/0/Name", "Field1");
112                 setupString("Sone/1/Profile/Fields/0/Value", "Value1");
113                 setupString("Sone/1/Profile/Fields/1/Name", "Field2");
114                 setupString("Sone/1/Profile/Fields/1/Value", "Value2");
115                 setupString("Sone/1/Profile/Fields/2/Name", null);
116         }
117
118         private void setupString(String nodeName, String value) {
119                 when(configuration.getStringValue(eq(nodeName))).thenReturn(
120                                 new TestValue<String>(value));
121         }
122
123         private void setupInteger(String nodeName, Integer value) {
124                 when(configuration.getIntValue(eq(nodeName))).thenReturn(
125                                 new TestValue<Integer>(value));
126         }
127
128         @Test
129         public void postsAreParsedCorrectly() {
130                 setupCompletePosts();
131                 PostBuilderFactory postBuilderFactory = createPostBuilderFactory();
132                 Collection<Post> posts =
133                                 configurationSoneParser.parsePosts(postBuilderFactory);
134                 assertThat(posts,
135                                 Matchers.<Post>containsInAnyOrder(
136                                                 isPost("P0", 1000L, "T0", Optional.<String>absent()),
137                                                 isPost("P1", 1001L, "T1",
138                                                                 of("1234567890123456789012345678901234567890123"))));
139         }
140
141         private PostBuilderFactory createPostBuilderFactory() {
142                 PostBuilderFactory postBuilderFactory =
143                                 mock(PostBuilderFactory.class);
144                 when(postBuilderFactory.newPostBuilder()).thenAnswer(
145                                 new Answer<PostBuilder>() {
146                                         @Override
147                                         public PostBuilder answer(InvocationOnMock invocation)
148                                         throws Throwable {
149                                                 return new TestPostBuilder();
150                                         }
151                                 });
152                 return postBuilderFactory;
153         }
154
155         private void setupCompletePosts() {
156                 setupPost("0", "P0", 1000L, "T0", null);
157                 setupPost("1", "P1", 1001L, "T1",
158                                 "1234567890123456789012345678901234567890123");
159                 setupPost("2", null, 0L, null, null);
160         }
161
162         private void setupPost(String postNumber, String postId, long time,
163                         String text, String recipientId) {
164                 setupString("Sone/1/Posts/" + postNumber + "/ID", postId);
165                 setupLong("Sone/1/Posts/" + postNumber + "/Time", time);
166                 setupString("Sone/1/Posts/" + postNumber + "/Text", text);
167                 setupString("Sone/1/Posts/" + postNumber + "/Recipient", recipientId);
168         }
169
170         private void setupLong(String nodeName, Long value) {
171                 when(configuration.getLongValue(eq(nodeName))).thenReturn(
172                                 new TestValue<Long>(value));
173         }
174
175         @Test(expected = InvalidPostFound.class)
176         public void postWithoutTimeIsRecognized() {
177                 setupPostWithoutTime();
178                 configurationSoneParser.parsePosts(createPostBuilderFactory());
179         }
180
181         private void setupPostWithoutTime() {
182                 setupPost("0", "P0", 0L, "T0", null);
183         }
184
185         @Test(expected = InvalidPostFound.class)
186         public void postWithoutTextIsRecognized() {
187                 setupPostWithoutText();
188                 configurationSoneParser.parsePosts(createPostBuilderFactory());
189         }
190
191         private void setupPostWithoutText() {
192                 setupPost("0", "P0", 1000L, null, null);
193         }
194
195         @Test
196         public void postWithInvalidRecipientIdIsRecognized() {
197                 setupPostWithInvalidRecipientId();
198                 Collection<Post> posts = configurationSoneParser.parsePosts(
199                                 createPostBuilderFactory());
200                 assertThat(posts, contains(
201                                 isPost("P0", 1000L, "T0", Optional.<String>absent())));
202         }
203
204         private void setupPostWithInvalidRecipientId() {
205                 setupPost("0", "P0", 1000L, "T0", "123");
206                 setupPost("1", null, 0L, null, null);
207         }
208
209         @Test
210         public void postRepliesAreParsedCorrectly() {
211                 setupPostReplies();
212                 PostReplyBuilderFactory postReplyBuilderFactory =
213                                 new PostReplyBuilderFactory() {
214                                         @Override
215                                         public PostReplyBuilder newPostReplyBuilder() {
216                                                 return new TestPostReplyBuilder();
217                                         }
218                                 };
219                 Collection<PostReply> postReplies =
220                                 configurationSoneParser.parsePostReplies(
221                                                 postReplyBuilderFactory);
222                 assertThat(postReplies, hasSize(2));
223                 assertThat(postReplies,
224                                 containsInAnyOrder(isPostReply("R0", "P0", 1000L, "T0"),
225                                                 isPostReply("R1", "P1", 1001L, "T1")));
226         }
227
228         private void setupPostReplies() {
229                 setupPostReply("0", "R0", "P0", 1000L, "T0");
230                 setupPostReply("1", "R1", "P1", 1001L, "T1");
231                 setupPostReply("2", null, null, 0L, null);
232         }
233
234         private void setupPostReply(String postReplyNumber, String postReplyId,
235                         String postId, long time, String text) {
236                 setupString("Sone/1/Replies/" + postReplyNumber + "/ID", postReplyId);
237                 setupString("Sone/1/Replies/" + postReplyNumber + "/Post/ID", postId);
238                 setupLong("Sone/1/Replies/" + postReplyNumber + "/Time", time);
239                 setupString("Sone/1/Replies/" + postReplyNumber + "/Text", text);
240         }
241
242         @Test(expected = InvalidPostReplyFound.class)
243         public void missingPostIdIsRecognized() {
244                 setupPostReplyWithMissingPostId();
245                 configurationSoneParser.parsePostReplies(null);
246         }
247
248         private void setupPostReplyWithMissingPostId() {
249                 setupPostReply("0", "R0", null, 1000L, "T0");
250         }
251
252         @Test(expected = InvalidPostReplyFound.class)
253         public void missingPostReplyTimeIsRecognized() {
254                 setupPostReplyWithMissingPostReplyTime();
255                 configurationSoneParser.parsePostReplies(null);
256         }
257
258         private void setupPostReplyWithMissingPostReplyTime() {
259                 setupPostReply("0", "R0", "P0", 0L, "T0");
260         }
261
262         @Test(expected = InvalidPostReplyFound.class)
263         public void missingPostReplyTextIsRecognized() {
264                 setupPostReplyWithMissingPostReplyText();
265                 configurationSoneParser.parsePostReplies(null);
266         }
267
268         private void setupPostReplyWithMissingPostReplyText() {
269                 setupPostReply("0", "R0", "P0", 1000L, null);
270         }
271
272         private static class TestValue<T> implements Value<T> {
273
274                 private final AtomicReference<T> value = new AtomicReference<T>();
275
276                 public TestValue(T originalValue) {
277                         value.set(originalValue);
278                 }
279
280                 @Override
281                 public T getValue() throws ConfigurationException {
282                         return value.get();
283                 }
284
285                 @Override
286                 public T getValue(T defaultValue) {
287                         final T realValue = value.get();
288                         return (realValue != null) ? realValue : defaultValue;
289                 }
290
291                 @Override
292                 public void setValue(T newValue) throws ConfigurationException {
293                         value.set(newValue);
294                 }
295
296         }
297
298         private static class TestPostBuilder implements PostBuilder {
299
300                 private final Post post = mock(Post.class);
301                 private String recipientId = null;
302
303                 @Override
304                 public PostBuilder copyPost(Post post) throws NullPointerException {
305                         return this;
306                 }
307
308                 @Override
309                 public PostBuilder from(String senderId) {
310                         final Sone sone = mock(Sone.class);
311                         when(sone.getId()).thenReturn(senderId);
312                         when(post.getSone()).thenReturn(sone);
313                         return this;
314                 }
315
316                 @Override
317                 public PostBuilder randomId() {
318                         when(post.getId()).thenReturn(randomUUID().toString());
319                         return this;
320                 }
321
322                 @Override
323                 public PostBuilder withId(String id) {
324                         when(post.getId()).thenReturn(id);
325                         return this;
326                 }
327
328                 @Override
329                 public PostBuilder currentTime() {
330                         when(post.getTime()).thenReturn(currentTimeMillis());
331                         return this;
332                 }
333
334                 @Override
335                 public PostBuilder withTime(long time) {
336                         when(post.getTime()).thenReturn(time);
337                         return this;
338                 }
339
340                 @Override
341                 public PostBuilder withText(String text) {
342                         when(post.getText()).thenReturn(text);
343                         return this;
344                 }
345
346                 @Override
347                 public PostBuilder to(String recipientId) {
348                         this.recipientId = recipientId;
349                         return this;
350                 }
351
352                 @Override
353                 public Post build() throws IllegalStateException {
354                         when(post.getRecipientId()).thenReturn(fromNullable(recipientId));
355                         return post;
356                 }
357
358         }
359
360         private static class TestPostReplyBuilder implements PostReplyBuilder {
361
362                 private final PostReply postReply = mock(PostReply.class);
363
364                 @Override
365                 public PostReplyBuilder to(String postId) {
366                         when(postReply.getPostId()).thenReturn(postId);
367                         return this;
368                 }
369
370                 @Override
371                 public PostReply build() throws IllegalStateException {
372                         return postReply;
373                 }
374
375                 @Override
376                 public PostReplyBuilder randomId() {
377                         when(postReply.getId()).thenReturn(randomUUID().toString());
378                         return this;
379                 }
380
381                 @Override
382                 public PostReplyBuilder withId(String id) {
383                         when(postReply.getId()).thenReturn(id);
384                         return this;
385                 }
386
387                 @Override
388                 public PostReplyBuilder from(String senderId) {
389                         Sone sone = mock(Sone.class);
390                         when(sone.getId()).thenReturn(senderId);
391                         when(postReply.getSone()).thenReturn(sone);
392                         return this;
393                 }
394
395                 @Override
396                 public PostReplyBuilder currentTime() {
397                         when(postReply.getTime()).thenReturn(currentTimeMillis());
398                         return this;
399                 }
400
401                 @Override
402                 public PostReplyBuilder withTime(long time) {
403                         when(postReply.getTime()).thenReturn(time);
404                         return this;
405                 }
406
407                 @Override
408                 public PostReplyBuilder withText(String text) {
409                         when(postReply.getText()).thenReturn(text);
410                         return this;
411                 }
412
413         }
414
415 }