f56a1d4de7a4f40e72f60fd49547ededb8fbb825
[Sone.git] / src / test / java / net / pterodactylus / sone / data / Mocks.java
1 /*
2  * Sone - Mocks.java - Copyright © 2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.data;
19
20 import static com.google.common.base.Optional.absent;
21 import static com.google.common.base.Optional.fromNullable;
22 import static com.google.common.base.Optional.of;
23 import static com.google.common.collect.ArrayListMultimap.create;
24 import static com.google.common.collect.Maps.newHashMap;
25 import static com.google.common.collect.Ordering.from;
26 import static java.util.Collections.emptySet;
27 import static org.mockito.Matchers.anyString;
28 import static org.mockito.Matchers.eq;
29 import static org.mockito.Mockito.doAnswer;
30 import static org.mockito.Mockito.mock;
31 import static org.mockito.Mockito.when;
32
33 import java.net.URI;
34 import java.net.URISyntaxException;
35 import java.util.Collection;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39
40 import net.pterodactylus.sone.core.Core;
41 import net.pterodactylus.sone.core.Options;
42 import net.pterodactylus.sone.core.Options.DefaultOption;
43 import net.pterodactylus.sone.core.Options.Option;
44 import net.pterodactylus.sone.core.Options.OptionWatcher;
45 import net.pterodactylus.sone.core.Preferences;
46 import net.pterodactylus.sone.core.SoneInserter;
47 import net.pterodactylus.sone.data.impl.DefaultPostBuilder;
48 import net.pterodactylus.sone.data.impl.DefaultPostReplyBuilder;
49 import net.pterodactylus.sone.database.Database;
50 import net.pterodactylus.sone.database.PostReplyBuilder;
51 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
52 import net.pterodactylus.sone.utils.IntegerRangePredicate;
53 import net.pterodactylus.sone.web.WebInterface;
54 import net.pterodactylus.sone.web.page.FreenetRequest;
55
56 import freenet.clients.http.HTTPRequestImpl;
57 import freenet.support.api.HTTPRequest;
58
59 import com.google.common.base.Function;
60 import com.google.common.base.Optional;
61 import com.google.common.base.Predicates;
62 import com.google.common.collect.FluentIterable;
63 import com.google.common.collect.HashMultimap;
64 import com.google.common.collect.Multimap;
65 import com.google.common.collect.Ordering;
66 import com.google.common.collect.SetMultimap;
67 import org.mockito.Matchers;
68 import org.mockito.invocation.InvocationOnMock;
69 import org.mockito.stubbing.Answer;
70
71 /**
72  * Mocks reusable in multiple tests.
73  *
74  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
75  */
76 public class Mocks {
77
78         private final Multimap<Sone, Post> sonePosts = create();
79         private final Map<String, Sone> sones = newHashMap();
80         private final Multimap<Post, PostReply> postReplies = create();
81         private final Multimap<String, Post> directedPosts = create();
82         private final SetMultimap<Post, Sone> postLikingSones = HashMultimap.create();
83         private final SetMultimap<PostReply, Sone> postReplyLikingSones = HashMultimap.create();
84         public final Database database;
85         public final Core core;
86         public final WebInterface webInterface;
87
88         public Mocks() {
89                 database = mockDatabase();
90                 core = mockCore(database);
91                 webInterface = mockWebInterface(core);
92                 when(database.getSone()).thenReturn(new Function<String, Optional<Sone>>() {
93                         @Override
94                         public Optional<Sone> apply(String soneId) {
95                                 return (soneId == null) ? Optional.<Sone>absent() : fromNullable(sones.get(soneId));
96                         }
97                 });
98                 when(core.getSones()).then(new Answer<Collection<Sone>>() {
99                         @Override
100                         public Collection<Sone> answer(InvocationOnMock invocation) throws Throwable {
101                                 return sones.values();
102                         }
103                 });
104                 when(core.getLocalSone(anyString())).then(new Answer<Optional<Sone>>() {
105                         @Override
106                         public Optional<Sone> answer(InvocationOnMock invocation) throws Throwable {
107                                 Sone localSone = sones.get(invocation.getArguments()[0]);
108                                 return ((localSone == null) || (!localSone.isLocal())) ? Optional.<Sone>absent() : of(localSone);
109                         }
110                 });
111                 when(core.getLocalSones()).then(new Answer<Collection<Sone>>() {
112                         @Override
113                         public Collection<Sone> answer(InvocationOnMock invocation) throws Throwable {
114                                 return FluentIterable.from(sones.values()).filter(Sone.LOCAL_SONE_FILTER).toList();
115                         }
116                 });
117                 Options options = createOptions();
118                 when(core.getPreferences()).thenReturn(new Preferences(options));
119                 when(database.getDirectedPosts(anyString())).then(new Answer<Collection<Post>>() {
120                         @Override
121                         public Collection<Post> answer(InvocationOnMock invocation) throws Throwable {
122                                 return directedPosts.get((String) invocation.getArguments()[0]);
123                         }
124                 });
125         }
126
127         private Options createOptions() {
128                 Options options = new Options();
129                 options.addIntegerOption("InsertionDelay", new DefaultOption<Integer>(60, new IntegerRangePredicate(0, Integer.MAX_VALUE)));
130                 options.addIntegerOption("PostsPerPage", new DefaultOption<Integer>(10, new IntegerRangePredicate(1, Integer.MAX_VALUE)));
131                 options.addIntegerOption("ImagesPerPage", new DefaultOption<Integer>(9, new IntegerRangePredicate(1, Integer.MAX_VALUE)));
132                 options.addIntegerOption("CharactersPerPost", new DefaultOption<Integer>(400, Predicates.<Integer>or(new IntegerRangePredicate(50, Integer.MAX_VALUE), Predicates.equalTo(-1))));
133                 options.addIntegerOption("PostCutOffLength", new DefaultOption<Integer>(200, Predicates.<Integer>or(new IntegerRangePredicate(50, Integer.MAX_VALUE), Predicates.equalTo(-1))));
134                 options.addBooleanOption("RequireFullAccess", new DefaultOption<Boolean>(false));
135                 options.addIntegerOption("PositiveTrust", new DefaultOption<Integer>(75, new IntegerRangePredicate(0, 100)));
136                 options.addIntegerOption("NegativeTrust", new DefaultOption<Integer>(-25, new IntegerRangePredicate(-100, 100)));
137                 options.addStringOption("TrustComment", new DefaultOption<String>("Set from Sone Web Interface"));
138                 options.addBooleanOption("ActivateFcpInterface", new DefaultOption<Boolean>(false));
139                 options.addIntegerOption("FcpFullAccessRequired", new DefaultOption<Integer>(2));
140                 return options;
141         }
142
143         private static Core mockCore(Database database) {
144                 Core core = mock(Core.class);
145                 when(core.getDatabase()).thenReturn(database);
146                 when(core.getSone(anyString())).thenReturn(Optional.<Sone>absent());
147                 return core;
148         }
149
150         private static Database mockDatabase() {
151                 Database database = mock(Database.class);
152                 when(database.getSone(anyString())).thenReturn(Optional.<Sone>absent());
153                 when(database.getPost(anyString())).thenReturn(Optional.<Post>absent());
154                 when(database.getPostReply(anyString())).thenReturn(Optional.<PostReply>absent());
155                 return database;
156         }
157
158         private static WebInterface mockWebInterface(Core core) {
159                 WebInterface webInterface = mock(WebInterface.class);
160                 when(webInterface.getCore()).thenReturn(core);
161                 return webInterface;
162         }
163
164         public SoneMocker mockSone(String id) {
165                 return new SoneMocker(id);
166         }
167
168         public PostMocker mockPost(Sone sone, String postId) {
169                 return new PostMocker(postId, sone);
170         }
171
172         public PostReplyMocker mockPostReply(Sone sone, String replyId) {
173                 return new PostReplyMocker(replyId, sone);
174         }
175
176         public FreenetRequest mockRequest(String path) {
177                 HTTPRequest httpRequest = mock(HTTPRequest.class);
178                 when(httpRequest.getMethod()).thenReturn("GET");
179                 when(httpRequest.getPath()).thenReturn(path);
180                 FreenetRequest request = mock(FreenetRequest.class);
181                 when(request.getHttpRequest()).thenReturn(httpRequest);
182                 return request;
183         }
184
185         public class SoneMocker {
186
187                 private final Sone mockedSone = mock(Sone.class);
188                 private final String id;
189                 private boolean local;
190                 private Optional<String> name = absent();
191                 private long time;
192                 private Profile profile = new Profile(mockedSone);
193                 private Collection<String> friends = emptySet();
194
195                 private SoneMocker(String id) {
196                         this.id = id;
197                 }
198
199                 public SoneMocker local() {
200                         local = true;
201                         return this;
202                 }
203
204                 public SoneMocker withName(String name) {
205                         this.name = fromNullable(name);
206                         return this;
207                 }
208
209                 public SoneMocker withTime(long time) {
210                         this.time = time;
211                         return this;
212                 }
213
214                 public SoneMocker withProfileName(String firstName, String middleName, String lastName) {
215                         profile.modify().setFirstName(firstName).setMiddleName(middleName).setLastName(lastName).update();
216                         return this;
217                 }
218
219                 public SoneMocker addProfileField(String fieldName, String fieldValue) {
220                         profile.setField(profile.addField(fieldName), fieldValue);
221                         return this;
222                 }
223
224                 public SoneMocker withFriends(Collection<String> friends) {
225                         this.friends = friends;
226                         return this;
227                 }
228
229                 public Sone create() {
230                         when(mockedSone.getId()).thenReturn(id);
231                         when(mockedSone.isLocal()).thenReturn(local);
232                         if (name.isPresent()) {
233                                 when(mockedSone.getName()).thenReturn(name.get());
234                         }
235                         when(mockedSone.getTime()).thenReturn(time);
236                         when(mockedSone.getProfile()).thenReturn(profile);
237                         if (local) {
238                                 when(mockedSone.newPostBuilder()).thenReturn(new DefaultPostBuilder(database, id));
239                                 when(mockedSone.newPostReplyBuilder(anyString())).then(new Answer<PostReplyBuilder>() {
240                                         @Override
241                                         public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
242                                                 return new DefaultPostReplyBuilder(database, id, (String) invocation.getArguments()[0]);
243                                         }
244                                 });
245                                 when(mockedSone.hasFriend(anyString())).thenReturn(false);
246                                 when(mockedSone.getFriends()).thenReturn(friends);
247                                 when(mockedSone.hasFriend(anyString())).then(new Answer<Boolean>() {
248                                         @Override
249                                         public Boolean answer(InvocationOnMock invocation) throws Throwable {
250                                                 return friends.contains(invocation.getArguments()[0]);
251                                         }
252                                 });
253                         } else {
254                                 when(mockedSone.newPostBuilder()).thenThrow(IllegalStateException.class);
255                                 when(mockedSone.newPostReplyBuilder(anyString())).thenThrow(IllegalStateException.class);
256                         }
257                         when(core.getSone(eq(id))).thenReturn(of(mockedSone));
258                         when(database.getSone(eq(id))).thenReturn(of(mockedSone));
259                         when(mockedSone.getPosts()).then(new Answer<List<Post>>() {
260                                 @Override
261                                 public List<Post> answer(InvocationOnMock invocationOnMock) throws Throwable {
262                                         return from(Post.TIME_COMPARATOR).sortedCopy(sonePosts.get(mockedSone));
263                                 }
264                         });
265                         when(mockedSone.toString()).thenReturn(String.format("Sone[%s]", id));
266                         sones.put(id, mockedSone);
267                         return mockedSone;
268                 }
269
270         }
271
272         public class PostMocker {
273
274                 private final Post post = mock(Post.class);
275                 private final String id;
276                 private final Sone sone;
277                 private Optional<String> recipientId = absent();
278                 private long time;
279                 private Optional<String> text = absent();
280
281                 public PostMocker(String id, Sone sone) {
282                         this.id = id;
283                         this.sone = sone;
284                 }
285
286                 public PostMocker withRecipient(String recipientId) {
287                         this.recipientId = fromNullable(recipientId);
288                         return this;
289                 }
290
291                 public PostMocker withTime(long time) {
292                         this.time = time;
293                         return this;
294                 }
295
296                 public PostMocker withText(String text) {
297                         this.text = fromNullable(text);
298                         return this;
299                 }
300
301                 public Post create() {
302                         when(post.getId()).thenReturn(id);
303                         when(post.getSone()).thenReturn(sone);
304                         when(post.getRecipientId()).thenReturn(recipientId);
305                         if (recipientId.isPresent()) {
306                                 directedPosts.put(recipientId.get(), post);
307                         }
308                         when(post.getTime()).thenReturn(time);
309                         if (text.isPresent()) {
310                                 when(post.getText()).thenReturn(text.get());
311                         }
312                         when(database.getPost(eq(id))).thenReturn(of(post));
313                         sonePosts.put(sone, post);
314                         when(post.getReplies()).then(new Answer<List<PostReply>>() {
315                                 @Override
316                                 public List<PostReply> answer(InvocationOnMock invocation) throws Throwable {
317                                         return Ordering.from(Reply.TIME_COMPARATOR).sortedCopy(postReplies.get(post));
318                                 }
319                         });
320                         doAnswer(new Answer<Void>() {
321                                 @Override
322                                 public Void answer(InvocationOnMock invocation) throws Throwable {
323                                         postLikingSones.put(post, (Sone) invocation.getArguments()[0]);
324                                         return null;
325                                 }
326                         }).when(post).like(Matchers.<Sone>any());
327                         doAnswer(new Answer<Void>() {
328                                 @Override
329                                 public Void answer(InvocationOnMock invocation) throws Throwable {
330                                         postLikingSones.remove(post, (Sone) invocation.getArguments()[0]);
331                                         return null;
332                                 }
333                         }).when(post).unlike(Matchers.<Sone>any());
334                         when(post.getLikes()).thenAnswer(new Answer<Set<Sone>>() {
335                                 @Override
336                                 public Set<Sone> answer(InvocationOnMock invocation) throws Throwable {
337                                         return postLikingSones.get(post);
338                                 }
339                         });
340                         return post;
341                 }
342
343         }
344
345         public class PostReplyMocker {
346
347                 private final PostReply postReply = mock(PostReply.class);
348                 private final String id;
349                 private final Sone sone;
350                 private Optional<Post> post = absent();
351                 private long time;
352                 private Optional<String> text = absent();
353
354                 public PostReplyMocker(String id, Sone sone) {
355                         this.id = id;
356                         this.sone = sone;
357                 }
358
359                 public PostReplyMocker inReplyTo(Post post) {
360                         this.post = fromNullable(post);
361                         return this;
362                 }
363
364                 public PostReplyMocker withTime(long time) {
365                         this.time = time;
366                         return this;
367                 }
368
369                 public PostReplyMocker withText(String text) {
370                         this.text = fromNullable(text);
371                         return this;
372                 }
373
374                 public PostReply create() {
375                         when(postReply.getId()).thenReturn(id);
376                         when(postReply.getSone()).thenReturn(sone);
377                         when(postReply.getTime()).thenReturn(time);
378                         when(database.getPostReply(eq(id))).thenReturn(of(postReply));
379                         if (post.isPresent()) {
380                                 postReplies.put(post.get(), postReply);
381                         }
382                         if (text.isPresent()) {
383                                 when(postReply.getText()).thenReturn(text.get());
384                         }
385                         doAnswer(new Answer<Void>() {
386                                 @Override
387                                 public Void answer(InvocationOnMock invocation) throws Throwable {
388                                         postReplyLikingSones.put(postReply, (Sone) invocation.getArguments()[0]);
389                                         return null;
390                                 }
391                         }).when(postReply).like(Matchers.<Sone>any());
392                         doAnswer(new Answer<Void>() {
393                                 @Override
394                                 public Void answer(InvocationOnMock invocation) throws Throwable {
395                                         postReplyLikingSones.remove(postReply, invocation.getArguments()[0]);
396                                         return null;
397                                 }
398                         }).when(postReply).unlike(Matchers.<Sone>any());
399                         when(postReply.getLikes()).thenAnswer(new Answer<Set<Sone>>() {
400                                 @Override
401                                 public Set<Sone> answer(InvocationOnMock invocation) throws Throwable {
402                                         return postReplyLikingSones.get(postReply);
403                                 }
404                         });
405                         return postReply;
406                 }
407         }
408
409 }