aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorGravatar alx2019-03-16 23:56:27 +0300
committerGravatar alx2019-03-16 23:56:27 +0300
commit06105f76dbfa3b65e63ed06f9c4d5107bd49ed88 (patch)
tree5702c01cec9688039d891f4a711878706101c1c5 /src/test
parent3ea4cd1942fa4e763034da11c5fa429407b67829 (diff)
parenta49105285d0d7719d7f222a507af2d5ac5b4bdb1 (diff)
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'src/test')
-rw-r--r--src/test/java/com/juick/MessageTest.java36
-rw-r--r--src/test/java/com/juick/server/configuration/TestActivityConfiguration.java19
-rw-r--r--src/test/java/com/juick/server/tests/ServerTests.java439
-rw-r--r--src/test/java/com/juick/test/util/MockUtils.java2
-rw-r--r--src/test/resources/2936611-57.jpgbin0 -> 101818 bytes
-rw-r--r--src/test/resources/announce.json1
-rw-r--r--src/test/resources/data.sql1
-rw-r--r--src/test/resources/delete_user.json1
-rw-r--r--src/test/resources/hubzilla_activity.json185
-rw-r--r--src/test/resources/hubzilla_follow.json1
-rw-r--r--src/test/resources/mocks/activity/testfollow.json15
-rw-r--r--src/test/resources/mocks/activity/testuser.json27
-rw-r--r--src/test/resources/test.p12bin0 -> 2386 bytes
13 files changed, 583 insertions, 144 deletions
diff --git a/src/test/java/com/juick/MessageTest.java b/src/test/java/com/juick/MessageTest.java
index 1d59a8db..eabeeb0f 100644
--- a/src/test/java/com/juick/MessageTest.java
+++ b/src/test/java/com/juick/MessageTest.java
@@ -20,6 +20,8 @@ package com.juick;
import com.juick.model.Entity;
import com.juick.test.util.MockUtils;
import com.juick.util.MessageUtils;
+import com.overzealous.remark.Options;
+import com.overzealous.remark.Remark;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
@@ -191,5 +193,39 @@ public class MessageTest {
assertThat(entities.stream().filter(e -> e.getType().equals("q")).count(), is(1L));
assertThat(entities.stream().filter(e -> e.getType().equals("u")).count(), is(1L));
assertThat(entities.stream().filter(e -> e.getType().equals("a")).count(), is(4L));
+ Entity yo = entities.stream().filter(e -> e.getType().equals("q")).findFirst().orElseThrow(IllegalStateException::new);
+ assertThat(yo.getText(), is("how ?"));
+ assertThat(yo.getStart(), is(0));
+ assertThat(yo.getEnd(), is(7));
+ }
+ @Test
+ public void ActivityStreamsHTMLtoMarkdownTest() {
+ String text = "<p><span class=\"h-card\"><a href=\"https://juick.com/stanislavv/\" class=\"u-url mention\">@<span>stanislavv</span></a></span> я в <span class=\"h-card\"><a href=\"https://mastodonsocial.ru/@rf\" class=\"u-url mention\">@<span>rf</span></a></span> выкладывал =)</p>";
+ Options options = new Options();
+ options.inlineLinks = true;
+ Remark remark = new Remark(options);
+ String parsed = remark.convertFragment(text);
+ }
+ @Test
+ public void messageFormatTest() {
+ String msg = "> quote\nmessage";
+ assertThat(MessageUtils.formatMessage(msg), is("<q>quote</q>message"));
+ String brokenComment = "<!-- read next";
+ assertThat(MessageUtils.formatMessage(brokenComment), is("&lt;!-- read next"));
+ String url = "[ya](http://ya.ru)";
+ assertThat(MessageUtils.formatMessage(url), is("<a href=\"http://ya.ru\" rel=\"nofollow\">ya</a>"));
+ String complexMessage = "У футболистов нет мозгов. Что в России, что в Беларуси:\n" +
+ "\n" +
+ ">Отец футболиста Лухвича, объехавшего пробку по тротуару: «Сына задержали, Infiniti арестовали» https://auto.onliner.by/2019/01/23/probka-9\n" +
+ "\n" +
+ "Вкратце: малолетний долбоёб ездил по встрече, по тротуарам, парковался где хотел и всё это выкладывал в сеть, мол, хули вы мне сделоете. Сделали. Ибо нехуй.";
+ String formattedMessage = "У футболистов нет мозгов. Что в России, что в Беларуси:<br/>\n<br/>\n" +
+ "<q>Отец футболиста Лухвича, объехавшего пробку по тротуару: «Сына задержали, Infiniti арестовали» <a href=\"https://auto.onliner.by/2019/01/23/probka-9\" rel=\"nofollow\">auto.onliner.by</a></q>" +
+ "<br/>\n" +
+ "Вкратце: малолетний долбоёб ездил по встрече, по тротуарам, парковался где хотел и всё это выкладывал в сеть, мол, хули вы мне сделоете. Сделали. Ибо нехуй.";
+
+ assertThat(MessageUtils.formatMessage(complexMessage), is(formattedMessage));
+ String multiQuote = "> quote line 1\n> quote line 2\nmessage";
+ assertThat(MessageUtils.formatMessage(multiQuote), is("<q>quote line 1<br/>\nquote line 2</q>message"));
}
}
diff --git a/src/test/java/com/juick/server/configuration/TestActivityConfiguration.java b/src/test/java/com/juick/server/configuration/TestActivityConfiguration.java
new file mode 100644
index 00000000..5daf4900
--- /dev/null
+++ b/src/test/java/com/juick/server/configuration/TestActivityConfiguration.java
@@ -0,0 +1,19 @@
+package com.juick.server.configuration;
+
+import com.juick.server.KeystoreManager;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+
+import java.io.IOException;
+
+@Configuration
+public class TestActivityConfiguration {
+ @Value("classpath:test.p12")
+ Resource keystoreFile;
+ @Bean
+ public KeystoreManager testKeystoreManager() throws IOException {
+ return new KeystoreManager(keystoreFile.getFile().getAbsolutePath(), "secret");
+ }
+}
diff --git a/src/test/java/com/juick/server/tests/ServerTests.java b/src/test/java/com/juick/server/tests/ServerTests.java
index deef6f30..7f46968f 100644
--- a/src/test/java/com/juick/server/tests/ServerTests.java
+++ b/src/test/java/com/juick/server/tests/ServerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2017, Juick
+ * Copyright (C) 2008-2019, Juick
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -19,6 +19,7 @@ package com.juick.server.tests;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gargoylesoftware.htmlunit.CookieManager;
import com.gargoylesoftware.htmlunit.WebClient;
@@ -33,27 +34,22 @@ import com.juick.model.PrivateChats;
import com.juick.model.TagStats;
import com.juick.server.*;
import com.juick.server.api.activity.model.Context;
-import com.juick.server.api.activity.model.activities.Create;
-import com.juick.server.api.activity.model.activities.Delete;
-import com.juick.server.api.activity.model.activities.Follow;
-import com.juick.server.api.activity.model.activities.Like;
-import com.juick.server.api.activity.model.activities.Undo;
+import com.juick.server.api.activity.model.activities.*;
import com.juick.server.api.activity.model.objects.Note;
import com.juick.server.api.activity.model.objects.Person;
import com.juick.server.api.webfinger.model.Account;
import com.juick.server.api.xnodeinfo2.model.NodeInfo;
import com.juick.server.util.HttpUtils;
import com.juick.server.util.ImageUtils;
-import com.juick.server.xmpp.helpers.XMPPStatus;
-import com.juick.server.xmpp.s2s.ConnectionIn;
+import com.juick.server.www.WebApp;
import com.juick.service.*;
import com.juick.service.component.MessageEvent;
+import com.juick.test.util.MockUtils;
import com.juick.util.DateFormattersHolder;
import com.juick.util.MessageUtils;
import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.error.PebbleException;
import com.mitchellbosecke.pebble.template.PebbleTemplate;
-import org.apache.commons.codec.CharEncoding;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.io.IOUtils;
@@ -70,16 +66,22 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
import org.springframework.http.*;
+import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.DigestUtils;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.ResourceAccessException;
+import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.w3c.dom.Document;
@@ -91,9 +93,6 @@ import rocks.xmpp.addr.Jid;
import rocks.xmpp.core.session.Extension;
import rocks.xmpp.core.session.XmppSession;
import rocks.xmpp.core.session.XmppSessionConfiguration;
-import rocks.xmpp.core.stanza.model.StanzaError;
-import rocks.xmpp.core.stanza.model.client.ClientMessage;
-import rocks.xmpp.core.stanza.model.errors.Condition;
import javax.inject.Inject;
import javax.servlet.http.Cookie;
@@ -105,7 +104,6 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
-import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
@@ -117,17 +115,20 @@ import java.sql.Timestamp;
import java.time.Instant;
import java.util.*;
import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;
+import static com.juick.server.api.activity.model.Context.ACTIVITY_MEDIA_TYPE;
import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
+import static org.springframework.test.web.client.ExpectedCount.times;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@@ -140,9 +141,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@TestPropertySource(properties = {
"broken_ssl_hosts=localhost,serverstorageisfull.tld",
"ios_app_id=12345678.com.juick.ExampleApp",
- "xmppbot_jid=juick@localhost/Juick",
- "hostname=localhost",
- "componentname=localhost",
"spring.jackson.default-property-inclusion=non_default"
})
@AutoConfigureMockMvc
@@ -163,12 +161,8 @@ public class ServerTests {
@Inject
private ObjectMapper jsonMapper;
@Inject
- private XMPPServer server;
- @Inject
private CommandsManager commandsManager;
@Inject
- private XMPPConnection router;
- @Inject
private SubscriptionService subscriptionService;
@Inject
private PrivacyQueriesService privacyQueriesService;
@@ -188,10 +182,6 @@ public class ServerTests {
private ServerManager serverManager;
@Inject
private KeystoreManager keystoreManager;
- @Value("${hostname:localhost}")
- private Jid jid;
- @Value("${xmppbot_jid:juick@localhost}")
- private Jid botJid;
@Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
private String tmpDir;
@Value("${img_path:#{systemEnvironment['TEMP'] ?: '/tmp'}}")
@@ -204,6 +194,32 @@ public class ServerTests {
private SignatureManager signatureManager;
@Inject
private ActivityPubManager activityPubManager;
+ @Inject
+ private WebApp webApp;
+ @Inject
+ private RestTemplate apClient;
+
+ @Value("classpath:mocks/activity/testuser.json")
+ private Resource testuserResponse;
+ @Value("classpath:mocks/activity/testfollow.json")
+ private Resource testfollowRequest;
+ @Value("classpath:static/av-96.png")
+ private Resource defaultAvatar;
+ @Value("classpath:cmyk.jpg")
+ private Resource cmykJpeg;
+ @Value("classpath:nojfif.jpg")
+ private Resource nojfif;
+ @Value("classpath:hubzilla_activity.json")
+ private Resource hubzillaActivity;
+ @Value("classpath:hubzilla_follow.json")
+ private Resource hubzillaFollow;
+ @Value("classpath:announce.json")
+ private Resource noteWithDocument;
+ @Value("classpath:2936611-57.jpg")
+ private Resource jpegNoJfifTiff;
+
+ @Inject
+ private KeystoreManager testKeystoreManager;
private static User ugnich, freefd, juick;
static String ugnichName, ugnichPassword, freefdName, freefdPassword, juickName, juickPassword;
@@ -217,10 +233,16 @@ public class ServerTests {
FileSystemUtils.deleteRecursively(Paths.get(imgDir, "photos-1024"));
FileSystemUtils.deleteRecursively(Paths.get(imgDir, "photos-512"));
FileSystemUtils.deleteRecursively(Paths.get(imgDir, "ps"));
+ FileSystemUtils.deleteRecursively(Paths.get(imgDir, "a"));
+ FileSystemUtils.deleteRecursively(Paths.get(imgDir, "ao"));
+ FileSystemUtils.deleteRecursively(Paths.get(imgDir, "as"));
Files.createDirectory(Paths.get(imgDir, "p"));
Files.createDirectory(Paths.get(imgDir, "photos-1024"));
Files.createDirectory(Paths.get(imgDir, "photos-512"));
Files.createDirectory(Paths.get(imgDir, "ps"));
+ Files.createDirectory(Paths.get(imgDir, "a"));
+ Files.createDirectory(Paths.get(imgDir, "ao"));
+ Files.createDirectory(Paths.get(imgDir, "as"));
if (!isSetUp) {
ugnichName = "ugnich";
ugnichPassword = "secret";
@@ -339,7 +361,7 @@ public class ServerTests {
Message msg3 = messagesService.getMessage(mid2).get();
assertEquals(2, msg3.getReplies());
- assertEquals("weather", msg3.getTags().get(0).getName());
+ assertEquals("weather", msg3.getTags().stream().findFirst().get().getName());
assertEquals(ugnich.getUid(), userService.checkPassword(ugnich.getName(), "x"));
assertEquals(-1, userService.checkPassword(ugnich.getName(), "xy"));
@@ -411,7 +433,7 @@ public class ServerTests {
Timestamp.class, mid).toInstant();
assertThat(rts, greaterThan(ts));
Message msg = messagesService.getReply(mid, rid);
- assertThat(rts, equalTo(msg.getTimestamp()));
+ assertThat(rts, equalTo(msg.getCreated()));
messagesService.deleteMessage(ugnich_id, mid);
}
@@ -436,11 +458,10 @@ public class ServerTests {
@Test
public void homeTestWithMessages() throws Exception {
String msgText = "Привет, я - Угнич";
- CommandResult result = commandsManager.processCommand(ugnich, msgText, URI.create("http://static.juick.com/settings/facebook.png"));
+ CommandResult result = commandsManager.processCommand(ugnich, msgText, URI.create("https://static.juick.com/settings/facebook.png"));
int mid = result.getNewMessage().get().getMid();
Message msg = messagesService.getMessage(mid).get();
tagService.createTag("тест");
- ClassPathResource defaultAvatar = new ClassPathResource("static/av-96.png");
String hash = DigestUtils.md5DigestAsHex(IOUtils.toByteArray(defaultAvatar.getInputStream()));
mockMvc.perform(
get("/api/home")
@@ -449,7 +470,7 @@ public class ServerTests {
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$[0].mid", is(msg.getMid())))
.andExpect(jsonPath("$[0].timestamp",
- is(DateFormattersHolder.getMessageFormatterInstance().format(msg.getTimestamp()))))
+ is(DateFormattersHolder.getMessageFormatterInstance().format(msg.getCreated()))))
.andExpect(jsonPath("$[0].body", is(msg.getText())))
.andExpect(jsonPath("$[0].attachment.url",
is(String.format("https://i.juick.com/p/%d.png", msg.getMid()))))
@@ -481,6 +502,11 @@ public class ServerTests {
.header("Origin", "http://api.example.net"))
.andExpect(status().isOk())
.andExpect(header().string("Access-Control-Allow-Origin", "*"));
+ mockMvc.perform(
+ get("/u/ugnich")
+ .header("Origin", "http://api.example.net"))
+ .andExpect(status().isOk())
+ .andExpect(header().string("Access-Control-Allow-Origin", "*"));
}
@Test
@@ -675,63 +701,6 @@ public class ServerTests {
.andExpect(xpath("/rss/channel/description").string("The latest messages at Juick"));
}
@Test
- public void statusPageIsUp() throws Exception {
- mockMvc.perform(get("/api/status").with(httpBasic(ugnichName, ugnichPassword))).andExpect(status().isOk());
- assertThat(server.getJid(), equalTo(jid));
- }
- @Test
- public void botIsUpAndProcessingResourceConstraints() throws Exception {
- jdbcTemplate.execute("DELETE FROM users WHERE nick='renha'");
- int renhaId = userService.createUser("renha", "umnnbt");
- Jid from = Jid.of("renha@serverstorageisfull.tld");
- jdbcTemplate.update("INSERT INTO jids(user_id,jid,active) VALUES(?,?,?)",
- renhaId, from.toEscapedString(), 1);
- rocks.xmpp.core.stanza.model.Message xmppMessage = new rocks.xmpp.core.stanza.model.Message();
- xmppMessage.setType(rocks.xmpp.core.stanza.model.Message.Type.ERROR);
- xmppMessage.setFrom(from);
- xmppMessage.setTo(botJid);
- StanzaError err = new StanzaError(StanzaError.Type.CANCEL, Condition.RESOURCE_CONSTRAINT);
- xmppMessage.setError(err);
- Function<Integer, Boolean> isActive = f -> jdbcTemplate.queryForObject("SELECT active FROM jids WHERE user_id=?", Integer.class, f) == 1;
- assertThat(isActive.apply(renhaId), equalTo(true));
- ClientMessage result = router.incomingMessage(xmppMessage);
- assertNull(result);
- assertThat(isActive.apply(renhaId), equalTo(false));
- xmppMessage.setError(null);
- xmppMessage.setType(rocks.xmpp.core.stanza.model.Message.Type.CHAT);
- xmppMessage.setBody("On");
- result = router.incomingMessage(xmppMessage);
- assertThat(result.getBody(), equalTo("XMPP notifications are activated"));
- assertTrue(isActive.apply(renhaId));
- xmppMessage.setBody("*test test");
- result = router.incomingMessage(xmppMessage);
- assertThat(result.getBody(), startsWith("New message posted"));
- xmppMessage.setFrom(from);
- xmppMessage.setBody("PING");
- result = router.incomingMessage(xmppMessage);
- assertThat(result.getBody(), equalTo("PONG"));
- int secretlySadId = userService.createUser("secretlysad", "bbk");
- xmppMessage.setTo(botJid.withLocal("secretlysad"));
- xmppMessage.setBody("What's up?");
- result = router.incomingMessage(xmppMessage);
- assertThat(result, is(nullValue()));
- xmppMessage.setTo(botJid);
- xmppMessage.setBody("@secretlysad Hey!");
- result = router.incomingMessage(xmppMessage);
- assertThat(result.getBody(), is("Private message sent"));
- assertThat(pmQueriesService.getPMMessages(renhaId, secretlySadId).size(), is(2));
- String xml = "<message xmlns=\"jabber:server\" from=\"" + botJid + "\" to=\"renha@serverstorageisfull.tld\" type=\"chat\"><body>@yo:\n" +
- "343432434\n" +
- "\n" +
- "#2 http://juick.com/2</body><thread>juick-2</thread><juick xmlns=\"http://juick.com/message\" mid=\"2\" privacy=\"1\" quote=\"\" replyto=\"0\" rid=\"0\" ts=\"2018-04-10 06:58:23\"><body>343432434</body><updated></updated><user xmlns=\"http://juick.com/user\" uname=\"yo\" uid=\"2\"></user></juick><nick xmlns=\"http://jabber.org/protocol/nick\">@yo</nick></message>";
- result = router.incomingMessage((rocks.xmpp.core.stanza.model.Message)server.parse(xml));
- String active = "<message xmlns=\"jabber:server\" from=\"renha@serverstorageisfull.tld/work\" to=\""
- + botJid.asBareJid().toEscapedString() + "\" type=\"chat\" id=\"purple553384c6\"><active xmlns=\"http://jabber.org/protocol/chatstates\"></active></message>";
- result = router.incomingMessage((rocks.xmpp.core.stanza.model.Message)server.parse(active));
- xmppMessage.setFrom(botJid);
- // TODO: assert events
- }
- @Test
public void botCommandsTests() throws Exception {
assertThat(commandsManager.processCommand(AnonymousUser.INSTANCE, "PING", emptyUri).getText(), is("PONG"));
// subscription commands have two lines, others have 1
@@ -743,21 +712,21 @@ public class ServerTests {
int uid = userService.createUser("me", "secret");
User user = userService.getUserByUID(uid).orElse(AnonymousUser.INSTANCE);
Tag yo = tagService.getTag("yo", true);
- Message msg = commandsManager.processCommand(user, "*yo yoyo", URI.create("http://static.juick.com/settings/facebook.png")).getNewMessage().get();
+ Message msg = commandsManager.processCommand(user, "*yo yoyo", URI.create("https://static.juick.com/settings/facebook.png")).getNewMessage().get();
assertThat(msg.getAttachmentType(), is("png"));
- Message msgreply = commandsManager.processCommand(user, "#" + msg.getMid() + " yyy", HttpUtils.downloadImage(URI.create("http://static.juick.com/settings/xmpp.png").toURL(), tmpDir)).getNewMessage().get();
+ Message msgreply = commandsManager.processCommand(user, "#" + msg.getMid() + " yyy", HttpUtils.downloadImage(URI.create("https://static.juick.com/settings/xmpp.png").toURL(), tmpDir)).getNewMessage().get();
assertThat(msgreply.getAttachmentType(), equalTo("png"));
assertEquals("text should match", "yoyo",
messagesService.getMessage(msg.getMid()).get().getText());
assertEquals("tag should match", "yo",
tagService.getMessageTags(msg.getMid()).get(0).getTag().getName());
- CommandResult yoyoMsg = commandsManager.processCommand(user, "*yo", URI.create("http://static.juick.com/settings/facebook.png"));
+ CommandResult yoyoMsg = commandsManager.processCommand(user, "*yo", URI.create("https://static.juick.com/settings/facebook.png"));
assertTrue(yoyoMsg.getNewMessage().isPresent());
- assertThat(yoyoMsg.getNewMessage().get().getTags().get(0), is(yo));
+ assertThat(yoyoMsg.getNewMessage().get().getTags().stream().findFirst().get(), is(yo));
Message msg2 = yoyoMsg.getNewMessage().get();
int mid = msg2.getMid();
Timestamp last = jdbcTemplate.queryForObject("SELECT lastmessage FROM users WHERE id=?", Timestamp.class, user.getUid());
- assertThat(last.toInstant(), equalTo(yoyoMsg.getNewMessage().get().getTimestamp()));
+ assertThat(last.toInstant(), equalTo(yoyoMsg.getNewMessage().get().getCreated()));
assertEquals("should be message", true,
commandsManager.processCommand(user, String.format("#%d", mid), emptyUri).getText().startsWith("@me"));
int readerUid = userService.createUser("dummyReader", "dummySecret");
@@ -795,14 +764,14 @@ public class ServerTests {
String expectedThirdReply = "Reply posted.\n#" + mid + "/3 "
+ "https://juick.com/m/" + mid + "#3";
assertEquals("should be second reply", expectedSecondReply,
- commandsManager.processCommand(user, "#" + mid + " yoyo", URI.create("http://static.juick.com/settings/facebook.png")).getText());
+ commandsManager.processCommand(user, "#" + mid + " yoyo", URI.create("https://static.juick.com/settings/facebook.png")).getText());
assertEquals("should be third reply", expectedThirdReply,
commandsManager.processCommand(user, " \t\n #" + mid + "/2 ",
- URI.create("http://static.juick.com/settings/facebook.png")).getText());
+ URI.create("https://static.juick.com/settings/facebook.png")).getText());
Message reply = messagesService.getReplies(user, mid).stream().filter(m -> m.getRid() == 3).findFirst()
.orElse(new Message());
Timestamp lastreply = jdbcTemplate.queryForObject("SELECT lastmessage FROM users WHERE id=?", Timestamp.class, user.getUid());
- assertThat(lastreply.toInstant(), equalTo(reply.getTimestamp()));
+ assertThat(lastreply.toInstant(), equalTo(reply.getCreated()));
assertEquals("should be reply to second comment", 2, reply.getReplyto());
assertThat(commandsManager.processCommand(readerUser, "#" + mid + " *yo *there", emptyUri)
.getText(), startsWith("Reply posted"));
@@ -857,11 +826,11 @@ public class ServerTests {
"> }";
String codeAndTags = "*code\n" + expectedCodeMessage;
Message codeAndTagsMessage = commandsManager.processCommand(user, codeAndTags, emptyUri).getNewMessage().get();
- List<Tag> codeAndTagsTags = codeAndTagsMessage.getTags();
+ Set<Tag> codeAndTagsTags = codeAndTagsMessage.getTags();
assertEquals("expected single tag", 1,
codeAndTagsTags.size());
assertEquals("the single tag should be the 'code'", "code",
- codeAndTagsTags.get(0).getName());
+ codeAndTagsTags.stream().findFirst().get().getName());
assertEquals("and the message should be with a C-code and without tags", expectedCodeMessage,
codeAndTagsMessage.getText());
CommandResult result = commandsManager.processCommand(user, "*one *two *three *four *five *six test", emptyUri);
@@ -885,7 +854,7 @@ public class ServerTests {
data = "* \u043c\u0443\u0441\u043e\u0440\\n\u0423 \u043c\u0435\u043d\u044f \u043a\u0430\u0436\u0434\u0443\u044e \u043d\u0435\u0434\u0435\u043b\u044e \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u043f\u043e 4-5 \u0431\u0443\u0442\u044b\u043b\u043e\u043a 1,5\u043b \u041f\u0415\u0422. \u041c\u043d\u0435 \u0433\u0435\u043c\u043e\u0440\u043d\u043e \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u043e \u043a\u0438\u043b\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0438\u043b\u0438 \u043f\u043e 5\u043a\u0433 \u044d\u0442\u043e\u0433\u043e \u043c\u0443\u0441\u043e\u0440\u0430, \u0447\u0442\u043e\u0431\u044b \u0432\u0435\u0437\u0442\u0438 \u0435\u0433\u043e \u0435\u0449\u0435 \u043a\u0443\u0434\u0430-\u0442\u043e.\\n\u041d\u0435, \u043d\u0443 \u0435\u0441\u0442\u044c \u043b\u044e\u0434\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442 \u0432\u0442\u043e\u0440\u0441\u044b\u0440\u044c\u0435 \u043f\u043e \u043c\u0443\u0441\u043e\u0440\u043a\u0430\u043c, \u0441\u0432\u0430\u043b\u043a\u0430\u043c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0434\u0435\u043d\u044c\u0433\u0438 \u043d\u0443\u0436\u043d\u044b. \u0418 \u0431\u044b\u0432\u0430\u044e\u0442 \u0441\u0442\u043e\u044f\u0442 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b-\u043a\u043b\u0435\u0442\u043a\u0438 \u0434\u043b\u044f \u043f\u043b\u0430\u0441\u0442\u0438\u043a\u0430, \u043d\u043e \u0442\u0430\u043a \u043a\u0430\u043a \u043c\u0443\u0441\u043e\u0440 \u0432\u044b\u0432\u043e\u0437\u044f\u0442 \u043d\u0435 \u0447\u0430\u0441\u0442\u043e \u0438\u043b\u0438 \u043b\u044e\u0434\u0438 \u0432\u043d\u0435\u0437\u0430\u043f\u043d\u043e \u0432\u044b\u043a\u0438\u0434\u044b\u0432\u0430\u044e\u0442 \u043c\u043d\u043e\u0433\u043e \u043c\u0443\u0441\u043e\u0440\u0430, \u0442\u043e \u0432 \u0442\u043e\u0439 \u043a\u043b\u0435\u0442\u043a\u0435 \u0442\u043e\u0442 \u043c\u0443\u0441\u043e\u0440, \u0447\u0442\u043e \u043d\u0435 \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043b\u0441\u044f \u0432 \u043e\u0431\u044b\u0447\u043d\u044b\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\u0445.";
result = commandsManager.processCommand(user, String.format("%s %s", tags, data), emptyUri);
assertThat(result.getNewMessage().get().getTags().size(), equalTo(2));
- assertThat(result.getNewMessage().get().getTags().get(0).getName(), equalTo("Киев"));
+ assertThat(result.getNewMessage().get().getTags().stream().findFirst().get().getName(), equalTo("Киев"));
assertThat(result.getNewMessage().get().getText(), equalTo(data));
result = commandsManager.processCommand(user, "S @unknown-user", emptyUri);
assertThat(result.getNewMessage(), is(Optional.empty()));
@@ -933,7 +902,7 @@ public class ServerTests {
mockMvc.perform(get("/api/thread?mid=" + mid + "&hash=" + freefdHash))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].recommendations.length()", is(1)))
- .andExpect(jsonPath("$[0].recommendations[0]", is(freefdName)));
+ .andExpect(jsonPath("$[0].recommendations[0].uname", is(freefdName)));
mockMvc.perform(post("/api/like?mid=" + freefdMid + "&hash=" + freefdHash))
.andExpect(status().isForbidden());
}
@@ -1029,8 +998,8 @@ public class ServerTests {
tagService.updateTags(newMid, Collections.singletonList(banned));
assertThat(messagesService.getMessage(newMid).get().getTags().size(), is(0));
privacyQueriesService.blacklistUser(freefd, userService.getUserByUID(newUid).orElse(AnonymousUser.INSTANCE));
- assertTrue(messagesService.getMessages(AnonymousUser.INSTANCE, messagesService.getMyFeed(freefd.getUid(), 0, true))
- .stream().noneMatch(m -> m.getMid() == newMid));
+ assertTrue(messagesService.getMyFeed(freefd.getUid(), 0, true)
+ .stream().noneMatch(m -> m == newMid));
}
@Test
public void tagsShouldBeDeserializedFromXml() throws JAXBException {
@@ -1055,18 +1024,21 @@ public class ServerTests {
Tag xmlTag = (Tag) unmarshaller.unmarshal(new StringReader(tag));
assertThat(xmlTag.getName(), equalTo("yo"));
Message juickMessage = xmppMessage.getExtension(Message.class);
- assertThat(juickMessage.getTags().get(0).getName(), equalTo("yo"));
+ List<Tag> tags = new ArrayList<>(juickMessage.getTags());
+ assertThat(tags.get(0).getName(), equalTo("yo"));
}
@Test
public void messageParserSerializer() throws ParserConfigurationException,
IOException, SAXException, JAXBException {
+ Set<Tag> tags = MessageUtils.parseTags("test test" + (char) 0xA0 + "2 test3");
+ List<Tag> tagList = new ArrayList<>(tags);
+ assertEquals("First tag must be", "test", tagList.get(0).getName());
+ assertEquals("Third tag must be", "test3", tagList.get(2).getName());
+ assertEquals("Count of tags must be", 3, tagList.size());
Message msg = new Message();
- msg.setTags(MessageUtils.parseTags("test test" + (char) 0xA0 + "2 test3"));
- assertEquals("First tag must be", "test", msg.getTags().get(0).getName());
- assertEquals("Third tag must be", "test3", msg.getTags().get(2).getName());
- assertEquals("Count of tags must be", 3, msg.getTags().size());
+ msg.setTags(tags);
Instant currentDate = Instant.now();
- msg.setTimestamp(currentDate);
+ msg.setCreated(currentDate);
String jsonMessage = jsonMapper.writeValueAsString(msg);
assertEquals("date should be in timestamp field", DateFormattersHolder.getMessageFormatterInstance().format(currentDate),
JsonPath.read(jsonMessage, "$.timestamp"));
@@ -1081,7 +1053,7 @@ public class ServerTests {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
- Document doc = db.parse(new ByteArrayInputStream(sw.toString().getBytes(CharEncoding.UTF_8)));
+ Document doc = db.parse(new ByteArrayInputStream(sw.toString().getBytes(StandardCharsets.UTF_8)));
Node juickNode = doc.getDocumentElement();
NamedNodeMap attrs = juickNode.getAttributes();
assertEquals("date should be in ts field", DateFormattersHolder.getMessageFormatterInstance().format(currentDate),
@@ -1168,7 +1140,7 @@ public class ServerTests {
}
@Test
public void cmykJpegShouldBeProcessedCorrectly() throws Exception {
- CommandResult postJpgCmyk = commandsManager.processCommand(ugnich, "YO", new ClassPathResource("cmyk.jpg").getURI());
+ CommandResult postJpgCmyk = commandsManager.processCommand(ugnich, "YO", cmykJpeg.getURI());
assertThat(postJpgCmyk.getNewMessage().isPresent(), is(true));
int mid = postJpgCmyk.getNewMessage().get().getMid();
File originalFile = Paths.get(imgDir, "p", String.format("%d.jpg", mid)).toFile();
@@ -1182,7 +1154,7 @@ public class ServerTests {
}
@Test
public void JpegWithoutJfifShouldBeProcessedCorrectly() throws Exception {
- CommandResult postJpgCmyk = commandsManager.processCommand(ugnich, "YO", new ClassPathResource("nojfif.jpg").getURI());
+ CommandResult postJpgCmyk = commandsManager.processCommand(ugnich, "YO", nojfif.getURI());
assertThat(postJpgCmyk.getNewMessage().isPresent(), is(true));
int mid = postJpgCmyk.getNewMessage().get().getMid();
File originalFile = Paths.get(imgDir, "p", String.format("%d.jpg", mid)).toFile();
@@ -1210,12 +1182,19 @@ public class ServerTests {
assertThat(postJpgiPhone.getNewMessage().get().getAttachment().getHeight(), is(1280));
assertThat(postJpgiPhone.getNewMessage().get().getAttachment().getMedium().getHeight(), is(1024));
assertThat(postJpgiPhone.getNewMessage().get().getAttachment().getSmall().getHeight(), is(512));
+ CommandResult postNojfifTiff = commandsManager.processCommand(ugnich, "YO2", jpegNoJfifTiff.getURI());
+ assertThat(postNojfifTiff.getNewMessage().isPresent(), is(true));
+ int mid2 = postNojfifTiff.getNewMessage().get().getMid();
+ File originalFile2 = Paths.get(imgDir, "p", String.format("%d.jpg", mid2)).toFile();
+ assertThat(originalFile2.exists(), is(true));
+ File mediumFile2 = Paths.get(imgDir, "photos-1024", String.format("%d.jpg", mid2)).toFile();
+ assertThat(mediumFile2.exists(), is(true));
}
@Test
public void changeExtensionWhenReceiveFileWithWrongContentType() throws Exception {
Path pngOutput = Paths.get(tmpDir, "cmyk.png");
Files.deleteIfExists(pngOutput);
- Files.copy(Paths.get(new ClassPathResource("cmyk.jpg").getURI()), pngOutput);
+ Files.copy(Paths.get(cmykJpeg.getURI()), pngOutput);
assertThat(pngOutput.toFile().exists(), is(true));
CommandResult postJpgCmyk = commandsManager.processCommand(ugnich, "YO", pngOutput.toUri());
assertThat(postJpgCmyk.getNewMessage().isPresent(), is(true));
@@ -1231,7 +1210,7 @@ public class ServerTests {
Message original = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class)
.getNewMessage().get();
assertThat(original.getText(), equalTo("YO"));
- assertThat(original.getUpdatedAt(), equalTo(original.getTimestamp()));
+ assertThat(original.getUpdatedAt(), equalTo(original.getCreated()));
// to have updated_at greater than ts
Thread.sleep(1000);
result = mockMvc.perform(post("/api/update").with(httpBasic(ugnichName, ugnichPassword))
@@ -1240,7 +1219,7 @@ public class ServerTests {
Message edited = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class)
.getNewMessage().get();
assertThat(edited.getText(), equalTo("PEOPLE"));
- assertThat(edited.getUpdatedAt(), greaterThan(edited.getTimestamp()));
+ assertThat(edited.getUpdatedAt(), greaterThan(edited.getCreated()));
mockMvc.perform(post("/api/update").with(httpBasic(freefdName, freefdPassword))
.param("mid", String.valueOf(original.getMid()))
.param("body", "PEOPLE")).andExpect(status().is(403));
@@ -1249,7 +1228,7 @@ public class ServerTests {
.param("body", "HEY")).andExpect(status().is2xxSuccessful()).andReturn();
CommandResult comment = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class);
assertThat(comment.getNewMessage().get().getText(), is("HEY"));
- assertThat(comment.getNewMessage().get().getUpdatedAt(), is(comment.getNewMessage().get().getTimestamp()));
+ assertThat(comment.getNewMessage().get().getUpdatedAt(), is(comment.getNewMessage().get().getCreated()));
// to have updated_at greater than ts
Thread.sleep(1000);
result = mockMvc.perform(post("/api/update").with(httpBasic(freefdName, freefdPassword))
@@ -1259,7 +1238,7 @@ public class ServerTests {
Message editedComment = jsonMapper.readValue(result.getResponse().getContentAsString(), CommandResult.class)
.getNewMessage().get();
assertThat(editedComment.getText(), is("HEY, JOE"));
- assertThat(editedComment.getUpdatedAt(), greaterThan(editedComment.getTimestamp()));
+ assertThat(editedComment.getUpdatedAt(), greaterThan(editedComment.getCreated()));
messagesService.deleteMessage(ugnich.getUid(), original.getMid());
}
@Test
@@ -1316,25 +1295,6 @@ public class ServerTests {
assertThat(subscriptionService.getUsersSubscribedToComments(msg, reply).size(), is(0));
}
@Test
- public void xmppStatusApi() throws Exception {
- Supplier<XMPPStatus> getStatus = () -> {
- try {
- MvcResult result = mockMvc.perform(get("/api/xmpp-status").with(httpBasic(ugnichName, ugnichPassword)))
- .andExpect(status().isOk()).andReturn();
- return jsonMapper.readValue(result.getResponse().getContentAsString(), XMPPStatus.class);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- };
- assertThat(getStatus.get().getInbound(), is(nullValue()));
- ConnectionIn test = new ConnectionIn(server, new Socket("localhost", server.getServerPort()));
- test.from.add(Jid.of("test"));
- server.getInConnections().clear();
- server.addConnectionIn(test);
- assertThat(getStatus.get().getInbound().size(), is(1));
- }
- @Test
public void credentialsShouldNeverBeSerialized() throws Exception {
int uid = userService.createUser("yyy", "xxxx");
User yyy = userService.getUserByUID(uid).get();
@@ -1405,11 +1365,18 @@ public class ServerTests {
assertThat(messagesService.recommendMessage(mid, ermineId), is(MessagesService.RecommendStatus.Added));
assertThat(messagesService.recommendMessage(mid, fmapId), is(MessagesService.RecommendStatus.Added));
assertThat(messagesService.recommendMessage(mid, pogoId), is(MessagesService.RecommendStatus.Added));
- assertThat(messagesService.getMessage(mid).get().getLikes(), is(3));
- assertThat(CollectionUtils.isEqualCollection(messagesService.getMessageRecommendations(mid), Arrays.asList("fmap", "ermine", "pogo")), is(true));
- privacyQueriesService.blacklistUser(userService.getUserByName("monstreek"), userService.getUserByName("pogo"));
- assertThat(messagesService.getMessage(mid).get().getLikes(), is(3));
- assertThat(CollectionUtils.isEqualCollection(messagesService.getMessageRecommendations(mid), Arrays.asList("fmap", "ermine")), is(true));
+ jdbcTemplate.update("INSERT INTO favorites(user_id, user_uri, message_id, like_id, ts) " +
+ "values (0, 'http://example.com/u/test', ?, 1, now())", mid);
+ assertThat(messagesService.getMessage(mid).get().getLikes(), is(4));
+ assertThat(CollectionUtils.isEqualCollection(messagesService.getMessageRecommendations(mid)
+ .stream().map(User::getName).collect(Collectors.toList()),
+ Arrays.asList("fmap", "ermine", "pogo", "Anonymous")), is(true));
+ privacyQueriesService.blacklistUser(userService.getUserByName("monstreek"),
+ userService.getUserByName("pogo"));
+ assertThat(messagesService.getMessage(mid).get().getLikes(), is(4));
+ assertThat(CollectionUtils.isEqualCollection(messagesService.getMessageRecommendations(mid)
+ .stream().map(User::getName).collect(Collectors.toList()),
+ Arrays.asList("fmap", "ermine", "Anonymous")), is(true));
}
@Test
public void bannedUserShouldNotBeVisibleToOthers() {
@@ -1696,9 +1663,10 @@ public class ServerTests {
CommandResult result = commandsManager.processCommand(ugnich, "*test1 *test2 *test1 test3", emptyUri);
assertThat(result.getNewMessage().isPresent(), is(true));
Message msg = result.getNewMessage().get();
- assertThat(msg.getTags().size(), is (2));
- assertThat(msg.getTags().get(0).getName(), is("test1"));
- assertThat(msg.getTags().get(1).getName(), is("test2"));
+ List<Tag> tags = new ArrayList<>(msg.getTags());
+ assertThat(tags.size(), is (2));
+ assertThat(tags.get(0).getName(), is("test1"));
+ assertThat(tags.get(1).getName(), is("test2"));
assertThat(msg.getText(), is("test3"));
}
@Test
@@ -1759,6 +1727,7 @@ public class ServerTests {
replyNote.setTo(Collections.singletonList(activityPubManager.personUri(ugnich)));
replyNote.setContent("HI");
Create create = new Create();
+ create.setId(replyNote.getId());
create.setActor("http://localhost:8080/u/freefd");
create.setObject(replyNote);
signatureManager.post((Person) signatureManager.getContext(URI.create("http://localhost:8080/u/freefd")).get(),
@@ -1776,6 +1745,55 @@ public class ServerTests {
signatureManager.post(from, to, follow);
}
@Test
+ public void serviceSignatureAuth() throws Exception {
+ String meUri = "/api/me";
+ String testHost = "localhost:8080";
+ Person ugnichPerson = (Person) signatureManager.discoverPerson("ugnich@localhost:8080").get();
+ Instant now = Instant.now();
+ String requestDate = DateFormattersHolder.getHttpDateFormatter().format(now);
+ String signatureString = signatureManager.addSignature(ugnichPerson, testHost, "GET", meUri, requestDate);
+ MvcResult me = mockMvc.perform(get("/api/me")
+ .header("Host", testHost)
+ .header("Date", requestDate)
+ .header("Signature", signatureString))
+ .andExpect(status().isOk())
+ .andReturn();
+ User meUser = jsonMapper.readValue(me.getResponse().getContentAsString(), User.class);
+ assertThat(meUser, is(ugnich));
+ String testuserResponseString = IOUtils.toString(testuserResponse.getInputStream(), StandardCharsets.UTF_8);
+ ClientHttpRequestFactory originalRequestFactory = apClient.getRequestFactory();
+ URI testuserUri = URI.create("https://example.com/u/testuser");
+ URI testuserkeyUri = URI.create("https://example.com/u/testuser#main-key");
+ MockRestServiceServer restServiceServer = MockRestServiceServer.createServer(apClient);
+ restServiceServer.expect(times(3), requestTo(testuserUri))
+ .andRespond(withSuccess(testuserResponseString, MediaType.APPLICATION_JSON_UTF8));
+ restServiceServer.expect(times(3), requestTo(testuserkeyUri))
+ .andRespond(withSuccess(testuserResponseString, MediaType.APPLICATION_JSON_UTF8));
+ Person testuser = (Person)signatureManager.getContext(testuserUri).get();
+ Assert.assertThat(testuser.getPublicKey().getPublicKeyPem(), is(testKeystoreManager.getPublicKeyPem()));
+ Instant now2 = Instant.now();
+ String testRequestDate = DateFormattersHolder.getHttpDateFormatter().format(now2);
+ String inboxUri = "/api/inbox";
+ String testSignatureString =
+ signatureManager.addSignature(testuser, testHost, "POST",
+ inboxUri, testRequestDate, testKeystoreManager);
+ mockMvc.perform(post(inboxUri)
+ .header("Host", testHost)
+ .header("Date", testRequestDate)
+ .header("Signature", testSignatureString)
+ .contentType(Context.LD_JSON_MEDIA_TYPE)
+ .content(IOUtils.toByteArray(testfollowRequest.getInputStream())))
+ .andExpect(status().isAccepted());
+ mockMvc.perform(post(inboxUri)
+ .header("Host", "wronghost")
+ .header("Date", testRequestDate)
+ .header("Signature", testSignatureString)
+ .contentType(Context.LD_JSON_MEDIA_TYPE)
+ .content(IOUtils.toByteArray(testfollowRequest.getInputStream())))
+ .andExpect(status().isUnauthorized());
+ apClient.setRequestFactory(originalRequestFactory);
+ }
+ @Test
public void hostmeta() throws Exception {
MvcResult result = mockMvc.perform(get("/.well-known/host-meta"))
.andExpect(status().isOk()).andReturn();
@@ -1858,4 +1876,139 @@ public class ServerTests {
jdbcTemplate.update("INSERT INTO facebook(user_id, fb_id) VALUES(?, ?)", ugnich.getUid(), "100001866137681");
assertThat(userService.getUserByName("ugnich").isVerified(), is(true));
}
+ @Test
+ public void avatarUploadOverApi() throws Exception {
+ ClassPathResource defaultAvatar = new ClassPathResource("static/av-96.png");
+ String hash = DigestUtils.md5DigestAsHex(IOUtils.toByteArray(defaultAvatar.getInputStream()));
+ assertThat(webApp.getAvatarUrl(userService.getUserByName(freefdName)), is(String.format("http://localhost:8080/av-96-%s.png", hash)));
+
+ ClassPathResource newAvatar = new ClassPathResource("static/durov.png");
+ byte[] newAvatarData = IOUtils.toByteArray(newAvatar.getInputStream());
+ mockMvc.perform(MockMvcRequestBuilders.multipart("/api/me/upload")
+ .file("avatar", newAvatarData)
+ .with(httpBasic(freefdName, freefdPassword))
+ ).andExpect(status().isOk());
+ String newHash = DigestUtils.md5DigestAsHex(newAvatarData);
+ URI newUri = Paths.get(imgDir, "ao", String.format("%d.png", freefd.getUid())).toUri();
+ assertThat(DigestUtils.md5DigestAsHex(IOUtils.toByteArray(newUri)), is(newHash));
+ }
+ @Test
+ public void varyMvcResponse() throws Exception {
+ mockMvc.perform(get("/"))
+ .andExpect(status().isOk())
+ .andExpect(header().string("Vary", "Accept-Language"));
+ mockMvc.perform(get("/rss/ugnich/blog"))
+ .andExpect(status().isOk())
+ .andExpect(header().string("Vary", "Accept-Language"));
+ mockMvc.perform(get("/api/messages"))
+ .andExpect(status().isOk())
+ .andExpect(header().string("Vary", "Accept-Language"));
+ }
+ @Test
+ public void apiInfo() throws Exception {
+ userService.createUser("tst", "tst");
+ MvcResult result = mockMvc.perform(get("/api/info/tst"))
+ .andExpect(status().isOk())
+ .andReturn();
+ User tst = jsonMapper.readValue(result.getResponse().getContentAsString(), User.class);
+ assertThat(tst.getReaders(), is(nullValue()));
+ commandsManager.processCommand(ugnich, "S @tst", emptyUri);
+ result = mockMvc.perform(get("/api/info/tst"))
+ .andExpect(status().isOk())
+ .andReturn();
+ tst = jsonMapper.readValue(result.getResponse().getContentAsString(), User.class);
+ assertThat(tst.getReaders().size(), is(1));
+ }
+ @Test
+ public void federatedUserDeletionFlow() throws Exception {
+ String deleteJsonStr = IOUtils.toString(new ClassPathResource("delete_user.json").getURI(), StandardCharsets.UTF_8);
+ Delete delete = jsonMapper.readValue(deleteJsonStr, Delete.class);
+ ClientHttpRequestFactory originalRequestFactory = apClient.getRequestFactory();
+ MockRestServiceServer restServiceServer = MockRestServiceServer.createServer(apClient);
+ restServiceServer.expect(times(2), requestTo((String) delete.getObject()))
+ .andRespond(withStatus(HttpStatus.GONE));
+ restServiceServer.expect(requestTo((String) delete.getObject()))
+ .andRespond(response -> {
+ throw new ResourceAccessException("Connection reset");
+ });
+ mockMvc.perform(post("/api/inbox")
+ .contentType(ACTIVITY_MEDIA_TYPE)
+ .content(deleteJsonStr))
+ .andExpect(status().isAccepted());
+ mockMvc.perform(post("/api/inbox")
+ .contentType(ACTIVITY_MEDIA_TYPE)
+ .content(deleteJsonStr)
+ .header("Signature", "keyId=\"https://example.com/users/deleted#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"wHoU91JJBsIYcR1W1/57B0oG98t5Aa/TvGPw1B8KQlAp5KhpePnOzD1MZRgivBx7YKO6eYwDx+AX9dn6tjlAvzRLygv21H6UoDZFihWzeE1HM8pY2Pe4EhUgYBN0YuiKUi7W4TS9bDRAJ5vGNPUWATe+2o5Jcbux5cZYXFKKYbLBLD+/IlqPdHA2IXLZ52HFVVfBkPH5sSklV6XJtD/PHLK9R/I9w/mUpj9moUPQu44rR7KvxiGNuHla3vfDtJbkBqLMdScX91EG8373AulXPUiCCF7R2lJB0fFQedm2nSbcwBoJ32GEyOyOPFgPKG5zd9Fd5TfB1pmA8ZIE0sChfA==\""))
+ .andExpect(status().isAccepted());
+ apClient.setRequestFactory(originalRequestFactory);
+ }
+ @Test
+ public void handleIncorrectCertificates() throws Exception {
+ String deleteJsonStr = IOUtils.toString(new ClassPathResource("delete_user.json").getURI(), StandardCharsets.UTF_8);
+ Delete delete = jsonMapper.readValue(deleteJsonStr, Delete.class);
+ ClientHttpRequestFactory originalRequestFactory = apClient.getRequestFactory();
+ MockRestServiceServer restServiceServer = MockRestServiceServer.createServer(apClient);
+ restServiceServer.expect(requestTo((String) delete.getObject()))
+ .andRespond(response -> {
+ throw new ResourceAccessException("Connection reset");
+ });
+ mockMvc.perform(post("/api/inbox")
+ .contentType(ACTIVITY_MEDIA_TYPE)
+ .content(deleteJsonStr)
+ .header("Signature", "keyId=\"https://example.com/users/deleted#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"wHoU91JJBsIYcR1W1/57B0oG98t5Aa/TvGPw1B8KQlAp5KhpePnOzD1MZRgivBx7YKO6eYwDx+AX9dn6tjlAvzRLygv21H6UoDZFihWzeE1HM8pY2Pe4EhUgYBN0YuiKUi7W4TS9bDRAJ5vGNPUWATe+2o5Jcbux5cZYXFKKYbLBLD+/IlqPdHA2IXLZ52HFVVfBkPH5sSklV6XJtD/PHLK9R/I9w/mUpj9moUPQu44rR7KvxiGNuHla3vfDtJbkBqLMdScX91EG8373AulXPUiCCF7R2lJB0fFQedm2nSbcwBoJ32GEyOyOPFgPKG5zd9Fd5TfB1pmA8ZIE0sChfA==\""))
+ .andExpect(status().isAccepted());
+ apClient.setRequestFactory(originalRequestFactory);
+ }
+
+ @Test
+ public void legacyAvatarEndpoint() throws Exception {
+ mockMvc.perform(get("/api/avatar")
+ .param("uname", "unknown"))
+ .andExpect(status().isOk())
+ .andExpect(content().bytes(IOUtils.toByteArray(defaultAvatar.getInputStream())));
+ }
+ @Test
+ public void federatedAttachmentsAsLinks() throws Exception {
+ int mid = messagesService.createMessage(ugnich.getUid(), "test", StringUtils.EMPTY, Collections.emptyList());
+ Message testMessage = MockUtils.mockMessage(mid, freefd, "reply");
+ String activity = IOUtils.toString(noteWithDocument.getInputStream(), StandardCharsets.UTF_8);
+ Announce announce = jsonMapper.readValue(activity, Announce.class);
+ }
+ @Test
+ public void hubzillaActor() throws Exception {
+ String activity = IOUtils.toString(hubzillaActivity.getInputStream(), StandardCharsets.UTF_8);
+ Create create = jsonMapper.readValue(activity, Create.class);
+ String followData = IOUtils.toString(hubzillaFollow.getInputStream(), StandardCharsets.UTF_8);
+ Follow follow = jsonMapper.readValue(followData, Follow.class);
+ assertThat(follow.getActor(), is("https://ussr.win/channel/zlax"));
+ }
+ @Test
+ public void nodeinfo() throws Exception {
+ MvcResult nodeinfoXRD = mockMvc.perform(get("/.well-known/nodeinfo")
+ .contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andReturn();
+ JsonNode node = jsonMapper.readTree(nodeinfoXRD.getResponse().getContentAsString());
+ assertThat(node.get("links"), notNullValue());
+ String nodeinfoUrl = node.get("links").get(0).get("href").textValue();
+ MvcResult nodeinfoData = mockMvc.perform(get(nodeinfoUrl)
+ .contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andReturn();
+ JsonNode nodeinfo = jsonMapper.readTree(nodeinfoData.getResponse().getContentAsString());
+ assertThat(nodeinfo.get("software"), notNullValue());
+ assertThat(nodeinfo.get("server"), nullValue());
+ MvcResult xnodeinfoData = mockMvc.perform(get("/.well-known/x-nodeinfo2")
+ .contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andReturn();
+ JsonNode xnodeinfo = jsonMapper.readTree(xnodeinfoData.getResponse().getContentAsString());
+ assertThat(xnodeinfo.get("server"), notNullValue());
+ assertThat(xnodeinfo.get("software"), nullValue());
+ }
+ @Test
+ public void anonymousUserFromZero() {
+ User user = userService.getUserByUID(0).orElse(AnonymousUser.INSTANCE);
+ assertThat(user.isAnonymous(), is(true));
+ }
}
diff --git a/src/test/java/com/juick/test/util/MockUtils.java b/src/test/java/com/juick/test/util/MockUtils.java
index 017af4d1..8f6b821d 100644
--- a/src/test/java/com/juick/test/util/MockUtils.java
+++ b/src/test/java/com/juick/test/util/MockUtils.java
@@ -34,7 +34,7 @@ public class MockUtils {
msg.setMid(mid);
msg.setUser(user);
msg.setText(messageText == null ? generator.generate(24) : messageText);
- msg.setTimestamp(Instant.now());
+ msg.setCreated(Instant.now());
return msg;
}
diff --git a/src/test/resources/2936611-57.jpg b/src/test/resources/2936611-57.jpg
new file mode 100644
index 00000000..af4f9c91
--- /dev/null
+++ b/src/test/resources/2936611-57.jpg
Binary files differ
diff --git a/src/test/resources/announce.json b/src/test/resources/announce.json
new file mode 100644
index 00000000..4ae4a2ad
--- /dev/null
+++ b/src/test/resources/announce.json
@@ -0,0 +1 @@
+{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":{"@id":"as:movedTo","@type":"@id"},"alsoKnownAs":{"@id":"as:alsoKnownAs","@type":"@id"},"Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji","focalPoint":{"@container":"@list","@id":"toot:focalPoint"},"featured":{"@id":"toot:featured","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value"}],"id":"https://mastodon.social/users/armitage/statuses/101641036945418885/activity","type":"Announce","actor":"https://mastodon.social/users/armitage","published":"2019-02-23T10:52:22Z","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://juick.com/u/netneladno","https://mastodon.social/users/armitage/followers"],"atomUri":"https://mastodon.social/users/armitage/statuses/101641036945418885/activity","object":{"id":"https://juick.com/n/2936427-0","type":"Note","summary":null,"inReplyTo":null,"published":"2019-02-23T10:13:59Z","url":"https://juick.com/m/2936427","attributedTo":"https://juick.com/u/netneladno","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://mastodon.social/users/netneladno/followers"],"sensitive":false,"atomUri":null,"inReplyToAtomUri":null,"conversation":"tag:mastodon.social,2019-02-23:objectId=86866293:objectType=Conversation","content":"\u003cp\u003e\u003c/p\u003e\n","attachment":[{"type":"Document","mediaType":"image/jpeg","url":"https://i.juick.com/photos-1024/2936427.jpg","name":null}],"tag":[]},"signature":{"type":"RsaSignature2017","creator":"https://mastodon.social/users/armitage#main-key","created":"2019-02-23T10:52:22Z","signatureValue":"hEbN5wumGY98T/t5ZbgBLxQNEHX6PJgO9dGwIdTq0T+GJ518J3duhHlhljBQkGgnH/68aSBWHxDQ+57YRVlR6lukvl0hKCg8Rx1NOYhtU+9OH//MH2uMt/XXh2pE1dv+RVA7GS4zkCMjKKMhY6WNzbJQS4FfvaCmmEn6gqiCSXbo1lLo4bkhBRyfpAH8Z9Z8/wEbRAl0qCVZlim8ELOZ8rWVfnmgF3gGDWwpgzSEqIbxEytpHtNeYC+ZC3FVlxMAomvxuyj2XCv6B854ol5UQ30O8MLVNiTCoUa/7w6k/I3pmN1etpJ53XD8Si0Qg6QKHO8R3Wp6S9LSoF0Hj/GiDQ=="}} \ No newline at end of file
diff --git a/src/test/resources/data.sql b/src/test/resources/data.sql
index 102b11f4..aff3e286 100644
--- a/src/test/resources/data.sql
+++ b/src/test/resources/data.sql
@@ -1,3 +1,4 @@
+INSERT INTO users(id, nick, passw) VALUES(0, 'Anonymous', 'password');
INSERT INTO tags(tag_id, name) VALUES(2, 'juick');
INSERT INTO reactions (like_id, description) VALUES (1, 'like');
INSERT INTO reactions (like_id, description) VALUES (2, 'love');
diff --git a/src/test/resources/delete_user.json b/src/test/resources/delete_user.json
new file mode 100644
index 00000000..b68db011
--- /dev/null
+++ b/src/test/resources/delete_user.json
@@ -0,0 +1 @@
+{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":{"@id":"as:movedTo","@type":"@id"},"alsoKnownAs":{"@id":"as:alsoKnownAs","@type":"@id"},"Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji","focalPoint":{"@container":"@list","@id":"toot:focalPoint"},"featured":{"@id":"toot:featured","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value"}],"id":"https://example.com/users/deleted#delete","type":"Delete","actor":"https://example.com/users/deleted","to":["https://www.w3.org/ns/activitystreams#Public"],"object":"https://example.com/users/deleted","signature":{"type":"RsaSignature2017","creator":"https://mastodon.social/users/andoniserra#main-key","created":"2019-01-29T14:50:13Z","signatureValue":"svq8NDQeXb0widXDL1jygye+a536L4GFPTT+8euXgdHhzij6y5dIpT+s0I0ZheAIfHEe+k3N5XysQMvJ4Jmh8douWZ14DkZNai5luk4Ftg5v/RynYAY65UgsldTf9XUvAbSiRGAK4s2b8qE3zsQihEHRIUrzb2bgvhKUkr8FuuuNDDDSS9i9bxnzQp8DSVivqdW2zJYm3ARtW7sWKSXoSaiP2KxIfRPC6UdDDSFbRr3zHckxRjsPnfWr8VvhjxggzVYcp4ZIJDqJj0qoy1lyRIRTWaDJwZIjFX7JjE5OVoKBt++IcY6IARpTGVxV4GXeeFMB7/y1tMaZoold6VlqyQ=="}} \ No newline at end of file
diff --git a/src/test/resources/hubzilla_activity.json b/src/test/resources/hubzilla_activity.json
new file mode 100644
index 00000000..b25aca87
--- /dev/null
+++ b/src/test/resources/hubzilla_activity.json
@@ -0,0 +1,185 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ "https://ussr.win/apschema/v1.2"
+ ],
+ "type": "Create",
+ "id": "https://ussr.win/activity/e46a6100e4ba83d602eab5fe0e035405a32d7d36094c95dc85c5c9fcd3d93ab2%40ussr.win",
+ "published": "2019-02-14T11:23:29Z",
+ "updated": "2019-02-14T11:38:48Z",
+ "actor": {
+ "type": "Person",
+ "id": "https://ussr.win/channel/zlax",
+ "preferredUsername": "zlax",
+ "name": "ivan zlax",
+ "icon": {
+ "type": "Image",
+ "mediaType": "image/jpeg",
+ "url": "https://ussr.win/photo/profile/l/2",
+ "height": 300,
+ "width": 300
+ },
+ "url": {
+ "type": "Link",
+ "mediaType": "text/html",
+ "href": "https://ussr.win/channel/zlax"
+ },
+ "inbox": "https://ussr.win/inbox/zlax",
+ "outbox": "https://ussr.win/outbox/zlax",
+ "followers": "https://ussr.win/followers/zlax",
+ "following": "https://ussr.win/following/zlax",
+ "endpoints": {
+ "sharedInbox": "https://ussr.win/inbox"
+ },
+ "publicKey": {
+ "id": "https://ussr.win/channel/zlax/public_key_pem",
+ "owner": "https://ussr.win/channel/zlax",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtFSIKFVzo+9NzzY0xho9\nA8CqO0j12f3HEOZUDBDj1NBbQ6Cj1p1f5mB1AlRr+a05fqpraCWIJyCXVTtdyjem\niQ/ObT2PuZIhAY04/ptv78S9EM9ImZi2aDNWp2nWQu23dJajb2TMpIxGNS1M3DVR\ngXwRVEVIEEDzftWx7vF4335/2uJvPfwTzKI3pR972xp1iZCd/G3BtYzUW4swIFSu\n+5ZLKhgwO6A4Ge5+sn2k6x+K56YCgeEEQheOg+PbggIQ68xfvYCQyfuShqAXEadv\nOvQ1dN8WO+tgKqYDRQgILIONB8/2/XXMoQbSVRZs2GmLKXHhIeVKiSZ1vKwHsPx1\nCYUayQo/BB1JpbjkmyDQcHAX+KZ7PQUhpQn976f6Ycp5rznvBH5Zm96VKySaGMy7\nGWefafOfcCNfVZpq17gPPfSbC0XOHdvm6T1e1OPwFl/ho9HxatbA60DIDEd7xf4V\nq9aTSZ2MeQW5JXaxJXSgwucPZ9mhVfucFtCyLdlHZ7gA5yJ+pyYmMe8pjCSNWztD\nUOoJC46vv2l1RbMGTJy2AY9ZuPvyrmJHlsRsTYcmDYRFSk6sgGi1eswSDHlruE3u\nKG4E0nsA8qxzgzpCViQV4DIgXhMInBwo7ZhSyWD+tF2a4tpXorHqmg35x/aqJlaq\nE2xF+yrfh/Kx8O+7P6C1aGcCAwEAAQ==\n-----END PUBLIC KEY-----\n"
+ },
+ "nomadicLocations": [
+ {
+ "id": "https://ussr.win/locs/zlax",
+ "type": "nomadicLocation",
+ "locationAddress": "acct:zlax@ussr.win",
+ "locationPrimary": true,
+ "locationDeleted": false
+ }
+ ]
+ },
+ "object": {
+ "type": "Note",
+ "id": "https://ussr.win/item/e46a6100e4ba83d602eab5fe0e035405a32d7d36094c95dc85c5c9fcd3d93ab2%40ussr.win",
+ "published": "2019-02-14T11:23:29Z",
+ "updated": "2019-02-14T11:38:48Z",
+ "url": {
+ "type": "text/html",
+ "rel": "alternate",
+ "href": "https://ussr.win/channel/zlax/?f=&mid=e46a6100e4ba83d602eab5fe0e035405a32d7d36094c95dc85c5c9fcd3d93ab2@ussr.win"
+ },
+ "attributedTo": "https://ussr.win/channel/zlax",
+ "content": "<span class=\"bookmark-identifier\">#^</span><a class=\"bookmark\" href=\"https://twitter.com/Dalatrm/status/1095677403198906369\" target=\"_blank\" rel=\"nofollow noopener\" >https://twitter.com/Dalatrm/status/1095677403198906369</a><br /><span class=\"bookmark-identifier\">#^</span><a class=\"bookmark\" href=\"https://twitter.com/Dalatrm/status/1095677401433022466\" target=\"_blank\" rel=\"nofollow noopener\" >https://twitter.com/Dalatrm/status/1095677401433022466</a><br /><img class=\"zrl\" src=\"https://ussr.win/photo/6776e315a9692860af53c19518e524f50ab047a4317435403532c922a7c484d4-2.jpg\" style=\"width: 100%; max-width: 590px;\" alt=\"Image/photo\" /><br /><br /><img class=\"zrl\" src=\"https://ussr.win/photo/afb21af40be39ad19589dec5dfa0161b2364b59331bba95aae823e68b3acfbf3-2.jpg\" style=\"width: 100%; max-width: 623px;\" alt=\"Image/photo\" /><br />Продюсер BBC признал постановочными сцены после химатаки в Сирии<br /><span class=\"bookmark-identifier\">#^</span><a class=\"bookmark\" href=\"https://www.vedomosti.ru/politics/news/2019/02/14/794104-bbc-himataki\" target=\"_blank\" rel=\"nofollow noopener\" >https://www.vedomosti.ru/politics/news/2019/02/14/794104-bbc-himataki</a><br /><blockquote>Сцены в госпитале сирийского города Дума после предполагаемой химической атаки были постановочными для достижения «максимального эффекта». Об этом в своем твиттере написал продюсер BBC по Сирии Риам Далати. «Спустя почти шесть месяцев расследования я могу подтвердить без тени сомнения, что сцены в госпитале Думы были постановочные. Погибших в больнице не было», — признал он.<br /><br />Сам факт химической атаки, якобы имевшей место 7 апреля 2018 г., пока не подтвержден Организацией по запрещению химического оружия (ОЗХО). «Атака была, зарин не использовался, но мы должны дождаться, когда ОЗХО подтвердит, использовался ли хлор или что-то еще. Однако все остальное вокруг атаки было создано для максимального эффекта», — написал Далати.<br /><br />Информацию о применении в сирийской Думе химического оружия с использованием хлора и нервно-паралитического агента распространили общественные организации, в том числе «Белые каски». По данным организации, жертвами атаки стали около 70 человек.</blockquote><br />#<a class=\"zrl\" href=\"https://ussr.win/search?tag=bbc\" target=\"_blank\" rel=\"nofollow noopener\" >bbc</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=capitalism\" target=\"_blank\" rel=\"nofollow noopener\" >capitalism</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=conspiracy\" target=\"_blank\" rel=\"nofollow noopener\" >conspiracy</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=history\" target=\"_blank\" rel=\"nofollow noopener\" >history</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=hoax\" target=\"_blank\" rel=\"nofollow noopener\" >hoax</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=metaprogramming\" target=\"_blank\" rel=\"nofollow noopener\" >metaprogramming</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=revision\" target=\"_blank\" rel=\"nofollow noopener\" >revision</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=syria\" target=\"_blank\" rel=\"nofollow noopener\" >syria</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=terrorism\" target=\"_blank\" rel=\"nofollow noopener\" >terrorism</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=uk\" target=\"_blank\" rel=\"nofollow noopener\" >uk</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=war\" target=\"_blank\" rel=\"nofollow noopener\" >war</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=whitehelments\" target=\"_blank\" rel=\"nofollow noopener\" >whitehelments</a>",
+ "actor": {
+ "type": "Person",
+ "id": "https://ussr.win/channel/zlax",
+ "preferredUsername": "zlax",
+ "name": "ivan zlax",
+ "icon": {
+ "type": "Image",
+ "mediaType": "image/jpeg",
+ "url": "https://ussr.win/photo/profile/l/2",
+ "height": 300,
+ "width": 300
+ },
+ "url": {
+ "type": "Link",
+ "mediaType": "text/html",
+ "href": "https://ussr.win/channel/zlax"
+ },
+ "inbox": "https://ussr.win/inbox/zlax",
+ "outbox": "https://ussr.win/outbox/zlax",
+ "followers": "https://ussr.win/followers/zlax",
+ "following": "https://ussr.win/following/zlax",
+ "endpoints": {
+ "sharedInbox": "https://ussr.win/inbox"
+ },
+ "publicKey": {
+ "id": "https://ussr.win/channel/zlax/public_key_pem",
+ "owner": "https://ussr.win/channel/zlax",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtFSIKFVzo+9NzzY0xho9\nA8CqO0j12f3HEOZUDBDj1NBbQ6Cj1p1f5mB1AlRr+a05fqpraCWIJyCXVTtdyjem\niQ/ObT2PuZIhAY04/ptv78S9EM9ImZi2aDNWp2nWQu23dJajb2TMpIxGNS1M3DVR\ngXwRVEVIEEDzftWx7vF4335/2uJvPfwTzKI3pR972xp1iZCd/G3BtYzUW4swIFSu\n+5ZLKhgwO6A4Ge5+sn2k6x+K56YCgeEEQheOg+PbggIQ68xfvYCQyfuShqAXEadv\nOvQ1dN8WO+tgKqYDRQgILIONB8/2/XXMoQbSVRZs2GmLKXHhIeVKiSZ1vKwHsPx1\nCYUayQo/BB1JpbjkmyDQcHAX+KZ7PQUhpQn976f6Ycp5rznvBH5Zm96VKySaGMy7\nGWefafOfcCNfVZpq17gPPfSbC0XOHdvm6T1e1OPwFl/ho9HxatbA60DIDEd7xf4V\nq9aTSZ2MeQW5JXaxJXSgwucPZ9mhVfucFtCyLdlHZ7gA5yJ+pyYmMe8pjCSNWztD\nUOoJC46vv2l1RbMGTJy2AY9ZuPvyrmJHlsRsTYcmDYRFSk6sgGi1eswSDHlruE3u\nKG4E0nsA8qxzgzpCViQV4DIgXhMInBwo7ZhSyWD+tF2a4tpXorHqmg35x/aqJlaq\nE2xF+yrfh/Kx8O+7P6C1aGcCAwEAAQ==\n-----END PUBLIC KEY-----\n"
+ },
+ "nomadicLocations": [
+ {
+ "id": "https://ussr.win/locs/zlax",
+ "type": "nomadicLocation",
+ "locationAddress": "acct:zlax@ussr.win",
+ "locationPrimary": true,
+ "locationDeleted": false
+ }
+ ]
+ },
+ "tag": [
+ {
+ "id": "https://ussr.win/search?tag=metaprogramming",
+ "name": "#metaprogramming"
+ },
+ {
+ "id": "https://ussr.win/search?tag=whitehelments",
+ "name": "#whitehelments"
+ },
+ {
+ "id": "https://ussr.win/search?tag=capitalism",
+ "name": "#capitalism"
+ },
+ {
+ "id": "https://ussr.win/search?tag=conspiracy",
+ "name": "#conspiracy"
+ },
+ {
+ "id": "https://ussr.win/search?tag=terrorism",
+ "name": "#terrorism"
+ },
+ {
+ "id": "https://ussr.win/search?tag=revision",
+ "name": "#revision"
+ },
+ {
+ "id": "https://ussr.win/search?tag=history",
+ "name": "#history"
+ },
+ {
+ "id": "https://ussr.win/search?tag=syria",
+ "name": "#syria"
+ },
+ {
+ "id": "https://ussr.win/search?tag=hoax",
+ "name": "#hoax"
+ },
+ {
+ "id": "https://ussr.win/search?tag=bbc",
+ "name": "#bbc"
+ },
+ {
+ "id": "https://ussr.win/search?tag=war",
+ "name": "#war"
+ },
+ {
+ "id": "https://ussr.win/search?tag=uk",
+ "name": "#uk"
+ }
+ ],
+ "attachment": [
+ {
+ "type": "Image",
+ "url": "https://ussr.win/photo/6776e315a9692860af53c19518e524f50ab047a4317435403532c922a7c484d4-2.jpg"
+ },
+ {
+ "type": "Image",
+ "url": "https://ussr.win/photo/afb21af40be39ad19589dec5dfa0161b2364b59331bba95aae823e68b3acfbf3-2.jpg"
+ }
+ ],
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "cc": [
+ "https://ussr.win/followers/zlax"
+ ]
+ },
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "cc": [
+ "https://ussr.win/followers/zlax"
+ ],
+ "signature": {
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1"
+ ],
+ "type": "RsaSignature2017",
+ "nonce": "c4f8b31f4277d4322a528697d550aefd78a4a7709f0da25285032f7529d3f550",
+ "creator": "https://ussr.win/channel/zlax/public_key_pem",
+ "created": "2019-02-14T11:39:01Z",
+ "signatureValue": "WnXFcSm3nR1Su4QwSVJq6vQ7xdtPQTfs5iTMcAcxasoGHphJaAZRISV1QRPuMhw5jap4Lmb4ZaFRfqGm4M4HND+mI06dC2HlgCh0vnNvGsbcDa2RybSnIUo/xswEa/Mcpf6uy2dcLwaZY6tXOkkJmtZOseN54CUO3bQHbd3KmSiqkvREMXv2+qrVJhohZ4R9tLXamUTkoJmq1wSS2s8XSUlmO2v+Th3OAEoHeiS1SehglvIzua83IKlU2/iAs8akGVyYng5uLO7YeUsmiKHggef1ss8XKcNuhaAW8b3DUTnUwBZCPiiCoeXEQMfYdciCtTAKARHLr5RlHPE7etf1Fqk9Pozo/b2EDpfTxbZ26ZKY02pl4g82u95L43hTmCLbZ+wbKt5M0uHRld+/WkJrkIVS2Vj0MJ84Pp94Ij5A0MaXnBDiIj8YpvbRWWVEdXWKs/N8Dy0c11NiHCPhurZTMQDwv96bBphLQGlTp4USYjbKlS95JyyxbfjRbbubM4Q3MGsZFWbRi7C0exvZiw8zHBGn3hnbnZsDdlDax2weuXsSzHPlw8DxG6XEXZy3GWuK4LlQCVXaa9AcSd9LODVIrxaelr0pdW38a2rot1rlkGHdYPR8oD5zV3fj4kWs3n68mU9VJRTvubbX1zKV8EIX9UvVJ6wuRHqyVOFKJPkKloc="
+ }
+} \ No newline at end of file
diff --git a/src/test/resources/hubzilla_follow.json b/src/test/resources/hubzilla_follow.json
new file mode 100644
index 00000000..fff33c36
--- /dev/null
+++ b/src/test/resources/hubzilla_follow.json
@@ -0,0 +1 @@
+{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1","https://ussr.win/apschema/v1.3"],"id":"https://ussr.win/follow/1216","type":"Follow","actor":{"type":"Person","id":"https://ussr.win/channel/zlax","preferredUsername":"zlax","name":"ivan zlax","icon":{"type":"Image","mediaType":"image/jpeg","url":"https://ussr.win/photo/profile/l/2","height":300,"width":300},"url":{"type":"Link","mediaType":"text/html","href":"https://ussr.win/channel/zlax"},"inbox":"https://ussr.win/inbox/zlax","outbox":"https://ussr.win/outbox/zlax","followers":"https://ussr.win/followers/zlax","following":"https://ussr.win/following/zlax","endpoints":{"sharedInbox":"https://ussr.win/inbox"},"publicKey":{"id":"https://ussr.win/channel/zlax/public_key_pem","owner":"https://ussr.win/channel/zlax","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtFSIKFVzo+9NzzY0xho9\nA8CqO0j12f3HEOZUDBDj1NBbQ6Cj1p1f5mB1AlRr+a05fqpraCWIJyCXVTtdyjem\niQ/ObT2PuZIhAY04/ptv78S9EM9ImZi2aDNWp2nWQu23dJajb2TMpIxGNS1M3DVR\ngXwRVEVIEEDzftWx7vF4335/2uJvPfwTzKI3pR972xp1iZCd/G3BtYzUW4swIFSu\n+5ZLKhgwO6A4Ge5+sn2k6x+K56YCgeEEQheOg+PbggIQ68xfvYCQyfuShqAXEadv\nOvQ1dN8WO+tgKqYDRQgILIONB8/2/XXMoQbSVRZs2GmLKXHhIeVKiSZ1vKwHsPx1\nCYUayQo/BB1JpbjkmyDQcHAX+KZ7PQUhpQn976f6Ycp5rznvBH5Zm96VKySaGMy7\nGWefafOfcCNfVZpq17gPPfSbC0XOHdvm6T1e1OPwFl/ho9HxatbA60DIDEd7xf4V\nq9aTSZ2MeQW5JXaxJXSgwucPZ9mhVfucFtCyLdlHZ7gA5yJ+pyYmMe8pjCSNWztD\nUOoJC46vv2l1RbMGTJy2AY9ZuPvyrmJHlsRsTYcmDYRFSk6sgGi1eswSDHlruE3u\nKG4E0nsA8qxzgzpCViQV4DIgXhMInBwo7ZhSyWD+tF2a4tpXorHqmg35x/aqJlaq\nE2xF+yrfh/Kx8O+7P6C1aGcCAwEAAQ==\n-----END PUBLIC KEY-----\n"},"nomadicLocations":[{"id":"https://ussr.win/locs/zlax","type":"nomadicLocation","locationAddress":"acct:zlax@ussr.win","locationPrimary":true,"locationDeleted":false}]},"object":"https://juick.com/u/netneladno","to":["https://juick.com/u/netneladno"],"signature":{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],"type":"RsaSignature2017","nonce":"1ee5df16edb30d1944fcb07859849ee4293da98c13322c13e2f929b822da91a7","creator":"https://ussr.win/channel/zlax/public_key_pem","created":"2019-02-23T11:09:56Z","signatureValue":"M6551dhNfk0vsSQ6hYvxXVURb7NviNk8ExYBLsax6uYr1oyV/zQJuUtoqufr47t/2NvfzLVn0criLAKbF586siIFd2YBO4wPWtSDPPAWgaQxDrCzRG4PimuavgT0ghIKzmmMnCG6BLC4O0+ohKPUFzaOHgsgdM++TY21OdLQUaauwYkvn9eWYGXzkS5LEnxEKuFpQarOtzChpP9btI9P3iDDRaOqTTlDw0w1xZ8/LqqPCqoA5W8NS+xSyhHsvFi1ApNinFQNM5+e1Gj09Lvtumn2rQE9Smn8sXG9jp5MSBKFdmuG20PDqidfQ3uvZm5qPdwfJnWjZ2VD00aOSji3ahcHJWkbxpNQki2TBb6u2FDABPlUsBvJ2Z23Ubdm+kcjNklmlZeAgcTvWoZhlzushlGjukRwMymRJZXQNIAgVoKbWhvm2w8NX1cQWs1XS211IPl44sT33T5ZoDNMiDPru+XtHWXrZxA2jpLrHec18bZ15y66KTi67s8QNoYn7cUSqETtch7Vuixw2oxQZNfDj0yhthbi7+lFoqB1Ilo37rJx/9wMY9O271n4rBmuSoHmMDVUSZBZxPjpzvm3zS/NjxczwNJ8a8hZSGcDvViHf2Lcqj+qt+DBbI1jtPGRTieR6pVRjxa7MjjZipDMhYczn13poKQ4KK3lYakyaNhjgHg="}} \ No newline at end of file
diff --git a/src/test/resources/mocks/activity/testfollow.json b/src/test/resources/mocks/activity/testfollow.json
new file mode 100644
index 00000000..e308e52e
--- /dev/null
+++ b/src/test/resources/mocks/activity/testfollow.json
@@ -0,0 +1,15 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "schema": "http://schema.org#",
+ "PropertyValue": "schema:PropertyValue",
+ "value": "schema:value"
+ }
+ ],
+ "id": "https://example.com/12345678",
+ "type": "Follow",
+ "actor": "https://example.com/u/testuser",
+ "object": "http://localhost:8080/u/ugnich"
+} \ No newline at end of file
diff --git a/src/test/resources/mocks/activity/testuser.json b/src/test/resources/mocks/activity/testuser.json
new file mode 100644
index 00000000..95fc2aa9
--- /dev/null
+++ b/src/test/resources/mocks/activity/testuser.json
@@ -0,0 +1,27 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "schema": "http://schema.org#",
+ "PropertyValue": "schema:PropertyValue",
+ "value": "schema:value"
+ }
+ ],
+ "id": "https://example.com/u/testuser",
+ "type": "Person",
+ "following": "https://example.com/u/testuser/following",
+ "followers": "https://example.com/u/testuser/followers",
+ "inbox": "https://example.com/u/testuser/inbox",
+ "outbox": "https://example.com/u/testuser/outbox",
+ "preferredUsername": "testuser",
+ "url": "https://example.com/@testuser",
+ "publicKey": {
+ "id": "https://example.com/u/testuser#main-key",
+ "owner": "https://example.com/u/testuser",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiHKRdKFFeT4P/MVlNbxC\nbbgXOkEdeQzvJB/wAJgSYbUwm9SzNFzttePQXk3/MWoK2awWUInZTduVHsWt8zU7\nO3d9PAW6YH6L1oDkjgMLAb9aUWV2ClQWMwsn88WKK9Rb1WOmd8BrXjPfmeFK2ypQ\n9eg8aKpH36WAXiiaTDfBupBZ0Ki2+E87BrWxpbUeDC1dkV+zbl8BMm7X0rp+reoC\nYUWMcjQMzhMmQOXUd4zwJIDPZDMdF4beq/y6WPSUTVgjs4kPDS1HT60ATnsUqyPE\n6tuGxG4j0msb4TTre87PKxMU5YPOxSiqNL0O/3u9/2shVPpjDa/uy9W+VaeBHbFm\nSQIDAQAB\n-----END PUBLIC KEY-----\n"
+ },
+ "endpoints": {
+ "sharedInbox": "https://example.com/inbox"
+ }
+} \ No newline at end of file
diff --git a/src/test/resources/test.p12 b/src/test/resources/test.p12
new file mode 100644
index 00000000..7f7457eb
--- /dev/null
+++ b/src/test/resources/test.p12
Binary files differ