mirror of
https://github.com/Athou/commafeed.git
synced 2026-09-24 21:15:13 +00:00
use google java formatter to avoid downloading eclipse during build
This commit is contained in:
@@ -1,35 +1,37 @@
|
||||
package com.commafeed;
|
||||
|
||||
import jakarta.enterprise.inject.spi.CDI;
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.kohsuke.MetaInfServices;
|
||||
|
||||
import io.quarkus.runtime.ShutdownEvent;
|
||||
import io.quarkus.runtime.StartupEvent;
|
||||
import io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback;
|
||||
import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
|
||||
import jakarta.enterprise.inject.spi.CDI;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import org.hibernate.Session;
|
||||
import org.kohsuke.MetaInfServices;
|
||||
|
||||
/**
|
||||
* Resets database between tests
|
||||
*/
|
||||
/** Resets database between tests */
|
||||
@MetaInfServices
|
||||
public class DatabaseReset implements QuarkusTestBeforeEachCallback {
|
||||
|
||||
@Override
|
||||
public void beforeEach(QuarkusTestMethodContext context) {
|
||||
// stop the application to make sure that there are no active transactions when we truncate the tables
|
||||
getBean(CommaFeedApplication.class).stop(new ShutdownEvent());
|
||||
@Override
|
||||
public void beforeEach(QuarkusTestMethodContext context) {
|
||||
// stop the application to make sure that there are no active transactions when we truncate
|
||||
// the
|
||||
// tables
|
||||
getBean(CommaFeedApplication.class).stop(new ShutdownEvent());
|
||||
|
||||
// truncate all tables so that we have a clean slate for the next test
|
||||
getBean(EntityManager.class).unwrap(Session.class).getSessionFactory().getSchemaManager().truncateMappedObjects();
|
||||
// truncate all tables so that we have a clean slate for the next test
|
||||
getBean(EntityManager.class)
|
||||
.unwrap(Session.class)
|
||||
.getSessionFactory()
|
||||
.getSchemaManager()
|
||||
.truncateMappedObjects();
|
||||
|
||||
// restart the application
|
||||
getBean(CommaFeedApplication.class).start(new StartupEvent());
|
||||
}
|
||||
// restart the application
|
||||
getBean(CommaFeedApplication.class).start(new StartupEvent());
|
||||
}
|
||||
|
||||
private static <T> T getBean(Class<T> clazz) {
|
||||
return CDI.current().select(clazz).get();
|
||||
}
|
||||
private static <T> T getBean(Class<T> clazz) {
|
||||
return CDI.current().select(clazz).get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,54 @@
|
||||
package com.commafeed;
|
||||
|
||||
import com.rometools.rome.feed.CopyFrom;
|
||||
import com.rometools.rome.feed.module.Module;
|
||||
import com.rometools.rome.io.WireFeedGenerator;
|
||||
import com.rometools.rome.io.WireFeedParser;
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.reflections.Reflections;
|
||||
import org.reflections.scanners.Scanners;
|
||||
|
||||
import com.rometools.rome.feed.CopyFrom;
|
||||
import com.rometools.rome.feed.module.Module;
|
||||
import com.rometools.rome.io.WireFeedGenerator;
|
||||
import com.rometools.rome.io.WireFeedParser;
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||
|
||||
class NativeImageClassesTest {
|
||||
|
||||
@Test
|
||||
void annotationContainsAllRequiredRomeClasses() {
|
||||
Reflections reflections = new Reflections("com.rometools");
|
||||
Set<Class<?>> classesInAnnotation = Set
|
||||
.copyOf(List.of(NativeImageClasses.class.getAnnotation(RegisterForReflection.class).targets()));
|
||||
@Test
|
||||
void annotationContainsAllRequiredRomeClasses() {
|
||||
Reflections reflections = new Reflections("com.rometools");
|
||||
Set<Class<?>> classesInAnnotation =
|
||||
Set.copyOf(
|
||||
List.of(
|
||||
NativeImageClasses.class
|
||||
.getAnnotation(RegisterForReflection.class)
|
||||
.targets()));
|
||||
|
||||
List<Class<?>> missingClasses = new ArrayList<>();
|
||||
for (Class<?> clazz : List.of(Module.class, Cloneable.class, CopyFrom.class, WireFeedParser.class, WireFeedGenerator.class)) {
|
||||
Set<Class<?>> moduleClasses = new HashSet<>(reflections.get(Scanners.SubTypes.of(clazz).asClass()));
|
||||
moduleClasses.removeIf(c -> c.isInterface() || Modifier.isAbstract(c.getModifiers()) || !Modifier.isPublic(c.getModifiers()));
|
||||
moduleClasses.removeAll(classesInAnnotation);
|
||||
missingClasses.addAll(moduleClasses);
|
||||
}
|
||||
List<Class<?>> missingClasses = new ArrayList<>();
|
||||
for (Class<?> clazz :
|
||||
List.of(
|
||||
Module.class,
|
||||
Cloneable.class,
|
||||
CopyFrom.class,
|
||||
WireFeedParser.class,
|
||||
WireFeedGenerator.class)) {
|
||||
Set<Class<?>> moduleClasses =
|
||||
new HashSet<>(reflections.get(Scanners.SubTypes.of(clazz).asClass()));
|
||||
moduleClasses.removeIf(
|
||||
c ->
|
||||
c.isInterface()
|
||||
|| Modifier.isAbstract(c.getModifiers())
|
||||
|| !Modifier.isPublic(c.getModifiers()));
|
||||
moduleClasses.removeAll(classesInAnnotation);
|
||||
missingClasses.addAll(moduleClasses);
|
||||
}
|
||||
|
||||
missingClasses.sort(Comparator.comparing(Class::getName));
|
||||
missingClasses.forEach(c -> System.out.println(c.getName() + ".class,"));
|
||||
Assertions.assertEquals(List.of(), missingClasses);
|
||||
}
|
||||
|
||||
}
|
||||
missingClasses.sort(Comparator.comparing(Class::getName));
|
||||
missingClasses.forEach(c -> System.out.println(c.getName() + ".class,"));
|
||||
Assertions.assertEquals(List.of(), missingClasses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.commafeed;
|
||||
|
||||
public class TestConstants {
|
||||
public static final String ADMIN_USERNAME = "admin";
|
||||
public static final String ADMIN_PASSWORD = "admin";
|
||||
public static final String ADMIN_USERNAME = "admin";
|
||||
public static final String ADMIN_PASSWORD = "admin";
|
||||
}
|
||||
|
||||
@@ -5,14 +5,14 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
class DigestsTest {
|
||||
|
||||
@Test
|
||||
void sha1Hex() {
|
||||
Assertions.assertEquals("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d", Digests.sha1Hex("hello"));
|
||||
}
|
||||
@Test
|
||||
void sha1Hex() {
|
||||
Assertions.assertEquals(
|
||||
"aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d", Digests.sha1Hex("hello"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void md5Hex() {
|
||||
Assertions.assertEquals("5d41402abc4b2a76b9719d911017c592", Digests.md5Hex("hello"));
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
void md5Hex() {
|
||||
Assertions.assertEquals("5d41402abc4b2a76b9719d911017c592", Digests.md5Hex("hello"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
package com.commafeed.backend;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.CommaFeedVersion;
|
||||
import com.commafeed.backend.HttpGetter.HttpResponseException;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.HttpGetter.NotModifiedException;
|
||||
import com.commafeed.backend.HttpGetter.TooManyRequestsException;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
import io.quarkus.runtime.configuration.MemorySize;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@@ -15,7 +24,6 @@ import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.hc.client5.http.ConnectTimeoutException;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
@@ -35,387 +43,508 @@ import org.mockserver.model.HttpRequest;
|
||||
import org.mockserver.model.HttpResponse;
|
||||
import org.mockserver.model.MediaType;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.CommaFeedVersion;
|
||||
import com.commafeed.backend.HttpGetter.HttpResponseException;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.HttpGetter.NotModifiedException;
|
||||
import com.commafeed.backend.HttpGetter.TooManyRequestsException;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
||||
import io.quarkus.runtime.configuration.MemorySize;
|
||||
|
||||
class HttpGetterTest {
|
||||
|
||||
private static final Instant NOW = Instant.now();
|
||||
|
||||
private MockServerClient mockServerClient;
|
||||
private String feedUrl;
|
||||
private byte[] feedContent;
|
||||
|
||||
private CommaFeedConfiguration config;
|
||||
|
||||
private HttpClientFactory provider;
|
||||
private HttpGetter getter;
|
||||
|
||||
@BeforeEach
|
||||
void init() throws IOException {
|
||||
this.mockServerClient = ClientAndServer.startClientAndServer(0);
|
||||
this.feedUrl = "http://localhost:" + this.mockServerClient.getPort() + "/";
|
||||
this.feedContent = IOUtils.toByteArray(Objects.requireNonNull(getClass().getResource("/feed/rss.xml")));
|
||||
|
||||
this.config = Mockito.mock(CommaFeedConfiguration.class, Mockito.RETURNS_DEEP_STUBS);
|
||||
Mockito.when(config.httpClient().userAgent()).thenReturn(Optional.of("http-getter-test"));
|
||||
Mockito.when(config.httpClient().connectTimeout()).thenReturn(Duration.ofSeconds(30));
|
||||
Mockito.when(config.httpClient().sslHandshakeTimeout()).thenReturn(Duration.ofSeconds(30));
|
||||
Mockito.when(config.httpClient().socketTimeout()).thenReturn(Duration.ofSeconds(30));
|
||||
Mockito.when(config.httpClient().responseTimeout()).thenReturn(Duration.ofSeconds(30));
|
||||
Mockito.when(config.httpClient().connectionTimeToLive()).thenReturn(Duration.ofSeconds(30));
|
||||
Mockito.when(config.httpClient().maxResponseSize()).thenReturn(new MemorySize(new BigInteger("10000")));
|
||||
Mockito.when(config.httpClient().cache().enabled()).thenReturn(true);
|
||||
Mockito.when(config.httpClient().cache().maximumMemorySize()).thenReturn(new MemorySize(new BigInteger("100000")));
|
||||
Mockito.when(config.httpClient().cache().expiration()).thenReturn(Duration.ofMinutes(1));
|
||||
Mockito.when(config.feedRefresh().httpThreads()).thenReturn(3);
|
||||
|
||||
this.provider = new HttpClientFactory(config, Mockito.mock(CommaFeedVersion.class));
|
||||
this.getter = new HttpGetter(config, () -> NOW, provider, Mockito.mock(MetricRegistry.class));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
if (this.mockServerClient != null) {
|
||||
this.mockServerClient.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(
|
||||
ints = { HttpStatus.SC_UNAUTHORIZED, HttpStatus.SC_FORBIDDEN, HttpStatus.SC_NOT_FOUND, HttpStatus.SC_INTERNAL_SERVER_ERROR })
|
||||
void errorCodes(int code) {
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(HttpResponse.response().withStatusCode(code));
|
||||
|
||||
HttpResponseException e = Assertions.assertThrows(HttpResponseException.class, () -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(code, e.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validFeed() throws Exception {
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(HttpResponse.response()
|
||||
.withBody(feedContent)
|
||||
.withContentType(MediaType.APPLICATION_ATOM_XML)
|
||||
.withHeader(HttpHeaders.LAST_MODIFIED, "123456")
|
||||
.withHeader(HttpHeaders.ETAG, "78910")
|
||||
.withHeader(HttpHeaders.CACHE_CONTROL, "max-age=60, must-revalidate")
|
||||
.withHeader(HttpHeaders.RETRY_AFTER, "120"));
|
||||
|
||||
HttpResult result = getter.get(this.feedUrl);
|
||||
Assertions.assertArrayEquals(feedContent, result.content());
|
||||
Assertions.assertEquals(MediaType.APPLICATION_ATOM_XML.toString(), result.contentType());
|
||||
Assertions.assertEquals("123456", result.lastModifiedSince());
|
||||
Assertions.assertEquals("78910", result.eTag());
|
||||
Assertions.assertEquals(Duration.ofSeconds(60), result.validFor());
|
||||
Assertions.assertEquals(this.feedUrl, result.urlAfterRedirect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ignoreInvalidCacheControlValue() throws Exception {
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(HttpResponse.response()
|
||||
.withBody(feedContent)
|
||||
.withContentType(MediaType.APPLICATION_ATOM_XML)
|
||||
.withHeader(HttpHeaders.CACHE_CONTROL, "max-age=60; must-revalidate"));
|
||||
|
||||
HttpResult result = getter.get(this.feedUrl);
|
||||
Assertions.assertEquals(Duration.ZERO, result.validFor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tooManyRequestsExceptionSeconds() {
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(
|
||||
HttpResponse.response().withStatusCode(HttpStatus.SC_TOO_MANY_REQUESTS).withHeader(HttpHeaders.RETRY_AFTER, "120"));
|
||||
|
||||
TooManyRequestsException e = Assertions.assertThrows(TooManyRequestsException.class, () -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(NOW.plusSeconds(120), e.getRetryAfter());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tooManyRequestsExceptionDate() {
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(HttpResponse.response()
|
||||
.withStatusCode(HttpStatus.SC_TOO_MANY_REQUESTS)
|
||||
.withHeader(HttpHeaders.RETRY_AFTER, "Wed, 21 Oct 2015 07:28:00 GMT"));
|
||||
|
||||
TooManyRequestsException e = Assertions.assertThrows(TooManyRequestsException.class, () -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(Instant.parse("2015-10-21T07:28:00Z"), e.getRetryAfter());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(
|
||||
ints = { HttpStatus.SC_MOVED_PERMANENTLY, HttpStatus.SC_MOVED_TEMPORARILY, HttpStatus.SC_TEMPORARY_REDIRECT,
|
||||
HttpStatus.SC_PERMANENT_REDIRECT })
|
||||
void followRedirects(int code) throws Exception {
|
||||
// first redirect
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withPath("/"))
|
||||
.respond(HttpResponse.response()
|
||||
.withStatusCode(code)
|
||||
.withHeader(HttpHeaders.LOCATION, "http://localhost:" + this.mockServerClient.getPort() + "/redirected"));
|
||||
|
||||
// second redirect
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withPath("/redirected"))
|
||||
.respond(HttpResponse.response()
|
||||
.withStatusCode(code)
|
||||
.withHeader(HttpHeaders.LOCATION, "http://localhost:" + this.mockServerClient.getPort() + "/redirected-2"));
|
||||
|
||||
// final destination
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withPath("/redirected-2"))
|
||||
.respond(HttpResponse.response().withBody(feedContent).withContentType(MediaType.APPLICATION_ATOM_XML));
|
||||
|
||||
HttpResult result = getter.get(this.feedUrl);
|
||||
Assertions.assertEquals("http://localhost:" + this.mockServerClient.getPort() + "/redirected-2", result.urlAfterRedirect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void dataTimeout() {
|
||||
Mockito.when(config.httpClient().responseTimeout()).thenReturn(Duration.ofMillis(500));
|
||||
this.getter = new HttpGetter(config, () -> NOW, provider, Mockito.mock(MetricRegistry.class));
|
||||
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(HttpResponse.response().withDelay(Delay.milliseconds(1000)));
|
||||
|
||||
Assertions.assertThrows(SocketTimeoutException.class, () -> getter.get(this.feedUrl));
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectTimeout() {
|
||||
Mockito.when(config.httpClient().connectTimeout()).thenReturn(Duration.ofMillis(500));
|
||||
this.getter = new HttpGetter(config, () -> NOW, provider, Mockito.mock(MetricRegistry.class));
|
||||
// try to connect to a non-routable address
|
||||
// https://stackoverflow.com/a/904609
|
||||
Exception e = Assertions.assertThrows(Exception.class, () -> getter.get("http://10.255.255.1"));
|
||||
Assertions.assertTrue(e instanceof ConnectTimeoutException
|
||||
// A NoRouteToHostException can also be thrown in some cases
|
||||
// depending on the underlying network configuration
|
||||
// https://github.com/Athou/commafeed/issues/1876
|
||||
|| e instanceof NoRouteToHostException,
|
||||
"Expected ConnectTimeoutException or NoRouteToHostException, but got: " + e.getClass().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void userAgent() throws Exception {
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withHeader(HttpHeaders.USER_AGENT, "http-getter-test"))
|
||||
.respond(HttpResponse.response().withBody("ok"));
|
||||
|
||||
HttpResult result = getter.get(this.feedUrl);
|
||||
Assertions.assertEquals("ok", new String(result.content()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void lastModifiedReturns304() {
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withHeader(HttpHeaders.IF_MODIFIED_SINCE, "123456"))
|
||||
.respond(HttpResponse.response().withStatusCode(HttpStatus.SC_NOT_MODIFIED));
|
||||
|
||||
Assertions.assertThrows(NotModifiedException.class,
|
||||
() -> getter.get(HttpGetter.HttpRequest.builder(this.feedUrl).lastModified("123456").build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void eTagReturns304() {
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withHeader(HttpHeaders.IF_NONE_MATCH, "78910"))
|
||||
.respond(HttpResponse.response().withStatusCode(HttpStatus.SC_NOT_MODIFIED));
|
||||
|
||||
Assertions.assertThrows(NotModifiedException.class,
|
||||
() -> getter.get(HttpGetter.HttpRequest.builder(this.feedUrl).eTag("78910").build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void ignoreCookie() {
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(req -> {
|
||||
calls.incrementAndGet();
|
||||
|
||||
if (req.containsHeader(HttpHeaders.COOKIE)) {
|
||||
throw new Exception("cookie should not be sent by the client");
|
||||
}
|
||||
|
||||
return HttpResponse.response().withBody("ok").withHeader(HttpHeaders.SET_COOKIE, "foo=bar");
|
||||
});
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> getter.get(this.feedUrl));
|
||||
Assertions.assertDoesNotThrow(() -> getter.get(this.feedUrl + "?foo=bar"));
|
||||
Assertions.assertEquals(2, calls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cacheSubsequentCalls() throws Exception {
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(req -> {
|
||||
calls.incrementAndGet();
|
||||
return HttpResponse.response().withBody("ok");
|
||||
});
|
||||
|
||||
HttpResult result = getter.get(this.feedUrl);
|
||||
Assertions.assertEquals(result, getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(1, calls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void largeFeedWithContentLengthHeader() {
|
||||
byte[] bytes = new byte[100000];
|
||||
Arrays.fill(bytes, (byte) 1);
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(HttpResponse.response().withBody(bytes));
|
||||
|
||||
IOException e = Assertions.assertThrows(IOException.class, () -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals("Response size (100000 bytes) exceeds the maximum allowed size (10000 bytes)", e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void largeFeedWithoutContentLengthHeader() {
|
||||
byte[] bytes = new byte[100000];
|
||||
Arrays.fill(bytes, (byte) 1);
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(HttpResponse.response()
|
||||
.withBody(bytes)
|
||||
.withConnectionOptions(ConnectionOptions.connectionOptions().withSuppressContentLengthHeader(true)));
|
||||
|
||||
IOException e = Assertions.assertThrows(IOException.class, () -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals("Response size exceeds the maximum allowed size (10000 bytes)", e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ignoreInvalidSsl() throws Exception {
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(HttpResponse.response().withBody("ok"));
|
||||
|
||||
HttpResult result = getter.get("https://localhost:" + this.mockServerClient.getPort());
|
||||
Assertions.assertEquals("ok", new String(result.content()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotUseUpgradeProtocolHeader() {
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(req -> {
|
||||
calls.incrementAndGet();
|
||||
|
||||
if (req.containsHeader(HttpHeaders.UPGRADE)) {
|
||||
throw new Exception("upgrade header should not be sent by the client");
|
||||
}
|
||||
|
||||
return HttpResponse.response().withBody("ok");
|
||||
});
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(1, calls.get());
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Compression {
|
||||
|
||||
private static final String ACCEPT_ENCODING = "gzip, deflate, br";
|
||||
|
||||
@Test
|
||||
void gzip() throws Exception {
|
||||
supportsCompression("gzip", GZIPOutputStream::new);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deflate() throws Exception {
|
||||
supportsCompression("deflate", DeflaterOutputStream::new);
|
||||
}
|
||||
|
||||
void supportsCompression(String encoding, CompressionFunction compressionFunction) throws Exception {
|
||||
String body = "my body";
|
||||
|
||||
HttpGetterTest.this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(req -> {
|
||||
String acceptEncodingHeader = req.getFirstHeader(HttpHeaders.ACCEPT_ENCODING);
|
||||
if (!ACCEPT_ENCODING.equals(acceptEncodingHeader)) {
|
||||
throw new Exception("Wrong value in the Accept-Encoding header, should be '%s' but was '%s'".formatted(ACCEPT_ENCODING,
|
||||
acceptEncodingHeader));
|
||||
}
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
try (OutputStream compressionOutputStream = compressionFunction.apply(output)) {
|
||||
compressionOutputStream.write(body.getBytes());
|
||||
}
|
||||
|
||||
return HttpResponse.response().withBody(output.toByteArray()).withHeader(HttpHeaders.CONTENT_ENCODING, encoding);
|
||||
});
|
||||
|
||||
HttpResult result = getter.get(HttpGetterTest.this.feedUrl);
|
||||
Assertions.assertEquals(body, new String(result.content()));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CompressionFunction {
|
||||
OutputStream apply(OutputStream input) throws IOException;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class SchemeNotAllowed {
|
||||
@Test
|
||||
void file() {
|
||||
Assertions.assertThrows(HttpGetter.SchemeNotAllowedException.class, () -> getter.get("file://localhost"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void ftp() {
|
||||
Assertions.assertThrows(HttpGetter.SchemeNotAllowedException.class, () -> getter.get("ftp://localhost"));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class HostNotAllowed {
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
Mockito.when(config.httpClient().blockLocalAddresses()).thenReturn(true);
|
||||
getter = new HttpGetter(config, () -> NOW, provider, Mockito.mock(MetricRegistry.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void localhost() {
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://localhost"));
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://127.0.0.1"));
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://2130706433"));
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://0x7F.0x00.0x00.0X01"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void zero() {
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://0.0.0.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void linkLocal() {
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://169.254.12.34"));
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://169.254.169.254"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void multicast() {
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://224.2.3.4"));
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://239.255.255.254"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateIpv4Ranges() {
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://10.0.0.1"));
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://172.16.0.1"));
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://192.168.0.1"));
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://100.64.0.1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateIpv6Ranges() {
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://[fe80::215:5dff:fe15:102]"));
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://[fd00:dead:beef::50]"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private static final Instant NOW = Instant.now();
|
||||
|
||||
private MockServerClient mockServerClient;
|
||||
private String feedUrl;
|
||||
private byte[] feedContent;
|
||||
|
||||
private CommaFeedConfiguration config;
|
||||
|
||||
private HttpClientFactory provider;
|
||||
private HttpGetter getter;
|
||||
|
||||
@BeforeEach
|
||||
void init() throws IOException {
|
||||
this.mockServerClient = ClientAndServer.startClientAndServer(0);
|
||||
this.feedUrl = "http://localhost:" + this.mockServerClient.getPort() + "/";
|
||||
this.feedContent =
|
||||
IOUtils.toByteArray(
|
||||
Objects.requireNonNull(getClass().getResource("/feed/rss.xml")));
|
||||
|
||||
this.config = Mockito.mock(CommaFeedConfiguration.class, Mockito.RETURNS_DEEP_STUBS);
|
||||
Mockito.when(config.httpClient().userAgent()).thenReturn(Optional.of("http-getter-test"));
|
||||
Mockito.when(config.httpClient().connectTimeout()).thenReturn(Duration.ofSeconds(30));
|
||||
Mockito.when(config.httpClient().sslHandshakeTimeout()).thenReturn(Duration.ofSeconds(30));
|
||||
Mockito.when(config.httpClient().socketTimeout()).thenReturn(Duration.ofSeconds(30));
|
||||
Mockito.when(config.httpClient().responseTimeout()).thenReturn(Duration.ofSeconds(30));
|
||||
Mockito.when(config.httpClient().connectionTimeToLive()).thenReturn(Duration.ofSeconds(30));
|
||||
Mockito.when(config.httpClient().maxResponseSize())
|
||||
.thenReturn(new MemorySize(new BigInteger("10000")));
|
||||
Mockito.when(config.httpClient().cache().enabled()).thenReturn(true);
|
||||
Mockito.when(config.httpClient().cache().maximumMemorySize())
|
||||
.thenReturn(new MemorySize(new BigInteger("100000")));
|
||||
Mockito.when(config.httpClient().cache().expiration()).thenReturn(Duration.ofMinutes(1));
|
||||
Mockito.when(config.feedRefresh().httpThreads()).thenReturn(3);
|
||||
|
||||
this.provider = new HttpClientFactory(config, Mockito.mock(CommaFeedVersion.class));
|
||||
this.getter =
|
||||
new HttpGetter(config, () -> NOW, provider, Mockito.mock(MetricRegistry.class));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
if (this.mockServerClient != null) {
|
||||
this.mockServerClient.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(
|
||||
ints = {
|
||||
HttpStatus.SC_UNAUTHORIZED,
|
||||
HttpStatus.SC_FORBIDDEN,
|
||||
HttpStatus.SC_NOT_FOUND,
|
||||
HttpStatus.SC_INTERNAL_SERVER_ERROR
|
||||
})
|
||||
void errorCodes(int code) {
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(HttpResponse.response().withStatusCode(code));
|
||||
|
||||
HttpResponseException e =
|
||||
Assertions.assertThrows(
|
||||
HttpResponseException.class, () -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(code, e.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validFeed() throws Exception {
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(
|
||||
HttpResponse.response()
|
||||
.withBody(feedContent)
|
||||
.withContentType(MediaType.APPLICATION_ATOM_XML)
|
||||
.withHeader(HttpHeaders.LAST_MODIFIED, "123456")
|
||||
.withHeader(HttpHeaders.ETAG, "78910")
|
||||
.withHeader(
|
||||
HttpHeaders.CACHE_CONTROL, "max-age=60, must-revalidate")
|
||||
.withHeader(HttpHeaders.RETRY_AFTER, "120"));
|
||||
|
||||
HttpResult result = getter.get(this.feedUrl);
|
||||
Assertions.assertArrayEquals(feedContent, result.content());
|
||||
Assertions.assertEquals(MediaType.APPLICATION_ATOM_XML.toString(), result.contentType());
|
||||
Assertions.assertEquals("123456", result.lastModifiedSince());
|
||||
Assertions.assertEquals("78910", result.eTag());
|
||||
Assertions.assertEquals(Duration.ofSeconds(60), result.validFor());
|
||||
Assertions.assertEquals(this.feedUrl, result.urlAfterRedirect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ignoreInvalidCacheControlValue() throws Exception {
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(
|
||||
HttpResponse.response()
|
||||
.withBody(feedContent)
|
||||
.withContentType(MediaType.APPLICATION_ATOM_XML)
|
||||
.withHeader(
|
||||
HttpHeaders.CACHE_CONTROL, "max-age=60; must-revalidate"));
|
||||
|
||||
HttpResult result = getter.get(this.feedUrl);
|
||||
Assertions.assertEquals(Duration.ZERO, result.validFor());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tooManyRequestsExceptionSeconds() {
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(
|
||||
HttpResponse.response()
|
||||
.withStatusCode(HttpStatus.SC_TOO_MANY_REQUESTS)
|
||||
.withHeader(HttpHeaders.RETRY_AFTER, "120"));
|
||||
|
||||
TooManyRequestsException e =
|
||||
Assertions.assertThrows(
|
||||
TooManyRequestsException.class, () -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(NOW.plusSeconds(120), e.getRetryAfter());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tooManyRequestsExceptionDate() {
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(
|
||||
HttpResponse.response()
|
||||
.withStatusCode(HttpStatus.SC_TOO_MANY_REQUESTS)
|
||||
.withHeader(
|
||||
HttpHeaders.RETRY_AFTER, "Wed, 21 Oct 2015 07:28:00 GMT"));
|
||||
|
||||
TooManyRequestsException e =
|
||||
Assertions.assertThrows(
|
||||
TooManyRequestsException.class, () -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(Instant.parse("2015-10-21T07:28:00Z"), e.getRetryAfter());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(
|
||||
ints = {
|
||||
HttpStatus.SC_MOVED_PERMANENTLY,
|
||||
HttpStatus.SC_MOVED_TEMPORARILY,
|
||||
HttpStatus.SC_TEMPORARY_REDIRECT,
|
||||
HttpStatus.SC_PERMANENT_REDIRECT
|
||||
})
|
||||
void followRedirects(int code) throws Exception {
|
||||
// first redirect
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET").withPath("/"))
|
||||
.respond(
|
||||
HttpResponse.response()
|
||||
.withStatusCode(code)
|
||||
.withHeader(
|
||||
HttpHeaders.LOCATION,
|
||||
"http://localhost:"
|
||||
+ this.mockServerClient.getPort()
|
||||
+ "/redirected"));
|
||||
|
||||
// second redirect
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET").withPath("/redirected"))
|
||||
.respond(
|
||||
HttpResponse.response()
|
||||
.withStatusCode(code)
|
||||
.withHeader(
|
||||
HttpHeaders.LOCATION,
|
||||
"http://localhost:"
|
||||
+ this.mockServerClient.getPort()
|
||||
+ "/redirected-2"));
|
||||
|
||||
// final destination
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET").withPath("/redirected-2"))
|
||||
.respond(
|
||||
HttpResponse.response()
|
||||
.withBody(feedContent)
|
||||
.withContentType(MediaType.APPLICATION_ATOM_XML));
|
||||
|
||||
HttpResult result = getter.get(this.feedUrl);
|
||||
Assertions.assertEquals(
|
||||
"http://localhost:" + this.mockServerClient.getPort() + "/redirected-2",
|
||||
result.urlAfterRedirect());
|
||||
}
|
||||
|
||||
@Test
|
||||
void dataTimeout() {
|
||||
Mockito.when(config.httpClient().responseTimeout()).thenReturn(Duration.ofMillis(500));
|
||||
this.getter =
|
||||
new HttpGetter(config, () -> NOW, provider, Mockito.mock(MetricRegistry.class));
|
||||
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(HttpResponse.response().withDelay(Delay.milliseconds(1000)));
|
||||
|
||||
Assertions.assertThrows(SocketTimeoutException.class, () -> getter.get(this.feedUrl));
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectTimeout() {
|
||||
Mockito.when(config.httpClient().connectTimeout()).thenReturn(Duration.ofMillis(500));
|
||||
this.getter =
|
||||
new HttpGetter(config, () -> NOW, provider, Mockito.mock(MetricRegistry.class));
|
||||
// try to connect to a non-routable address
|
||||
// https://stackoverflow.com/a/904609
|
||||
Exception e =
|
||||
Assertions.assertThrows(Exception.class, () -> getter.get("http://10.255.255.1"));
|
||||
Assertions.assertTrue(
|
||||
e instanceof ConnectTimeoutException
|
||||
// A NoRouteToHostException can also be thrown in some cases
|
||||
// depending on the underlying network configuration
|
||||
// https://github.com/Athou/commafeed/issues/1876
|
||||
|| e instanceof NoRouteToHostException,
|
||||
"Expected ConnectTimeoutException or NoRouteToHostException, but got: "
|
||||
+ e.getClass().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void userAgent() throws Exception {
|
||||
this.mockServerClient
|
||||
.when(
|
||||
HttpRequest.request()
|
||||
.withMethod("GET")
|
||||
.withHeader(HttpHeaders.USER_AGENT, "http-getter-test"))
|
||||
.respond(HttpResponse.response().withBody("ok"));
|
||||
|
||||
HttpResult result = getter.get(this.feedUrl);
|
||||
Assertions.assertEquals("ok", new String(result.content()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void lastModifiedReturns304() {
|
||||
this.mockServerClient
|
||||
.when(
|
||||
HttpRequest.request()
|
||||
.withMethod("GET")
|
||||
.withHeader(HttpHeaders.IF_MODIFIED_SINCE, "123456"))
|
||||
.respond(HttpResponse.response().withStatusCode(HttpStatus.SC_NOT_MODIFIED));
|
||||
|
||||
Assertions.assertThrows(
|
||||
NotModifiedException.class,
|
||||
() ->
|
||||
getter.get(
|
||||
HttpGetter.HttpRequest.builder(this.feedUrl)
|
||||
.lastModified("123456")
|
||||
.build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void eTagReturns304() {
|
||||
this.mockServerClient
|
||||
.when(
|
||||
HttpRequest.request()
|
||||
.withMethod("GET")
|
||||
.withHeader(HttpHeaders.IF_NONE_MATCH, "78910"))
|
||||
.respond(HttpResponse.response().withStatusCode(HttpStatus.SC_NOT_MODIFIED));
|
||||
|
||||
Assertions.assertThrows(
|
||||
NotModifiedException.class,
|
||||
() ->
|
||||
getter.get(
|
||||
HttpGetter.HttpRequest.builder(this.feedUrl)
|
||||
.eTag("78910")
|
||||
.build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void ignoreCookie() {
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(
|
||||
req -> {
|
||||
calls.incrementAndGet();
|
||||
|
||||
if (req.containsHeader(HttpHeaders.COOKIE)) {
|
||||
throw new Exception("cookie should not be sent by the client");
|
||||
}
|
||||
|
||||
return HttpResponse.response()
|
||||
.withBody("ok")
|
||||
.withHeader(HttpHeaders.SET_COOKIE, "foo=bar");
|
||||
});
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> getter.get(this.feedUrl));
|
||||
Assertions.assertDoesNotThrow(() -> getter.get(this.feedUrl + "?foo=bar"));
|
||||
Assertions.assertEquals(2, calls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cacheSubsequentCalls() throws Exception {
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(
|
||||
req -> {
|
||||
calls.incrementAndGet();
|
||||
return HttpResponse.response().withBody("ok");
|
||||
});
|
||||
|
||||
HttpResult result = getter.get(this.feedUrl);
|
||||
Assertions.assertEquals(result, getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(1, calls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void largeFeedWithContentLengthHeader() {
|
||||
byte[] bytes = new byte[100000];
|
||||
Arrays.fill(bytes, (byte) 1);
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(HttpResponse.response().withBody(bytes));
|
||||
|
||||
IOException e = Assertions.assertThrows(IOException.class, () -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(
|
||||
"Response size (100000 bytes) exceeds the maximum allowed size (10000 bytes)",
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void largeFeedWithoutContentLengthHeader() {
|
||||
byte[] bytes = new byte[100000];
|
||||
Arrays.fill(bytes, (byte) 1);
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(
|
||||
HttpResponse.response()
|
||||
.withBody(bytes)
|
||||
.withConnectionOptions(
|
||||
ConnectionOptions.connectionOptions()
|
||||
.withSuppressContentLengthHeader(true)));
|
||||
|
||||
IOException e = Assertions.assertThrows(IOException.class, () -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(
|
||||
"Response size exceeds the maximum allowed size (10000 bytes)", e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ignoreInvalidSsl() throws Exception {
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(HttpResponse.response().withBody("ok"));
|
||||
|
||||
HttpResult result = getter.get("https://localhost:" + this.mockServerClient.getPort());
|
||||
Assertions.assertEquals("ok", new String(result.content()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotUseUpgradeProtocolHeader() {
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(
|
||||
req -> {
|
||||
calls.incrementAndGet();
|
||||
|
||||
if (req.containsHeader(HttpHeaders.UPGRADE)) {
|
||||
throw new Exception(
|
||||
"upgrade header should not be sent by the client");
|
||||
}
|
||||
|
||||
return HttpResponse.response().withBody("ok");
|
||||
});
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(1, calls.get());
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Compression {
|
||||
|
||||
private static final String ACCEPT_ENCODING = "gzip, deflate, br";
|
||||
|
||||
@Test
|
||||
void gzip() throws Exception {
|
||||
supportsCompression("gzip", GZIPOutputStream::new);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deflate() throws Exception {
|
||||
supportsCompression("deflate", DeflaterOutputStream::new);
|
||||
}
|
||||
|
||||
void supportsCompression(String encoding, CompressionFunction compressionFunction)
|
||||
throws Exception {
|
||||
String body = "my body";
|
||||
|
||||
HttpGetterTest.this
|
||||
.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(
|
||||
req -> {
|
||||
String acceptEncodingHeader =
|
||||
req.getFirstHeader(HttpHeaders.ACCEPT_ENCODING);
|
||||
if (!ACCEPT_ENCODING.equals(acceptEncodingHeader)) {
|
||||
throw new Exception(
|
||||
"Wrong value in the Accept-Encoding header, should be '%s' but was '%s'"
|
||||
.formatted(
|
||||
ACCEPT_ENCODING, acceptEncodingHeader));
|
||||
}
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
try (OutputStream compressionOutputStream =
|
||||
compressionFunction.apply(output)) {
|
||||
compressionOutputStream.write(body.getBytes());
|
||||
}
|
||||
|
||||
return HttpResponse.response()
|
||||
.withBody(output.toByteArray())
|
||||
.withHeader(HttpHeaders.CONTENT_ENCODING, encoding);
|
||||
});
|
||||
|
||||
HttpResult result = getter.get(HttpGetterTest.this.feedUrl);
|
||||
Assertions.assertEquals(body, new String(result.content()));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CompressionFunction {
|
||||
OutputStream apply(OutputStream input) throws IOException;
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class SchemeNotAllowed {
|
||||
@Test
|
||||
void file() {
|
||||
Assertions.assertThrows(
|
||||
HttpGetter.SchemeNotAllowedException.class,
|
||||
() -> getter.get("file://localhost"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void ftp() {
|
||||
Assertions.assertThrows(
|
||||
HttpGetter.SchemeNotAllowedException.class,
|
||||
() -> getter.get("ftp://localhost"));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class HostNotAllowed {
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
Mockito.when(config.httpClient().blockLocalAddresses()).thenReturn(true);
|
||||
getter =
|
||||
new HttpGetter(config, () -> NOW, provider, Mockito.mock(MetricRegistry.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void localhost() {
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://localhost"));
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://127.0.0.1"));
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://2130706433"));
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://0x7F.0x00.0x00.0X01"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void zero() {
|
||||
Assertions.assertThrows(UnknownHostException.class, () -> getter.get("http://0.0.0.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void linkLocal() {
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://169.254.12.34"));
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://169.254.169.254"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void multicast() {
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://224.2.3.4"));
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://239.255.255.254"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateIpv4Ranges() {
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://10.0.0.1"));
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://172.16.0.1"));
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://192.168.0.1"));
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://100.64.0.1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateIpv6Ranges() {
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class,
|
||||
() -> getter.get("http://[fe80::215:5dff:fe15:102]"));
|
||||
Assertions.assertThrows(
|
||||
UnknownHostException.class, () -> getter.get("http://[fd00:dead:beef::50]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,81 +5,105 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
class UrlsTest {
|
||||
|
||||
@Test
|
||||
void testNormalization() {
|
||||
String urla1 = "http://example.com/hello?a=1&b=2";
|
||||
String urla2 = "http://www.example.com/hello?a=1&b=2";
|
||||
String urla3 = "http://EXAmPLe.com/HELLo?a=1&b=2";
|
||||
String urla4 = "http://example.com/hello?b=2&a=1";
|
||||
String urla5 = "https://example.com/hello?a=1&b=2";
|
||||
@Test
|
||||
void testNormalization() {
|
||||
String urla1 = "http://example.com/hello?a=1&b=2";
|
||||
String urla2 = "http://www.example.com/hello?a=1&b=2";
|
||||
String urla3 = "http://EXAmPLe.com/HELLo?a=1&b=2";
|
||||
String urla4 = "http://example.com/hello?b=2&a=1";
|
||||
String urla5 = "https://example.com/hello?a=1&b=2";
|
||||
|
||||
String urlb1 = "http://ftr.fivefilters.org/makefulltextfeed.php?url=http%3A%2F%2Ffeeds.howtogeek.com%2FHowToGeek&max=10&summary=1";
|
||||
String urlb2 = "http://ftr.fivefilters.org/makefulltextfeed.php?url=http://feeds.howtogeek.com/HowToGeek&max=10&summary=1";
|
||||
String urlb1 =
|
||||
"http://ftr.fivefilters.org/makefulltextfeed.php?url=http%3A%2F%2Ffeeds.howtogeek.com%2FHowToGeek&max=10&summary=1";
|
||||
String urlb2 =
|
||||
"http://ftr.fivefilters.org/makefulltextfeed.php?url=http://feeds.howtogeek.com/HowToGeek&max=10&summary=1";
|
||||
|
||||
String urlc1 = "http://feeds.feedburner.com/Frandroid";
|
||||
String urlc2 = "http://feeds2.feedburner.com/frandroid";
|
||||
String urlc3 = "http://feedproxy.google.com/frandroid";
|
||||
String urlc4 = "http://feeds.feedburner.com/Frandroid/";
|
||||
String urlc5 = "http://feeds.feedburner.com/Frandroid?format=rss";
|
||||
String urlc1 = "http://feeds.feedburner.com/Frandroid";
|
||||
String urlc2 = "http://feeds2.feedburner.com/frandroid";
|
||||
String urlc3 = "http://feedproxy.google.com/frandroid";
|
||||
String urlc4 = "http://feeds.feedburner.com/Frandroid/";
|
||||
String urlc5 = "http://feeds.feedburner.com/Frandroid?format=rss";
|
||||
|
||||
String urld1 = "http://fivefilters.org/content-only/makefulltextfeed.php?url=http://feeds.feedburner.com/Frandroid";
|
||||
String urld2 = "http://fivefilters.org/content-only/makefulltextfeed.php?url=http://feeds2.feedburner.com/Frandroid";
|
||||
String urld1 =
|
||||
"http://fivefilters.org/content-only/makefulltextfeed.php?url=http://feeds.feedburner.com/Frandroid";
|
||||
String urld2 =
|
||||
"http://fivefilters.org/content-only/makefulltextfeed.php?url=http://feeds2.feedburner.com/Frandroid";
|
||||
|
||||
Assertions.assertEquals(Urls.normalize(urla1), Urls.normalize(urla2));
|
||||
Assertions.assertEquals(Urls.normalize(urla1), Urls.normalize(urla3));
|
||||
Assertions.assertEquals(Urls.normalize(urla1), Urls.normalize(urla4));
|
||||
Assertions.assertEquals(Urls.normalize(urla1), Urls.normalize(urla5));
|
||||
Assertions.assertEquals(Urls.normalize(urla1), Urls.normalize(urla2));
|
||||
Assertions.assertEquals(Urls.normalize(urla1), Urls.normalize(urla3));
|
||||
Assertions.assertEquals(Urls.normalize(urla1), Urls.normalize(urla4));
|
||||
Assertions.assertEquals(Urls.normalize(urla1), Urls.normalize(urla5));
|
||||
|
||||
Assertions.assertEquals(Urls.normalize(urlb1), Urls.normalize(urlb2));
|
||||
Assertions.assertEquals(Urls.normalize(urlb1), Urls.normalize(urlb2));
|
||||
|
||||
Assertions.assertEquals(Urls.normalize(urlc1), Urls.normalize(urlc2));
|
||||
Assertions.assertEquals(Urls.normalize(urlc1), Urls.normalize(urlc3));
|
||||
Assertions.assertEquals(Urls.normalize(urlc1), Urls.normalize(urlc4));
|
||||
Assertions.assertEquals(Urls.normalize(urlc1), Urls.normalize(urlc5));
|
||||
Assertions.assertEquals(Urls.normalize(urlc1), Urls.normalize(urlc2));
|
||||
Assertions.assertEquals(Urls.normalize(urlc1), Urls.normalize(urlc3));
|
||||
Assertions.assertEquals(Urls.normalize(urlc1), Urls.normalize(urlc4));
|
||||
Assertions.assertEquals(Urls.normalize(urlc1), Urls.normalize(urlc5));
|
||||
|
||||
Assertions.assertNotEquals(Urls.normalize(urld1), Urls.normalize(urld2));
|
||||
Assertions.assertNotEquals(Urls.normalize(urld1), Urls.normalize(urld2));
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
void testToAbsoluteUrl() {
|
||||
String expected = "http://a.com/blog/entry/1";
|
||||
|
||||
@Test
|
||||
void testToAbsoluteUrl() {
|
||||
String expected = "http://a.com/blog/entry/1";
|
||||
// usual cases
|
||||
Assertions.assertEquals(
|
||||
expected,
|
||||
Urls.toAbsolute(
|
||||
"http://a.com/blog/entry/1", "http://a.com/feed/", "http://a.com/feed/"));
|
||||
Assertions.assertEquals(
|
||||
expected,
|
||||
Urls.toAbsolute(
|
||||
"http://a.com/blog/entry/1", "http://a.com/feed", "http://a.com/feed"));
|
||||
|
||||
// usual cases
|
||||
Assertions.assertEquals(expected, Urls.toAbsolute("http://a.com/blog/entry/1", "http://a.com/feed/", "http://a.com/feed/"));
|
||||
Assertions.assertEquals(expected, Urls.toAbsolute("http://a.com/blog/entry/1", "http://a.com/feed", "http://a.com/feed"));
|
||||
// relative links
|
||||
Assertions.assertEquals(
|
||||
expected,
|
||||
Urls.toAbsolute("../blog/entry/1", "http://a.com/feed/", "http://a.com/feed/"));
|
||||
Assertions.assertEquals(
|
||||
expected,
|
||||
Urls.toAbsolute("../blog/entry/1", "feed.xml", "http://a.com/feed/feed.xml"));
|
||||
|
||||
// relative links
|
||||
Assertions.assertEquals(expected, Urls.toAbsolute("../blog/entry/1", "http://a.com/feed/", "http://a.com/feed/"));
|
||||
Assertions.assertEquals(expected, Urls.toAbsolute("../blog/entry/1", "feed.xml", "http://a.com/feed/feed.xml"));
|
||||
// root-relative links
|
||||
Assertions.assertEquals(
|
||||
expected, Urls.toAbsolute("/blog/entry/1", "/feed", "http://a.com/feed"));
|
||||
|
||||
// root-relative links
|
||||
Assertions.assertEquals(expected, Urls.toAbsolute("/blog/entry/1", "/feed", "http://a.com/feed"));
|
||||
// real cases
|
||||
Assertions.assertEquals(
|
||||
"https://github.com/erusev/parsedown/releases/tag/1.3.0",
|
||||
Urls.toAbsolute(
|
||||
"/erusev/parsedown/releases/tag/1.3.0",
|
||||
"/erusev/parsedown/releases",
|
||||
"https://github.com/erusev/parsedown/tags.atom"));
|
||||
Assertions.assertEquals(
|
||||
"http://ergoemacs.org/emacs/elisp_all_about_lines.html",
|
||||
Urls.toAbsolute(
|
||||
"elisp_all_about_lines.html",
|
||||
"blog.xml",
|
||||
"http://ergoemacs.org/emacs/blog.xml"));
|
||||
|
||||
// real cases
|
||||
Assertions.assertEquals("https://github.com/erusev/parsedown/releases/tag/1.3.0", Urls.toAbsolute(
|
||||
"/erusev/parsedown/releases/tag/1.3.0", "/erusev/parsedown/releases", "https://github.com/erusev/parsedown/tags.atom"));
|
||||
Assertions.assertEquals("http://ergoemacs.org/emacs/elisp_all_about_lines.html",
|
||||
Urls.toAbsolute("elisp_all_about_lines.html", "blog.xml", "http://ergoemacs.org/emacs/blog.xml"));
|
||||
// invalid relative urls
|
||||
Assertions.assertEquals(
|
||||
"title:10001280",
|
||||
Urls.toAbsolute(
|
||||
"title:10001280",
|
||||
"https://www.berliner-zeitung.de",
|
||||
"https://www.berliner-zeitung.de/feed.xml"));
|
||||
}
|
||||
|
||||
// invalid relative urls
|
||||
Assertions.assertEquals("title:10001280",
|
||||
Urls.toAbsolute("title:10001280", "https://www.berliner-zeitung.de", "https://www.berliner-zeitung.de/feed.xml"));
|
||||
@Test
|
||||
void testRemoveTrailingSlash() {
|
||||
final String url = "http://localhost/";
|
||||
final String result = Urls.removeTrailingSlash(url);
|
||||
Assertions.assertEquals("http://localhost", result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveTrailingSlash() {
|
||||
final String url = "http://localhost/";
|
||||
final String result = Urls.removeTrailingSlash(url);
|
||||
Assertions.assertEquals("http://localhost", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveTrailingSlashLastSlashOnly() {
|
||||
final String url = "http://localhost//";
|
||||
final String result = Urls.removeTrailingSlash(url);
|
||||
Assertions.assertEquals("http://localhost/", result);
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
void testRemoveTrailingSlashLastSlashOnly() {
|
||||
final String url = "http://localhost//";
|
||||
final String result = Urls.removeTrailingSlash(url);
|
||||
Assertions.assertEquals("http://localhost/", result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.commafeed.backend.favicon;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import java.time.Duration;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -12,67 +13,68 @@ import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class FacebookFaviconFetcherTest {
|
||||
|
||||
@Mock
|
||||
private HttpGetter httpGetter;
|
||||
@Mock private HttpGetter httpGetter;
|
||||
|
||||
private FacebookFaviconFetcher faviconFetcher;
|
||||
private FacebookFaviconFetcher faviconFetcher;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
faviconFetcher = new FacebookFaviconFetcher(httpGetter);
|
||||
}
|
||||
@BeforeEach
|
||||
void init() {
|
||||
faviconFetcher = new FacebookFaviconFetcher(httpGetter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithValidFacebookUrl() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://www.facebook.com/something?id=validUserId");
|
||||
@Test
|
||||
void testFetchWithValidFacebookUrl() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://www.facebook.com/something?id=validUserId");
|
||||
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
|
||||
HttpResult httpResult = new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://graph.facebook.com/validUserId/picture?type=square&height=16")).thenReturn(httpResult);
|
||||
HttpResult httpResult =
|
||||
new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(
|
||||
httpGetter.get(
|
||||
"https://graph.facebook.com/validUserId/picture?type=square&height=16"))
|
||||
.thenReturn(httpResult);
|
||||
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithNonFacebookUrl() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com");
|
||||
@Test
|
||||
void testFetchWithNonFacebookUrl() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com");
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verifyNoInteractions(httpGetter);
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verifyNoInteractions(httpGetter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithFacebookUrlButNoUserId() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://www.facebook.com/something");
|
||||
@Test
|
||||
void testFetchWithFacebookUrlButNoUserId() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://www.facebook.com/something");
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verifyNoInteractions(httpGetter);
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verifyNoInteractions(httpGetter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithHttpGetterException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://www.facebook.com/something?id=validUserId");
|
||||
@Test
|
||||
void testFetchWithHttpGetterException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://www.facebook.com/something?id=validUserId");
|
||||
|
||||
Mockito.when(httpGetter.get("https://graph.facebook.com/validUserId/picture?type=square&height=16"))
|
||||
.thenThrow(new RuntimeException("Network error"));
|
||||
Mockito.when(
|
||||
httpGetter.get(
|
||||
"https://graph.facebook.com/validUserId/picture?type=square&height=16"))
|
||||
.thenThrow(new RuntimeException("Network error"));
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.commafeed.backend.favicon;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import java.time.Duration;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -12,58 +13,55 @@ import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class FeedFaviconFetcherTest {
|
||||
|
||||
@Mock
|
||||
private HttpGetter httpGetter;
|
||||
@Mock private HttpGetter httpGetter;
|
||||
|
||||
private FeedFaviconFetcher faviconFetcher;
|
||||
private FeedFaviconFetcher faviconFetcher;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
faviconFetcher = new FeedFaviconFetcher(httpGetter);
|
||||
}
|
||||
@BeforeEach
|
||||
void init() {
|
||||
faviconFetcher = new FeedFaviconFetcher(httpGetter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithNullIconUrl() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
@Test
|
||||
void testFetchWithNullIconUrl() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verifyNoInteractions(httpGetter);
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verifyNoInteractions(httpGetter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithValidIconUrl() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setIconUrl("https://example.com/icon.png");
|
||||
@Test
|
||||
void testFetchWithValidIconUrl() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setIconUrl("https://example.com/icon.png");
|
||||
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
HttpResult httpResult = new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/icon.png")).thenReturn(httpResult);
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
HttpResult httpResult =
|
||||
new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/icon.png")).thenReturn(httpResult);
|
||||
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithHttpGetterException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setIconUrl("https://example.com/icon.png");
|
||||
@Test
|
||||
void testFetchWithHttpGetterException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setIconUrl("https://example.com/icon.png");
|
||||
|
||||
Mockito.when(httpGetter.get("https://example.com/icon.png")).thenThrow(new RuntimeException("Network error"));
|
||||
Mockito.when(httpGetter.get("https://example.com/icon.png"))
|
||||
.thenThrow(new RuntimeException("Network error"));
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.commafeed.backend.favicon;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import java.time.Duration;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -12,111 +13,118 @@ import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class HtmlFaviconFetcherTest {
|
||||
|
||||
@Mock
|
||||
private HttpGetter httpGetter;
|
||||
@Mock private HttpGetter httpGetter;
|
||||
|
||||
private HtmlFaviconFetcher faviconFetcher;
|
||||
private HtmlFaviconFetcher faviconFetcher;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
faviconFetcher = new HtmlFaviconFetcher(httpGetter);
|
||||
}
|
||||
@BeforeEach
|
||||
void init() {
|
||||
faviconFetcher = new HtmlFaviconFetcher(httpGetter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithNullLink() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
@Test
|
||||
void testFetchWithNullLink() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verifyNoInteractions(httpGetter);
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verifyNoInteractions(httpGetter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithValidIconLink() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setLink("https://example.com");
|
||||
@Test
|
||||
void testFetchWithValidIconLink() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setLink("https://example.com");
|
||||
|
||||
String html = "<html><head><link rel=\"icon\" href=\"/favicon.png\" /></head><body></body></html>";
|
||||
HttpResult pageResult = new HttpResult(html.getBytes(), "text/html", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com")).thenReturn(pageResult);
|
||||
String html =
|
||||
"<html><head><link rel=\"icon\" href=\"/favicon.png\" /></head><body></body></html>";
|
||||
HttpResult pageResult =
|
||||
new HttpResult(html.getBytes(), "text/html", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com")).thenReturn(pageResult);
|
||||
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
HttpResult iconResult = new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/favicon.png")).thenReturn(iconResult);
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
HttpResult iconResult =
|
||||
new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/favicon.png")).thenReturn(iconResult);
|
||||
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithShortcutIconLink() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setLink("https://example.com");
|
||||
@Test
|
||||
void testFetchWithShortcutIconLink() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setLink("https://example.com");
|
||||
|
||||
String html = "<html><head><link rel=\"shortcut icon\" href=\"https://example.com/shortcut-favicon.ico\" /></head><body></body></html>";
|
||||
HttpResult pageResult = new HttpResult(html.getBytes(), "text/html", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com")).thenReturn(pageResult);
|
||||
String html =
|
||||
"<html><head><link rel=\"shortcut icon\" href=\"https://example.com/shortcut-favicon.ico\" /></head><body></body></html>";
|
||||
HttpResult pageResult =
|
||||
new HttpResult(html.getBytes(), "text/html", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com")).thenReturn(pageResult);
|
||||
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/x-icon";
|
||||
HttpResult iconResult = new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/shortcut-favicon.ico")).thenReturn(iconResult);
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/x-icon";
|
||||
HttpResult iconResult =
|
||||
new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/shortcut-favicon.ico"))
|
||||
.thenReturn(iconResult);
|
||||
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithNoIconInPage() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setLink("https://example.com");
|
||||
@Test
|
||||
void testFetchWithNoIconInPage() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setLink("https://example.com");
|
||||
|
||||
String html = "<html><head></head><body></body></html>";
|
||||
HttpResult pageResult = new HttpResult(html.getBytes(), "text/html", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com")).thenReturn(pageResult);
|
||||
String html = "<html><head></head><body></body></html>";
|
||||
HttpResult pageResult =
|
||||
new HttpResult(html.getBytes(), "text/html", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com")).thenReturn(pageResult);
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithPageFetchException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setLink("https://example.com");
|
||||
@Test
|
||||
void testFetchWithPageFetchException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setLink("https://example.com");
|
||||
|
||||
Mockito.when(httpGetter.get("https://example.com")).thenThrow(new RuntimeException("Network error"));
|
||||
Mockito.when(httpGetter.get("https://example.com"))
|
||||
.thenThrow(new RuntimeException("Network error"));
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithIconFetchException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setLink("https://example.com");
|
||||
@Test
|
||||
void testFetchWithIconFetchException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
feed.setLink("https://example.com");
|
||||
|
||||
String html = "<html><head><link rel=\"icon\" href=\"https://example.com/favicon.png\" /></head><body></body></html>";
|
||||
HttpResult pageResult = new HttpResult(html.getBytes(), "text/html", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com")).thenReturn(pageResult);
|
||||
Mockito.when(httpGetter.get("https://example.com/favicon.png")).thenThrow(new RuntimeException("Network error"));
|
||||
String html =
|
||||
"<html><head><link rel=\"icon\" href=\"https://example.com/favicon.png\" /></head><body></body></html>";
|
||||
HttpResult pageResult =
|
||||
new HttpResult(html.getBytes(), "text/html", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com")).thenReturn(pageResult);
|
||||
Mockito.when(httpGetter.get("https://example.com/favicon.png"))
|
||||
.thenThrow(new RuntimeException("Network error"));
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.commafeed.backend.favicon;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import java.time.Duration;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -12,66 +13,64 @@ import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RootFaviconFetcherTest {
|
||||
|
||||
@Mock
|
||||
private HttpGetter httpGetter;
|
||||
@Mock private HttpGetter httpGetter;
|
||||
|
||||
private RootFaviconFetcher faviconFetcher;
|
||||
private RootFaviconFetcher faviconFetcher;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
faviconFetcher = new RootFaviconFetcher(httpGetter);
|
||||
}
|
||||
@BeforeEach
|
||||
void init() {
|
||||
faviconFetcher = new RootFaviconFetcher(httpGetter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchUsesLinkWhenAvailable() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://feeds.example.com/feed");
|
||||
feed.setLink("https://www.example.com/blog");
|
||||
@Test
|
||||
void testFetchUsesLinkWhenAvailable() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://feeds.example.com/feed");
|
||||
feed.setLink("https://www.example.com/blog");
|
||||
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/x-icon";
|
||||
HttpResult httpResult = new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://www.example.com/favicon.ico")).thenReturn(httpResult);
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/x-icon";
|
||||
HttpResult httpResult =
|
||||
new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://www.example.com/favicon.ico")).thenReturn(httpResult);
|
||||
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchFallsBackToUrlWhenLinkIsNull() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed.xml");
|
||||
@Test
|
||||
void testFetchFallsBackToUrlWhenLinkIsNull() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed.xml");
|
||||
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/x-icon";
|
||||
HttpResult httpResult = new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/favicon.ico")).thenReturn(httpResult);
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/x-icon";
|
||||
HttpResult httpResult =
|
||||
new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/favicon.ico")).thenReturn(httpResult);
|
||||
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithHttpGetterException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed.xml");
|
||||
feed.setLink("https://example.com");
|
||||
@Test
|
||||
void testFetchWithHttpGetterException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed.xml");
|
||||
feed.setLink("https://example.com");
|
||||
|
||||
Mockito.when(httpGetter.get("https://example.com/favicon.ico")).thenThrow(new RuntimeException("Network error"));
|
||||
Mockito.when(httpGetter.get("https://example.com/favicon.ico"))
|
||||
.thenThrow(new RuntimeException("Network error"));
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
package com.commafeed.backend.favicon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
@@ -21,170 +7,208 @@ import com.commafeed.backend.model.Feed;
|
||||
import com.fasterxml.jackson.core.JsonPointer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class YoutubeFaviconFetcherTest {
|
||||
|
||||
@Mock
|
||||
private HttpGetter httpGetter;
|
||||
@Mock private HttpGetter httpGetter;
|
||||
|
||||
@Mock
|
||||
private CommaFeedConfiguration config;
|
||||
@Mock private CommaFeedConfiguration config;
|
||||
|
||||
@Mock
|
||||
private ObjectMapper objectMapper;
|
||||
@Mock private ObjectMapper objectMapper;
|
||||
|
||||
private YoutubeFaviconFetcher faviconFetcher;
|
||||
private YoutubeFaviconFetcher faviconFetcher;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
faviconFetcher = new YoutubeFaviconFetcher(httpGetter, config, objectMapper);
|
||||
}
|
||||
@BeforeEach
|
||||
void init() {
|
||||
faviconFetcher = new YoutubeFaviconFetcher(httpGetter, config, objectMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithNonYoutubeUrl() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
@Test
|
||||
void testFetchWithNonYoutubeUrl() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verifyNoInteractions(httpGetter, objectMapper);
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verifyNoInteractions(httpGetter, objectMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithNoGoogleAuthKey() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?user=someUser");
|
||||
@Test
|
||||
void testFetchWithNoGoogleAuthKey() {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?user=someUser");
|
||||
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.empty());
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.empty());
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verify(config).googleAuthKey();
|
||||
Mockito.verifyNoInteractions(httpGetter, objectMapper);
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
Mockito.verify(config).googleAuthKey();
|
||||
Mockito.verifyNoInteractions(httpGetter, objectMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchForUser() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?user=testUser");
|
||||
@Test
|
||||
void testFetchForUser() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?user=testUser");
|
||||
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.of("test-api-key"));
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.of("test-api-key"));
|
||||
|
||||
byte[] apiResponse = """
|
||||
{"items":[{"snippet":{"thumbnails":{"default":{"url":"https://example.com/icon.png"}}}}]}""".getBytes();
|
||||
HttpResult apiHttpResult = new HttpResult(apiResponse, "application/json", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://www.googleapis.com/youtube/v3/channels?part=snippet&key=test-api-key&forUsername=testUser"))
|
||||
.thenReturn(apiHttpResult);
|
||||
byte[] apiResponse =
|
||||
"""
|
||||
{"items":[{"snippet":{"thumbnails":{"default":{"url":"https://example.com/icon.png"}}}}]}"""
|
||||
.getBytes();
|
||||
HttpResult apiHttpResult =
|
||||
new HttpResult(apiResponse, "application/json", null, null, null, Duration.ZERO);
|
||||
Mockito.when(
|
||||
httpGetter.get(
|
||||
"https://www.googleapis.com/youtube/v3/channels?part=snippet&key=test-api-key&forUsername=testUser"))
|
||||
.thenReturn(apiHttpResult);
|
||||
|
||||
JsonNode jsonNode = new ObjectMapper().readTree(apiResponse);
|
||||
Mockito.when(objectMapper.readTree(apiResponse)).thenReturn(jsonNode);
|
||||
JsonNode jsonNode = new ObjectMapper().readTree(apiResponse);
|
||||
Mockito.when(objectMapper.readTree(apiResponse)).thenReturn(jsonNode);
|
||||
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
HttpResult iconHttpResult = new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/icon.png")).thenReturn(iconHttpResult);
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
HttpResult iconHttpResult =
|
||||
new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/icon.png")).thenReturn(iconHttpResult);
|
||||
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchForChannel() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?channel_id=testChannelId");
|
||||
@Test
|
||||
void testFetchForChannel() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?channel_id=testChannelId");
|
||||
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.of("test-api-key"));
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.of("test-api-key"));
|
||||
|
||||
byte[] apiResponse = """
|
||||
{"items":[{"snippet":{"thumbnails":{"default":{"url":"https://example.com/icon.png"}}}}]}""".getBytes();
|
||||
HttpResult apiHttpResult = new HttpResult(apiResponse, "application/json", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://www.googleapis.com/youtube/v3/channels?part=snippet&key=test-api-key&id=testChannelId"))
|
||||
.thenReturn(apiHttpResult);
|
||||
byte[] apiResponse =
|
||||
"""
|
||||
{"items":[{"snippet":{"thumbnails":{"default":{"url":"https://example.com/icon.png"}}}}]}"""
|
||||
.getBytes();
|
||||
HttpResult apiHttpResult =
|
||||
new HttpResult(apiResponse, "application/json", null, null, null, Duration.ZERO);
|
||||
Mockito.when(
|
||||
httpGetter.get(
|
||||
"https://www.googleapis.com/youtube/v3/channels?part=snippet&key=test-api-key&id=testChannelId"))
|
||||
.thenReturn(apiHttpResult);
|
||||
|
||||
JsonNode jsonNode = new ObjectMapper().readTree(apiResponse);
|
||||
Mockito.when(objectMapper.readTree(apiResponse)).thenReturn(jsonNode);
|
||||
JsonNode jsonNode = new ObjectMapper().readTree(apiResponse);
|
||||
Mockito.when(objectMapper.readTree(apiResponse)).thenReturn(jsonNode);
|
||||
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
HttpResult iconHttpResult = new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/icon.png")).thenReturn(iconHttpResult);
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
HttpResult iconHttpResult =
|
||||
new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/icon.png")).thenReturn(iconHttpResult);
|
||||
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchForPlaylist() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?playlist_id=testPlaylistId");
|
||||
@Test
|
||||
void testFetchForPlaylist() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?playlist_id=testPlaylistId");
|
||||
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.of("test-api-key"));
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.of("test-api-key"));
|
||||
|
||||
byte[] playlistResponse = """
|
||||
{"items":[{"snippet":{"channelId":"testChannelId"}}]}""".getBytes();
|
||||
HttpResult playlistHttpResult = new HttpResult(playlistResponse, "application/json", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://www.googleapis.com/youtube/v3/playlists?part=snippet&key=test-api-key&id=testPlaylistId"))
|
||||
.thenReturn(playlistHttpResult);
|
||||
byte[] playlistResponse =
|
||||
"""
|
||||
{"items":[{"snippet":{"channelId":"testChannelId"}}]}"""
|
||||
.getBytes();
|
||||
HttpResult playlistHttpResult =
|
||||
new HttpResult(
|
||||
playlistResponse, "application/json", null, null, null, Duration.ZERO);
|
||||
Mockito.when(
|
||||
httpGetter.get(
|
||||
"https://www.googleapis.com/youtube/v3/playlists?part=snippet&key=test-api-key&id=testPlaylistId"))
|
||||
.thenReturn(playlistHttpResult);
|
||||
|
||||
JsonNode playlistJsonNode = new ObjectMapper().readTree(playlistResponse);
|
||||
Mockito.when(objectMapper.readTree(playlistResponse)).thenReturn(playlistJsonNode);
|
||||
JsonNode playlistJsonNode = new ObjectMapper().readTree(playlistResponse);
|
||||
Mockito.when(objectMapper.readTree(playlistResponse)).thenReturn(playlistJsonNode);
|
||||
|
||||
byte[] channelResponse = """
|
||||
{"items":[{"snippet":{"thumbnails":{"default":{"url":"https://example.com/icon.png"}}}}]}""".getBytes();
|
||||
HttpResult channelHttpResult = new HttpResult(channelResponse, "application/json", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://www.googleapis.com/youtube/v3/channels?part=snippet&key=test-api-key&id=testChannelId"))
|
||||
.thenReturn(channelHttpResult);
|
||||
byte[] channelResponse =
|
||||
"""
|
||||
{"items":[{"snippet":{"thumbnails":{"default":{"url":"https://example.com/icon.png"}}}}]}"""
|
||||
.getBytes();
|
||||
HttpResult channelHttpResult =
|
||||
new HttpResult(
|
||||
channelResponse, "application/json", null, null, null, Duration.ZERO);
|
||||
Mockito.when(
|
||||
httpGetter.get(
|
||||
"https://www.googleapis.com/youtube/v3/channels?part=snippet&key=test-api-key&id=testChannelId"))
|
||||
.thenReturn(channelHttpResult);
|
||||
|
||||
JsonNode channelJsonNode = new ObjectMapper().readTree(channelResponse);
|
||||
Mockito.when(objectMapper.readTree(channelResponse)).thenReturn(channelJsonNode);
|
||||
JsonNode channelJsonNode = new ObjectMapper().readTree(channelResponse);
|
||||
Mockito.when(objectMapper.readTree(channelResponse)).thenReturn(channelJsonNode);
|
||||
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
HttpResult iconHttpResult = new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/icon.png")).thenReturn(iconHttpResult);
|
||||
byte[] iconBytes = new byte[1000];
|
||||
String contentType = "image/png";
|
||||
HttpResult iconHttpResult =
|
||||
new HttpResult(iconBytes, contentType, null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://example.com/icon.png")).thenReturn(iconHttpResult);
|
||||
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
Favicon result = faviconFetcher.fetch(feed);
|
||||
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertEquals(iconBytes, result.icon());
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf(contentType)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithHttpGetterException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?user=testUser");
|
||||
@Test
|
||||
void testFetchWithHttpGetterException() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?user=testUser");
|
||||
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.of("test-api-key"));
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.of("test-api-key"));
|
||||
|
||||
Mockito.when(httpGetter.get("https://www.googleapis.com/youtube/v3/channels?part=snippet&key=test-api-key&forUsername=testUser"))
|
||||
.thenThrow(new IOException("Network error"));
|
||||
Mockito.when(
|
||||
httpGetter.get(
|
||||
"https://www.googleapis.com/youtube/v3/channels?part=snippet&key=test-api-key&forUsername=testUser"))
|
||||
.thenThrow(new IOException("Network error"));
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchWithEmptyApiResponse() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?user=testUser");
|
||||
@Test
|
||||
void testFetchWithEmptyApiResponse() throws Exception {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl("https://youtube.com/feeds/videos.xml?user=testUser");
|
||||
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.of("test-api-key"));
|
||||
Mockito.when(config.googleAuthKey()).thenReturn(Optional.of("test-api-key"));
|
||||
|
||||
byte[] apiResponse = "{}".getBytes();
|
||||
HttpResult apiHttpResult = new HttpResult(apiResponse, "application/json", null, null, null, Duration.ZERO);
|
||||
Mockito.when(httpGetter.get("https://www.googleapis.com/youtube/v3/channels?part=snippet&key=test-api-key&forUsername=testUser"))
|
||||
.thenReturn(apiHttpResult);
|
||||
byte[] apiResponse = "{}".getBytes();
|
||||
HttpResult apiHttpResult =
|
||||
new HttpResult(apiResponse, "application/json", null, null, null, Duration.ZERO);
|
||||
Mockito.when(
|
||||
httpGetter.get(
|
||||
"https://www.googleapis.com/youtube/v3/channels?part=snippet&key=test-api-key&forUsername=testUser"))
|
||||
.thenReturn(apiHttpResult);
|
||||
|
||||
JsonNode jsonNode = Mockito.mock(JsonNode.class);
|
||||
Mockito.when(objectMapper.readTree(apiResponse)).thenReturn(jsonNode);
|
||||
Mockito.when(jsonNode.at(Mockito.any(JsonPointer.class))).thenReturn(jsonNode);
|
||||
Mockito.when(jsonNode.isMissingNode()).thenReturn(true);
|
||||
JsonNode jsonNode = Mockito.mock(JsonNode.class);
|
||||
Mockito.when(objectMapper.readTree(apiResponse)).thenReturn(jsonNode);
|
||||
Mockito.when(jsonNode.at(Mockito.any(JsonPointer.class))).thenReturn(jsonNode);
|
||||
Mockito.when(jsonNode.isMissingNode()).thenReturn(true);
|
||||
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
}
|
||||
Assertions.assertNull(faviconFetcher.fetch(feed));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.commafeed.backend.Digests;
|
||||
import com.commafeed.backend.HttpGetter;
|
||||
import com.commafeed.backend.HttpGetter.HttpResult;
|
||||
@@ -21,63 +9,94 @@ import com.commafeed.backend.feed.parser.FeedParser;
|
||||
import com.commafeed.backend.feed.parser.FeedParser.FeedParsingException;
|
||||
import com.commafeed.backend.feed.parser.FeedParserResult;
|
||||
import com.commafeed.backend.urlprovider.FeedURLProvider;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class FeedFetcherTest {
|
||||
|
||||
@Mock
|
||||
private FeedParser parser;
|
||||
@Mock private FeedParser parser;
|
||||
|
||||
@Mock
|
||||
private HttpGetter getter;
|
||||
@Mock private HttpGetter getter;
|
||||
|
||||
@Mock
|
||||
private FeedURLProvider urlProvider;
|
||||
@Mock private FeedURLProvider urlProvider;
|
||||
|
||||
private FeedFetcher fetcher;
|
||||
private FeedFetcher fetcher;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
fetcher = new FeedFetcher(parser, getter, List.of(urlProvider));
|
||||
}
|
||||
@BeforeEach
|
||||
void init() {
|
||||
fetcher = new FeedFetcher(parser, getter, List.of(urlProvider));
|
||||
}
|
||||
|
||||
@Test
|
||||
void findsUrlInPage() throws Exception {
|
||||
String htmlUrl = "https://aaa.com";
|
||||
byte[] html = "html".getBytes();
|
||||
Mockito.when(getter.get(HttpGetter.HttpRequest.builder(htmlUrl).build()))
|
||||
.thenReturn(new HttpResult(html, "text/html", null, null, htmlUrl, Duration.ZERO));
|
||||
Mockito.when(parser.parse(htmlUrl, html)).thenThrow(new FeedParsingException("invalid feed"));
|
||||
@Test
|
||||
void findsUrlInPage() throws Exception {
|
||||
String htmlUrl = "https://aaa.com";
|
||||
byte[] html = "html".getBytes();
|
||||
Mockito.when(getter.get(HttpGetter.HttpRequest.builder(htmlUrl).build()))
|
||||
.thenReturn(new HttpResult(html, "text/html", null, null, htmlUrl, Duration.ZERO));
|
||||
Mockito.when(parser.parse(htmlUrl, html))
|
||||
.thenThrow(new FeedParsingException("invalid feed"));
|
||||
|
||||
String feedUrl = "https://bbb.com/feed";
|
||||
byte[] feed = "feed".getBytes();
|
||||
Mockito.when(getter.get(HttpGetter.HttpRequest.builder(feedUrl).build()))
|
||||
.thenReturn(new HttpResult(feed, "application/atom+xml", null, null, feedUrl, Duration.ZERO));
|
||||
Mockito.when(parser.parse(feedUrl, feed)).thenReturn(new FeedParserResult("title", "link", "iconUrl", null, null, null, null));
|
||||
String feedUrl = "https://bbb.com/feed";
|
||||
byte[] feed = "feed".getBytes();
|
||||
Mockito.when(getter.get(HttpGetter.HttpRequest.builder(feedUrl).build()))
|
||||
.thenReturn(
|
||||
new HttpResult(
|
||||
feed, "application/atom+xml", null, null, feedUrl, Duration.ZERO));
|
||||
Mockito.when(parser.parse(feedUrl, feed))
|
||||
.thenReturn(
|
||||
new FeedParserResult("title", "link", "iconUrl", null, null, null, null));
|
||||
|
||||
Mockito.when(urlProvider.get(htmlUrl, new String(html))).thenReturn(List.of(feedUrl));
|
||||
Mockito.when(urlProvider.get(htmlUrl, new String(html))).thenReturn(List.of(feedUrl));
|
||||
|
||||
FeedFetcherResult result = fetcher.fetch(htmlUrl, true, null, null, null, null);
|
||||
Assertions.assertEquals("title", result.feed().title());
|
||||
}
|
||||
FeedFetcherResult result = fetcher.fetch(htmlUrl, true, null, null, null, null);
|
||||
Assertions.assertEquals("title", result.feed().title());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updatesHeaderWhenContentDitNotChange() throws Exception {
|
||||
String url = "https://aaa.com";
|
||||
String lastModified = "last-modified-1";
|
||||
String etag = "etag-1";
|
||||
byte[] content = "content".getBytes();
|
||||
String lastContentHash = Digests.sha1Hex(content);
|
||||
@Test
|
||||
void updatesHeaderWhenContentDitNotChange() throws Exception {
|
||||
String url = "https://aaa.com";
|
||||
String lastModified = "last-modified-1";
|
||||
String etag = "etag-1";
|
||||
byte[] content = "content".getBytes();
|
||||
String lastContentHash = Digests.sha1Hex(content);
|
||||
|
||||
Mockito.when(getter.get(HttpGetter.HttpRequest.builder(url).lastModified(lastModified).eTag(etag).build()))
|
||||
.thenReturn(new HttpResult(content, "content-type", "last-modified-2", "etag-2", null, Duration.ZERO));
|
||||
Mockito.when(
|
||||
getter.get(
|
||||
HttpGetter.HttpRequest.builder(url)
|
||||
.lastModified(lastModified)
|
||||
.eTag(etag)
|
||||
.build()))
|
||||
.thenReturn(
|
||||
new HttpResult(
|
||||
content,
|
||||
"content-type",
|
||||
"last-modified-2",
|
||||
"etag-2",
|
||||
null,
|
||||
Duration.ZERO));
|
||||
|
||||
NotModifiedException e = Assertions.assertThrows(NotModifiedException.class,
|
||||
() -> fetcher.fetch(url, false, lastModified, etag, Instant.now(), lastContentHash));
|
||||
|
||||
Assertions.assertEquals("last-modified-2", e.getNewLastModifiedHeader());
|
||||
Assertions.assertEquals("etag-2", e.getNewEtagHeader());
|
||||
|
||||
}
|
||||
NotModifiedException e =
|
||||
Assertions.assertThrows(
|
||||
NotModifiedException.class,
|
||||
() ->
|
||||
fetcher.fetch(
|
||||
url,
|
||||
false,
|
||||
lastModified,
|
||||
etag,
|
||||
Instant.now(),
|
||||
lastContentHash));
|
||||
|
||||
Assertions.assertEquals("last-modified-2", e.getNewLastModifiedHeader());
|
||||
Assertions.assertEquals("etag-2", e.getNewEtagHeader());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.CommaFeedConfiguration.FeedRefreshErrorHandling;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.InstantSource;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
@@ -15,264 +16,289 @@ import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.CommaFeedConfiguration.FeedRefreshErrorHandling;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class FeedRefreshIntervalCalculatorTest {
|
||||
|
||||
private static final Instant NOW = Instant.now();
|
||||
private static final Duration DEFAULT_INTERVAL = Duration.ofHours(1);
|
||||
private static final Duration MAX_INTERVAL = Duration.ofDays(1);
|
||||
private static final Instant NOW = Instant.now();
|
||||
private static final Duration DEFAULT_INTERVAL = Duration.ofHours(1);
|
||||
private static final Duration MAX_INTERVAL = Duration.ofDays(1);
|
||||
|
||||
@Mock
|
||||
private InstantSource instantSource;
|
||||
@Mock private InstantSource instantSource;
|
||||
|
||||
@Mock
|
||||
private CommaFeedConfiguration config;
|
||||
@Mock private CommaFeedConfiguration config;
|
||||
|
||||
@Mock
|
||||
private FeedRefreshErrorHandling errorHandling;
|
||||
@Mock private FeedRefreshErrorHandling errorHandling;
|
||||
|
||||
private FeedRefreshIntervalCalculator calculator;
|
||||
private FeedRefreshIntervalCalculator calculator;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(instantSource.instant()).thenReturn(NOW);
|
||||
Mockito.when(config.feedRefresh()).thenReturn(Mockito.mock(CommaFeedConfiguration.FeedRefresh.class));
|
||||
Mockito.when(config.feedRefresh().interval()).thenReturn(DEFAULT_INTERVAL);
|
||||
Mockito.when(config.feedRefresh().maxInterval()).thenReturn(MAX_INTERVAL);
|
||||
Mockito.when(config.feedRefresh().errors()).thenReturn(errorHandling);
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(instantSource.instant()).thenReturn(NOW);
|
||||
Mockito.when(config.feedRefresh())
|
||||
.thenReturn(Mockito.mock(CommaFeedConfiguration.FeedRefresh.class));
|
||||
Mockito.when(config.feedRefresh().interval()).thenReturn(DEFAULT_INTERVAL);
|
||||
Mockito.when(config.feedRefresh().maxInterval()).thenReturn(MAX_INTERVAL);
|
||||
Mockito.when(config.feedRefresh().errors()).thenReturn(errorHandling);
|
||||
|
||||
calculator = new FeedRefreshIntervalCalculator(config, instantSource);
|
||||
}
|
||||
calculator = new FeedRefreshIntervalCalculator(config, instantSource);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FetchSuccess {
|
||||
@Nested
|
||||
class FetchSuccess {
|
||||
|
||||
@Nested
|
||||
class EmpiricalDisabled {
|
||||
@ParameterizedTest
|
||||
@ValueSource(longs = { 0, 1, 300, 86400000L })
|
||||
void withoutValidFor(long averageEntryInterval) {
|
||||
// averageEntryInterval is ignored when empirical is disabled
|
||||
Instant result = calculator.onFetchSuccess(NOW.minus(Duration.ofDays(5)), averageEntryInterval, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
@Nested
|
||||
class EmpiricalDisabled {
|
||||
@ParameterizedTest
|
||||
@ValueSource(longs = {0, 1, 300, 86400000L})
|
||||
void withoutValidFor(long averageEntryInterval) {
|
||||
// averageEntryInterval is ignored when empirical is disabled
|
||||
Instant result =
|
||||
calculator.onFetchSuccess(
|
||||
NOW.minus(Duration.ofDays(5)), averageEntryInterval, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withValidForGreaterThanMaxInterval() {
|
||||
Instant result = calculator.onFetchSuccess(NOW.minus(Duration.ofDays(5)), 1L, MAX_INTERVAL.plusDays(1));
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
@Test
|
||||
void withValidForGreaterThanMaxInterval() {
|
||||
Instant result =
|
||||
calculator.onFetchSuccess(
|
||||
NOW.minus(Duration.ofDays(5)), 1L, MAX_INTERVAL.plusDays(1));
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withValidForLowerThanMaxInterval() {
|
||||
Instant result = calculator.onFetchSuccess(NOW.minus(Duration.ofDays(5)), 1L, MAX_INTERVAL.minusSeconds(1));
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL).minusSeconds(1), result);
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void withValidForLowerThanMaxInterval() {
|
||||
Instant result =
|
||||
calculator.onFetchSuccess(
|
||||
NOW.minus(Duration.ofDays(5)), 1L, MAX_INTERVAL.minusSeconds(1));
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL).minusSeconds(1), result);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class EmpiricalEnabled {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(config.feedRefresh().intervalEmpirical()).thenReturn(true);
|
||||
calculator = new FeedRefreshIntervalCalculator(config, instantSource);
|
||||
}
|
||||
@Nested
|
||||
class EmpiricalEnabled {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(config.feedRefresh().intervalEmpirical()).thenReturn(true);
|
||||
calculator = new FeedRefreshIntervalCalculator(config, instantSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withNullPublishedDate() {
|
||||
Instant result = calculator.onFetchSuccess(null, 1L, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
@Test
|
||||
void withNullPublishedDate() {
|
||||
Instant result = calculator.onFetchSuccess(null, 1L, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void with31DaysOldPublishedDate() {
|
||||
Instant result = calculator.onFetchSuccess(NOW.minus(Duration.ofDays(31)), 1L, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
@Test
|
||||
void with31DaysOldPublishedDate() {
|
||||
Instant result =
|
||||
calculator.onFetchSuccess(
|
||||
NOW.minus(Duration.ofDays(31)), 1L, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void with15DaysOldPublishedDate() {
|
||||
Instant result = calculator.onFetchSuccess(NOW.minus(Duration.ofDays(15)), 1L, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(2)), result);
|
||||
}
|
||||
@Test
|
||||
void with15DaysOldPublishedDate() {
|
||||
Instant result =
|
||||
calculator.onFetchSuccess(
|
||||
NOW.minus(Duration.ofDays(15)), 1L, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(2)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void with8DaysOldPublishedDate() {
|
||||
Instant result = calculator.onFetchSuccess(NOW.minus(Duration.ofDays(8)), 1L, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(4)), result);
|
||||
}
|
||||
@Test
|
||||
void with8DaysOldPublishedDate() {
|
||||
Instant result =
|
||||
calculator.onFetchSuccess(NOW.minus(Duration.ofDays(8)), 1L, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(4)), result);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FiveDaysOld {
|
||||
@Test
|
||||
void averageBetweenBounds() {
|
||||
Instant result = calculator.onFetchSuccess(NOW.minus(Duration.ofDays(5)), Duration.ofHours(4).toMillis(),
|
||||
Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(Duration.ofHours(2)), result);
|
||||
}
|
||||
@Nested
|
||||
class FiveDaysOld {
|
||||
@Test
|
||||
void averageBetweenBounds() {
|
||||
Instant result =
|
||||
calculator.onFetchSuccess(
|
||||
NOW.minus(Duration.ofDays(5)),
|
||||
Duration.ofHours(4).toMillis(),
|
||||
Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(Duration.ofHours(2)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void averageBelowMinimum() {
|
||||
Instant result = calculator.onFetchSuccess(NOW.minus(Duration.ofDays(5)), 10L, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
@Test
|
||||
void averageBelowMinimum() {
|
||||
Instant result =
|
||||
calculator.onFetchSuccess(
|
||||
NOW.minus(Duration.ofDays(5)), 10L, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void averageAboveMaximum() {
|
||||
Instant result = calculator.onFetchSuccess(NOW.minus(Duration.ofDays(5)), Long.MAX_VALUE, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(4)), result);
|
||||
}
|
||||
@Test
|
||||
void averageAboveMaximum() {
|
||||
Instant result =
|
||||
calculator.onFetchSuccess(
|
||||
NOW.minus(Duration.ofDays(5)), Long.MAX_VALUE, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(4)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void noAverage() {
|
||||
Instant result = calculator.onFetchSuccess(NOW.minus(Duration.ofDays(5)), null, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void noAverage() {
|
||||
Instant result =
|
||||
calculator.onFetchSuccess(
|
||||
NOW.minus(Duration.ofDays(5)), null, Duration.ZERO);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FeedNotModified {
|
||||
@Nested
|
||||
class FeedNotModified {
|
||||
|
||||
@Nested
|
||||
class EmpiricalDisabled {
|
||||
@ParameterizedTest
|
||||
@ValueSource(longs = { 0, 1, 300, 86400000L })
|
||||
void withoutValidFor(long averageEntryInterval) {
|
||||
// averageEntryInterval is ignored when empirical is disabled
|
||||
Instant result = calculator.onFeedNotModified(NOW.minus(Duration.ofDays(5)), averageEntryInterval);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
}
|
||||
@Nested
|
||||
class EmpiricalDisabled {
|
||||
@ParameterizedTest
|
||||
@ValueSource(longs = {0, 1, 300, 86400000L})
|
||||
void withoutValidFor(long averageEntryInterval) {
|
||||
// averageEntryInterval is ignored when empirical is disabled
|
||||
Instant result =
|
||||
calculator.onFeedNotModified(
|
||||
NOW.minus(Duration.ofDays(5)), averageEntryInterval);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class EmpiricalEnabled {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(config.feedRefresh().intervalEmpirical()).thenReturn(true);
|
||||
calculator = new FeedRefreshIntervalCalculator(config, instantSource);
|
||||
}
|
||||
@Nested
|
||||
class EmpiricalEnabled {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(config.feedRefresh().intervalEmpirical()).thenReturn(true);
|
||||
calculator = new FeedRefreshIntervalCalculator(config, instantSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withNullPublishedDate() {
|
||||
Instant result = calculator.onFeedNotModified(null, 1L);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
@Test
|
||||
void withNullPublishedDate() {
|
||||
Instant result = calculator.onFeedNotModified(null, 1L);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void with31DaysOldPublishedDate() {
|
||||
Instant result = calculator.onFeedNotModified(NOW.minus(Duration.ofDays(31)), 1L);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
@Test
|
||||
void with31DaysOldPublishedDate() {
|
||||
Instant result = calculator.onFeedNotModified(NOW.minus(Duration.ofDays(31)), 1L);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void with15DaysOldPublishedDate() {
|
||||
Instant result = calculator.onFeedNotModified(NOW.minus(Duration.ofDays(15)), 1L);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(2)), result);
|
||||
}
|
||||
@Test
|
||||
void with15DaysOldPublishedDate() {
|
||||
Instant result = calculator.onFeedNotModified(NOW.minus(Duration.ofDays(15)), 1L);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(2)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void with8DaysOldPublishedDate() {
|
||||
Instant result = calculator.onFeedNotModified(NOW.minus(Duration.ofDays(8)), 1L);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(4)), result);
|
||||
}
|
||||
@Test
|
||||
void with8DaysOldPublishedDate() {
|
||||
Instant result = calculator.onFeedNotModified(NOW.minus(Duration.ofDays(8)), 1L);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(4)), result);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FiveDaysOld {
|
||||
@Test
|
||||
void averageBetweenBounds() {
|
||||
Instant result = calculator.onFeedNotModified(NOW.minus(Duration.ofDays(5)), Duration.ofHours(4).toMillis());
|
||||
Assertions.assertEquals(NOW.plus(Duration.ofHours(2)), result);
|
||||
}
|
||||
@Nested
|
||||
class FiveDaysOld {
|
||||
@Test
|
||||
void averageBetweenBounds() {
|
||||
Instant result =
|
||||
calculator.onFeedNotModified(
|
||||
NOW.minus(Duration.ofDays(5)), Duration.ofHours(4).toMillis());
|
||||
Assertions.assertEquals(NOW.plus(Duration.ofHours(2)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void averageBelowMinimum() {
|
||||
Instant result = calculator.onFeedNotModified(NOW.minus(Duration.ofDays(5)), 10L);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
@Test
|
||||
void averageBelowMinimum() {
|
||||
Instant result =
|
||||
calculator.onFeedNotModified(NOW.minus(Duration.ofDays(5)), 10L);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void averageAboveMaximum() {
|
||||
Instant result = calculator.onFeedNotModified(NOW.minus(Duration.ofDays(5)), Long.MAX_VALUE);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(4)), result);
|
||||
}
|
||||
@Test
|
||||
void averageAboveMaximum() {
|
||||
Instant result =
|
||||
calculator.onFeedNotModified(
|
||||
NOW.minus(Duration.ofDays(5)), Long.MAX_VALUE);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL.dividedBy(4)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void noAverage() {
|
||||
Instant result = calculator.onFeedNotModified(NOW.minus(Duration.ofDays(5)), null);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void noAverage() {
|
||||
Instant result =
|
||||
calculator.onFeedNotModified(NOW.minus(Duration.ofDays(5)), null);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FetchError {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(config.feedRefresh().errors().retriesBeforeBackoff()).thenReturn(3);
|
||||
}
|
||||
@Nested
|
||||
class FetchError {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(config.feedRefresh().errors().retriesBeforeBackoff()).thenReturn(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void lowErrorCount() {
|
||||
Instant result = calculator.onFetchError(1);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
@Test
|
||||
void lowErrorCount() {
|
||||
Instant result = calculator.onFetchError(1);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void highErrorCount() {
|
||||
Mockito.when(config.feedRefresh().errors().backoffInterval()).thenReturn(Duration.ofHours(1));
|
||||
@Test
|
||||
void highErrorCount() {
|
||||
Mockito.when(config.feedRefresh().errors().backoffInterval())
|
||||
.thenReturn(Duration.ofHours(1));
|
||||
|
||||
Instant result = calculator.onFetchError(5);
|
||||
Assertions.assertEquals(NOW.plus(Duration.ofHours(3)), result);
|
||||
}
|
||||
Instant result = calculator.onFetchError(5);
|
||||
Assertions.assertEquals(NOW.plus(Duration.ofHours(3)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void veryHighErrorCount() {
|
||||
Mockito.when(config.feedRefresh().errors().backoffInterval()).thenReturn(Duration.ofHours(1));
|
||||
@Test
|
||||
void veryHighErrorCount() {
|
||||
Mockito.when(config.feedRefresh().errors().backoffInterval())
|
||||
.thenReturn(Duration.ofHours(1));
|
||||
|
||||
Instant result = calculator.onFetchError(100000);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
}
|
||||
Instant result = calculator.onFetchError(100000);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class TooManyRequests {
|
||||
@Nested
|
||||
class TooManyRequests {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(config.feedRefresh().errors().retriesBeforeBackoff()).thenReturn(3);
|
||||
}
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(config.feedRefresh().errors().retriesBeforeBackoff()).thenReturn(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withRetryAfterZero() {
|
||||
Instant result = calculator.onTooManyRequests(NOW, 1);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
@Test
|
||||
void withRetryAfterZero() {
|
||||
Instant result = calculator.onTooManyRequests(NOW, 1);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withRetryAfterLowerThanInterval() {
|
||||
Instant retryAfter = NOW.plus(DEFAULT_INTERVAL.minusSeconds(10));
|
||||
Instant result = calculator.onTooManyRequests(retryAfter, 1);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
@Test
|
||||
void withRetryAfterLowerThanInterval() {
|
||||
Instant retryAfter = NOW.plus(DEFAULT_INTERVAL.minusSeconds(10));
|
||||
Instant result = calculator.onTooManyRequests(retryAfter, 1);
|
||||
Assertions.assertEquals(NOW.plus(DEFAULT_INTERVAL), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withRetryAfterBetweenBounds() {
|
||||
Instant retryAfter = NOW.plus(DEFAULT_INTERVAL.plusSeconds(10));
|
||||
Instant result = calculator.onTooManyRequests(retryAfter, 1);
|
||||
Assertions.assertEquals(retryAfter, result);
|
||||
}
|
||||
@Test
|
||||
void withRetryAfterBetweenBounds() {
|
||||
Instant retryAfter = NOW.plus(DEFAULT_INTERVAL.plusSeconds(10));
|
||||
Instant result = calculator.onTooManyRequests(retryAfter, 1);
|
||||
Assertions.assertEquals(retryAfter, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withRetryAfterGreaterThanMaxInterval() {
|
||||
Instant retryAfter = NOW.plus(MAX_INTERVAL.plusSeconds(10));
|
||||
Instant result = calculator.onTooManyRequests(retryAfter, 1);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void withRetryAfterGreaterThanMaxInterval() {
|
||||
Instant retryAfter = NOW.plus(MAX_INTERVAL.plusSeconds(10));
|
||||
Instant result = calculator.onTooManyRequests(retryAfter, 1);
|
||||
Assertions.assertEquals(NOW.plus(MAX_INTERVAL), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
package com.commafeed.backend.feed;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.frontend.model.Entry;
|
||||
import com.rometools.rome.feed.synd.SyndEntry;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class FeedUtilsTest {
|
||||
|
||||
@Test
|
||||
void asRss() {
|
||||
Entry entry = new Entry();
|
||||
entry.setId("1");
|
||||
entry.setGuid("guid-1");
|
||||
entry.setTitle("Test Entry");
|
||||
entry.setContent("This is a test entry content.");
|
||||
entry.setCategories("test,example");
|
||||
entry.setRtl(false);
|
||||
entry.setAuthor("Author Name");
|
||||
entry.setEnclosureUrl("http://example.com/enclosure.mp3");
|
||||
entry.setEnclosureType("audio/mpeg");
|
||||
entry.setDate(Instant.ofEpochSecond(1));
|
||||
entry.setUrl("http://example.com/test-entry");
|
||||
@Test
|
||||
void asRss() {
|
||||
Entry entry = new Entry();
|
||||
entry.setId("1");
|
||||
entry.setGuid("guid-1");
|
||||
entry.setTitle("Test Entry");
|
||||
entry.setContent("This is a test entry content.");
|
||||
entry.setCategories("test,example");
|
||||
entry.setRtl(false);
|
||||
entry.setAuthor("Author Name");
|
||||
entry.setEnclosureUrl("http://example.com/enclosure.mp3");
|
||||
entry.setEnclosureType("audio/mpeg");
|
||||
entry.setDate(Instant.ofEpochSecond(1));
|
||||
entry.setUrl("http://example.com/test-entry");
|
||||
|
||||
SyndEntry syndEntry = FeedUtils.asRss(entry);
|
||||
Assertions.assertEquals("guid-1", syndEntry.getUri());
|
||||
Assertions.assertEquals("Test Entry", syndEntry.getTitle());
|
||||
Assertions.assertEquals("Author Name", syndEntry.getAuthor());
|
||||
Assertions.assertEquals(1, syndEntry.getContents().size());
|
||||
Assertions.assertEquals("This is a test entry content.", syndEntry.getContents().getFirst().getValue());
|
||||
Assertions.assertEquals(1, syndEntry.getEnclosures().size());
|
||||
Assertions.assertEquals("http://example.com/enclosure.mp3", syndEntry.getEnclosures().getFirst().getUrl());
|
||||
Assertions.assertEquals("audio/mpeg", syndEntry.getEnclosures().getFirst().getType());
|
||||
Assertions.assertEquals("http://example.com/test-entry", syndEntry.getLink());
|
||||
Assertions.assertEquals(Date.from(Instant.ofEpochSecond(1)), syndEntry.getPublishedDate());
|
||||
}
|
||||
}
|
||||
SyndEntry syndEntry = FeedUtils.asRss(entry);
|
||||
Assertions.assertEquals("guid-1", syndEntry.getUri());
|
||||
Assertions.assertEquals("Test Entry", syndEntry.getTitle());
|
||||
Assertions.assertEquals("Author Name", syndEntry.getAuthor());
|
||||
Assertions.assertEquals(1, syndEntry.getContents().size());
|
||||
Assertions.assertEquals(
|
||||
"This is a test entry content.", syndEntry.getContents().getFirst().getValue());
|
||||
Assertions.assertEquals(1, syndEntry.getEnclosures().size());
|
||||
Assertions.assertEquals(
|
||||
"http://example.com/enclosure.mp3", syndEntry.getEnclosures().getFirst().getUrl());
|
||||
Assertions.assertEquals("audio/mpeg", syndEntry.getEnclosures().getFirst().getType());
|
||||
Assertions.assertEquals("http://example.com/test-entry", syndEntry.getLink());
|
||||
Assertions.assertEquals(Date.from(Instant.ofEpochSecond(1)), syndEntry.getPublishedDate());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,56 +8,56 @@ import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
class ImageProxyUrlTest {
|
||||
|
||||
@BeforeAll
|
||||
static void init() {
|
||||
ImageProxyUrl.generateKey();
|
||||
}
|
||||
@BeforeAll
|
||||
static void init() {
|
||||
ImageProxyUrl.generateKey();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(
|
||||
strings = {
|
||||
// simple URL
|
||||
"http://example.com/image.jpg",
|
||||
@ParameterizedTest
|
||||
@ValueSource(
|
||||
strings = {
|
||||
// simple URL
|
||||
"http://example.com/image.jpg",
|
||||
|
||||
// URL with query parameters
|
||||
"https://example.com/image.png?width=100&height=200",
|
||||
// URL with query parameters
|
||||
"https://example.com/image.png?width=100&height=200",
|
||||
|
||||
// URL with special characters
|
||||
"https://example.com/path/image.gif?param=value&other=test#fragment",
|
||||
// URL with special characters
|
||||
"https://example.com/path/image.gif?param=value&other=test#fragment",
|
||||
|
||||
// URL with non-ascii characters
|
||||
"https://example.com/image-ñáéíóú.jpg",
|
||||
// URL with non-ascii characters
|
||||
"https://example.com/image-ñáéíóú.jpg",
|
||||
|
||||
// blank URL
|
||||
"",
|
||||
// blank URL
|
||||
"",
|
||||
|
||||
// URL with port number
|
||||
"http://localhost:8080/images/photo.jpg",
|
||||
// URL with port number
|
||||
"http://localhost:8080/images/photo.jpg",
|
||||
|
||||
// long URL
|
||||
"https://very-long-domain-name-example.com/very/long/path/to/image/file/with/many/segments/image.jpg?param1=value1¶m2=value2¶m3=value3",
|
||||
// long URL
|
||||
"https://very-long-domain-name-example.com/very/long/path/to/image/file/with/many/segments/image.jpg?param1=value1¶m2=value2¶m3=value3",
|
||||
|
||||
// URL with mixed case
|
||||
"HTTPS://EXAMPLE.COM/Image.JPG",
|
||||
// URL with mixed case
|
||||
"HTTPS://EXAMPLE.COM/Image.JPG",
|
||||
|
||||
// URL with port number
|
||||
"https://example123.com/image123.jpg?id=456789", })
|
||||
void testEncodingDecoding(String originalUrl) {
|
||||
String encoded = ImageProxyUrl.encode(originalUrl);
|
||||
String decoded = ImageProxyUrl.decode(encoded);
|
||||
// URL with port number
|
||||
"https://example123.com/image123.jpg?id=456789",
|
||||
})
|
||||
void testEncodingDecoding(String originalUrl) {
|
||||
String encoded = ImageProxyUrl.encode(originalUrl);
|
||||
String decoded = ImageProxyUrl.decode(encoded);
|
||||
|
||||
Assertions.assertEquals(originalUrl, decoded);
|
||||
}
|
||||
Assertions.assertEquals(originalUrl, decoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeProducesDifferentResultForDifferentUrls() {
|
||||
String url1 = "https://example.com/image1.jpg";
|
||||
String url2 = "https://example.com/image2.jpg";
|
||||
@Test
|
||||
void encodeProducesDifferentResultForDifferentUrls() {
|
||||
String url1 = "https://example.com/image1.jpg";
|
||||
String url2 = "https://example.com/image2.jpg";
|
||||
|
||||
String encoded1 = ImageProxyUrl.encode(url1);
|
||||
String encoded2 = ImageProxyUrl.encode(url2);
|
||||
String encoded1 = ImageProxyUrl.encode(url1);
|
||||
String encoded2 = ImageProxyUrl.encode(url2);
|
||||
|
||||
Assertions.assertNotEquals(encoded1, encoded2);
|
||||
}
|
||||
|
||||
}
|
||||
Assertions.assertNotEquals(encoded1, encoded2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,20 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
class EncodingDetectorTest {
|
||||
|
||||
EncodingDetector encodingDetector = new EncodingDetector();
|
||||
EncodingDetector encodingDetector = new EncodingDetector();
|
||||
|
||||
@Test
|
||||
void testExtractDeclaredEncoding() {
|
||||
Assertions.assertNull(encodingDetector.extractDeclaredEncoding("<?xml ?>".getBytes()));
|
||||
Assertions.assertNull(encodingDetector.extractDeclaredEncoding("<feed></feed>".getBytes()));
|
||||
Assertions.assertEquals("UTF-8", encodingDetector.extractDeclaredEncoding("<?xml encoding=\"UTF-8\" ?>".getBytes()));
|
||||
Assertions.assertEquals("UTF-8", encodingDetector.extractDeclaredEncoding("<?xml encoding='UTF-8' ?>".getBytes()));
|
||||
Assertions.assertEquals("UTF-8", encodingDetector.extractDeclaredEncoding("<?xml encoding='UTF-8'?>".getBytes()));
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
void testExtractDeclaredEncoding() {
|
||||
Assertions.assertNull(encodingDetector.extractDeclaredEncoding("<?xml ?>".getBytes()));
|
||||
Assertions.assertNull(encodingDetector.extractDeclaredEncoding("<feed></feed>".getBytes()));
|
||||
Assertions.assertEquals(
|
||||
"UTF-8",
|
||||
encodingDetector.extractDeclaredEncoding("<?xml encoding=\"UTF-8\" ?>".getBytes()));
|
||||
Assertions.assertEquals(
|
||||
"UTF-8",
|
||||
encodingDetector.extractDeclaredEncoding("<?xml encoding='UTF-8' ?>".getBytes()));
|
||||
Assertions.assertEquals(
|
||||
"UTF-8",
|
||||
encodingDetector.extractDeclaredEncoding("<?xml encoding='UTF-8'?>".getBytes()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,49 +5,93 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
class TextDirectionDetectorTest {
|
||||
|
||||
@Test
|
||||
void testEstimateDirection() {
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.LEFT_TO_RIGHT, TextDirectionDetector.detect(""));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.LEFT_TO_RIGHT, TextDirectionDetector.detect(" "));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.LEFT_TO_RIGHT, TextDirectionDetector.detect("! (...)"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.LEFT_TO_RIGHT, TextDirectionDetector.detect("Pure Ascii content"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.LEFT_TO_RIGHT, TextDirectionDetector.detect("-17.0%"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.LEFT_TO_RIGHT, TextDirectionDetector.detect("http://foo/bar/"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.LEFT_TO_RIGHT,
|
||||
TextDirectionDetector.detect("http://foo/bar/?s=\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0"
|
||||
+ "\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0" + "\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.RIGHT_TO_LEFT, TextDirectionDetector.detect("\u05d0"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.RIGHT_TO_LEFT, TextDirectionDetector.detect("\u05d0"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect("http://foo/bar/ \u05d0 http://foo2/bar2/ http://foo3/bar3/"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect("\u05d0\u05d9\u05df \u05de\u05de\u05e9 " + "\u05de\u05d4 \u05dc\u05e8\u05d0\u05d5\u05ea: "
|
||||
+ "\u05dc\u05d0 \u05e6\u05d9\u05dc\u05de\u05ea\u05d9 " + "\u05d4\u05e8\u05d1\u05d4 \u05d5\u05d2\u05dd \u05d0"
|
||||
+ "\u05dd \u05d4\u05d9\u05d9\u05ea\u05d9 \u05de\u05e6\u05dc" + "\u05dd, \u05d4\u05d9\u05d4 \u05e9\u05dd"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect("\u05db\u05d0\u05df - http://geek.co.il/gallery/v/2007-06"
|
||||
+ " - \u05d0\u05d9\u05df \u05de\u05de\u05e9 \u05de\u05d4 " + "\u05dc\u05e8\u05d0\u05d5\u05ea: \u05dc\u05d0 \u05e6"
|
||||
+ "\u05d9\u05dc\u05de\u05ea\u05d9 \u05d4\u05e8\u05d1\u05d4 "
|
||||
+ "\u05d5\u05d2\u05dd \u05d0\u05dd \u05d4\u05d9\u05d9\u05ea"
|
||||
+ "\u05d9 \u05de\u05e6\u05dc\u05dd, \u05d4\u05d9\u05d4 "
|
||||
+ "\u05e9\u05dd \u05d1\u05e2\u05d9\u05e7\u05e8 \u05d4\u05e8" + "\u05d1\u05d4 \u05d0\u05e0\u05e9\u05d9\u05dd. \u05de"
|
||||
+ "\u05d4 \u05e9\u05db\u05df - \u05d0\u05e4\u05e9\u05e8 " + "\u05dc\u05e0\u05e6\u05dc \u05d0\u05ea \u05d4\u05d4 "
|
||||
+ "\u05d3\u05d6\u05de\u05e0\u05d5\u05ea \u05dc\u05d4\u05e1" + "\u05ea\u05db\u05dc \u05e2\u05dc \u05db\u05de\u05d4 "
|
||||
+ "\u05ea\u05de\u05d5\u05e0\u05d5\u05ea \u05de\u05e9\u05e2"
|
||||
+ "\u05e9\u05e2\u05d5\u05ea \u05d9\u05e9\u05e0\u05d5\u05ea " + "\u05d9\u05d5\u05ea\u05e8 \u05e9\u05d9\u05e9 \u05dc"
|
||||
+ "\u05d9 \u05d1\u05d0\u05ea\u05e8"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect("CAPTCHA \u05de\u05e9\u05d5\u05db\u05dc\u05dc " + "\u05de\u05d3\u05d9?"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect("Yes Prime Minister \u05e2\u05d3\u05db\u05d5\u05df. "
|
||||
+ "\u05e9\u05d0\u05dc\u05d5 \u05d0\u05d5\u05ea\u05d9 " + "\u05de\u05d4 \u05d0\u05e0\u05d9 \u05e8\u05d5\u05e6"
|
||||
+ "\u05d4 \u05de\u05ea\u05e0\u05d4 \u05dc\u05d7\u05d2"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.RIGHT_TO_LEFT, TextDirectionDetector
|
||||
.detect("17.4.02 \u05e9\u05e2\u05d4:13-20 .15-00 .\u05dc\u05d0 " + "\u05d4\u05d9\u05d9\u05ea\u05d9 \u05db\u05d0\u05df."));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect("5710 5720 5730. \u05d4\u05d3\u05dc\u05ea. " + "\u05d4\u05e0\u05e9\u05d9\u05e7\u05d4"));
|
||||
Assertions.assertEquals(TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect("\u05d4\u05d3\u05dc\u05ea http://www.google.com " + "http://www.gmail.com"));
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
void testEstimateDirection() {
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.LEFT_TO_RIGHT, TextDirectionDetector.detect(""));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.LEFT_TO_RIGHT, TextDirectionDetector.detect(" "));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.LEFT_TO_RIGHT,
|
||||
TextDirectionDetector.detect("! (...)"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.LEFT_TO_RIGHT,
|
||||
TextDirectionDetector.detect("Pure Ascii content"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.LEFT_TO_RIGHT,
|
||||
TextDirectionDetector.detect("-17.0%"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.LEFT_TO_RIGHT,
|
||||
TextDirectionDetector.detect("http://foo/bar/"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.LEFT_TO_RIGHT,
|
||||
TextDirectionDetector.detect(
|
||||
"http://foo/bar/?s=\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0"
|
||||
+ "\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0"
|
||||
+ "\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect("\u05d0"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect("\u05d0"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect(
|
||||
"http://foo/bar/ \u05d0 http://foo2/bar2/ http://foo3/bar3/"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect(
|
||||
"\u05d0\u05d9\u05df \u05de\u05de\u05e9 "
|
||||
+ "\u05de\u05d4 \u05dc\u05e8\u05d0\u05d5\u05ea: "
|
||||
+ "\u05dc\u05d0 \u05e6\u05d9\u05dc\u05de\u05ea\u05d9 "
|
||||
+ "\u05d4\u05e8\u05d1\u05d4 \u05d5\u05d2\u05dd \u05d0"
|
||||
+ "\u05dd \u05d4\u05d9\u05d9\u05ea\u05d9 \u05de\u05e6\u05dc"
|
||||
+ "\u05dd, \u05d4\u05d9\u05d4 \u05e9\u05dd"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect(
|
||||
"\u05db\u05d0\u05df - http://geek.co.il/gallery/v/2007-06"
|
||||
+ " - \u05d0\u05d9\u05df \u05de\u05de\u05e9 \u05de\u05d4 "
|
||||
+ "\u05dc\u05e8\u05d0\u05d5\u05ea: \u05dc\u05d0 \u05e6"
|
||||
+ "\u05d9\u05dc\u05de\u05ea\u05d9 \u05d4\u05e8\u05d1\u05d4 "
|
||||
+ "\u05d5\u05d2\u05dd \u05d0\u05dd \u05d4\u05d9\u05d9\u05ea"
|
||||
+ "\u05d9 \u05de\u05e6\u05dc\u05dd, \u05d4\u05d9\u05d4 "
|
||||
+ "\u05e9\u05dd \u05d1\u05e2\u05d9\u05e7\u05e8 \u05d4\u05e8"
|
||||
+ "\u05d1\u05d4 \u05d0\u05e0\u05e9\u05d9\u05dd. \u05de"
|
||||
+ "\u05d4 \u05e9\u05db\u05df - \u05d0\u05e4\u05e9\u05e8 "
|
||||
+ "\u05dc\u05e0\u05e6\u05dc \u05d0\u05ea \u05d4\u05d4 "
|
||||
+ "\u05d3\u05d6\u05de\u05e0\u05d5\u05ea \u05dc\u05d4\u05e1"
|
||||
+ "\u05ea\u05db\u05dc \u05e2\u05dc \u05db\u05de\u05d4 "
|
||||
+ "\u05ea\u05de\u05d5\u05e0\u05d5\u05ea \u05de\u05e9\u05e2"
|
||||
+ "\u05e9\u05e2\u05d5\u05ea \u05d9\u05e9\u05e0\u05d5\u05ea "
|
||||
+ "\u05d9\u05d5\u05ea\u05e8 \u05e9\u05d9\u05e9 \u05dc"
|
||||
+ "\u05d9 \u05d1\u05d0\u05ea\u05e8"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect(
|
||||
"CAPTCHA \u05de\u05e9\u05d5\u05db\u05dc\u05dc " + "\u05de\u05d3\u05d9?"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect(
|
||||
"Yes Prime Minister \u05e2\u05d3\u05db\u05d5\u05df. "
|
||||
+ "\u05e9\u05d0\u05dc\u05d5 \u05d0\u05d5\u05ea\u05d9 "
|
||||
+ "\u05de\u05d4 \u05d0\u05e0\u05d9 \u05e8\u05d5\u05e6"
|
||||
+ "\u05d4 \u05de\u05ea\u05e0\u05d4 \u05dc\u05d7\u05d2"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect(
|
||||
"17.4.02 \u05e9\u05e2\u05d4:13-20 .15-00 .\u05dc\u05d0 "
|
||||
+ "\u05d4\u05d9\u05d9\u05ea\u05d9 \u05db\u05d0\u05df."));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect(
|
||||
"5710 5720 5730. \u05d4\u05d3\u05dc\u05ea. "
|
||||
+ "\u05d4\u05e0\u05e9\u05d9\u05e7\u05d4"));
|
||||
Assertions.assertEquals(
|
||||
TextDirectionDetector.Direction.RIGHT_TO_LEFT,
|
||||
TextDirectionDetector.detect(
|
||||
"\u05d4\u05d3\u05dc\u05ea http://www.google.com "
|
||||
+ "http://www.gmail.com"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,266 +6,297 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
class XMLCleanerTest {
|
||||
|
||||
XMLCleaner xmlCleaner = new XMLCleaner();
|
||||
XMLCleaner xmlCleaner = new XMLCleaner();
|
||||
|
||||
@Nested
|
||||
class RemoveCharactersBeforeFirstXmlTag {
|
||||
@Test
|
||||
void removesWhitespaceBeforeXmlTag() {
|
||||
String xml = " \n\t<feed>content</feed>";
|
||||
Assertions.assertEquals("<feed>content</feed>", xmlCleaner.removeCharactersBeforeFirstXmlTag(xml));
|
||||
}
|
||||
@Nested
|
||||
class RemoveCharactersBeforeFirstXmlTag {
|
||||
@Test
|
||||
void removesWhitespaceBeforeXmlTag() {
|
||||
String xml = " \n\t<feed>content</feed>";
|
||||
Assertions.assertEquals(
|
||||
"<feed>content</feed>", xmlCleaner.removeCharactersBeforeFirstXmlTag(xml));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removesTextBeforeXmlTag() {
|
||||
String xml = "some text here<feed>content</feed>";
|
||||
Assertions.assertEquals("<feed>content</feed>", xmlCleaner.removeCharactersBeforeFirstXmlTag(xml));
|
||||
}
|
||||
@Test
|
||||
void removesTextBeforeXmlTag() {
|
||||
String xml = "some text here<feed>content</feed>";
|
||||
Assertions.assertEquals(
|
||||
"<feed>content</feed>", xmlCleaner.removeCharactersBeforeFirstXmlTag(xml));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsUnchangedWhenStartsWithXmlTag() {
|
||||
String xml = "<feed>content</feed>";
|
||||
Assertions.assertEquals("<feed>content</feed>", xmlCleaner.removeCharactersBeforeFirstXmlTag(xml));
|
||||
}
|
||||
@Test
|
||||
void returnsUnchangedWhenStartsWithXmlTag() {
|
||||
String xml = "<feed>content</feed>";
|
||||
Assertions.assertEquals(
|
||||
"<feed>content</feed>", xmlCleaner.removeCharactersBeforeFirstXmlTag(xml));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenNoXmlTagFound() {
|
||||
String xml = "no xml tags here";
|
||||
Assertions.assertNull(xmlCleaner.removeCharactersBeforeFirstXmlTag(xml));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenNoXmlTagFound() {
|
||||
String xml = "no xml tags here";
|
||||
Assertions.assertNull(xmlCleaner.removeCharactersBeforeFirstXmlTag(xml));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsNull() {
|
||||
Assertions.assertNull(xmlCleaner.removeCharactersBeforeFirstXmlTag(null));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsNull() {
|
||||
Assertions.assertNull(xmlCleaner.removeCharactersBeforeFirstXmlTag(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsEmpty() {
|
||||
Assertions.assertNull(xmlCleaner.removeCharactersBeforeFirstXmlTag(""));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsEmpty() {
|
||||
Assertions.assertNull(xmlCleaner.removeCharactersBeforeFirstXmlTag(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsBlank() {
|
||||
Assertions.assertNull(xmlCleaner.removeCharactersBeforeFirstXmlTag(" \n\t "));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsBlank() {
|
||||
Assertions.assertNull(xmlCleaner.removeCharactersBeforeFirstXmlTag(" \n\t "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void preservesMultipleXmlTags() {
|
||||
String xml = "garbage<feed><item>content</item></feed>";
|
||||
Assertions.assertEquals("<feed><item>content</item></feed>", xmlCleaner.removeCharactersBeforeFirstXmlTag(xml));
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void preservesMultipleXmlTags() {
|
||||
String xml = "garbage<feed><item>content</item></feed>";
|
||||
Assertions.assertEquals(
|
||||
"<feed><item>content</item></feed>",
|
||||
xmlCleaner.removeCharactersBeforeFirstXmlTag(xml));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class RemoveInvalidXmlCharacters {
|
||||
@Test
|
||||
void removesNullCharacter() {
|
||||
String xml = "<feed>content\u0000here</feed>";
|
||||
Assertions.assertEquals("<feed>contenthere</feed>", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
@Nested
|
||||
class RemoveInvalidXmlCharacters {
|
||||
@Test
|
||||
void removesNullCharacter() {
|
||||
String xml = "<feed>content\u0000here</feed>";
|
||||
Assertions.assertEquals(
|
||||
"<feed>contenthere</feed>", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removesInvalidControlCharacters() {
|
||||
String xml = "<feed>content\u0001\u0002\u0003here</feed>";
|
||||
Assertions.assertEquals("<feed>contenthere</feed>", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
@Test
|
||||
void removesInvalidControlCharacters() {
|
||||
String xml = "<feed>content\u0001\u0002\u0003here</feed>";
|
||||
Assertions.assertEquals(
|
||||
"<feed>contenthere</feed>", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
|
||||
@Test
|
||||
void preservesValidXmlCharacters() {
|
||||
String xml = "<feed>content with\ttab\nand newline</feed>";
|
||||
Assertions.assertEquals("<feed>content with\ttab\nand newline</feed>", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
@Test
|
||||
void preservesValidXmlCharacters() {
|
||||
String xml = "<feed>content with\ttab\nand newline</feed>";
|
||||
Assertions.assertEquals(
|
||||
"<feed>content with\ttab\nand newline</feed>",
|
||||
xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
|
||||
@Test
|
||||
void preservesUnicodeCharacters() {
|
||||
String xml = "<feed>café résumé 中文 العربية</feed>";
|
||||
Assertions.assertEquals("<feed>café résumé 中文 العربية</feed>", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
@Test
|
||||
void preservesUnicodeCharacters() {
|
||||
String xml = "<feed>café résumé 中文 العربية</feed>";
|
||||
Assertions.assertEquals(
|
||||
"<feed>café résumé 中文 العربية</feed>",
|
||||
xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
|
||||
@Test
|
||||
void preservesEmojiCharacters() {
|
||||
String xml = "<feed>🎮💪✅</feed>";
|
||||
Assertions.assertEquals("<feed>🎮💪✅</feed>", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
@Test
|
||||
void preservesEmojiCharacters() {
|
||||
String xml = "<feed>🎮💪✅</feed>";
|
||||
Assertions.assertEquals(
|
||||
"<feed>🎮💪✅</feed>", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removesMultipleInvalidCharacters() {
|
||||
String xml = "test\u0000test\u0001test\u0002test";
|
||||
Assertions.assertEquals("testtesttesttest", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
@Test
|
||||
void removesMultipleInvalidCharacters() {
|
||||
String xml = "test\u0000test\u0001test\u0002test";
|
||||
Assertions.assertEquals("testtesttesttest", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsNull() {
|
||||
Assertions.assertNull(xmlCleaner.removeInvalidXmlCharacters(null));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsNull() {
|
||||
Assertions.assertNull(xmlCleaner.removeInvalidXmlCharacters(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsEmpty() {
|
||||
Assertions.assertNull(xmlCleaner.removeInvalidXmlCharacters(""));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsEmpty() {
|
||||
Assertions.assertNull(xmlCleaner.removeInvalidXmlCharacters(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsBlank() {
|
||||
Assertions.assertNull(xmlCleaner.removeInvalidXmlCharacters(" "));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsBlank() {
|
||||
Assertions.assertNull(xmlCleaner.removeInvalidXmlCharacters(" "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void handlesStringWithOnlyInvalidCharacters() {
|
||||
String xml = "\u0000\u0001\u0002";
|
||||
Assertions.assertEquals("", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void handlesStringWithOnlyInvalidCharacters() {
|
||||
String xml = "\u0000\u0001\u0002";
|
||||
Assertions.assertEquals("", xmlCleaner.removeInvalidXmlCharacters(xml));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Entities {
|
||||
@Test
|
||||
void testReplaceHtmlEntitiesWithNumericEntities() {
|
||||
String source = "<source>T´l´phone ′</source>";
|
||||
Assertions.assertEquals("<source>T´l´phone ′</source>",
|
||||
xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
@Nested
|
||||
class Entities {
|
||||
@Test
|
||||
void testReplaceHtmlEntitiesWithNumericEntities() {
|
||||
String source = "<source>T´l´phone ′</source>";
|
||||
Assertions.assertEquals(
|
||||
"<source>T´l´phone ′</source>",
|
||||
xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacesMultipleOccurrencesOfSameEntity() {
|
||||
String source = " ";
|
||||
Assertions.assertEquals("   ", xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
@Test
|
||||
void replacesMultipleOccurrencesOfSameEntity() {
|
||||
String source = " ";
|
||||
Assertions.assertEquals(
|
||||
"   ",
|
||||
xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void preservesTextWithoutEntities() {
|
||||
String source = "<feed>regular content</feed>";
|
||||
Assertions.assertEquals("<feed>regular content</feed>", xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
@Test
|
||||
void preservesTextWithoutEntities() {
|
||||
String source = "<feed>regular content</feed>";
|
||||
Assertions.assertEquals(
|
||||
"<feed>regular content</feed>",
|
||||
xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void preservesNumericEntities() {
|
||||
String source = "´′";
|
||||
Assertions.assertEquals("´′", xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
@Test
|
||||
void preservesNumericEntities() {
|
||||
String source = "´′";
|
||||
Assertions.assertEquals(
|
||||
"´′", xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacesCommonHtmlEntities() {
|
||||
String source = "&"";
|
||||
Assertions.assertEquals("&"", xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
@Test
|
||||
void replacesCommonHtmlEntities() {
|
||||
String source = "&"";
|
||||
Assertions.assertEquals(
|
||||
"&"", xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void handlesPartialEntityMatches() {
|
||||
String source = "&lifier";
|
||||
String result = xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source);
|
||||
Assertions.assertTrue(result.startsWith("&") || result.equals("&lifier"));
|
||||
}
|
||||
@Test
|
||||
void handlesPartialEntityMatches() {
|
||||
String source = "&lifier";
|
||||
String result = xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source);
|
||||
Assertions.assertTrue(result.startsWith("&") || result.equals("&lifier"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsNull() {
|
||||
Assertions.assertNull(xmlCleaner.replaceHtmlEntitiesWithNumericEntities(null));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsNull() {
|
||||
Assertions.assertNull(xmlCleaner.replaceHtmlEntitiesWithNumericEntities(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsEmpty() {
|
||||
Assertions.assertNull(xmlCleaner.replaceHtmlEntitiesWithNumericEntities(""));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsEmpty() {
|
||||
Assertions.assertNull(xmlCleaner.replaceHtmlEntitiesWithNumericEntities(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsBlank() {
|
||||
Assertions.assertNull(xmlCleaner.replaceHtmlEntitiesWithNumericEntities(" "));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsBlank() {
|
||||
Assertions.assertNull(xmlCleaner.replaceHtmlEntitiesWithNumericEntities(" "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void handlesEntityAtStartOfString() {
|
||||
String source = "&test";
|
||||
Assertions.assertEquals("&test", xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
@Test
|
||||
void handlesEntityAtStartOfString() {
|
||||
String source = "&test";
|
||||
Assertions.assertEquals(
|
||||
"&test", xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void handlesEntityAtEndOfString() {
|
||||
String source = "test&";
|
||||
Assertions.assertEquals("test&", xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
@Test
|
||||
void handlesEntityAtEndOfString() {
|
||||
String source = "test&";
|
||||
Assertions.assertEquals(
|
||||
"test&", xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void handlesMixedEntitiesAndText() {
|
||||
String source = "Hello World! Test.";
|
||||
String result = xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source);
|
||||
Assertions.assertTrue(result.contains("&#"));
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void handlesMixedEntitiesAndText() {
|
||||
String source = "Hello World! Test.";
|
||||
String result = xmlCleaner.replaceHtmlEntitiesWithNumericEntities(source);
|
||||
Assertions.assertTrue(result.contains("&#"));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Doctype {
|
||||
@Test
|
||||
void testRemoveDoctype() {
|
||||
String source = "<!DOCTYPE html><html><head></head><body></body></html>";
|
||||
Assertions.assertEquals("<html><head></head><body></body></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
@Nested
|
||||
class Doctype {
|
||||
@Test
|
||||
void testRemoveDoctype() {
|
||||
String source = "<!DOCTYPE html><html><head></head><body></body></html>";
|
||||
Assertions.assertEquals(
|
||||
"<html><head></head><body></body></html>",
|
||||
xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveMultilineDoctype() {
|
||||
String source = """
|
||||
@Test
|
||||
void testRemoveMultilineDoctype() {
|
||||
String source =
|
||||
"""
|
||||
<!DOCTYPE
|
||||
html
|
||||
>
|
||||
<html><head></head><body></body></html>""";
|
||||
Assertions.assertEquals("""
|
||||
Assertions.assertEquals(
|
||||
"""
|
||||
|
||||
<html><head></head><body></body></html>""", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
<html><head></head><body></body></html>""",
|
||||
xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removesComplexDoctypeWithSystemId() {
|
||||
String source = "<!DOCTYPE html SYSTEM \"about:legacy-compat\"><html><body></body></html>";
|
||||
Assertions.assertEquals("<html><body></body></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
@Test
|
||||
void removesComplexDoctypeWithSystemId() {
|
||||
String source =
|
||||
"<!DOCTYPE html SYSTEM \"about:legacy-compat\"><html><body></body></html>";
|
||||
Assertions.assertEquals(
|
||||
"<html><body></body></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removesComplexDoctypeWithPublicId() {
|
||||
String source = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"><html></html>";
|
||||
Assertions.assertEquals("<html></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
@Test
|
||||
void removesComplexDoctypeWithPublicId() {
|
||||
String source =
|
||||
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"><html></html>";
|
||||
Assertions.assertEquals("<html></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removesCaseInsensitiveDoctype() {
|
||||
String source = "<!doctype html><html></html>";
|
||||
Assertions.assertEquals("<html></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
@Test
|
||||
void removesCaseInsensitiveDoctype() {
|
||||
String source = "<!doctype html><html></html>";
|
||||
Assertions.assertEquals("<html></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removesMixedCaseDoctype() {
|
||||
String source = "<!DoCtYpE html><html></html>";
|
||||
Assertions.assertEquals("<html></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
@Test
|
||||
void removesMixedCaseDoctype() {
|
||||
String source = "<!DoCtYpE html><html></html>";
|
||||
Assertions.assertEquals("<html></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void removesMultipleDoctypeDeclarations() {
|
||||
String source = "<!DOCTYPE html><!DOCTYPE html><html></html>";
|
||||
Assertions.assertEquals("<html></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
@Test
|
||||
void removesMultipleDoctypeDeclarations() {
|
||||
String source = "<!DOCTYPE html><!DOCTYPE html><html></html>";
|
||||
Assertions.assertEquals("<html></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void preservesContentWithoutDoctype() {
|
||||
String source = "<html><body>No doctype here</body></html>";
|
||||
Assertions.assertEquals("<html><body>No doctype here</body></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
@Test
|
||||
void preservesContentWithoutDoctype() {
|
||||
String source = "<html><body>No doctype here</body></html>";
|
||||
Assertions.assertEquals(
|
||||
"<html><body>No doctype here</body></html>",
|
||||
xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsNull() {
|
||||
Assertions.assertNull(xmlCleaner.removeDoctypeDeclarations(null));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsNull() {
|
||||
Assertions.assertNull(xmlCleaner.removeDoctypeDeclarations(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsEmpty() {
|
||||
Assertions.assertNull(xmlCleaner.removeDoctypeDeclarations(""));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsEmpty() {
|
||||
Assertions.assertNull(xmlCleaner.removeDoctypeDeclarations(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsNullWhenInputIsBlank() {
|
||||
Assertions.assertNull(xmlCleaner.removeDoctypeDeclarations(" "));
|
||||
}
|
||||
@Test
|
||||
void returnsNullWhenInputIsBlank() {
|
||||
Assertions.assertNull(xmlCleaner.removeDoctypeDeclarations(" "));
|
||||
}
|
||||
|
||||
@Test
|
||||
void handlesDoctypeWithExtraWhitespace() {
|
||||
String source = "<!DOCTYPE html ><html></html>";
|
||||
Assertions.assertEquals("<html></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void handlesDoctypeWithExtraWhitespace() {
|
||||
String source = "<!DOCTYPE html ><html></html>";
|
||||
Assertions.assertEquals("<html></html>", xmlCleaner.removeDoctypeDeclarations(source));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,135 +6,363 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
class FeedEntryContentTest {
|
||||
|
||||
@Nested
|
||||
class EquivalentTo {
|
||||
@Nested
|
||||
class EquivalentTo {
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenComparedWithNull() {
|
||||
Assertions.assertFalse(new FeedEntryContent().equivalentTo(null));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnFalseWhenComparedWithNull() {
|
||||
Assertions.assertFalse(new FeedEntryContent().equivalentTo(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrueWhenComparedWithIdenticalContent() {
|
||||
FeedEntryContent content1 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
FeedEntryContent content2 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
Assertions.assertTrue(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnTrueWhenComparedWithIdenticalContent() {
|
||||
FeedEntryContent content1 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
FeedEntryContent content2 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
Assertions.assertTrue(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenTitleDiffers() {
|
||||
FeedEntryContent content1 = createContent("title1", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
FeedEntryContent content2 = createContent("title2", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnFalseWhenTitleDiffers() {
|
||||
FeedEntryContent content1 =
|
||||
createContent(
|
||||
"title1",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
FeedEntryContent content2 =
|
||||
createContent(
|
||||
"title2",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenContentDiffers() {
|
||||
FeedEntryContent content1 = createContent("title", "content1", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
FeedEntryContent content2 = createContent("title", "content2", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnFalseWhenContentDiffers() {
|
||||
FeedEntryContent content1 =
|
||||
createContent(
|
||||
"title",
|
||||
"content1",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
FeedEntryContent content2 =
|
||||
createContent(
|
||||
"title",
|
||||
"content2",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenAuthorDiffers() {
|
||||
FeedEntryContent content1 = createContent("title", "content", "author1", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
FeedEntryContent content2 = createContent("title", "content", "author2", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnFalseWhenAuthorDiffers() {
|
||||
FeedEntryContent content1 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author1",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
FeedEntryContent content2 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author2",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenCategoriesDiffer() {
|
||||
FeedEntryContent content1 = createContent("title", "content", "author", "categories1", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
FeedEntryContent content2 = createContent("title", "content", "author", "categories2", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnFalseWhenCategoriesDiffer() {
|
||||
FeedEntryContent content1 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories1",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
FeedEntryContent content2 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories2",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenEnclosureUrlDiffers() {
|
||||
FeedEntryContent content1 = createContent("title", "content", "author", "categories", "enclosureUrl1", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
FeedEntryContent content2 = createContent("title", "content", "author", "categories", "enclosureUrl2", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnFalseWhenEnclosureUrlDiffers() {
|
||||
FeedEntryContent content1 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl1",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
FeedEntryContent content2 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl2",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenEnclosureTypeDiffers() {
|
||||
FeedEntryContent content1 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType1",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
FeedEntryContent content2 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType2",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnFalseWhenEnclosureTypeDiffers() {
|
||||
FeedEntryContent content1 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType1",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
FeedEntryContent content2 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType2",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenMediaDescriptionDiffers() {
|
||||
FeedEntryContent content1 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription1", "mediaThumbnailUrl", 10, 20);
|
||||
FeedEntryContent content2 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription2", "mediaThumbnailUrl", 10, 20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnFalseWhenMediaDescriptionDiffers() {
|
||||
FeedEntryContent content1 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription1",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
FeedEntryContent content2 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription2",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenMediaThumbnailUrlDiffers() {
|
||||
FeedEntryContent content1 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl1", 10, 20);
|
||||
FeedEntryContent content2 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl2", 10, 20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnFalseWhenMediaThumbnailUrlDiffers() {
|
||||
FeedEntryContent content1 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl1",
|
||||
10,
|
||||
20);
|
||||
FeedEntryContent content2 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl2",
|
||||
10,
|
||||
20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenMediaThumbnailWidthDiffers() {
|
||||
FeedEntryContent content1 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
FeedEntryContent content2 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 15, 20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnFalseWhenMediaThumbnailWidthDiffers() {
|
||||
FeedEntryContent content1 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
FeedEntryContent content2 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
15,
|
||||
20);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenMediaThumbnailHeightDiffers() {
|
||||
FeedEntryContent content1 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 20);
|
||||
FeedEntryContent content2 = createContent("title", "content", "author", "categories", "enclosureUrl", "enclosureType",
|
||||
"mediaDescription", "mediaThumbnailUrl", 10, 25);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnFalseWhenMediaThumbnailHeightDiffers() {
|
||||
FeedEntryContent content1 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
20);
|
||||
FeedEntryContent content2 =
|
||||
createContent(
|
||||
"title",
|
||||
"content",
|
||||
"author",
|
||||
"categories",
|
||||
"enclosureUrl",
|
||||
"enclosureType",
|
||||
"mediaDescription",
|
||||
"mediaThumbnailUrl",
|
||||
10,
|
||||
25);
|
||||
Assertions.assertFalse(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrueWhenNullFieldsAreEqual() {
|
||||
FeedEntryContent content1 = new FeedEntryContent();
|
||||
FeedEntryContent content2 = new FeedEntryContent();
|
||||
Assertions.assertTrue(content1.equivalentTo(content2));
|
||||
}
|
||||
@Test
|
||||
void shouldReturnTrueWhenNullFieldsAreEqual() {
|
||||
FeedEntryContent content1 = new FeedEntryContent();
|
||||
FeedEntryContent content2 = new FeedEntryContent();
|
||||
Assertions.assertTrue(content1.equivalentTo(content2));
|
||||
}
|
||||
|
||||
private FeedEntryContent createContent(String title, String content, String author, String categories, String enclosureUrl,
|
||||
String enclosureType, String mediaDescription, String mediaThumbnailUrl, Integer mediaThumbnailWidth,
|
||||
Integer mediaThumbnailHeight) {
|
||||
FeedEntryContent feedEntryContent = new FeedEntryContent();
|
||||
feedEntryContent.setTitle(title);
|
||||
feedEntryContent.setContent(content);
|
||||
feedEntryContent.setAuthor(author);
|
||||
feedEntryContent.setCategories(categories);
|
||||
feedEntryContent.setEnclosureUrl(enclosureUrl);
|
||||
feedEntryContent.setEnclosureType(enclosureType);
|
||||
feedEntryContent.setMediaDescription(mediaDescription);
|
||||
feedEntryContent.setMediaThumbnailUrl(mediaThumbnailUrl);
|
||||
feedEntryContent.setMediaThumbnailWidth(mediaThumbnailWidth);
|
||||
feedEntryContent.setMediaThumbnailHeight(mediaThumbnailHeight);
|
||||
return feedEntryContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
private FeedEntryContent createContent(
|
||||
String title,
|
||||
String content,
|
||||
String author,
|
||||
String categories,
|
||||
String enclosureUrl,
|
||||
String enclosureType,
|
||||
String mediaDescription,
|
||||
String mediaThumbnailUrl,
|
||||
Integer mediaThumbnailWidth,
|
||||
Integer mediaThumbnailHeight) {
|
||||
FeedEntryContent feedEntryContent = new FeedEntryContent();
|
||||
feedEntryContent.setTitle(title);
|
||||
feedEntryContent.setContent(content);
|
||||
feedEntryContent.setAuthor(author);
|
||||
feedEntryContent.setCategories(categories);
|
||||
feedEntryContent.setEnclosureUrl(enclosureUrl);
|
||||
feedEntryContent.setEnclosureType(enclosureType);
|
||||
feedEntryContent.setMediaDescription(mediaDescription);
|
||||
feedEntryContent.setMediaThumbnailUrl(mediaThumbnailUrl);
|
||||
feedEntryContent.setMediaThumbnailWidth(mediaThumbnailWidth);
|
||||
feedEntryContent.setMediaThumbnailHeight(mediaThumbnailHeight);
|
||||
return feedEntryContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
package com.commafeed.backend.opml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
@@ -19,115 +8,124 @@ import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.rometools.opml.feed.opml.Opml;
|
||||
import com.rometools.opml.feed.opml.Outline;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class OPMLExporterTest {
|
||||
|
||||
@Mock
|
||||
private FeedCategoryDAO feedCategoryDAO;
|
||||
@Mock
|
||||
private FeedSubscriptionDAO feedSubscriptionDAO;
|
||||
@Mock private FeedCategoryDAO feedCategoryDAO;
|
||||
@Mock private FeedSubscriptionDAO feedSubscriptionDAO;
|
||||
|
||||
private final User user = new User();
|
||||
private final User user = new User();
|
||||
|
||||
private final FeedCategory cat1 = new FeedCategory();
|
||||
private final FeedCategory cat2 = new FeedCategory();
|
||||
private final FeedCategory cat1 = new FeedCategory();
|
||||
private final FeedCategory cat2 = new FeedCategory();
|
||||
|
||||
private final FeedSubscription rootFeed = newFeedSubscription("rootFeed", "rootFeed.com");
|
||||
private final FeedSubscription cat1Feed = newFeedSubscription("cat1Feed", "cat1Feed.com");
|
||||
private final FeedSubscription cat2Feed = newFeedSubscription("cat2Feed", "cat2Feed.com");
|
||||
private final FeedSubscription rootFeed = newFeedSubscription("rootFeed", "rootFeed.com");
|
||||
private final FeedSubscription cat1Feed = newFeedSubscription("cat1Feed", "cat1Feed.com");
|
||||
private final FeedSubscription cat2Feed = newFeedSubscription("cat2Feed", "cat2Feed.com");
|
||||
|
||||
private final List<FeedCategory> categories = new ArrayList<>();
|
||||
private final List<FeedSubscription> subscriptions = new ArrayList<>();
|
||||
private final List<FeedCategory> categories = new ArrayList<>();
|
||||
private final List<FeedSubscription> subscriptions = new ArrayList<>();
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
user.setName("John Doe");
|
||||
@BeforeEach
|
||||
void init() {
|
||||
user.setName("John Doe");
|
||||
|
||||
cat1.setId(1L);
|
||||
cat1.setName("cat1");
|
||||
cat1.setParent(null);
|
||||
cat1.setId(1L);
|
||||
cat1.setName("cat1");
|
||||
cat1.setParent(null);
|
||||
|
||||
cat2.setId(2L);
|
||||
cat2.setName("cat2");
|
||||
cat2.setParent(cat1);
|
||||
cat2.setId(2L);
|
||||
cat2.setName("cat2");
|
||||
cat2.setParent(cat1);
|
||||
|
||||
rootFeed.setCategory(null);
|
||||
cat1Feed.setCategory(cat1);
|
||||
cat2Feed.setCategory(cat2);
|
||||
rootFeed.setCategory(null);
|
||||
cat1Feed.setCategory(cat1);
|
||||
cat2Feed.setCategory(cat2);
|
||||
|
||||
categories.add(cat1);
|
||||
categories.add(cat2);
|
||||
categories.add(cat1);
|
||||
categories.add(cat2);
|
||||
|
||||
subscriptions.add(rootFeed);
|
||||
subscriptions.add(cat1Feed);
|
||||
subscriptions.add(cat2Feed);
|
||||
}
|
||||
subscriptions.add(rootFeed);
|
||||
subscriptions.add(cat1Feed);
|
||||
subscriptions.add(cat2Feed);
|
||||
}
|
||||
|
||||
private Feed newFeed(String url) {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl(url);
|
||||
return feed;
|
||||
}
|
||||
private Feed newFeed(String url) {
|
||||
Feed feed = new Feed();
|
||||
feed.setUrl(url);
|
||||
return feed;
|
||||
}
|
||||
|
||||
private FeedSubscription newFeedSubscription(String title, String url) {
|
||||
FeedSubscription feedSubscription = new FeedSubscription();
|
||||
feedSubscription.setTitle(title);
|
||||
feedSubscription.setFeed(newFeed(url));
|
||||
return feedSubscription;
|
||||
}
|
||||
private FeedSubscription newFeedSubscription(String title, String url) {
|
||||
FeedSubscription feedSubscription = new FeedSubscription();
|
||||
feedSubscription.setTitle(title);
|
||||
feedSubscription.setFeed(newFeed(url));
|
||||
return feedSubscription;
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatesOpmlCorrectly() {
|
||||
Mockito.when(feedCategoryDAO.findAll(user)).thenReturn(categories);
|
||||
Mockito.when(feedSubscriptionDAO.findAll(user)).thenReturn(subscriptions);
|
||||
@Test
|
||||
void generatesOpmlCorrectly() {
|
||||
Mockito.when(feedCategoryDAO.findAll(user)).thenReturn(categories);
|
||||
Mockito.when(feedSubscriptionDAO.findAll(user)).thenReturn(subscriptions);
|
||||
|
||||
Opml opml = new OPMLExporter(feedCategoryDAO, feedSubscriptionDAO).export(user);
|
||||
Opml opml = new OPMLExporter(feedCategoryDAO, feedSubscriptionDAO).export(user);
|
||||
|
||||
List<Outline> rootOutlines = opml.getOutlines();
|
||||
Assertions.assertEquals(2, rootOutlines.size());
|
||||
Assertions.assertTrue(containsCategory(rootOutlines, "cat1"));
|
||||
Assertions.assertTrue(containsFeed(rootOutlines, "rootFeed", "rootFeed.com"));
|
||||
List<Outline> rootOutlines = opml.getOutlines();
|
||||
Assertions.assertEquals(2, rootOutlines.size());
|
||||
Assertions.assertTrue(containsCategory(rootOutlines, "cat1"));
|
||||
Assertions.assertTrue(containsFeed(rootOutlines, "rootFeed", "rootFeed.com"));
|
||||
|
||||
Outline cat1Outline = getCategoryOutline(rootOutlines, "cat1");
|
||||
List<Outline> cat1Children = cat1Outline.getChildren();
|
||||
Assertions.assertEquals(2, cat1Children.size());
|
||||
Assertions.assertTrue(containsCategory(cat1Children, "cat2"));
|
||||
Assertions.assertTrue(containsFeed(cat1Children, "cat1Feed", "cat1Feed.com"));
|
||||
Outline cat1Outline = getCategoryOutline(rootOutlines, "cat1");
|
||||
List<Outline> cat1Children = cat1Outline.getChildren();
|
||||
Assertions.assertEquals(2, cat1Children.size());
|
||||
Assertions.assertTrue(containsCategory(cat1Children, "cat2"));
|
||||
Assertions.assertTrue(containsFeed(cat1Children, "cat1Feed", "cat1Feed.com"));
|
||||
|
||||
Outline cat2Outline = getCategoryOutline(cat1Children, "cat2");
|
||||
List<Outline> cat2Children = cat2Outline.getChildren();
|
||||
Assertions.assertEquals(1, cat2Children.size());
|
||||
Assertions.assertTrue(containsFeed(cat2Children, "cat2Feed", "cat2Feed.com"));
|
||||
}
|
||||
Outline cat2Outline = getCategoryOutline(cat1Children, "cat2");
|
||||
List<Outline> cat2Children = cat2Outline.getChildren();
|
||||
Assertions.assertEquals(1, cat2Children.size());
|
||||
Assertions.assertTrue(containsFeed(cat2Children, "cat2Feed", "cat2Feed.com"));
|
||||
}
|
||||
|
||||
private boolean containsCategory(List<Outline> outlines, String category) {
|
||||
for (Outline o : outlines) {
|
||||
if (!"rss".equals(o.getType()) && category.equals(o.getTitle())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private boolean containsCategory(List<Outline> outlines, String category) {
|
||||
for (Outline o : outlines) {
|
||||
if (!"rss".equals(o.getType()) && category.equals(o.getTitle())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean containsFeed(List<Outline> outlines, String title, String url) {
|
||||
for (Outline o : outlines) {
|
||||
if ("rss".equals(o.getType()) && title.equals(o.getTitle()) && o.getAttributeValue("xmlUrl").equals(url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private boolean containsFeed(List<Outline> outlines, String title, String url) {
|
||||
for (Outline o : outlines) {
|
||||
if ("rss".equals(o.getType())
|
||||
&& title.equals(o.getTitle())
|
||||
&& o.getAttributeValue("xmlUrl").equals(url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Outline getCategoryOutline(List<Outline> outlines, String title) {
|
||||
for (Outline o : outlines) {
|
||||
if (o.getTitle().equals(title)) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
private Outline getCategoryOutline(List<Outline> outlines, String title) {
|
||||
for (Outline o : outlines) {
|
||||
if (o.getTitle().equals(title)) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,62 @@
|
||||
package com.commafeed.backend.opml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||
import com.commafeed.backend.feed.parser.XMLCleaner;
|
||||
import com.commafeed.backend.model.FeedCategory;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.service.FeedSubscriptionService;
|
||||
import com.rometools.rome.io.FeedException;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
class OPMLImporterTest {
|
||||
|
||||
@Test
|
||||
void testOpmlV10() throws IOException, IllegalArgumentException, FeedException {
|
||||
testOpmlVersion("/opml/opml_v1.0.xml");
|
||||
}
|
||||
@Test
|
||||
void testOpmlV10() throws IOException, IllegalArgumentException, FeedException {
|
||||
testOpmlVersion("/opml/opml_v1.0.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOpmlV11() throws IOException, IllegalArgumentException, FeedException {
|
||||
testOpmlVersion("/opml/opml_v1.1.xml");
|
||||
}
|
||||
@Test
|
||||
void testOpmlV11() throws IOException, IllegalArgumentException, FeedException {
|
||||
testOpmlVersion("/opml/opml_v1.1.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOpmlV20() throws IOException, IllegalArgumentException, FeedException {
|
||||
testOpmlVersion("/opml/opml_v2.0.xml");
|
||||
}
|
||||
@Test
|
||||
void testOpmlV20() throws IOException, IllegalArgumentException, FeedException {
|
||||
testOpmlVersion("/opml/opml_v2.0.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOpmlNoVersion() throws IOException, IllegalArgumentException, FeedException {
|
||||
testOpmlVersion("/opml/opml_noversion.xml");
|
||||
}
|
||||
@Test
|
||||
void testOpmlNoVersion() throws IOException, IllegalArgumentException, FeedException {
|
||||
testOpmlVersion("/opml/opml_noversion.xml");
|
||||
}
|
||||
|
||||
private void testOpmlVersion(String fileName) throws IOException, IllegalArgumentException, FeedException {
|
||||
XMLCleaner xmlCleaner = Mockito.mock(XMLCleaner.class);
|
||||
FeedCategoryDAO feedCategoryDAO = Mockito.mock(FeedCategoryDAO.class);
|
||||
FeedSubscriptionService feedSubscriptionService = Mockito.mock(FeedSubscriptionService.class);
|
||||
User user = Mockito.mock(User.class);
|
||||
private void testOpmlVersion(String fileName)
|
||||
throws IOException, IllegalArgumentException, FeedException {
|
||||
XMLCleaner xmlCleaner = Mockito.mock(XMLCleaner.class);
|
||||
FeedCategoryDAO feedCategoryDAO = Mockito.mock(FeedCategoryDAO.class);
|
||||
FeedSubscriptionService feedSubscriptionService =
|
||||
Mockito.mock(FeedSubscriptionService.class);
|
||||
User user = Mockito.mock(User.class);
|
||||
|
||||
Mockito.when(xmlCleaner.clean(Mockito.anyString())).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
String xml = IOUtils.toString(getClass().getResourceAsStream(fileName), StandardCharsets.UTF_8);
|
||||
Mockito.when(xmlCleaner.clean(Mockito.anyString()))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
String xml =
|
||||
IOUtils.toString(getClass().getResourceAsStream(fileName), StandardCharsets.UTF_8);
|
||||
|
||||
OPMLImporter importer = new OPMLImporter(xmlCleaner, feedCategoryDAO, feedSubscriptionService);
|
||||
importer.importOpml(user, xml);
|
||||
|
||||
Mockito.verify(feedSubscriptionService)
|
||||
.subscribe(Mockito.eq(user), Mockito.anyString(), Mockito.anyString(), Mockito.any(FeedCategory.class), Mockito.anyInt());
|
||||
}
|
||||
OPMLImporter importer =
|
||||
new OPMLImporter(xmlCleaner, feedCategoryDAO, feedSubscriptionService);
|
||||
importer.importOpml(user, xml);
|
||||
|
||||
Mockito.verify(feedSubscriptionService)
|
||||
.subscribe(
|
||||
Mockito.eq(user),
|
||||
Mockito.anyString(),
|
||||
Mockito.anyString(),
|
||||
Mockito.any(FeedCategory.class),
|
||||
Mockito.anyInt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,27 +5,31 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
class FeedEntryContentCleaningServiceTest {
|
||||
|
||||
private final FeedEntryContentCleaningService feedEntryContentCleaningService = new FeedEntryContentCleaningService();
|
||||
private final FeedEntryContentCleaningService feedEntryContentCleaningService =
|
||||
new FeedEntryContentCleaningService();
|
||||
|
||||
@Test
|
||||
void testClean() {
|
||||
String content = """
|
||||
@Test
|
||||
void testClean() {
|
||||
String content =
|
||||
"""
|
||||
<p>
|
||||
Some text
|
||||
<img width="965" height="320" src="https://localhost/an-image.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="alt-desc" decoding="async" sizes="(max-width: 965px) 100vw, 965px" style="width: 100%; opacity: 0">
|
||||
<iframe src="url" style="width: 100%; opacity: 0"></iframe>
|
||||
<forbidden-element>aaa</forbidden-element>
|
||||
""";
|
||||
String result = feedEntryContentCleaningService.clean(content, "baseUri", false);
|
||||
String result = feedEntryContentCleaningService.clean(content, "baseUri", false);
|
||||
|
||||
Assertions.assertLinesMatch("""
|
||||
Assertions.assertLinesMatch(
|
||||
"""
|
||||
<p>
|
||||
Some text
|
||||
<img width="965" height="320" src="https://localhost/an-image.png" alt="alt-desc" style="width:100%;">
|
||||
<iframe src="url" style="width:100%;"></iframe>
|
||||
aaa
|
||||
</p>
|
||||
""".lines(), result.lines());
|
||||
}
|
||||
|
||||
}
|
||||
"""
|
||||
.lines(),
|
||||
result.lines());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,267 +1,321 @@
|
||||
package com.commafeed.backend.service;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.service.FeedEntryFilteringService.FeedEntryFilterException;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.service.FeedEntryFilteringService.FeedEntryFilterException;
|
||||
|
||||
class FeedEntryFilteringServiceTest {
|
||||
private CommaFeedConfiguration config;
|
||||
private CommaFeedConfiguration config;
|
||||
|
||||
private FeedEntryFilteringService service;
|
||||
private FeedEntryFilteringService service;
|
||||
|
||||
private FeedEntry entry;
|
||||
private FeedEntry entry;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
config = Mockito.mock(CommaFeedConfiguration.class, Mockito.RETURNS_DEEP_STUBS);
|
||||
Mockito.when(config.feedRefresh().filteringExpressionEvaluationTimeout()).thenReturn(Duration.ofSeconds(30));
|
||||
@BeforeEach
|
||||
void init() {
|
||||
config = Mockito.mock(CommaFeedConfiguration.class, Mockito.RETURNS_DEEP_STUBS);
|
||||
Mockito.when(config.feedRefresh().filteringExpressionEvaluationTimeout())
|
||||
.thenReturn(Duration.ofSeconds(30));
|
||||
|
||||
service = new FeedEntryFilteringService(config);
|
||||
service = new FeedEntryFilteringService(config);
|
||||
|
||||
entry = new FeedEntry();
|
||||
entry.setUrl("https://github.com/Athou/commafeed");
|
||||
entry = new FeedEntry();
|
||||
entry.setUrl("https://github.com/Athou/commafeed");
|
||||
|
||||
FeedEntryContent content = new FeedEntryContent();
|
||||
content.setAuthor("Athou");
|
||||
content.setTitle("Merge pull request #662 from Athou/dw8");
|
||||
content.setContent("Merge pull request #662 from Athou/dw8");
|
||||
entry.setContent(content);
|
||||
FeedEntryContent content = new FeedEntryContent();
|
||||
content.setAuthor("Athou");
|
||||
content.setTitle("Merge pull request #662 from Athou/dw8");
|
||||
content.setContent("Merge pull request #662 from Athou/dw8");
|
||||
entry.setContent(content);
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
void emptyFilterMatchesFilter() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry(null, entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyFilterMatchesFilter() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry(null, entry));
|
||||
}
|
||||
@Test
|
||||
void blankFilterMatchesFilter() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void blankFilterMatchesFilter() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("", entry));
|
||||
}
|
||||
@Test
|
||||
void simpleEqualsExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("author == \"Athou\"", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void simpleEqualsExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("author == \"Athou\"", entry));
|
||||
}
|
||||
@Test
|
||||
void simpleNotEqualsExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("author != \"other\"", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void simpleNotEqualsExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("author != \"other\"", entry));
|
||||
}
|
||||
@Test
|
||||
void containsExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("author.contains(\"Athou\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("author.contains(\"Athou\")", entry));
|
||||
}
|
||||
@Test
|
||||
void titleContainsExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("title.contains(\"Merge\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void titleContainsExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("title.contains(\"Merge\")", entry));
|
||||
}
|
||||
@Test
|
||||
void urlContainsExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("url.contains(\"github\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void urlContainsExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("url.contains(\"github\")", entry));
|
||||
}
|
||||
@Test
|
||||
void andExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(
|
||||
service.filterMatchesEntry(
|
||||
"author == \"Athou\" && url.contains(\"github\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void andExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("author == \"Athou\" && url.contains(\"github\")", entry));
|
||||
}
|
||||
@Test
|
||||
void orExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(
|
||||
service.filterMatchesEntry(
|
||||
"author == \"other\" || url.contains(\"github\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void orExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("author == \"other\" || url.contains(\"github\")", entry));
|
||||
}
|
||||
@Test
|
||||
void notExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("!(author == \"other\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void notExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("!(author == \"other\")", entry));
|
||||
}
|
||||
@Test
|
||||
void incorrectExpressionThrowsException() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("not valid cel", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void incorrectExpressionThrowsException() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class, () -> service.filterMatchesEntry("not valid cel", entry));
|
||||
}
|
||||
@Test
|
||||
void falseValueReturnsFalse() throws FeedEntryFilterException {
|
||||
Assertions.assertFalse(service.filterMatchesEntry("false", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void falseValueReturnsFalse() throws FeedEntryFilterException {
|
||||
Assertions.assertFalse(service.filterMatchesEntry("false", entry));
|
||||
}
|
||||
@Test
|
||||
void trueValueReturnsTrue() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("true", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void trueValueReturnsTrue() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("true", entry));
|
||||
}
|
||||
@Test
|
||||
void startsWithExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("title.startsWith(\"Merge\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void startsWithExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("title.startsWith(\"Merge\")", entry));
|
||||
}
|
||||
@Test
|
||||
void endsWithExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("url.endsWith(\"commafeed\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsWithExpression() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("url.endsWith(\"commafeed\")", entry));
|
||||
}
|
||||
@Test
|
||||
void categoriesContainsExpression() throws FeedEntryFilterException {
|
||||
FeedEntryContent content = entry.getContent();
|
||||
content.setCategories("tech, programming, java");
|
||||
entry.setContent(content);
|
||||
Assertions.assertTrue(
|
||||
service.filterMatchesEntry("categories.contains(\"programming\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void categoriesContainsExpression() throws FeedEntryFilterException {
|
||||
FeedEntryContent content = entry.getContent();
|
||||
content.setCategories("tech, programming, java");
|
||||
entry.setContent(content);
|
||||
Assertions.assertTrue(service.filterMatchesEntry("categories.contains(\"programming\")", entry));
|
||||
}
|
||||
@Test
|
||||
void caseInsensitiveAuthorMatchUsingLowerVariable() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("authorLower == \"athou\"", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void caseInsensitiveAuthorMatchUsingLowerVariable() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("authorLower == \"athou\"", entry));
|
||||
}
|
||||
@Test
|
||||
void caseInsensitiveTitleMatchUsingLowerVariable() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("titleLower.contains(\"merge\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void caseInsensitiveTitleMatchUsingLowerVariable() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("titleLower.contains(\"merge\")", entry));
|
||||
}
|
||||
@Test
|
||||
void caseInsensitiveUrlMatchUsingLowerVariable() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("urlLower.contains(\"github\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void caseInsensitiveUrlMatchUsingLowerVariable() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("urlLower.contains(\"github\")", entry));
|
||||
}
|
||||
@Test
|
||||
void caseInsensitiveContentMatchUsingLowerVariable() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(
|
||||
service.filterMatchesEntry("contentLower.contains(\"merge\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void caseInsensitiveContentMatchUsingLowerVariable() throws FeedEntryFilterException {
|
||||
Assertions.assertTrue(service.filterMatchesEntry("contentLower.contains(\"merge\")", entry));
|
||||
}
|
||||
@Test
|
||||
void caseInsensitiveCategoriesMatchUsingLowerVariable() throws FeedEntryFilterException {
|
||||
FeedEntryContent content = entry.getContent();
|
||||
content.setCategories("Tech, Programming, Java");
|
||||
entry.setContent(content);
|
||||
Assertions.assertTrue(
|
||||
service.filterMatchesEntry("categoriesLower.contains(\"tech\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void caseInsensitiveCategoriesMatchUsingLowerVariable() throws FeedEntryFilterException {
|
||||
FeedEntryContent content = entry.getContent();
|
||||
content.setCategories("Tech, Programming, Java");
|
||||
entry.setContent(content);
|
||||
Assertions.assertTrue(service.filterMatchesEntry("categoriesLower.contains(\"tech\")", entry));
|
||||
}
|
||||
@Nested
|
||||
class Sandbox {
|
||||
|
||||
@Nested
|
||||
class Sandbox {
|
||||
@Test
|
||||
void sandboxBlocksSystemPropertyAccess() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() ->
|
||||
service.filterMatchesEntry(
|
||||
"java.lang.System.getProperty(\"user.home\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksSystemPropertyAccess() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("java.lang.System.getProperty(\"user.home\")", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksRuntimeExec() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() ->
|
||||
service.filterMatchesEntry(
|
||||
"java.lang.Runtime.getRuntime().exec(\"calc\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksRuntimeExec() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("java.lang.Runtime.getRuntime().exec(\"calc\")", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksProcessBuilder() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() ->
|
||||
service.filterMatchesEntry(
|
||||
"new java.lang.ProcessBuilder(\"cmd\").start()", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksProcessBuilder() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("new java.lang.ProcessBuilder(\"cmd\").start()", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksClassLoading() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() ->
|
||||
service.filterMatchesEntry(
|
||||
"java.lang.Class.forName(\"java.lang.Runtime\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksClassLoading() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("java.lang.Class.forName(\"java.lang.Runtime\")", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksReflection() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("title.getClass().getMethods()", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksReflection() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("title.getClass().getMethods()", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksFileAccess() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() ->
|
||||
service.filterMatchesEntry(
|
||||
"new java.io.File(\"/etc/passwd\").exists()", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksFileAccess() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("new java.io.File(\"/etc/passwd\").exists()", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksFileRead() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() ->
|
||||
service.filterMatchesEntry(
|
||||
"java.nio.file.Files.readString(java.nio.file.Paths.get(\"/etc/passwd\"))",
|
||||
entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksFileRead() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("java.nio.file.Files.readString(java.nio.file.Paths.get(\"/etc/passwd\"))", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksNetworkAccess() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() ->
|
||||
service.filterMatchesEntry(
|
||||
"new java.net.URL(\"http://evil.com\").openConnection()",
|
||||
entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksNetworkAccess() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("new java.net.URL(\"http://evil.com\").openConnection()", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksScriptEngine() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() ->
|
||||
service.filterMatchesEntry(
|
||||
"new javax.script.ScriptEngineManager().getEngineByName(\"js\").eval(\"1+1\")",
|
||||
entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksScriptEngine() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class, () -> service
|
||||
.filterMatchesEntry("new javax.script.ScriptEngineManager().getEngineByName(\"js\").eval(\"1+1\")", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksThreadCreation() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("new java.lang.Thread().start()", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksThreadCreation() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("new java.lang.Thread().start()", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksEnvironmentVariableAccess() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("java.lang.System.getenv(\"PATH\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksEnvironmentVariableAccess() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("java.lang.System.getenv(\"PATH\")", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksExitCall() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("java.lang.System.exit(0)", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksExitCall() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class, () -> service.filterMatchesEntry("java.lang.System.exit(0)", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksUndeclaredVariables() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("unknownVariable == \"test\"", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksUndeclaredVariables() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class, () -> service.filterMatchesEntry("unknownVariable == \"test\"", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksMethodInvocationOnStrings() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("title.toCharArray()", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksMethodInvocationOnStrings() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class, () -> service.filterMatchesEntry("title.toCharArray()", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksArbitraryJavaMethodCalls() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("title.getBytes()", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksArbitraryJavaMethodCalls() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class, () -> service.filterMatchesEntry("title.getBytes()", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxOnlyAllowsDeclaredVariables() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("System", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxOnlyAllowsDeclaredVariables() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class, () -> service.filterMatchesEntry("System", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksConstructorCalls() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("new String(\"test\")", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksConstructorCalls() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class, () -> service.filterMatchesEntry("new String(\"test\")", entry));
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksStaticMethodCalls() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("String.valueOf(123)", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksStaticMethodCalls() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class, () -> service.filterMatchesEntry("String.valueOf(123)", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksLambdaExpressions() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class, () -> service.filterMatchesEntry("() -> true", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksObjectInstantiation() {
|
||||
Assertions.assertThrows(FeedEntryFilterException.class, () -> service.filterMatchesEntry("java.util.HashMap{}", entry));
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void sandboxBlocksLambdaExpressions() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("() -> true", entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sandboxBlocksObjectInstantiation() {
|
||||
Assertions.assertThrows(
|
||||
FeedEntryFilterException.class,
|
||||
() -> service.filterMatchesEntry("java.util.HashMap{}", entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package com.commafeed.backend.service;
|
||||
|
||||
import com.commafeed.backend.favicon.Favicon;
|
||||
import com.commafeed.backend.favicon.FaviconFetcher;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -13,139 +14,133 @@ import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.commafeed.backend.favicon.Favicon;
|
||||
import com.commafeed.backend.favicon.FaviconFetcher;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class FeedFaviconServiceTest {
|
||||
|
||||
@Mock
|
||||
private FaviconFetcher fetcher1;
|
||||
@Mock private FaviconFetcher fetcher1;
|
||||
|
||||
@Mock
|
||||
private FaviconFetcher fetcher2;
|
||||
@Mock private FaviconFetcher fetcher2;
|
||||
|
||||
private FeedFaviconService service;
|
||||
private Feed feed;
|
||||
private FeedFaviconService service;
|
||||
private Feed feed;
|
||||
|
||||
@BeforeEach
|
||||
void init() throws IOException {
|
||||
service = new FeedFaviconService(List.of(fetcher1, fetcher2));
|
||||
feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
}
|
||||
@BeforeEach
|
||||
void init() throws IOException {
|
||||
service = new FeedFaviconService(List.of(fetcher1, fetcher2));
|
||||
feed = new Feed();
|
||||
feed.setUrl("https://example.com/feed");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReturnsFirstValidFavicon() {
|
||||
byte[] iconBytes = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(iconBytes, "image/png");
|
||||
@Test
|
||||
void testReturnsFirstValidFavicon() {
|
||||
byte[] iconBytes = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(iconBytes, "image/png");
|
||||
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(validFavicon);
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(validFavicon);
|
||||
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
Mockito.verify(fetcher1).fetch(feed);
|
||||
Mockito.verifyNoInteractions(fetcher2);
|
||||
}
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
Mockito.verify(fetcher1).fetch(feed);
|
||||
Mockito.verifyNoInteractions(fetcher2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFallsBackToNextFetcherWhenFirstReturnsNull() {
|
||||
byte[] iconBytes = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(iconBytes, "image/png");
|
||||
@Test
|
||||
void testFallsBackToNextFetcherWhenFirstReturnsNull() {
|
||||
byte[] iconBytes = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(iconBytes, "image/png");
|
||||
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(null);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(validFavicon);
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(null);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(validFavicon);
|
||||
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
}
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFallsBackToNextFetcherWhenFirstReturnsTooSmallIcon() {
|
||||
byte[] tinyIcon = new byte[50];
|
||||
Favicon tinyFavicon = new Favicon(tinyIcon, "image/png");
|
||||
@Test
|
||||
void testFallsBackToNextFetcherWhenFirstReturnsTooSmallIcon() {
|
||||
byte[] tinyIcon = new byte[50];
|
||||
Favicon tinyFavicon = new Favicon(tinyIcon, "image/png");
|
||||
|
||||
byte[] validIcon = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(validIcon, "image/png");
|
||||
byte[] validIcon = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(validIcon, "image/png");
|
||||
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(tinyFavicon);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(validFavicon);
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(tinyFavicon);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(validFavicon);
|
||||
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
}
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFallsBackToNextFetcherWhenFirstReturnsTooLargeIcon() {
|
||||
byte[] hugeIcon = new byte[100001];
|
||||
Favicon hugeFavicon = new Favicon(hugeIcon, "image/png");
|
||||
@Test
|
||||
void testFallsBackToNextFetcherWhenFirstReturnsTooLargeIcon() {
|
||||
byte[] hugeIcon = new byte[100001];
|
||||
Favicon hugeFavicon = new Favicon(hugeIcon, "image/png");
|
||||
|
||||
byte[] validIcon = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(validIcon, "image/png");
|
||||
byte[] validIcon = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(validIcon, "image/png");
|
||||
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(hugeFavicon);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(validFavicon);
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(hugeFavicon);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(validFavicon);
|
||||
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
}
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFallsBackToNextFetcherWhenFirstReturnsBlacklistedContentType() {
|
||||
byte[] iconBytes = new byte[1000];
|
||||
Favicon xmlFavicon = new Favicon(iconBytes, "application/xml");
|
||||
@Test
|
||||
void testFallsBackToNextFetcherWhenFirstReturnsBlacklistedContentType() {
|
||||
byte[] iconBytes = new byte[1000];
|
||||
Favicon xmlFavicon = new Favicon(iconBytes, "application/xml");
|
||||
|
||||
byte[] validIcon = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(validIcon, "image/png");
|
||||
byte[] validIcon = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(validIcon, "image/png");
|
||||
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(xmlFavicon);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(validFavicon);
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(xmlFavicon);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(validFavicon);
|
||||
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
}
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFallsBackToNextFetcherWhenFirstReturnsHtmlContentType() {
|
||||
byte[] iconBytes = new byte[1000];
|
||||
Favicon htmlFavicon = new Favicon(iconBytes, "text/html");
|
||||
@Test
|
||||
void testFallsBackToNextFetcherWhenFirstReturnsHtmlContentType() {
|
||||
byte[] iconBytes = new byte[1000];
|
||||
Favicon htmlFavicon = new Favicon(iconBytes, "text/html");
|
||||
|
||||
byte[] validIcon = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(validIcon, "image/png");
|
||||
byte[] validIcon = new byte[1000];
|
||||
Favicon validFavicon = new Favicon(validIcon, "image/png");
|
||||
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(htmlFavicon);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(validFavicon);
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(htmlFavicon);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(validFavicon);
|
||||
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
}
|
||||
Assertions.assertEquals(validFavicon, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReturnsDefaultFaviconWhenAllFetchersFail() {
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(null);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(null);
|
||||
@Test
|
||||
void testReturnsDefaultFaviconWhenAllFetchersFail() {
|
||||
Mockito.when(fetcher1.fetch(feed)).thenReturn(null);
|
||||
Mockito.when(fetcher2.fetch(feed)).thenReturn(null);
|
||||
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
Favicon result = service.fetchFavicon(feed);
|
||||
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf("image/gif")));
|
||||
Assertions.assertTrue(result.icon().length > 0);
|
||||
}
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf("image/gif")));
|
||||
Assertions.assertTrue(result.icon().length > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReturnsDefaultFaviconWhenNoFetchersRegistered() throws IOException {
|
||||
FeedFaviconService emptyService = new FeedFaviconService(List.of());
|
||||
@Test
|
||||
void testReturnsDefaultFaviconWhenNoFetchersRegistered() throws IOException {
|
||||
FeedFaviconService emptyService = new FeedFaviconService(List.of());
|
||||
|
||||
Favicon result = emptyService.fetchFavicon(feed);
|
||||
Favicon result = emptyService.fetchFavicon(feed);
|
||||
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf("image/gif")));
|
||||
}
|
||||
Assertions.assertNotNull(result);
|
||||
Assertions.assertTrue(result.mediaType().isCompatible(MediaType.valueOf("image/gif")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
package com.commafeed.backend.service;
|
||||
|
||||
import java.util.HexFormat;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class PasswordEncryptionServiceTest {
|
||||
|
||||
@Test
|
||||
void authenticate() {
|
||||
String password = "password";
|
||||
byte[] salt = "abcdefgh".getBytes();
|
||||
@Test
|
||||
void authenticate() {
|
||||
String password = "password";
|
||||
byte[] salt = "abcdefgh".getBytes();
|
||||
|
||||
PasswordEncryptionService passwordEncryptionService = new PasswordEncryptionService();
|
||||
byte[] encryptedPassword = passwordEncryptionService.getEncryptedPassword(password, salt);
|
||||
PasswordEncryptionService passwordEncryptionService = new PasswordEncryptionService();
|
||||
byte[] encryptedPassword = passwordEncryptionService.getEncryptedPassword(password, salt);
|
||||
|
||||
// make sure the encrypted password is always the same for a fixed salt
|
||||
Assertions.assertEquals("8b4660158141d9f4f7865718b9a2b940a3e3cea9", HexFormat.of().formatHex(encryptedPassword));
|
||||
Assertions.assertTrue(passwordEncryptionService.authenticate(password, encryptedPassword, salt));
|
||||
}
|
||||
|
||||
}
|
||||
// make sure the encrypted password is always the same for a fixed salt
|
||||
Assertions.assertEquals(
|
||||
"8b4660158141d9f4f7865718b9a2b940a3e3cea9",
|
||||
HexFormat.of().formatHex(encryptedPassword));
|
||||
Assertions.assertTrue(
|
||||
passwordEncryptionService.authenticate(password, encryptedPassword, salt));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
package com.commafeed.backend.service;
|
||||
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.HttpClientFactory;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.UserSettings.PushNotificationType;
|
||||
import com.commafeed.backend.model.UserSettings.PushNotificationUserSettings;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -14,79 +23,79 @@ import org.mockserver.model.HttpResponse;
|
||||
import org.mockserver.model.JsonBody;
|
||||
import org.mockserver.model.MediaType;
|
||||
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.HttpClientFactory;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import com.commafeed.backend.model.FeedEntry;
|
||||
import com.commafeed.backend.model.FeedEntryContent;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
import com.commafeed.backend.model.UserSettings.PushNotificationType;
|
||||
import com.commafeed.backend.model.UserSettings.PushNotificationUserSettings;
|
||||
|
||||
@ExtendWith(MockServerExtension.class)
|
||||
class PushNotificationServiceTest {
|
||||
|
||||
private MockServerClient mockServerClient;
|
||||
private PushNotificationService pushNotificationService;
|
||||
private CommaFeedConfiguration config;
|
||||
private PushNotificationUserSettings userSettings;
|
||||
private FeedSubscription subscription;
|
||||
private FeedEntry entry;
|
||||
private MockServerClient mockServerClient;
|
||||
private PushNotificationService pushNotificationService;
|
||||
private CommaFeedConfiguration config;
|
||||
private PushNotificationUserSettings userSettings;
|
||||
private FeedSubscription subscription;
|
||||
private FeedEntry entry;
|
||||
|
||||
@BeforeEach
|
||||
void init(MockServerClient mockServerClient) {
|
||||
this.mockServerClient = mockServerClient;
|
||||
this.mockServerClient.reset();
|
||||
@BeforeEach
|
||||
void init(MockServerClient mockServerClient) {
|
||||
this.mockServerClient = mockServerClient;
|
||||
this.mockServerClient.reset();
|
||||
|
||||
this.config = Mockito.mock(CommaFeedConfiguration.class, Mockito.RETURNS_DEEP_STUBS);
|
||||
Mockito.when(config.pushNotifications().enabled()).thenReturn(true);
|
||||
Mockito.when(config.pushNotifications().threads()).thenReturn(1);
|
||||
Mockito.when(config.httpClient().responseTimeout()).thenReturn(Duration.ofSeconds(30));
|
||||
this.config = Mockito.mock(CommaFeedConfiguration.class, Mockito.RETURNS_DEEP_STUBS);
|
||||
Mockito.when(config.pushNotifications().enabled()).thenReturn(true);
|
||||
Mockito.when(config.pushNotifications().threads()).thenReturn(1);
|
||||
Mockito.when(config.httpClient().responseTimeout()).thenReturn(Duration.ofSeconds(30));
|
||||
|
||||
HttpClientFactory httpClientFactory = new HttpClientFactory(config, Mockito.mock(com.commafeed.CommaFeedVersion.class));
|
||||
MetricRegistry metricRegistry = Mockito.mock(MetricRegistry.class);
|
||||
Mockito.when(metricRegistry.meter(Mockito.anyString())).thenReturn(Mockito.mock(Meter.class));
|
||||
HttpClientFactory httpClientFactory =
|
||||
new HttpClientFactory(config, Mockito.mock(com.commafeed.CommaFeedVersion.class));
|
||||
MetricRegistry metricRegistry = Mockito.mock(MetricRegistry.class);
|
||||
Mockito.when(metricRegistry.meter(Mockito.anyString()))
|
||||
.thenReturn(Mockito.mock(Meter.class));
|
||||
|
||||
this.pushNotificationService = new PushNotificationService(httpClientFactory, metricRegistry, config);
|
||||
this.pushNotificationService =
|
||||
new PushNotificationService(httpClientFactory, metricRegistry, config);
|
||||
|
||||
this.userSettings = new PushNotificationUserSettings();
|
||||
this.userSettings = new PushNotificationUserSettings();
|
||||
|
||||
this.subscription = createSubscription("Test Feed");
|
||||
this.entry = createEntry("Test Entry", "http://example.com/entry");
|
||||
}
|
||||
this.subscription = createSubscription("Test Feed");
|
||||
this.entry = createEntry("Test Entry", "http://example.com/entry");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNtfyNotification() {
|
||||
userSettings.setType(PushNotificationType.NTFY);
|
||||
userSettings.setServerUrl("http://localhost:" + mockServerClient.getPort());
|
||||
userSettings.setTopic("test-topic");
|
||||
userSettings.setUserSecret("test-secret");
|
||||
@Test
|
||||
void testNtfyNotification() {
|
||||
userSettings.setType(PushNotificationType.NTFY);
|
||||
userSettings.setServerUrl("http://localhost:" + mockServerClient.getPort());
|
||||
userSettings.setTopic("test-topic");
|
||||
userSettings.setUserSecret("test-secret");
|
||||
|
||||
mockServerClient.when(HttpRequest.request()
|
||||
.withMethod("POST")
|
||||
.withPath("/test-topic")
|
||||
.withHeader("Title", "Test Feed")
|
||||
.withHeader("Click", "http://example.com/entry")
|
||||
.withHeader("Authorization", "Bearer test-secret")
|
||||
.withBody("Test Entry")).respond(HttpResponse.response().withStatusCode(200));
|
||||
mockServerClient
|
||||
.when(
|
||||
HttpRequest.request()
|
||||
.withMethod("POST")
|
||||
.withPath("/test-topic")
|
||||
.withHeader("Title", "Test Feed")
|
||||
.withHeader("Click", "http://example.com/entry")
|
||||
.withHeader("Authorization", "Bearer test-secret")
|
||||
.withBody("Test Entry"))
|
||||
.respond(HttpResponse.response().withStatusCode(200));
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> pushNotificationService.notify(userSettings, subscription, entry));
|
||||
}
|
||||
Assertions.assertDoesNotThrow(
|
||||
() -> pushNotificationService.notify(userSettings, subscription, entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGotifyNotification() {
|
||||
userSettings.setType(PushNotificationType.GOTIFY);
|
||||
userSettings.setServerUrl("http://localhost:" + mockServerClient.getPort());
|
||||
userSettings.setUserSecret("gotify-token");
|
||||
@Test
|
||||
void testGotifyNotification() {
|
||||
userSettings.setType(PushNotificationType.GOTIFY);
|
||||
userSettings.setServerUrl("http://localhost:" + mockServerClient.getPort());
|
||||
userSettings.setUserSecret("gotify-token");
|
||||
|
||||
mockServerClient.when(HttpRequest.request()
|
||||
.withMethod("POST")
|
||||
.withPath("/message")
|
||||
.withHeader("X-Gotify-Key", "gotify-token")
|
||||
.withContentType(MediaType.APPLICATION_JSON_UTF_8)
|
||||
.withBody(JsonBody.json("""
|
||||
mockServerClient
|
||||
.when(
|
||||
HttpRequest.request()
|
||||
.withMethod("POST")
|
||||
.withPath("/message")
|
||||
.withHeader("X-Gotify-Key", "gotify-token")
|
||||
.withContentType(MediaType.APPLICATION_JSON_UTF_8)
|
||||
.withBody(
|
||||
JsonBody.json(
|
||||
"""
|
||||
{
|
||||
"title": "Test Feed",
|
||||
"message": "Test Entry",
|
||||
@@ -99,38 +108,41 @@ class PushNotificationServiceTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
"""))).respond(HttpResponse.response().withStatusCode(200));
|
||||
""")))
|
||||
.respond(HttpResponse.response().withStatusCode(200));
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> pushNotificationService.notify(userSettings, subscription, entry));
|
||||
}
|
||||
Assertions.assertDoesNotThrow(
|
||||
() -> pushNotificationService.notify(userSettings, subscription, entry));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPushNotificationDisabled() {
|
||||
Mockito.when(config.pushNotifications().enabled()).thenReturn(false);
|
||||
userSettings.setType(PushNotificationType.NTFY);
|
||||
userSettings.setServerUrl("http://localhost:" + mockServerClient.getPort());
|
||||
userSettings.setTopic("test-topic");
|
||||
@Test
|
||||
void testPushNotificationDisabled() {
|
||||
Mockito.when(config.pushNotifications().enabled()).thenReturn(false);
|
||||
userSettings.setType(PushNotificationType.NTFY);
|
||||
userSettings.setServerUrl("http://localhost:" + mockServerClient.getPort());
|
||||
userSettings.setTopic("test-topic");
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> pushNotificationService.notify(userSettings, subscription, entry));
|
||||
mockServerClient.verifyZeroInteractions();
|
||||
}
|
||||
Assertions.assertDoesNotThrow(
|
||||
() -> pushNotificationService.notify(userSettings, subscription, entry));
|
||||
mockServerClient.verifyZeroInteractions();
|
||||
}
|
||||
|
||||
private static FeedSubscription createSubscription(String title) {
|
||||
FeedSubscription subscription = new FeedSubscription();
|
||||
subscription.setTitle(title);
|
||||
subscription.setFeed(new Feed());
|
||||
private static FeedSubscription createSubscription(String title) {
|
||||
FeedSubscription subscription = new FeedSubscription();
|
||||
subscription.setTitle(title);
|
||||
subscription.setFeed(new Feed());
|
||||
|
||||
return subscription;
|
||||
}
|
||||
return subscription;
|
||||
}
|
||||
|
||||
private static FeedEntry createEntry(String title, String url) {
|
||||
FeedEntry entry = new FeedEntry();
|
||||
private static FeedEntry createEntry(String title, String url) {
|
||||
FeedEntry entry = new FeedEntry();
|
||||
|
||||
FeedEntryContent content = new FeedEntryContent();
|
||||
content.setTitle(title);
|
||||
FeedEntryContent content = new FeedEntryContent();
|
||||
content.setTitle(title);
|
||||
|
||||
entry.setContent(content);
|
||||
entry.setUrl(url);
|
||||
return entry;
|
||||
}
|
||||
entry.setContent(content);
|
||||
entry.setUrl(url);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
package com.commafeed.backend.service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||
@@ -18,177 +8,201 @@ import com.commafeed.backend.dao.UserRoleDAO;
|
||||
import com.commafeed.backend.dao.UserSettingsDAO;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.service.internal.PostLoginActivities;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class UserServiceTest {
|
||||
|
||||
private static final byte[] SALT = new byte[] { 1, 2, 3 };
|
||||
private static final byte[] ENCRYPTED_PASSWORD = new byte[] { 5, 6, 7 };
|
||||
private static final byte[] SALT = new byte[] {1, 2, 3};
|
||||
private static final byte[] ENCRYPTED_PASSWORD = new byte[] {5, 6, 7};
|
||||
|
||||
@Mock
|
||||
private CommaFeedConfiguration commaFeedConfiguration;
|
||||
@Mock
|
||||
private FeedCategoryDAO feedCategoryDAO;
|
||||
@Mock
|
||||
private FeedSubscriptionDAO feedSubscriptionDAO;
|
||||
@Mock
|
||||
private UserDAO userDAO;
|
||||
@Mock
|
||||
private UserSettingsDAO userSettingsDAO;
|
||||
@Mock
|
||||
private UserRoleDAO userRoleDAO;
|
||||
@Mock
|
||||
private PasswordEncryptionService passwordEncryptionService;
|
||||
@Mock
|
||||
private PostLoginActivities postLoginActivities;
|
||||
@Mock private CommaFeedConfiguration commaFeedConfiguration;
|
||||
@Mock private FeedCategoryDAO feedCategoryDAO;
|
||||
@Mock private FeedSubscriptionDAO feedSubscriptionDAO;
|
||||
@Mock private UserDAO userDAO;
|
||||
@Mock private UserSettingsDAO userSettingsDAO;
|
||||
@Mock private UserRoleDAO userRoleDAO;
|
||||
@Mock private PasswordEncryptionService passwordEncryptionService;
|
||||
@Mock private PostLoginActivities postLoginActivities;
|
||||
|
||||
private User disabledUser;
|
||||
private User normalUser;
|
||||
private User disabledUser;
|
||||
private User normalUser;
|
||||
|
||||
private UserService userService;
|
||||
private UserService userService;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
userService = new UserService(feedCategoryDAO, feedSubscriptionDAO, userDAO, userRoleDAO, userSettingsDAO,
|
||||
passwordEncryptionService, commaFeedConfiguration, postLoginActivities);
|
||||
@BeforeEach
|
||||
void init() {
|
||||
userService =
|
||||
new UserService(
|
||||
feedCategoryDAO,
|
||||
feedSubscriptionDAO,
|
||||
userDAO,
|
||||
userRoleDAO,
|
||||
userSettingsDAO,
|
||||
passwordEncryptionService,
|
||||
commaFeedConfiguration,
|
||||
postLoginActivities);
|
||||
|
||||
disabledUser = new User();
|
||||
disabledUser.setDisabled(true);
|
||||
disabledUser = new User();
|
||||
disabledUser.setDisabled(true);
|
||||
|
||||
normalUser = new User();
|
||||
normalUser.setDisabled(false);
|
||||
normalUser.setSalt(SALT);
|
||||
normalUser.setPassword(ENCRYPTED_PASSWORD);
|
||||
}
|
||||
normalUser = new User();
|
||||
normalUser.setDisabled(false);
|
||||
normalUser.setSalt(SALT);
|
||||
normalUser.setPassword(ENCRYPTED_PASSWORD);
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingLoginShouldNotReturnUserObjectWhenGivenNullNameOrEmail() {
|
||||
Optional<User> user = userService.login(null, "password");
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
@Test
|
||||
void callingLoginShouldNotReturnUserObjectWhenGivenNullNameOrEmail() {
|
||||
Optional<User> user = userService.login(null, "password");
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingLoginShouldNotReturnUserObjectWhenGivenNullPassword() {
|
||||
Optional<User> user = userService.login("testusername", null);
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
@Test
|
||||
void callingLoginShouldNotReturnUserObjectWhenGivenNullPassword() {
|
||||
Optional<User> user = userService.login("testusername", null);
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingLoginShouldLookupUserByName() {
|
||||
userService.login("test", "password");
|
||||
Mockito.verify(userDAO).findByName("test");
|
||||
}
|
||||
@Test
|
||||
void callingLoginShouldLookupUserByName() {
|
||||
userService.login("test", "password");
|
||||
Mockito.verify(userDAO).findByName("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingLoginShouldLookupUserByEmailIfLookupByNameFailed() {
|
||||
Mockito.when(userDAO.findByName("test@test.com")).thenReturn(null);
|
||||
userService.login("test@test.com", "password");
|
||||
Mockito.verify(userDAO).findByEmail("test@test.com");
|
||||
}
|
||||
@Test
|
||||
void callingLoginShouldLookupUserByEmailIfLookupByNameFailed() {
|
||||
Mockito.when(userDAO.findByName("test@test.com")).thenReturn(null);
|
||||
userService.login("test@test.com", "password");
|
||||
Mockito.verify(userDAO).findByEmail("test@test.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingLoginShouldNotReturnUserObjectIfCouldNotFindUserByNameOrEmail() {
|
||||
Mockito.when(userDAO.findByName("test@test.com")).thenReturn(null);
|
||||
Mockito.when(userDAO.findByEmail("test@test.com")).thenReturn(null);
|
||||
@Test
|
||||
void callingLoginShouldNotReturnUserObjectIfCouldNotFindUserByNameOrEmail() {
|
||||
Mockito.when(userDAO.findByName("test@test.com")).thenReturn(null);
|
||||
Mockito.when(userDAO.findByEmail("test@test.com")).thenReturn(null);
|
||||
|
||||
Optional<User> user = userService.login("test@test.com", "password");
|
||||
Optional<User> user = userService.login("test@test.com", "password");
|
||||
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingLoginShouldNotReturnUserObjectIfUserIsDisabled() {
|
||||
Mockito.when(userDAO.findByName("test")).thenReturn(disabledUser);
|
||||
Optional<User> user = userService.login("test", "password");
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
@Test
|
||||
void callingLoginShouldNotReturnUserObjectIfUserIsDisabled() {
|
||||
Mockito.when(userDAO.findByName("test")).thenReturn(disabledUser);
|
||||
Optional<User> user = userService.login("test", "password");
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingLoginShouldTryToAuthenticateUserWhoIsNotDisabled() {
|
||||
Mockito.when(userDAO.findByName("test")).thenReturn(normalUser);
|
||||
Mockito.when(passwordEncryptionService.authenticate(Mockito.anyString(), Mockito.any(byte[].class), Mockito.any(byte[].class)))
|
||||
.thenReturn(false);
|
||||
@Test
|
||||
void callingLoginShouldTryToAuthenticateUserWhoIsNotDisabled() {
|
||||
Mockito.when(userDAO.findByName("test")).thenReturn(normalUser);
|
||||
Mockito.when(
|
||||
passwordEncryptionService.authenticate(
|
||||
Mockito.anyString(),
|
||||
Mockito.any(byte[].class),
|
||||
Mockito.any(byte[].class)))
|
||||
.thenReturn(false);
|
||||
|
||||
userService.login("test", "password");
|
||||
userService.login("test", "password");
|
||||
|
||||
Mockito.verify(passwordEncryptionService).authenticate("password", ENCRYPTED_PASSWORD, SALT);
|
||||
}
|
||||
Mockito.verify(passwordEncryptionService)
|
||||
.authenticate("password", ENCRYPTED_PASSWORD, SALT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingLoginShouldNotReturnUserObjectOnUnsuccessfulAuthentication() {
|
||||
Mockito.when(userDAO.findByName("test")).thenReturn(normalUser);
|
||||
Mockito.when(passwordEncryptionService.authenticate(Mockito.anyString(), Mockito.any(byte[].class), Mockito.any(byte[].class)))
|
||||
.thenReturn(false);
|
||||
@Test
|
||||
void callingLoginShouldNotReturnUserObjectOnUnsuccessfulAuthentication() {
|
||||
Mockito.when(userDAO.findByName("test")).thenReturn(normalUser);
|
||||
Mockito.when(
|
||||
passwordEncryptionService.authenticate(
|
||||
Mockito.anyString(),
|
||||
Mockito.any(byte[].class),
|
||||
Mockito.any(byte[].class)))
|
||||
.thenReturn(false);
|
||||
|
||||
Optional<User> authenticatedUser = userService.login("test", "password");
|
||||
Optional<User> authenticatedUser = userService.login("test", "password");
|
||||
|
||||
Assertions.assertFalse(authenticatedUser.isPresent());
|
||||
}
|
||||
Assertions.assertFalse(authenticatedUser.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingLoginShouldExecutePostLoginActivitiesForUserOnSuccessfulAuthentication() {
|
||||
Mockito.when(userDAO.findByName("test")).thenReturn(normalUser);
|
||||
Mockito.when(passwordEncryptionService.authenticate(Mockito.anyString(), Mockito.any(byte[].class), Mockito.any(byte[].class)))
|
||||
.thenReturn(true);
|
||||
Mockito.doNothing().when(postLoginActivities).executeFor(Mockito.any(User.class));
|
||||
@Test
|
||||
void callingLoginShouldExecutePostLoginActivitiesForUserOnSuccessfulAuthentication() {
|
||||
Mockito.when(userDAO.findByName("test")).thenReturn(normalUser);
|
||||
Mockito.when(
|
||||
passwordEncryptionService.authenticate(
|
||||
Mockito.anyString(),
|
||||
Mockito.any(byte[].class),
|
||||
Mockito.any(byte[].class)))
|
||||
.thenReturn(true);
|
||||
Mockito.doNothing().when(postLoginActivities).executeFor(Mockito.any(User.class));
|
||||
|
||||
userService.login("test", "password");
|
||||
userService.login("test", "password");
|
||||
|
||||
Mockito.verify(postLoginActivities).executeFor(normalUser);
|
||||
}
|
||||
Mockito.verify(postLoginActivities).executeFor(normalUser);
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingLoginShouldReturnUserObjectOnSuccessfulAuthentication() {
|
||||
Mockito.when(userDAO.findByName("test")).thenReturn(normalUser);
|
||||
Mockito.when(passwordEncryptionService.authenticate(Mockito.anyString(), Mockito.any(byte[].class), Mockito.any(byte[].class)))
|
||||
.thenReturn(true);
|
||||
Mockito.doNothing().when(postLoginActivities).executeFor(Mockito.any(User.class));
|
||||
@Test
|
||||
void callingLoginShouldReturnUserObjectOnSuccessfulAuthentication() {
|
||||
Mockito.when(userDAO.findByName("test")).thenReturn(normalUser);
|
||||
Mockito.when(
|
||||
passwordEncryptionService.authenticate(
|
||||
Mockito.anyString(),
|
||||
Mockito.any(byte[].class),
|
||||
Mockito.any(byte[].class)))
|
||||
.thenReturn(true);
|
||||
Mockito.doNothing().when(postLoginActivities).executeFor(Mockito.any(User.class));
|
||||
|
||||
Optional<User> authenticatedUser = userService.login("test", "password");
|
||||
Optional<User> authenticatedUser = userService.login("test", "password");
|
||||
|
||||
Assertions.assertTrue(authenticatedUser.isPresent());
|
||||
Assertions.assertEquals(normalUser, authenticatedUser.get());
|
||||
}
|
||||
Assertions.assertTrue(authenticatedUser.isPresent());
|
||||
Assertions.assertEquals(normalUser, authenticatedUser.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void apiLoginShouldNotReturnUserIfApikeyNull() {
|
||||
Optional<User> user = userService.login(null);
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
@Test
|
||||
void apiLoginShouldNotReturnUserIfApikeyNull() {
|
||||
Optional<User> user = userService.login(null);
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void apiLoginShouldLookupUserByApikey() {
|
||||
Mockito.when(userDAO.findByApiKey("apikey")).thenReturn(null);
|
||||
userService.login("apikey");
|
||||
Mockito.verify(userDAO).findByApiKey("apikey");
|
||||
}
|
||||
@Test
|
||||
void apiLoginShouldLookupUserByApikey() {
|
||||
Mockito.when(userDAO.findByApiKey("apikey")).thenReturn(null);
|
||||
userService.login("apikey");
|
||||
Mockito.verify(userDAO).findByApiKey("apikey");
|
||||
}
|
||||
|
||||
@Test
|
||||
void apiLoginShouldNotReturnUserIfUserNotFoundFromLookupByApikey() {
|
||||
Mockito.when(userDAO.findByApiKey("apikey")).thenReturn(null);
|
||||
Optional<User> user = userService.login("apikey");
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
@Test
|
||||
void apiLoginShouldNotReturnUserIfUserNotFoundFromLookupByApikey() {
|
||||
Mockito.when(userDAO.findByApiKey("apikey")).thenReturn(null);
|
||||
Optional<User> user = userService.login("apikey");
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void apiLoginShouldNotReturnUserIfUserFoundFromApikeyLookupIsDisabled() {
|
||||
Mockito.when(userDAO.findByApiKey("apikey")).thenReturn(disabledUser);
|
||||
Optional<User> user = userService.login("apikey");
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
@Test
|
||||
void apiLoginShouldNotReturnUserIfUserFoundFromApikeyLookupIsDisabled() {
|
||||
Mockito.when(userDAO.findByApiKey("apikey")).thenReturn(disabledUser);
|
||||
Optional<User> user = userService.login("apikey");
|
||||
Assertions.assertFalse(user.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void apiLoginShouldPerformPostLoginActivitiesIfUserFoundFromApikeyLookupNotDisabled() {
|
||||
Mockito.when(userDAO.findByApiKey("apikey")).thenReturn(normalUser);
|
||||
userService.login("apikey");
|
||||
Mockito.verify(postLoginActivities).executeFor(normalUser);
|
||||
}
|
||||
|
||||
@Test
|
||||
void apiLoginShouldReturnUserIfUserFoundFromApikeyLookupNotDisabled() {
|
||||
Mockito.when(userDAO.findByApiKey("apikey")).thenReturn(normalUser);
|
||||
Optional<User> returnedUser = userService.login("apikey");
|
||||
Assertions.assertEquals(Optional.of(normalUser), returnedUser);
|
||||
}
|
||||
@Test
|
||||
void apiLoginShouldPerformPostLoginActivitiesIfUserFoundFromApikeyLookupNotDisabled() {
|
||||
Mockito.when(userDAO.findByApiKey("apikey")).thenReturn(normalUser);
|
||||
userService.login("apikey");
|
||||
Mockito.verify(postLoginActivities).executeFor(normalUser);
|
||||
}
|
||||
|
||||
@Test
|
||||
void apiLoginShouldReturnUserIfUserFoundFromApikeyLookupNotDisabled() {
|
||||
Mockito.when(userDAO.findByApiKey("apikey")).thenReturn(normalUser);
|
||||
Optional<User> returnedUser = userService.login("apikey");
|
||||
Assertions.assertEquals(Optional.of(normalUser), returnedUser);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
package com.commafeed.backend.service.db;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.CommaFeedConfiguration;
|
||||
@@ -24,130 +10,146 @@ import com.commafeed.backend.dao.FeedEntryDAO.FeedCapacity;
|
||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||
import com.commafeed.backend.dao.UnitOfWork;
|
||||
import com.commafeed.backend.model.Feed;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Callable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DatabaseCleaningServiceTest {
|
||||
|
||||
private static final int BATCH_SIZE = 100;
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
@Mock
|
||||
private CommaFeedConfiguration config;
|
||||
@Mock private CommaFeedConfiguration config;
|
||||
|
||||
@Mock
|
||||
private CommaFeedConfiguration.Database databaseConfig;
|
||||
@Mock private CommaFeedConfiguration.Database databaseConfig;
|
||||
|
||||
@Mock
|
||||
private CommaFeedConfiguration.Database.Cleanup cleaningConfig;
|
||||
@Mock private CommaFeedConfiguration.Database.Cleanup cleaningConfig;
|
||||
|
||||
@Mock
|
||||
private UnitOfWork unitOfWork;
|
||||
@Mock private UnitOfWork unitOfWork;
|
||||
|
||||
@Mock
|
||||
private FeedDAO feedDAO;
|
||||
@Mock private FeedDAO feedDAO;
|
||||
|
||||
@Mock
|
||||
private FeedEntryDAO feedEntryDAO;
|
||||
@Mock private FeedEntryDAO feedEntryDAO;
|
||||
|
||||
@Mock
|
||||
private FeedEntryContentDAO feedEntryContentDAO;
|
||||
@Mock private FeedEntryContentDAO feedEntryContentDAO;
|
||||
|
||||
@Mock
|
||||
private FeedEntryStatusDAO feedEntryStatusDAO;
|
||||
@Mock private FeedEntryStatusDAO feedEntryStatusDAO;
|
||||
|
||||
@Mock
|
||||
private MetricRegistry metrics;
|
||||
@Mock private MetricRegistry metrics;
|
||||
|
||||
@Mock
|
||||
private Meter entriesDeletedMeter;
|
||||
@Mock private Meter entriesDeletedMeter;
|
||||
|
||||
private DatabaseCleaningService service;
|
||||
private DatabaseCleaningService service;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(config.database()).thenReturn(databaseConfig);
|
||||
Mockito.when(databaseConfig.cleanup()).thenReturn(cleaningConfig);
|
||||
Mockito.when(cleaningConfig.batchSize()).thenReturn(BATCH_SIZE);
|
||||
Mockito.when(metrics.meter(Mockito.anyString())).thenReturn(entriesDeletedMeter);
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
Mockito.when(config.database()).thenReturn(databaseConfig);
|
||||
Mockito.when(databaseConfig.cleanup()).thenReturn(cleaningConfig);
|
||||
Mockito.when(cleaningConfig.batchSize()).thenReturn(BATCH_SIZE);
|
||||
Mockito.when(metrics.meter(Mockito.anyString())).thenReturn(entriesDeletedMeter);
|
||||
|
||||
Mockito.when(unitOfWork.call(Mockito.any())).thenAnswer(invocation -> ((Callable<?>) invocation.getArgument(0)).call());
|
||||
Mockito.when(unitOfWork.call(Mockito.any()))
|
||||
.thenAnswer(invocation -> ((Callable<?>) invocation.getArgument(0)).call());
|
||||
|
||||
service = new DatabaseCleaningService(config, unitOfWork, feedDAO, feedEntryDAO, feedEntryContentDAO, feedEntryStatusDAO, metrics);
|
||||
}
|
||||
service =
|
||||
new DatabaseCleaningService(
|
||||
config,
|
||||
unitOfWork,
|
||||
feedDAO,
|
||||
feedEntryDAO,
|
||||
feedEntryContentDAO,
|
||||
feedEntryStatusDAO,
|
||||
metrics);
|
||||
}
|
||||
|
||||
@Test
|
||||
void cleanFeedsWithoutSubscriptionsDeletesFeedsAndEntries() {
|
||||
Feed feed1 = Mockito.mock(Feed.class);
|
||||
Feed feed2 = Mockito.mock(Feed.class);
|
||||
Mockito.when(feed1.getId()).thenReturn(1L);
|
||||
Mockito.when(feed2.getId()).thenReturn(2L);
|
||||
@Test
|
||||
void cleanFeedsWithoutSubscriptionsDeletesFeedsAndEntries() {
|
||||
Feed feed1 = Mockito.mock(Feed.class);
|
||||
Feed feed2 = Mockito.mock(Feed.class);
|
||||
Mockito.when(feed1.getId()).thenReturn(1L);
|
||||
Mockito.when(feed2.getId()).thenReturn(2L);
|
||||
|
||||
// First iteration returns feeds, second returns empty list to terminate loop
|
||||
Mockito.when(feedDAO.findWithoutSubscriptions(Mockito.anyInt()))
|
||||
.thenReturn(Arrays.asList(feed1, feed2))
|
||||
.thenReturn(Collections.emptyList());
|
||||
// First iteration returns feeds, second returns empty list to terminate loop
|
||||
Mockito.when(feedDAO.findWithoutSubscriptions(Mockito.anyInt()))
|
||||
.thenReturn(Arrays.asList(feed1, feed2))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
Mockito.when(feedEntryDAO.delete(1L, BATCH_SIZE)).thenReturn(10, 0);
|
||||
Mockito.when(feedEntryDAO.delete(2L, BATCH_SIZE)).thenReturn(5, 0);
|
||||
Mockito.when(feedDAO.delete(Mockito.anyList())).thenReturn(2, 0);
|
||||
Mockito.when(feedEntryDAO.delete(1L, BATCH_SIZE)).thenReturn(10, 0);
|
||||
Mockito.when(feedEntryDAO.delete(2L, BATCH_SIZE)).thenReturn(5, 0);
|
||||
Mockito.when(feedDAO.delete(Mockito.anyList())).thenReturn(2, 0);
|
||||
|
||||
service.cleanFeedsWithoutSubscriptions();
|
||||
service.cleanFeedsWithoutSubscriptions();
|
||||
|
||||
Mockito.verify(entriesDeletedMeter, Mockito.times(4)).mark(Mockito.anyLong());
|
||||
Mockito.verify(feedDAO, Mockito.times(2)).delete(Mockito.anyList());
|
||||
}
|
||||
Mockito.verify(entriesDeletedMeter, Mockito.times(4)).mark(Mockito.anyLong());
|
||||
Mockito.verify(feedDAO, Mockito.times(2)).delete(Mockito.anyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cleanContentsWithoutEntriesDeletesContents() {
|
||||
Mockito.when(feedEntryContentDAO.deleteWithoutEntries(Mockito.anyInt())).thenReturn(50L, 30L, 0L);
|
||||
@Test
|
||||
void cleanContentsWithoutEntriesDeletesContents() {
|
||||
Mockito.when(feedEntryContentDAO.deleteWithoutEntries(Mockito.anyInt()))
|
||||
.thenReturn(50L, 30L, 0L);
|
||||
|
||||
service.cleanContentsWithoutEntries();
|
||||
service.cleanContentsWithoutEntries();
|
||||
|
||||
Mockito.verify(feedEntryContentDAO, Mockito.times(3)).deleteWithoutEntries(Mockito.anyInt());
|
||||
}
|
||||
Mockito.verify(feedEntryContentDAO, Mockito.times(3))
|
||||
.deleteWithoutEntries(Mockito.anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cleanEntriesForFeedsExceedingCapacityDeletesOldEntries() {
|
||||
FeedCapacity feed1 = Mockito.mock(FeedCapacity.class);
|
||||
Mockito.when(feed1.id()).thenReturn(1L);
|
||||
Mockito.when(feed1.capacity()).thenReturn(180L);
|
||||
@Test
|
||||
void cleanEntriesForFeedsExceedingCapacityDeletesOldEntries() {
|
||||
FeedCapacity feed1 = Mockito.mock(FeedCapacity.class);
|
||||
Mockito.when(feed1.id()).thenReturn(1L);
|
||||
Mockito.when(feed1.capacity()).thenReturn(180L);
|
||||
|
||||
FeedCapacity feed2 = Mockito.mock(FeedCapacity.class);
|
||||
Mockito.when(feed2.id()).thenReturn(2L);
|
||||
Mockito.when(feed2.capacity()).thenReturn(120L);
|
||||
FeedCapacity feed2 = Mockito.mock(FeedCapacity.class);
|
||||
Mockito.when(feed2.id()).thenReturn(2L);
|
||||
Mockito.when(feed2.capacity()).thenReturn(120L);
|
||||
|
||||
Mockito.when(feedEntryDAO.findFeedsExceedingCapacity(50, BATCH_SIZE, false))
|
||||
.thenReturn(Arrays.asList(feed1, feed2))
|
||||
.thenReturn(Collections.emptyList());
|
||||
Mockito.when(feedEntryDAO.findFeedsExceedingCapacity(50, BATCH_SIZE, false))
|
||||
.thenReturn(Arrays.asList(feed1, feed2))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 100, false)).thenReturn(80);
|
||||
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 50, false)).thenReturn(50);
|
||||
Mockito.when(feedEntryDAO.deleteOldEntries(2L, 70, false)).thenReturn(70);
|
||||
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 100, false)).thenReturn(80);
|
||||
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 50, false)).thenReturn(50);
|
||||
Mockito.when(feedEntryDAO.deleteOldEntries(2L, 70, false)).thenReturn(70);
|
||||
|
||||
service.cleanEntriesForFeedsExceedingCapacity(50);
|
||||
service.cleanEntriesForFeedsExceedingCapacity(50);
|
||||
|
||||
Mockito.verify(entriesDeletedMeter, Mockito.times(3)).mark(Mockito.anyLong());
|
||||
}
|
||||
Mockito.verify(entriesDeletedMeter, Mockito.times(3)).mark(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cleanEntriesOlderThanDeletesOldEntries() {
|
||||
Instant cutoff = LocalDate.now().minusDays(30).atStartOfDay().toInstant(ZoneOffset.UTC);
|
||||
@Test
|
||||
void cleanEntriesOlderThanDeletesOldEntries() {
|
||||
Instant cutoff = LocalDate.now().minusDays(30).atStartOfDay().toInstant(ZoneOffset.UTC);
|
||||
|
||||
Mockito.when(feedEntryDAO.deleteEntriesOlderThan(cutoff, BATCH_SIZE, false)).thenReturn(100, 50, 0);
|
||||
Mockito.when(feedEntryDAO.deleteEntriesOlderThan(cutoff, BATCH_SIZE, false))
|
||||
.thenReturn(100, 50, 0);
|
||||
|
||||
service.cleanEntriesOlderThan(cutoff);
|
||||
service.cleanEntriesOlderThan(cutoff);
|
||||
|
||||
Mockito.verify(feedEntryDAO, Mockito.times(3)).deleteEntriesOlderThan(cutoff, BATCH_SIZE, false);
|
||||
Mockito.verify(entriesDeletedMeter, Mockito.times(3)).mark(Mockito.anyLong());
|
||||
}
|
||||
Mockito.verify(feedEntryDAO, Mockito.times(3))
|
||||
.deleteEntriesOlderThan(cutoff, BATCH_SIZE, false);
|
||||
Mockito.verify(entriesDeletedMeter, Mockito.times(3)).mark(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cleanStatusesOlderThanDeletesOldStatuses() {
|
||||
Instant cutoff = LocalDate.now().minusDays(60).atStartOfDay().toInstant(ZoneOffset.UTC);
|
||||
@Test
|
||||
void cleanStatusesOlderThanDeletesOldStatuses() {
|
||||
Instant cutoff = LocalDate.now().minusDays(60).atStartOfDay().toInstant(ZoneOffset.UTC);
|
||||
|
||||
Mockito.when(feedEntryStatusDAO.deleteOldStatuses(cutoff, BATCH_SIZE)).thenReturn(200L, 100L, 0L);
|
||||
Mockito.when(feedEntryStatusDAO.deleteOldStatuses(cutoff, BATCH_SIZE))
|
||||
.thenReturn(200L, 100L, 0L);
|
||||
|
||||
service.cleanStatusesOlderThan(cutoff);
|
||||
service.cleanStatusesOlderThan(cutoff);
|
||||
|
||||
Mockito.verify(feedEntryStatusDAO, Mockito.times(3)).deleteOldStatuses(cutoff, BATCH_SIZE);
|
||||
}
|
||||
}
|
||||
Mockito.verify(feedEntryStatusDAO, Mockito.times(3)).deleteOldStatuses(cutoff, BATCH_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package com.commafeed.backend.urlprovider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class InPageReferenceFeedURLProviderTest {
|
||||
|
||||
private final InPageReferenceFeedURLProvider provider = new InPageReferenceFeedURLProvider();
|
||||
private final InPageReferenceFeedURLProvider provider = new InPageReferenceFeedURLProvider();
|
||||
|
||||
@Test
|
||||
void extractUrls() {
|
||||
String url = "http://example.com";
|
||||
String html = """
|
||||
@Test
|
||||
void extractUrls() {
|
||||
String url = "http://example.com";
|
||||
String html =
|
||||
"""
|
||||
<html>
|
||||
<head>
|
||||
<link type="application/atom+xml" href="/feed.atom">
|
||||
@@ -22,24 +22,28 @@ class InPageReferenceFeedURLProviderTest {
|
||||
</body>
|
||||
</html>""";
|
||||
|
||||
Assertions.assertIterableEquals(List.of("http://example.com/feed.atom", "http://example.com/feed.rss"), provider.get(url, html));
|
||||
}
|
||||
Assertions.assertIterableEquals(
|
||||
List.of("http://example.com/feed.atom", "http://example.com/feed.rss"),
|
||||
provider.get(url, html));
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsEmptyListForNonHtmlContent() {
|
||||
String url = "http://example.com";
|
||||
String html = """
|
||||
@Test
|
||||
void returnsEmptyListForNonHtmlContent() {
|
||||
String url = "http://example.com";
|
||||
String html =
|
||||
"""
|
||||
<?xml version="1.0"?>
|
||||
<feed></feed>
|
||||
</xml>""";
|
||||
|
||||
Assertions.assertTrue(provider.get(url, html).isEmpty());
|
||||
}
|
||||
Assertions.assertTrue(provider.get(url, html).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsEmptyListForHtmlWithoutFeedLinks() {
|
||||
String url = "http://example.com";
|
||||
String html = """
|
||||
@Test
|
||||
void returnsEmptyListForHtmlWithoutFeedLinks() {
|
||||
String url = "http://example.com";
|
||||
String html =
|
||||
"""
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" href="/style.css">
|
||||
@@ -48,6 +52,6 @@ class InPageReferenceFeedURLProviderTest {
|
||||
</body>
|
||||
</html>""";
|
||||
|
||||
Assertions.assertTrue(provider.get(url, html).isEmpty());
|
||||
}
|
||||
}
|
||||
Assertions.assertTrue(provider.get(url, html).isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
package com.commafeed.backend.urlprovider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class YoutubeFeedURLProviderTest {
|
||||
|
||||
private final YoutubeFeedURLProvider provider = new YoutubeFeedURLProvider();
|
||||
private final YoutubeFeedURLProvider provider = new YoutubeFeedURLProvider();
|
||||
|
||||
@Test
|
||||
void matchesYoutubeChannelURL() {
|
||||
Assertions.assertIterableEquals(List.of("https://www.youtube.com/feeds/videos.xml?channel_id=abc"),
|
||||
provider.get("https://www.youtube.com/channel/abc", null));
|
||||
}
|
||||
@Test
|
||||
void matchesYoutubeChannelURL() {
|
||||
Assertions.assertIterableEquals(
|
||||
List.of("https://www.youtube.com/feeds/videos.xml?channel_id=abc"),
|
||||
provider.get("https://www.youtube.com/channel/abc", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotmatchYoutubeChannelURL() {
|
||||
Assertions.assertTrue(provider.get("https://www.anothersite.com/channel/abc", null).isEmpty());
|
||||
Assertions.assertTrue(provider.get("https://www.youtube.com/user/abc", null).isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
void doesNotmatchYoutubeChannelURL() {
|
||||
Assertions.assertTrue(
|
||||
provider.get("https://www.anothersite.com/channel/abc", null).isEmpty());
|
||||
Assertions.assertTrue(provider.get("https://www.youtube.com/user/abc", null).isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,60 @@
|
||||
package com.commafeed.e2e;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.assertions.PlaywrightAssertions;
|
||||
import com.microsoft.playwright.options.AriaRole;
|
||||
|
||||
import io.quarkiverse.playwright.InjectPlaywright;
|
||||
import io.quarkiverse.playwright.WithPlaywright;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
@WithPlaywright
|
||||
class AuthentificationIT {
|
||||
|
||||
@InjectPlaywright
|
||||
private BrowserContext context;
|
||||
@InjectPlaywright private BrowserContext context;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
PlaywrightTestUtils.initialSetup();
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
PlaywrightTestUtils.initialSetup();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
context.clearCookies();
|
||||
}
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
context.clearCookies();
|
||||
}
|
||||
|
||||
@Test
|
||||
void loginFail() {
|
||||
Page page = context.newPage();
|
||||
page.navigate(getLoginPageUrl());
|
||||
PlaywrightTestUtils.login(page, TestConstants.ADMIN_USERNAME, "wrong_password");
|
||||
PlaywrightAssertions.assertThat(page.getByRole(AriaRole.ALERT)).containsText("wrong username or password");
|
||||
}
|
||||
@Test
|
||||
void loginFail() {
|
||||
Page page = context.newPage();
|
||||
page.navigate(getLoginPageUrl());
|
||||
PlaywrightTestUtils.login(page, TestConstants.ADMIN_USERNAME, "wrong_password");
|
||||
PlaywrightAssertions.assertThat(page.getByRole(AriaRole.ALERT))
|
||||
.containsText("wrong username or password");
|
||||
}
|
||||
|
||||
@Test
|
||||
void loginSuccess() {
|
||||
Page page = context.newPage();
|
||||
page.navigate(getLoginPageUrl());
|
||||
PlaywrightTestUtils.login(page);
|
||||
PlaywrightAssertions.assertThat(page).hasURL("http://localhost:8085/#/app/category/all");
|
||||
}
|
||||
@Test
|
||||
void loginSuccess() {
|
||||
Page page = context.newPage();
|
||||
page.navigate(getLoginPageUrl());
|
||||
PlaywrightTestUtils.login(page);
|
||||
PlaywrightAssertions.assertThat(page).hasURL("http://localhost:8085/#/app/category/all");
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerSuccess() {
|
||||
Page page = context.newPage();
|
||||
page.navigate(getLoginPageUrl());
|
||||
page.getByText("Sign up!").click();
|
||||
PlaywrightTestUtils.register(page, "user", "user@domain.com", "MyPassword1!");
|
||||
PlaywrightAssertions.assertThat(page).hasURL("http://localhost:8085/#/app/category/all");
|
||||
}
|
||||
@Test
|
||||
void registerSuccess() {
|
||||
Page page = context.newPage();
|
||||
page.navigate(getLoginPageUrl());
|
||||
page.getByText("Sign up!").click();
|
||||
PlaywrightTestUtils.register(page, "user", "user@domain.com", "MyPassword1!");
|
||||
PlaywrightAssertions.assertThat(page).hasURL("http://localhost:8085/#/app/category/all");
|
||||
}
|
||||
|
||||
private String getLoginPageUrl() {
|
||||
return "http://localhost:8085/#/login";
|
||||
}
|
||||
private String getLoginPageUrl() {
|
||||
return "http://localhost:8085/#/login";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
package com.commafeed.e2e;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.assertions.PlaywrightAssertions;
|
||||
|
||||
import io.quarkiverse.playwright.InjectPlaywright;
|
||||
import io.quarkiverse.playwright.WithPlaywright;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
@WithPlaywright
|
||||
class DocumentationIT {
|
||||
|
||||
@InjectPlaywright
|
||||
private BrowserContext context;
|
||||
|
||||
@Test
|
||||
void documentationAvailable() {
|
||||
Page page = context.newPage();
|
||||
page.navigate("http://localhost:8085/api-documentation");
|
||||
PlaywrightAssertions.assertThat(page.getByText("CommaFeed API 1.0.0 OAS")).isVisible();
|
||||
}
|
||||
@InjectPlaywright private BrowserContext context;
|
||||
|
||||
@Test
|
||||
void documentationAvailable() {
|
||||
Page page = context.newPage();
|
||||
page.navigate("http://localhost:8085/api-documentation");
|
||||
PlaywrightAssertions.assertThat(page.getByText("CommaFeed API 1.0.0 OAS")).isVisible();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
package com.commafeed.e2e;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.assertions.PlaywrightAssertions;
|
||||
import com.microsoft.playwright.options.AriaRole;
|
||||
|
||||
import io.quarkiverse.playwright.InjectPlaywright;
|
||||
import io.quarkiverse.playwright.WithPlaywright;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
@WithPlaywright
|
||||
class InitialSetupIT {
|
||||
|
||||
@InjectPlaywright
|
||||
private BrowserContext context;
|
||||
@InjectPlaywright private BrowserContext context;
|
||||
|
||||
@Test
|
||||
void createAdminAccount() {
|
||||
Page page = context.newPage();
|
||||
page.navigate("http://localhost:8085");
|
||||
@Test
|
||||
void createAdminAccount() {
|
||||
Page page = context.newPage();
|
||||
page.navigate("http://localhost:8085");
|
||||
|
||||
page.getByPlaceholder("Admin User Name").fill(TestConstants.ADMIN_USERNAME);
|
||||
page.getByPlaceholder("Password").fill(TestConstants.ADMIN_PASSWORD);
|
||||
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Create Admin Account")).click();
|
||||
page.getByPlaceholder("Admin User Name").fill(TestConstants.ADMIN_USERNAME);
|
||||
page.getByPlaceholder("Password").fill(TestConstants.ADMIN_PASSWORD);
|
||||
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Create Admin Account"))
|
||||
.click();
|
||||
|
||||
PlaywrightAssertions.assertThat(page).hasURL("http://localhost:8085/#/app/category/all");
|
||||
}
|
||||
PlaywrightAssertions.assertThat(page).hasURL("http://localhost:8085/#/app/category/all");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.commafeed.frontend.model.request.InitialSetupRequest;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.Page.GetByRoleOptions;
|
||||
import com.microsoft.playwright.options.AriaRole;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import lombok.experimental.UtilityClass;
|
||||
@@ -13,29 +12,33 @@ import lombok.experimental.UtilityClass;
|
||||
@UtilityClass
|
||||
public class PlaywrightTestUtils {
|
||||
|
||||
public static void initialSetup() {
|
||||
InitialSetupRequest req = new InitialSetupRequest();
|
||||
req.setName(TestConstants.ADMIN_USERNAME);
|
||||
req.setPassword(TestConstants.ADMIN_PASSWORD);
|
||||
public static void initialSetup() {
|
||||
InitialSetupRequest req = new InitialSetupRequest();
|
||||
req.setName(TestConstants.ADMIN_USERNAME);
|
||||
req.setPassword(TestConstants.ADMIN_PASSWORD);
|
||||
|
||||
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/user/initialSetup").then().statusCode(200);
|
||||
}
|
||||
RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/user/initialSetup")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
}
|
||||
|
||||
public static void login(Page page) {
|
||||
login(page, TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
public static void login(Page page) {
|
||||
login(page, TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
public static void login(Page page, String username, String password) {
|
||||
page.getByPlaceholder("User Name or E-mail").fill(username);
|
||||
page.getByPlaceholder("Password").fill(password);
|
||||
page.getByRole(AriaRole.BUTTON, new GetByRoleOptions().setName("Log in")).click();
|
||||
}
|
||||
|
||||
public static void register(Page page, String username, String email, String password) {
|
||||
page.getByPlaceholder("E-mail address").fill(email);
|
||||
page.getByPlaceholder("User Name").fill(username);
|
||||
page.getByPlaceholder("Password").fill(password);
|
||||
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Sign up")).click();
|
||||
}
|
||||
public static void login(Page page, String username, String password) {
|
||||
page.getByPlaceholder("User Name or E-mail").fill(username);
|
||||
page.getByPlaceholder("Password").fill(password);
|
||||
page.getByRole(AriaRole.BUTTON, new GetByRoleOptions().setName("Log in")).click();
|
||||
}
|
||||
|
||||
public static void register(Page page, String username, String email, String password) {
|
||||
page.getByPlaceholder("E-mail address").fill(email);
|
||||
page.getByPlaceholder("User Name").fill(username);
|
||||
page.getByPlaceholder("Password").fill(password);
|
||||
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Sign up")).click();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
package com.commafeed.e2e;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.Entries;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Locator;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.assertions.PlaywrightAssertions;
|
||||
import com.microsoft.playwright.options.AriaRole;
|
||||
import io.quarkiverse.playwright.InjectPlaywright;
|
||||
import io.quarkiverse.playwright.WithPlaywright;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.awaitility.Awaitility;
|
||||
@@ -16,97 +26,99 @@ import org.mockserver.integration.ClientAndServer;
|
||||
import org.mockserver.model.HttpRequest;
|
||||
import org.mockserver.model.HttpResponse;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.Entries;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Locator;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.assertions.PlaywrightAssertions;
|
||||
import com.microsoft.playwright.options.AriaRole;
|
||||
|
||||
import io.quarkiverse.playwright.InjectPlaywright;
|
||||
import io.quarkiverse.playwright.WithPlaywright;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
@QuarkusTest
|
||||
@WithPlaywright
|
||||
class ReadingIT {
|
||||
|
||||
@InjectPlaywright
|
||||
private BrowserContext context;
|
||||
@InjectPlaywright private BrowserContext context;
|
||||
|
||||
private MockServerClient mockServerClient;
|
||||
private MockServerClient mockServerClient;
|
||||
|
||||
@BeforeEach
|
||||
void init() throws IOException {
|
||||
this.mockServerClient = ClientAndServer.startClientAndServer(0);
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(HttpResponse.response()
|
||||
.withBody(IOUtils.toString(getClass().getResource("/feed/rss.xml"), StandardCharsets.UTF_8))
|
||||
.withDelay(TimeUnit.MILLISECONDS, 100));
|
||||
@BeforeEach
|
||||
void init() throws IOException {
|
||||
this.mockServerClient = ClientAndServer.startClientAndServer(0);
|
||||
this.mockServerClient
|
||||
.when(HttpRequest.request().withMethod("GET"))
|
||||
.respond(
|
||||
HttpResponse.response()
|
||||
.withBody(
|
||||
IOUtils.toString(
|
||||
getClass().getResource("/feed/rss.xml"),
|
||||
StandardCharsets.UTF_8))
|
||||
.withDelay(TimeUnit.MILLISECONDS, 100));
|
||||
|
||||
PlaywrightTestUtils.initialSetup();
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
PlaywrightTestUtils.initialSetup();
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void scenario() {
|
||||
Page page = context.newPage();
|
||||
@Test
|
||||
void scenario() {
|
||||
Page page = context.newPage();
|
||||
|
||||
// login
|
||||
page.navigate("http://localhost:8085");
|
||||
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Log in")).click();
|
||||
PlaywrightTestUtils.login(page);
|
||||
// login
|
||||
page.navigate("http://localhost:8085");
|
||||
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Log in")).click();
|
||||
PlaywrightTestUtils.login(page);
|
||||
|
||||
Locator header = page.getByRole(AriaRole.BANNER);
|
||||
Locator sidebar = page.getByRole(AriaRole.NAVIGATION);
|
||||
Locator main = page.getByRole(AriaRole.MAIN);
|
||||
Locator header = page.getByRole(AriaRole.BANNER);
|
||||
Locator sidebar = page.getByRole(AriaRole.NAVIGATION);
|
||||
Locator main = page.getByRole(AriaRole.MAIN);
|
||||
|
||||
PlaywrightAssertions.assertThat(main.getByText("You don't have any subscriptions yet.")).hasCount(1);
|
||||
PlaywrightAssertions.assertThat(main.getByText("You don't have any subscriptions yet."))
|
||||
.hasCount(1);
|
||||
|
||||
// subscribe
|
||||
header.getByRole(AriaRole.BUTTON, new Locator.GetByRoleOptions().setName("Subscribe")).click();
|
||||
main.getByText("Feed URL *").fill("http://localhost:" + this.mockServerClient.getPort());
|
||||
main.getByRole(AriaRole.BUTTON, new Locator.GetByRoleOptions().setName("Next")).click();
|
||||
main.getByRole(AriaRole.BUTTON, new Locator.GetByRoleOptions().setName("Subscribe").setExact(true)).click();
|
||||
// subscribe
|
||||
header.getByRole(AriaRole.BUTTON, new Locator.GetByRoleOptions().setName("Subscribe"))
|
||||
.click();
|
||||
main.getByText("Feed URL *").fill("http://localhost:" + this.mockServerClient.getPort());
|
||||
main.getByRole(AriaRole.BUTTON, new Locator.GetByRoleOptions().setName("Next")).click();
|
||||
main.getByRole(
|
||||
AriaRole.BUTTON,
|
||||
new Locator.GetByRoleOptions().setName("Subscribe").setExact(true))
|
||||
.click();
|
||||
|
||||
// click on subscription
|
||||
sidebar.getByText(Pattern.compile("CommaFeed test feed\\d+")).click();
|
||||
// click on subscription
|
||||
sidebar.getByText(Pattern.compile("CommaFeed test feed\\d+")).click();
|
||||
|
||||
// we have two unread entries
|
||||
PlaywrightAssertions.assertThat(main.getByRole(AriaRole.ARTICLE)).hasCount(2);
|
||||
// we have two unread entries
|
||||
PlaywrightAssertions.assertThat(main.getByRole(AriaRole.ARTICLE)).hasCount(2);
|
||||
|
||||
// click on first entry
|
||||
main.getByText("Item 1").click();
|
||||
PlaywrightAssertions.assertThat(main.getByText("Item 1 description")).hasCount(1);
|
||||
PlaywrightAssertions.assertThat(main.getByText("Item 2 description")).hasCount(0);
|
||||
// click on first entry
|
||||
main.getByText("Item 1").click();
|
||||
PlaywrightAssertions.assertThat(main.getByText("Item 1 description")).hasCount(1);
|
||||
PlaywrightAssertions.assertThat(main.getByText("Item 2 description")).hasCount(0);
|
||||
|
||||
// wait for the entry to be marked as read since the UI is updated immediately while the entry is marked as read in the background
|
||||
Awaitility.await()
|
||||
.atMost(15, TimeUnit.SECONDS)
|
||||
.until(() -> RestAssured.given()
|
||||
.get("rest/category/entries?id=all&readType=unread")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class), e -> e.getEntries().size() == 1);
|
||||
// wait for the entry to be marked as read since the UI is updated immediately while the
|
||||
// entry
|
||||
// is marked as read in the background
|
||||
Awaitility.await()
|
||||
.atMost(15, TimeUnit.SECONDS)
|
||||
.until(
|
||||
() ->
|
||||
RestAssured.given()
|
||||
.get("rest/category/entries?id=all&readType=unread")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class),
|
||||
e -> e.getEntries().size() == 1);
|
||||
|
||||
// click on subscription
|
||||
sidebar.getByText(Pattern.compile("CommaFeed test feed\\d*")).click();
|
||||
// click on subscription
|
||||
sidebar.getByText(Pattern.compile("CommaFeed test feed\\d*")).click();
|
||||
|
||||
// only one unread entry now
|
||||
PlaywrightAssertions.assertThat(main.getByRole(AriaRole.ARTICLE)).hasCount(1);
|
||||
|
||||
// click on second entry
|
||||
main.getByText("Item 2").click();
|
||||
PlaywrightAssertions.assertThat(main.getByText("Item 1 description")).hasCount(0);
|
||||
PlaywrightAssertions.assertThat(main.getByText("Item 2 description")).hasCount(1);
|
||||
}
|
||||
// only one unread entry now
|
||||
PlaywrightAssertions.assertThat(main.getByRole(AriaRole.ARTICLE)).hasCount(1);
|
||||
|
||||
// click on second entry
|
||||
main.getByText("Item 2").click();
|
||||
PlaywrightAssertions.assertThat(main.getByText("Item 1 description")).hasCount(0);
|
||||
PlaywrightAssertions.assertThat(main.getByText("Item 2 description")).hasCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package com.commafeed.frontend.ws;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.backend.model.User;
|
||||
import jakarta.websocket.Session;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -10,73 +11,69 @@ import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.commafeed.backend.model.User;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class WebSocketSessionsTest {
|
||||
|
||||
@Mock
|
||||
private MetricRegistry metrics;
|
||||
@Mock private MetricRegistry metrics;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Session session1;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Session session1;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Session session2;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Session session2;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Session session3;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Session session3;
|
||||
|
||||
private WebSocketSessions webSocketSessions;
|
||||
private WebSocketSessions webSocketSessions;
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
webSocketSessions = new WebSocketSessions(metrics);
|
||||
}
|
||||
@BeforeEach
|
||||
void init() {
|
||||
webSocketSessions = new WebSocketSessions(metrics);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendsMessageToUser() {
|
||||
Mockito.when(session1.isOpen()).thenReturn(true);
|
||||
Mockito.when(session2.isOpen()).thenReturn(true);
|
||||
@Test
|
||||
void sendsMessageToUser() {
|
||||
Mockito.when(session1.isOpen()).thenReturn(true);
|
||||
Mockito.when(session2.isOpen()).thenReturn(true);
|
||||
|
||||
User user1 = newUser(1L);
|
||||
webSocketSessions.add(user1.getId(), session1);
|
||||
webSocketSessions.add(user1.getId(), session2);
|
||||
User user1 = newUser(1L);
|
||||
webSocketSessions.add(user1.getId(), session1);
|
||||
webSocketSessions.add(user1.getId(), session2);
|
||||
|
||||
User user2 = newUser(2L);
|
||||
webSocketSessions.add(user2.getId(), session3);
|
||||
User user2 = newUser(2L);
|
||||
webSocketSessions.add(user2.getId(), session3);
|
||||
|
||||
webSocketSessions.sendMessage(user1, "Hello");
|
||||
Mockito.verify(session1).getAsyncRemote();
|
||||
Mockito.verify(session2).getAsyncRemote();
|
||||
Mockito.verifyNoInteractions(session3);
|
||||
}
|
||||
webSocketSessions.sendMessage(user1, "Hello");
|
||||
Mockito.verify(session1).getAsyncRemote();
|
||||
Mockito.verify(session2).getAsyncRemote();
|
||||
Mockito.verifyNoInteractions(session3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void closedSessionsAreNotNotified() {
|
||||
Mockito.when(session1.isOpen()).thenReturn(false);
|
||||
@Test
|
||||
void closedSessionsAreNotNotified() {
|
||||
Mockito.when(session1.isOpen()).thenReturn(false);
|
||||
|
||||
User user1 = newUser(1L);
|
||||
webSocketSessions.add(user1.getId(), session1);
|
||||
User user1 = newUser(1L);
|
||||
webSocketSessions.add(user1.getId(), session1);
|
||||
|
||||
webSocketSessions.sendMessage(user1, "Hello");
|
||||
Mockito.verify(session1, Mockito.never()).getAsyncRemote();
|
||||
}
|
||||
webSocketSessions.sendMessage(user1, "Hello");
|
||||
Mockito.verify(session1, Mockito.never()).getAsyncRemote();
|
||||
}
|
||||
|
||||
@Test
|
||||
void removedSessionsAreNotNotified() {
|
||||
User user1 = newUser(1L);
|
||||
webSocketSessions.add(user1.getId(), session1);
|
||||
webSocketSessions.remove(session1);
|
||||
@Test
|
||||
void removedSessionsAreNotNotified() {
|
||||
User user1 = newUser(1L);
|
||||
webSocketSessions.add(user1.getId(), session1);
|
||||
webSocketSessions.remove(session1);
|
||||
|
||||
webSocketSessions.sendMessage(user1, "Hello");
|
||||
Mockito.verifyNoInteractions(session1);
|
||||
}
|
||||
webSocketSessions.sendMessage(user1, "Hello");
|
||||
Mockito.verifyNoInteractions(session1);
|
||||
}
|
||||
|
||||
private User newUser(Long userId) {
|
||||
User user = new User();
|
||||
user.setId(userId);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
private User newUser(Long userId) {
|
||||
User user = new User();
|
||||
user.setId(userId);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
package com.commafeed.integration;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.Category;
|
||||
import com.commafeed.frontend.model.Entries;
|
||||
import com.commafeed.frontend.model.Subscription;
|
||||
import com.commafeed.frontend.model.request.AddCategoryRequest;
|
||||
import com.commafeed.frontend.model.request.InitialSetupRequest;
|
||||
import com.commafeed.frontend.model.request.SubscribeRequest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.config.ObjectMapperConfig;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.http.Header;
|
||||
import io.restassured.mapper.ObjectMapperType;
|
||||
import jakarta.ws.rs.client.Client;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URL;
|
||||
@@ -7,10 +21,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import jakarta.ws.rs.client.Client;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.awaitility.Awaitility;
|
||||
@@ -21,192 +32,212 @@ import org.mockserver.integration.ClientAndServer;
|
||||
import org.mockserver.model.HttpRequest;
|
||||
import org.mockserver.model.HttpResponse;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.Category;
|
||||
import com.commafeed.frontend.model.Entries;
|
||||
import com.commafeed.frontend.model.Subscription;
|
||||
import com.commafeed.frontend.model.request.AddCategoryRequest;
|
||||
import com.commafeed.frontend.model.request.InitialSetupRequest;
|
||||
import com.commafeed.frontend.model.request.SubscribeRequest;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.config.ObjectMapperConfig;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.http.Header;
|
||||
import io.restassured.mapper.ObjectMapperType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public abstract class BaseIT {
|
||||
|
||||
private static final HttpRequest FEED_REQUEST = HttpRequest.request().withMethod("GET").withPath("/");
|
||||
private static final HttpRequest FEED_REQUEST =
|
||||
HttpRequest.request().withMethod("GET").withPath("/");
|
||||
|
||||
@Getter
|
||||
private MockServerClient mockServerClient;
|
||||
private Client client;
|
||||
private String feedUrl;
|
||||
private String baseUrl;
|
||||
private String apiBaseUrl;
|
||||
private String webSocketUrl;
|
||||
@Getter private MockServerClient mockServerClient;
|
||||
private Client client;
|
||||
private String feedUrl;
|
||||
private String baseUrl;
|
||||
private String apiBaseUrl;
|
||||
private String webSocketUrl;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws IOException {
|
||||
this.mockServerClient = ClientAndServer.startClientAndServer(0);
|
||||
@BeforeEach
|
||||
void beforeEach() throws IOException {
|
||||
this.mockServerClient = ClientAndServer.startClientAndServer(0);
|
||||
|
||||
this.feedUrl = "http://localhost:" + mockServerClient.getPort() + "/";
|
||||
this.baseUrl = "http://localhost:8085/";
|
||||
this.apiBaseUrl = this.baseUrl + "rest/";
|
||||
this.webSocketUrl = "ws://localhost:8085/ws";
|
||||
this.feedUrl = "http://localhost:" + mockServerClient.getPort() + "/";
|
||||
this.baseUrl = "http://localhost:8085/";
|
||||
this.apiBaseUrl = this.baseUrl + "rest/";
|
||||
this.webSocketUrl = "ws://localhost:8085/ws";
|
||||
|
||||
URL resource = Objects.requireNonNull(getClass().getResource("/feed/rss.xml"));
|
||||
this.mockServerClient.when(FEED_REQUEST)
|
||||
.respond(HttpResponse.response().withBody(IOUtils.toString(resource, StandardCharsets.UTF_8)));
|
||||
URL resource = Objects.requireNonNull(getClass().getResource("/feed/rss.xml"));
|
||||
this.mockServerClient
|
||||
.when(FEED_REQUEST)
|
||||
.respond(
|
||||
HttpResponse.response()
|
||||
.withBody(IOUtils.toString(resource, StandardCharsets.UTF_8)));
|
||||
|
||||
// remove when Quarkus supports Jackson 3
|
||||
RestAssured.config = RestAssured.config().objectMapperConfig(new ObjectMapperConfig(ObjectMapperType.JACKSON_2));
|
||||
}
|
||||
// remove when Quarkus supports Jackson 3
|
||||
RestAssured.config =
|
||||
RestAssured.config()
|
||||
.objectMapperConfig(new ObjectMapperConfig(ObjectMapperType.JACKSON_2));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
if (this.mockServerClient != null) {
|
||||
this.mockServerClient.close();
|
||||
}
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
if (this.mockServerClient != null) {
|
||||
this.mockServerClient.close();
|
||||
}
|
||||
|
||||
if (this.client != null) {
|
||||
this.client.close();
|
||||
}
|
||||
}
|
||||
if (this.client != null) {
|
||||
this.client.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected void feedNowReturnsMoreEntries() throws IOException {
|
||||
mockServerClient.clear(FEED_REQUEST);
|
||||
protected void feedNowReturnsMoreEntries() throws IOException {
|
||||
mockServerClient.clear(FEED_REQUEST);
|
||||
|
||||
URL resource = Objects.requireNonNull(getClass().getResource("/feed/rss_2.xml"));
|
||||
mockServerClient.when(FEED_REQUEST).respond(HttpResponse.response().withBody(IOUtils.toString(resource, StandardCharsets.UTF_8)));
|
||||
}
|
||||
URL resource = Objects.requireNonNull(getClass().getResource("/feed/rss_2.xml"));
|
||||
mockServerClient
|
||||
.when(FEED_REQUEST)
|
||||
.respond(
|
||||
HttpResponse.response()
|
||||
.withBody(IOUtils.toString(resource, StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
protected void initialSetup(String userName, String password) {
|
||||
InitialSetupRequest req = new InitialSetupRequest();
|
||||
req.setName(userName);
|
||||
req.setPassword(password);
|
||||
req.setEmail(userName + "@commafeed.com");
|
||||
protected void initialSetup(String userName, String password) {
|
||||
InitialSetupRequest req = new InitialSetupRequest();
|
||||
req.setName(userName);
|
||||
req.setPassword(password);
|
||||
req.setEmail(userName + "@commafeed.com");
|
||||
|
||||
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/user/initialSetup").then().statusCode(200);
|
||||
}
|
||||
RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/user/initialSetup")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
}
|
||||
|
||||
protected List<HttpCookie> login() {
|
||||
List<Header> setCookieHeaders = RestAssured.given()
|
||||
.auth()
|
||||
.none()
|
||||
.formParams("j_username", TestConstants.ADMIN_USERNAME, "j_password", TestConstants.ADMIN_PASSWORD)
|
||||
.post("j_security_check")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.headers()
|
||||
.getList(HttpHeaders.SET_COOKIE);
|
||||
return setCookieHeaders.stream().flatMap(h -> HttpCookie.parse(h.getValue()).stream()).toList();
|
||||
}
|
||||
protected List<HttpCookie> login() {
|
||||
List<Header> setCookieHeaders =
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.none()
|
||||
.formParams(
|
||||
"j_username",
|
||||
TestConstants.ADMIN_USERNAME,
|
||||
"j_password",
|
||||
TestConstants.ADMIN_PASSWORD)
|
||||
.post("j_security_check")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.headers()
|
||||
.getList(HttpHeaders.SET_COOKIE);
|
||||
return setCookieHeaders.stream()
|
||||
.flatMap(h -> HttpCookie.parse(h.getValue()).stream())
|
||||
.toList();
|
||||
}
|
||||
|
||||
protected String createCategory(String name) {
|
||||
AddCategoryRequest addCategoryRequest = new AddCategoryRequest();
|
||||
addCategoryRequest.setName(name);
|
||||
return RestAssured.given()
|
||||
.body(addCategoryRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/category/add")
|
||||
.then()
|
||||
.extract()
|
||||
.as(String.class);
|
||||
}
|
||||
protected String createCategory(String name) {
|
||||
AddCategoryRequest addCategoryRequest = new AddCategoryRequest();
|
||||
addCategoryRequest.setName(name);
|
||||
return RestAssured.given()
|
||||
.body(addCategoryRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/category/add")
|
||||
.then()
|
||||
.extract()
|
||||
.as(String.class);
|
||||
}
|
||||
|
||||
protected Category getRootCategory() {
|
||||
return RestAssured.given().get("rest/category/get").then().statusCode(HttpStatus.SC_OK).extract().as(Category.class);
|
||||
}
|
||||
protected Category getRootCategory() {
|
||||
return RestAssured.given()
|
||||
.get("rest/category/get")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Category.class);
|
||||
}
|
||||
|
||||
protected Long subscribe(String feedUrl) {
|
||||
return subscribe(feedUrl, null);
|
||||
}
|
||||
protected Long subscribe(String feedUrl) {
|
||||
return subscribe(feedUrl, null);
|
||||
}
|
||||
|
||||
protected Long subscribe(String feedUrl, String categoryId) {
|
||||
SubscribeRequest subscribeRequest = new SubscribeRequest();
|
||||
subscribeRequest.setUrl(feedUrl);
|
||||
subscribeRequest.setTitle("my title for this feed");
|
||||
subscribeRequest.setCategoryId(categoryId);
|
||||
return RestAssured.given()
|
||||
.body(subscribeRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/subscribe")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Long.class);
|
||||
}
|
||||
protected Long subscribe(String feedUrl, String categoryId) {
|
||||
SubscribeRequest subscribeRequest = new SubscribeRequest();
|
||||
subscribeRequest.setUrl(feedUrl);
|
||||
subscribeRequest.setTitle("my title for this feed");
|
||||
subscribeRequest.setCategoryId(categoryId);
|
||||
return RestAssured.given()
|
||||
.body(subscribeRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/subscribe")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Long.class);
|
||||
}
|
||||
|
||||
protected Long subscribeAndWaitForEntries(String feedUrl) {
|
||||
return subscribeAndWaitForEntries(feedUrl, null);
|
||||
}
|
||||
protected Long subscribeAndWaitForEntries(String feedUrl) {
|
||||
return subscribeAndWaitForEntries(feedUrl, null);
|
||||
}
|
||||
|
||||
protected Long subscribeAndWaitForEntries(String feedUrl, String categoryId) {
|
||||
Long subscriptionId = subscribe(feedUrl, categoryId);
|
||||
Awaitility.await().atMost(Duration.ofSeconds(15)).until(() -> getFeedEntries(subscriptionId), e -> e.getEntries().size() == 2);
|
||||
return subscriptionId;
|
||||
}
|
||||
protected Long subscribeAndWaitForEntries(String feedUrl, String categoryId) {
|
||||
Long subscriptionId = subscribe(feedUrl, categoryId);
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(15))
|
||||
.until(() -> getFeedEntries(subscriptionId), e -> e.getEntries().size() == 2);
|
||||
return subscriptionId;
|
||||
}
|
||||
|
||||
protected Subscription getSubscription(Long subscriptionId) {
|
||||
return RestAssured.given()
|
||||
.get("rest/feed/get/{id}", subscriptionId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Subscription.class);
|
||||
}
|
||||
protected Subscription getSubscription(Long subscriptionId) {
|
||||
return RestAssured.given()
|
||||
.get("rest/feed/get/{id}", subscriptionId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Subscription.class);
|
||||
}
|
||||
|
||||
protected Entries getFeedEntries(long subscriptionId) {
|
||||
return RestAssured.given()
|
||||
.get("rest/feed/entries?id={id}&readType=all", subscriptionId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
}
|
||||
protected Entries getFeedEntries(long subscriptionId) {
|
||||
return RestAssured.given()
|
||||
.get("rest/feed/entries?id={id}&readType=all", subscriptionId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
}
|
||||
|
||||
protected Entries getCategoryEntries(String categoryId) {
|
||||
return RestAssured.given()
|
||||
.get("rest/category/entries?id={id}&readType=all", categoryId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
}
|
||||
protected Entries getCategoryEntries(String categoryId) {
|
||||
return RestAssured.given()
|
||||
.get("rest/category/entries?id={id}&readType=all", categoryId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
}
|
||||
|
||||
protected Entries getCategoryEntries(String categoryId, int offset, int limit) {
|
||||
return RestAssured.given()
|
||||
.get("rest/category/entries?id={id}&readType=all&offset={offset}&limit={limit}", categoryId, offset, limit)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
}
|
||||
protected Entries getCategoryEntries(String categoryId, int offset, int limit) {
|
||||
return RestAssured.given()
|
||||
.get(
|
||||
"rest/category/entries?id={id}&readType=all&offset={offset}&limit={limit}",
|
||||
categoryId,
|
||||
offset,
|
||||
limit)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
}
|
||||
|
||||
protected Entries getCategoryEntries(String categoryId, String keywords) {
|
||||
return RestAssured.given()
|
||||
.get("rest/category/entries?id={id}&readType=all&keywords={keywords}", categoryId, keywords)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
}
|
||||
protected Entries getCategoryEntries(String categoryId, String keywords) {
|
||||
return RestAssured.given()
|
||||
.get(
|
||||
"rest/category/entries?id={id}&readType=all&keywords={keywords}",
|
||||
categoryId,
|
||||
keywords)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
}
|
||||
|
||||
protected Entries getTaggedEntries(String tag) {
|
||||
return RestAssured.given()
|
||||
.get("rest/category/entries?id=all&readType=all&tag={tag}", tag)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
}
|
||||
protected Entries getTaggedEntries(String tag) {
|
||||
return RestAssured.given()
|
||||
.get("rest/category/entries?id=all&readType=all&tag={tag}", tag)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
}
|
||||
|
||||
protected int forceRefreshAllFeeds() {
|
||||
return RestAssured.given().get("rest/feed/refreshAll").then().extract().statusCode();
|
||||
}
|
||||
protected int forceRefreshAllFeeds() {
|
||||
return RestAssured.given().get("rest/feed/refreshAll").then().extract().statusCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
package com.commafeed.integration;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
@QuarkusTest
|
||||
class CompressionIT {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "/rest/server/get", "/" })
|
||||
void servedWithCompression(String path) {
|
||||
RestAssured.given().when().get(path).then().statusCode(200).header(HttpHeaders.CONTENT_ENCODING, "gzip");
|
||||
}
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"/rest/server/get", "/"})
|
||||
void servedWithCompression(String path) {
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.get(path)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.header(HttpHeaders.CONTENT_ENCODING, "gzip");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
package com.commafeed.integration;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.backend.model.UserSettings.PushNotificationType;
|
||||
import com.commafeed.frontend.model.Settings;
|
||||
import com.commafeed.frontend.model.Subscription;
|
||||
import com.commafeed.frontend.model.request.FeedModificationRequest;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@@ -12,64 +19,69 @@ import org.mockserver.model.HttpRequest;
|
||||
import org.mockserver.model.HttpResponse;
|
||||
import org.mockserver.verify.VerificationTimes;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.backend.model.UserSettings.PushNotificationType;
|
||||
import com.commafeed.frontend.model.Settings;
|
||||
import com.commafeed.frontend.model.Subscription;
|
||||
import com.commafeed.frontend.model.request.FeedModificationRequest;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
|
||||
@QuarkusTest
|
||||
class PushNotificationIT extends BaseIT {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void receivedPushNotifications() throws IOException {
|
||||
// mock ntfy server
|
||||
HttpRequest ntfyPost = HttpRequest.request().withMethod("POST").withPath("/ntfy/integration-test");
|
||||
getMockServerClient().when(ntfyPost).respond(HttpResponse.response().withStatusCode(200));
|
||||
@Test
|
||||
void receivedPushNotifications() throws IOException {
|
||||
// mock ntfy server
|
||||
HttpRequest ntfyPost =
|
||||
HttpRequest.request().withMethod("POST").withPath("/ntfy/integration-test");
|
||||
getMockServerClient().when(ntfyPost).respond(HttpResponse.response().withStatusCode(200));
|
||||
|
||||
// enable push notifications
|
||||
Settings settings = RestAssured.given().get("rest/user/settings").then().extract().as(Settings.class);
|
||||
settings.getPushNotificationSettings().setType(PushNotificationType.NTFY);
|
||||
settings.getPushNotificationSettings().setServerUrl("http://localhost:" + getMockServerClient().getPort() + "/ntfy");
|
||||
settings.getPushNotificationSettings().setTopic("integration-test");
|
||||
RestAssured.given().body(settings).contentType(ContentType.JSON).post("rest/user/settings").then().statusCode(200);
|
||||
// enable push notifications
|
||||
Settings settings =
|
||||
RestAssured.given().get("rest/user/settings").then().extract().as(Settings.class);
|
||||
settings.getPushNotificationSettings().setType(PushNotificationType.NTFY);
|
||||
settings.getPushNotificationSettings()
|
||||
.setServerUrl("http://localhost:" + getMockServerClient().getPort() + "/ntfy");
|
||||
settings.getPushNotificationSettings().setTopic("integration-test");
|
||||
RestAssured.given()
|
||||
.body(settings)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/user/settings")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
// subscribe
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Subscription subscription = getSubscription(subscriptionId);
|
||||
// subscribe
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Subscription subscription = getSubscription(subscriptionId);
|
||||
|
||||
// enable push notifications
|
||||
FeedModificationRequest req = new FeedModificationRequest();
|
||||
req.setId(subscriptionId);
|
||||
req.setName(subscription.getName());
|
||||
req.setCategoryId(subscription.getCategoryId());
|
||||
req.setPosition(1);
|
||||
req.setPushNotificationsEnabled(true);
|
||||
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/feed/modify").then().statusCode(HttpStatus.SC_OK);
|
||||
// enable push notifications
|
||||
FeedModificationRequest req = new FeedModificationRequest();
|
||||
req.setId(subscriptionId);
|
||||
req.setName(subscription.getName());
|
||||
req.setCategoryId(subscription.getCategoryId());
|
||||
req.setPosition(1);
|
||||
req.setPushNotificationsEnabled(true);
|
||||
RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/modify")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
|
||||
// receive two additional entries, those will trigger two push notifications
|
||||
feedNowReturnsMoreEntries();
|
||||
forceRefreshAllFeeds();
|
||||
|
||||
// await push notification for the two entries in the feed
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(20))
|
||||
.untilAsserted(() -> getMockServerClient().verify(ntfyPost, VerificationTimes.exactly(2)));
|
||||
}
|
||||
// receive two additional entries, those will trigger two push notifications
|
||||
feedNowReturnsMoreEntries();
|
||||
forceRefreshAllFeeds();
|
||||
|
||||
// await push notification for the two entries in the feed
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(20))
|
||||
.untilAsserted(
|
||||
() -> getMockServerClient().verify(ntfyPost, VerificationTimes.exactly(2)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
package com.commafeed.integration;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.ExceptionMappers.UnauthorizedResponse;
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.Entries;
|
||||
@@ -18,135 +7,154 @@ import com.commafeed.frontend.model.UserModel;
|
||||
import com.commafeed.frontend.model.request.MarkRequest;
|
||||
import com.commafeed.frontend.model.request.ProfileModificationRequest;
|
||||
import com.commafeed.frontend.model.request.SubscribeRequest;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import java.net.HttpCookie;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
class SecurityIT extends BaseIT {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
@Test
|
||||
void notLoggedIn() {
|
||||
UnauthorizedResponse info = RestAssured.given()
|
||||
.get("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_UNAUTHORIZED)
|
||||
.extract()
|
||||
.as(UnauthorizedResponse.class);
|
||||
Assertions.assertTrue(info.allowRegistrations());
|
||||
}
|
||||
@Test
|
||||
void notLoggedIn() {
|
||||
UnauthorizedResponse info =
|
||||
RestAssured.given()
|
||||
.get("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_UNAUTHORIZED)
|
||||
.extract()
|
||||
.as(UnauthorizedResponse.class);
|
||||
Assertions.assertTrue(info.allowRegistrations());
|
||||
}
|
||||
|
||||
@Test
|
||||
void formLogin() {
|
||||
List<HttpCookie> cookies = login();
|
||||
cookies.forEach(c -> Assertions.assertTrue(c.getMaxAge() > 0));
|
||||
@Test
|
||||
void formLogin() {
|
||||
List<HttpCookie> cookies = login();
|
||||
cookies.forEach(c -> Assertions.assertTrue(c.getMaxAge() > 0));
|
||||
|
||||
RestAssured.given()
|
||||
.header(HttpHeaders.COOKIE, cookies.stream().map(HttpCookie::toString).collect(Collectors.joining(";")))
|
||||
.get("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
RestAssured.given()
|
||||
.header(
|
||||
HttpHeaders.COOKIE,
|
||||
cookies.stream().map(HttpCookie::toString).collect(Collectors.joining(";")))
|
||||
.get("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
void basicAuthLogin() {
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
|
||||
.get("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
@Test
|
||||
void basicAuthLogin() {
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
|
||||
.get("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
void wrongPassword() {
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, "wrong-password")
|
||||
.get("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_UNAUTHORIZED);
|
||||
}
|
||||
@Test
|
||||
void wrongPassword() {
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, "wrong-password")
|
||||
.get("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingRole() {
|
||||
RestAssured.given().auth().preemptive().basic("demo", "demo").get("rest/admin/metrics").then().statusCode(HttpStatus.SC_FORBIDDEN);
|
||||
}
|
||||
@Test
|
||||
void missingRole() {
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.preemptive()
|
||||
.basic("demo", "demo")
|
||||
.get("rest/admin/metrics")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void apiKey() {
|
||||
// create api key
|
||||
ProfileModificationRequest req = new ProfileModificationRequest();
|
||||
req.setCurrentPassword(TestConstants.ADMIN_PASSWORD);
|
||||
req.setNewApiKey(true);
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
@Test
|
||||
void apiKey() {
|
||||
// create api key
|
||||
ProfileModificationRequest req = new ProfileModificationRequest();
|
||||
req.setCurrentPassword(TestConstants.ADMIN_PASSWORD);
|
||||
req.setNewApiKey(true);
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
|
||||
// fetch api key
|
||||
String apiKey = RestAssured.given()
|
||||
.auth()
|
||||
.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
|
||||
.get("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(UserModel.class)
|
||||
.getApiKey();
|
||||
// fetch api key
|
||||
String apiKey =
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
|
||||
.get("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(UserModel.class)
|
||||
.getApiKey();
|
||||
|
||||
// subscribe to a feed
|
||||
SubscribeRequest subscribeRequest = new SubscribeRequest();
|
||||
subscribeRequest.setUrl(getFeedUrl());
|
||||
subscribeRequest.setTitle("my title for this feed");
|
||||
long subscriptionId = RestAssured.given()
|
||||
.auth()
|
||||
.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
|
||||
.body(subscribeRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/subscribe")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Long.class);
|
||||
// subscribe to a feed
|
||||
SubscribeRequest subscribeRequest = new SubscribeRequest();
|
||||
subscribeRequest.setUrl(getFeedUrl());
|
||||
subscribeRequest.setTitle("my title for this feed");
|
||||
long subscriptionId =
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
|
||||
.body(subscribeRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/subscribe")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Long.class);
|
||||
|
||||
// get entries with api key
|
||||
Entries entries = RestAssured.given()
|
||||
.queryParam("id", subscriptionId)
|
||||
.queryParam("readType", "unread")
|
||||
.queryParam("apiKey", apiKey)
|
||||
.get("rest/feed/entries")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
Assertions.assertEquals("my title for this feed", entries.getName());
|
||||
// get entries with api key
|
||||
Entries entries =
|
||||
RestAssured.given()
|
||||
.queryParam("id", subscriptionId)
|
||||
.queryParam("readType", "unread")
|
||||
.queryParam("apiKey", apiKey)
|
||||
.get("rest/feed/entries")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
Assertions.assertEquals("my title for this feed", entries.getName());
|
||||
|
||||
// mark entry as read and expect it won't work because it's not a GET request
|
||||
MarkRequest markRequest = new MarkRequest();
|
||||
markRequest.setId("1");
|
||||
markRequest.setRead(true);
|
||||
RestAssured.given()
|
||||
.body(markRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.queryParam("apiKey", apiKey)
|
||||
.post("rest/entry/mark")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_UNAUTHORIZED);
|
||||
}
|
||||
// mark entry as read and expect it won't work because it's not a GET request
|
||||
MarkRequest markRequest = new MarkRequest();
|
||||
markRequest.setId("1");
|
||||
markRequest.setRead(true);
|
||||
RestAssured.given()
|
||||
.body(markRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.queryParam("apiKey", apiKey)
|
||||
.post("rest/entry/mark")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
package com.commafeed.integration;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
@QuarkusTest
|
||||
class StaticFilesIT {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "/", "/openapi" })
|
||||
void servedWithoutCache(String path) {
|
||||
RestAssured.given().when().get(path).then().statusCode(200).header("Cache-Control", "no-cache");
|
||||
}
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"/", "/openapi"})
|
||||
void servedWithoutCache(String path) {
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.get(path)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.header("Cache-Control", "no-cache");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "/favicon.ico" })
|
||||
void servedWithCache(String path) {
|
||||
RestAssured.given().when().get(path).then().statusCode(200).header("Cache-Control", "public, immutable, max-age=31536000");
|
||||
}
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"/favicon.ico"})
|
||||
void servedWithCache(String path) {
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.get(path)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.header("Cache-Control", "public, immutable, max-age=31536000");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
package com.commafeed.integration;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.request.FeedModificationRequest;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import jakarta.websocket.ClientEndpointConfig;
|
||||
import jakarta.websocket.CloseReason;
|
||||
import jakarta.websocket.ContainerProvider;
|
||||
import jakarta.websocket.DeploymentException;
|
||||
import jakarta.websocket.Endpoint;
|
||||
import jakarta.websocket.EndpointConfig;
|
||||
import jakarta.websocket.Session;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
@@ -10,16 +23,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.websocket.ClientEndpointConfig;
|
||||
import jakarta.websocket.CloseReason;
|
||||
import jakarta.websocket.ContainerProvider;
|
||||
import jakarta.websocket.DeploymentException;
|
||||
import jakarta.websocket.Endpoint;
|
||||
import jakarta.websocket.EndpointConfig;
|
||||
import jakarta.websocket.Session;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@@ -27,137 +31,163 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.request.FeedModificationRequest;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@QuarkusTest
|
||||
@Slf4j
|
||||
class WebSocketIT extends BaseIT {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionClosedIfNotLoggedIn() throws DeploymentException, IOException {
|
||||
AtomicBoolean connected = new AtomicBoolean();
|
||||
AtomicReference<CloseReason> closeReasonRef = new AtomicReference<>();
|
||||
try (Session session = ContainerProvider.getWebSocketContainer().connectToServer(new Endpoint() {
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
connected.set(true);
|
||||
}
|
||||
@Test
|
||||
void sessionClosedIfNotLoggedIn() throws DeploymentException, IOException {
|
||||
AtomicBoolean connected = new AtomicBoolean();
|
||||
AtomicReference<CloseReason> closeReasonRef = new AtomicReference<>();
|
||||
try (Session session =
|
||||
ContainerProvider.getWebSocketContainer()
|
||||
.connectToServer(
|
||||
new Endpoint() {
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
connected.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session, CloseReason closeReason) {
|
||||
closeReasonRef.set(closeReason);
|
||||
}
|
||||
}, buildConfig(List.of()), URI.create(getWebSocketUrl()))) {
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).untilTrue(connected);
|
||||
log.info("connected to {}", session.getRequestURI());
|
||||
@Override
|
||||
public void onClose(Session session, CloseReason closeReason) {
|
||||
closeReasonRef.set(closeReason);
|
||||
}
|
||||
},
|
||||
buildConfig(List.of()),
|
||||
URI.create(getWebSocketUrl()))) {
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).untilTrue(connected);
|
||||
log.info("connected to {}", session.getRequestURI());
|
||||
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).until(() -> closeReasonRef.get() != null);
|
||||
}
|
||||
}
|
||||
Awaitility.await()
|
||||
.atMost(15, TimeUnit.SECONDS)
|
||||
.until(() -> closeReasonRef.get() != null);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void subscribeAndGetsNotified() throws DeploymentException, IOException {
|
||||
List<HttpCookie> cookies = login();
|
||||
@Test
|
||||
void subscribeAndGetsNotified() throws DeploymentException, IOException {
|
||||
List<HttpCookie> cookies = login();
|
||||
|
||||
AtomicBoolean connected = new AtomicBoolean();
|
||||
AtomicReference<String> messageRef = new AtomicReference<>();
|
||||
try (Session session = ContainerProvider.getWebSocketContainer().connectToServer(new Endpoint() {
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
session.addMessageHandler(String.class, messageRef::set);
|
||||
connected.set(true);
|
||||
}
|
||||
}, buildConfig(cookies), URI.create(getWebSocketUrl()))) {
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).untilTrue(connected);
|
||||
log.info("connected to {}", session.getRequestURI());
|
||||
AtomicBoolean connected = new AtomicBoolean();
|
||||
AtomicReference<String> messageRef = new AtomicReference<>();
|
||||
try (Session session =
|
||||
ContainerProvider.getWebSocketContainer()
|
||||
.connectToServer(
|
||||
new Endpoint() {
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
session.addMessageHandler(String.class, messageRef::set);
|
||||
connected.set(true);
|
||||
}
|
||||
},
|
||||
buildConfig(cookies),
|
||||
URI.create(getWebSocketUrl()))) {
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).untilTrue(connected);
|
||||
log.info("connected to {}", session.getRequestURI());
|
||||
|
||||
Long subscriptionId = subscribe(getFeedUrl());
|
||||
Long subscriptionId = subscribe(getFeedUrl());
|
||||
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).until(() -> messageRef.get() != null);
|
||||
Assertions.assertEquals("new-feed-entries:" + subscriptionId + ":2", messageRef.get());
|
||||
}
|
||||
}
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).until(() -> messageRef.get() != null);
|
||||
Assertions.assertEquals("new-feed-entries:" + subscriptionId + ":2", messageRef.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void notNotifiedForFilteredEntries() throws DeploymentException, IOException {
|
||||
List<HttpCookie> cookies = login();
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
@Test
|
||||
void notNotifiedForFilteredEntries() throws DeploymentException, IOException {
|
||||
List<HttpCookie> cookies = login();
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
FeedModificationRequest req = new FeedModificationRequest();
|
||||
req.setId(subscriptionId);
|
||||
req.setName("feed-name");
|
||||
req.setFilter("!titleLower.contains('item 4')");
|
||||
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/feed/modify").then().statusCode(HttpStatus.SC_OK);
|
||||
FeedModificationRequest req = new FeedModificationRequest();
|
||||
req.setId(subscriptionId);
|
||||
req.setName("feed-name");
|
||||
req.setFilter("!titleLower.contains('item 4')");
|
||||
RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/modify")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
|
||||
AtomicBoolean connected = new AtomicBoolean();
|
||||
AtomicReference<String> messageRef = new AtomicReference<>();
|
||||
try (Session session = ContainerProvider.getWebSocketContainer().connectToServer(new Endpoint() {
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
session.addMessageHandler(String.class, messageRef::set);
|
||||
connected.set(true);
|
||||
}
|
||||
}, buildConfig(cookies), URI.create(getWebSocketUrl()))) {
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).untilTrue(connected);
|
||||
log.info("connected to {}", session.getRequestURI());
|
||||
AtomicBoolean connected = new AtomicBoolean();
|
||||
AtomicReference<String> messageRef = new AtomicReference<>();
|
||||
try (Session session =
|
||||
ContainerProvider.getWebSocketContainer()
|
||||
.connectToServer(
|
||||
new Endpoint() {
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
session.addMessageHandler(String.class, messageRef::set);
|
||||
connected.set(true);
|
||||
}
|
||||
},
|
||||
buildConfig(cookies),
|
||||
URI.create(getWebSocketUrl()))) {
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).untilTrue(connected);
|
||||
log.info("connected to {}", session.getRequestURI());
|
||||
|
||||
feedNowReturnsMoreEntries();
|
||||
forceRefreshAllFeeds();
|
||||
feedNowReturnsMoreEntries();
|
||||
forceRefreshAllFeeds();
|
||||
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).until(() -> messageRef.get() != null);
|
||||
Assertions.assertEquals("new-feed-entries:" + subscriptionId + ":1", messageRef.get());
|
||||
}
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).until(() -> messageRef.get() != null);
|
||||
Assertions.assertEquals("new-feed-entries:" + subscriptionId + ":1", messageRef.get());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
void pingPong() throws DeploymentException, IOException {
|
||||
List<HttpCookie> cookies = login();
|
||||
|
||||
@Test
|
||||
void pingPong() throws DeploymentException, IOException {
|
||||
List<HttpCookie> cookies = login();
|
||||
AtomicBoolean connected = new AtomicBoolean();
|
||||
AtomicReference<String> messageRef = new AtomicReference<>();
|
||||
try (Session session =
|
||||
ContainerProvider.getWebSocketContainer()
|
||||
.connectToServer(
|
||||
new Endpoint() {
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
session.addMessageHandler(String.class, messageRef::set);
|
||||
connected.set(true);
|
||||
}
|
||||
},
|
||||
buildConfig(cookies),
|
||||
URI.create(getWebSocketUrl()))) {
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).untilTrue(connected);
|
||||
log.info("connected to {}", session.getRequestURI());
|
||||
|
||||
AtomicBoolean connected = new AtomicBoolean();
|
||||
AtomicReference<String> messageRef = new AtomicReference<>();
|
||||
try (Session session = ContainerProvider.getWebSocketContainer().connectToServer(new Endpoint() {
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
session.addMessageHandler(String.class, messageRef::set);
|
||||
connected.set(true);
|
||||
}
|
||||
}, buildConfig(cookies), URI.create(getWebSocketUrl()))) {
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).untilTrue(connected);
|
||||
log.info("connected to {}", session.getRequestURI());
|
||||
session.getAsyncRemote().sendText("ping");
|
||||
|
||||
session.getAsyncRemote().sendText("ping");
|
||||
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).until(() -> messageRef.get() != null);
|
||||
Assertions.assertEquals("pong", messageRef.get());
|
||||
}
|
||||
}
|
||||
|
||||
private ClientEndpointConfig buildConfig(List<HttpCookie> cookies) {
|
||||
return ClientEndpointConfig.Builder.create().configurator(new ClientEndpointConfig.Configurator() {
|
||||
@Override
|
||||
public void beforeRequest(Map<String, List<String>> headers) {
|
||||
headers.put(HttpHeaders.COOKIE,
|
||||
Collections.singletonList(cookies.stream().map(HttpCookie::toString).collect(Collectors.joining(";"))));
|
||||
}
|
||||
}).build();
|
||||
}
|
||||
Awaitility.await().atMost(15, TimeUnit.SECONDS).until(() -> messageRef.get() != null);
|
||||
Assertions.assertEquals("pong", messageRef.get());
|
||||
}
|
||||
}
|
||||
|
||||
private ClientEndpointConfig buildConfig(List<HttpCookie> cookies) {
|
||||
return ClientEndpointConfig.Builder.create()
|
||||
.configurator(
|
||||
new ClientEndpointConfig.Configurator() {
|
||||
@Override
|
||||
public void beforeRequest(Map<String, List<String>> headers) {
|
||||
headers.put(
|
||||
HttpHeaders.COOKIE,
|
||||
Collections.singletonList(
|
||||
cookies.stream()
|
||||
.map(HttpCookie::toString)
|
||||
.collect(Collectors.joining(";"))));
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
package com.commafeed.integration.cleanup;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.backend.service.db.DatabaseCleaningService;
|
||||
import com.commafeed.frontend.model.Entries;
|
||||
@@ -19,194 +8,229 @@ import com.commafeed.frontend.model.request.FeedModificationRequest;
|
||||
import com.commafeed.frontend.model.request.StarRequest;
|
||||
import com.commafeed.frontend.resource.CategoryREST;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
class DatabaseCleaningIT extends BaseIT {
|
||||
|
||||
@Inject
|
||||
DatabaseCleaningService databaseCleaningService;
|
||||
@Inject DatabaseCleaningService databaseCleaningService;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
private void starEntry(String entryId, Long subscriptionId) {
|
||||
StarRequest starRequest = new StarRequest();
|
||||
starRequest.setId(entryId);
|
||||
starRequest.setFeedId(subscriptionId);
|
||||
starRequest.setStarred(true);
|
||||
RestAssured.given().body(starRequest).contentType(ContentType.JSON).post("rest/entry/star").then().statusCode(200);
|
||||
}
|
||||
private void starEntry(String entryId, Long subscriptionId) {
|
||||
StarRequest starRequest = new StarRequest();
|
||||
starRequest.setId(entryId);
|
||||
starRequest.setFeedId(subscriptionId);
|
||||
starRequest.setStarred(true);
|
||||
RestAssured.given()
|
||||
.body(starRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/entry/star")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
}
|
||||
|
||||
private void unstarEntry(String entryId, Long subscriptionId) {
|
||||
StarRequest starRequest = new StarRequest();
|
||||
starRequest.setId(entryId);
|
||||
starRequest.setFeedId(subscriptionId);
|
||||
starRequest.setStarred(false);
|
||||
RestAssured.given().body(starRequest).contentType(ContentType.JSON).post("rest/entry/star").then().statusCode(200);
|
||||
}
|
||||
private void unstarEntry(String entryId, Long subscriptionId) {
|
||||
StarRequest starRequest = new StarRequest();
|
||||
starRequest.setId(entryId);
|
||||
starRequest.setFeedId(subscriptionId);
|
||||
starRequest.setStarred(false);
|
||||
RestAssured.given()
|
||||
.body(starRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/entry/star")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class KeepStarredEntries {
|
||||
@Nested
|
||||
class KeepStarredEntries {
|
||||
|
||||
@Test
|
||||
void starredEntriesAreKeptWhenCleaningFeedsExceedingCapacity() {
|
||||
// Subscribe to feed and wait for entries
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
@Test
|
||||
void starredEntriesAreKeptWhenCleaningFeedsExceedingCapacity() {
|
||||
// Subscribe to feed and wait for entries
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
// Verify we have 2 entries
|
||||
Entries entriesBefore = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(2, entriesBefore.getEntries().size());
|
||||
// Verify we have 2 entries
|
||||
Entries entriesBefore = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(2, entriesBefore.getEntries().size());
|
||||
|
||||
// Star the first entry
|
||||
Entry entryToStar = entriesBefore.getEntries().getFirst();
|
||||
starEntry(entryToStar.getId(), subscriptionId);
|
||||
// Star the first entry
|
||||
Entry entryToStar = entriesBefore.getEntries().getFirst();
|
||||
starEntry(entryToStar.getId(), subscriptionId);
|
||||
|
||||
// Verify the entry is starred
|
||||
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(1, starredEntries.getEntries().size());
|
||||
Assertions.assertEquals(entryToStar.getId(), starredEntries.getEntries().getFirst().getId());
|
||||
// Verify the entry is starred
|
||||
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(1, starredEntries.getEntries().size());
|
||||
Assertions.assertEquals(
|
||||
entryToStar.getId(), starredEntries.getEntries().getFirst().getId());
|
||||
|
||||
// Run cleanup with capacity of 0 (should delete all non-starred entries)
|
||||
// With keepStarredEntries=true (default), only non-starred entries are counted for capacity.
|
||||
// We have 2 entries, 1 starred and 1 non-starred. With capacity=0, the 1 non-starred entry exceeds capacity.
|
||||
databaseCleaningService.cleanEntriesForFeedsExceedingCapacity(0);
|
||||
// Run cleanup with capacity of 0 (should delete all non-starred entries)
|
||||
// With keepStarredEntries=true (default), only non-starred entries are counted for
|
||||
// capacity.
|
||||
// We have 2 entries, 1 starred and 1 non-starred. With capacity=0, the 1 non-starred
|
||||
// entry
|
||||
// exceeds capacity.
|
||||
databaseCleaningService.cleanEntriesForFeedsExceedingCapacity(0);
|
||||
|
||||
// Verify starred entry is still present
|
||||
Entries starredEntriesAfter = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(1, starredEntriesAfter.getEntries().size());
|
||||
Assertions.assertEquals(entryToStar.getId(), starredEntriesAfter.getEntries().getFirst().getId());
|
||||
// Verify starred entry is still present
|
||||
Entries starredEntriesAfter = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(1, starredEntriesAfter.getEntries().size());
|
||||
Assertions.assertEquals(
|
||||
entryToStar.getId(), starredEntriesAfter.getEntries().getFirst().getId());
|
||||
|
||||
// Verify the non-starred entry was deleted (only starred entry should remain)
|
||||
Entries entriesAfter = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(1, entriesAfter.getEntries().size());
|
||||
Assertions.assertEquals(entryToStar.getId(), entriesAfter.getEntries().getFirst().getId());
|
||||
}
|
||||
// Verify the non-starred entry was deleted (only starred entry should remain)
|
||||
Entries entriesAfter = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(1, entriesAfter.getEntries().size());
|
||||
Assertions.assertEquals(
|
||||
entryToStar.getId(), entriesAfter.getEntries().getFirst().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void starredEntriesAreKeptWhenCleaningOldEntries() {
|
||||
// Subscribe to feed and wait for entries
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
@Test
|
||||
void starredEntriesAreKeptWhenCleaningOldEntries() {
|
||||
// Subscribe to feed and wait for entries
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
// Verify we have 2 entries
|
||||
Entries entriesBefore = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(2, entriesBefore.getEntries().size());
|
||||
// Verify we have 2 entries
|
||||
Entries entriesBefore = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(2, entriesBefore.getEntries().size());
|
||||
|
||||
// Star the first entry (oldest one based on published date in rss.xml)
|
||||
Entry entryToStar = entriesBefore.getEntries().getFirst();
|
||||
starEntry(entryToStar.getId(), subscriptionId);
|
||||
// Star the first entry (oldest one based on published date in rss.xml)
|
||||
Entry entryToStar = entriesBefore.getEntries().getFirst();
|
||||
starEntry(entryToStar.getId(), subscriptionId);
|
||||
|
||||
// Verify the entry is starred
|
||||
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(1, starredEntries.getEntries().size());
|
||||
// Verify the entry is starred
|
||||
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(1, starredEntries.getEntries().size());
|
||||
|
||||
// Run cleanup for entries older than now (should try to delete all entries)
|
||||
// With keepStarredEntries=true (default), the starred entry should be preserved
|
||||
Instant olderThan = Instant.now().plus(Duration.ofDays(1));
|
||||
databaseCleaningService.cleanEntriesOlderThan(olderThan);
|
||||
// Run cleanup for entries older than now (should try to delete all entries)
|
||||
// With keepStarredEntries=true (default), the starred entry should be preserved
|
||||
Instant olderThan = Instant.now().plus(Duration.ofDays(1));
|
||||
databaseCleaningService.cleanEntriesOlderThan(olderThan);
|
||||
|
||||
// Verify starred entry is still present
|
||||
Entries starredEntriesAfter = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(1, starredEntriesAfter.getEntries().size());
|
||||
Assertions.assertEquals(entryToStar.getId(), starredEntriesAfter.getEntries().getFirst().getId());
|
||||
// Verify starred entry is still present
|
||||
Entries starredEntriesAfter = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(1, starredEntriesAfter.getEntries().size());
|
||||
Assertions.assertEquals(
|
||||
entryToStar.getId(), starredEntriesAfter.getEntries().getFirst().getId());
|
||||
|
||||
// Verify the non-starred entry was deleted
|
||||
Entries entriesAfter = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(1, entriesAfter.getEntries().size());
|
||||
Assertions.assertEquals(entryToStar.getId(), entriesAfter.getEntries().getFirst().getId());
|
||||
}
|
||||
// Verify the non-starred entry was deleted
|
||||
Entries entriesAfter = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(1, entriesAfter.getEntries().size());
|
||||
Assertions.assertEquals(
|
||||
entryToStar.getId(), entriesAfter.getEntries().getFirst().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleStarredEntriesAreAllKept() {
|
||||
// Subscribe to feed and wait for entries
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
@Test
|
||||
void multipleStarredEntriesAreAllKept() {
|
||||
// Subscribe to feed and wait for entries
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
// Verify we have 2 entries
|
||||
Entries entriesBefore = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(2, entriesBefore.getEntries().size());
|
||||
// Verify we have 2 entries
|
||||
Entries entriesBefore = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(2, entriesBefore.getEntries().size());
|
||||
|
||||
// Star both entries
|
||||
entriesBefore.getEntries().forEach(entry -> starEntry(entry.getId(), subscriptionId));
|
||||
// Star both entries
|
||||
entriesBefore.getEntries().forEach(entry -> starEntry(entry.getId(), subscriptionId));
|
||||
|
||||
// Verify both entries are starred
|
||||
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(2, starredEntries.getEntries().size());
|
||||
// Verify both entries are starred
|
||||
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(2, starredEntries.getEntries().size());
|
||||
|
||||
// Run cleanup with capacity of 0 (should delete all non-starred entries)
|
||||
databaseCleaningService.cleanEntriesForFeedsExceedingCapacity(0);
|
||||
// Run cleanup with capacity of 0 (should delete all non-starred entries)
|
||||
databaseCleaningService.cleanEntriesForFeedsExceedingCapacity(0);
|
||||
|
||||
// Verify both starred entries are still present
|
||||
Entries starredEntriesAfter = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(2, starredEntriesAfter.getEntries().size());
|
||||
// Verify both starred entries are still present
|
||||
Entries starredEntriesAfter = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(2, starredEntriesAfter.getEntries().size());
|
||||
|
||||
// Verify all entries are preserved (since all are starred)
|
||||
Entries entriesAfter = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(2, entriesAfter.getEntries().size());
|
||||
}
|
||||
// Verify all entries are preserved (since all are starred)
|
||||
Entries entriesAfter = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(2, entriesAfter.getEntries().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unstarringEntryMakesItEligibleForCleanup() {
|
||||
// Subscribe to feed and wait for entries
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
@Test
|
||||
void unstarringEntryMakesItEligibleForCleanup() {
|
||||
// Subscribe to feed and wait for entries
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
// Star the first entry
|
||||
Entries entriesBefore = getFeedEntries(subscriptionId);
|
||||
Entry entry = entriesBefore.getEntries().getFirst();
|
||||
starEntry(entry.getId(), subscriptionId);
|
||||
// Star the first entry
|
||||
Entries entriesBefore = getFeedEntries(subscriptionId);
|
||||
Entry entry = entriesBefore.getEntries().getFirst();
|
||||
starEntry(entry.getId(), subscriptionId);
|
||||
|
||||
// Verify entry is starred
|
||||
Assertions.assertEquals(1, getCategoryEntries(CategoryREST.STARRED).getEntries().size());
|
||||
// Verify entry is starred
|
||||
Assertions.assertEquals(
|
||||
1, getCategoryEntries(CategoryREST.STARRED).getEntries().size());
|
||||
|
||||
// Unstar the entry
|
||||
unstarEntry(entry.getId(), subscriptionId);
|
||||
// Unstar the entry
|
||||
unstarEntry(entry.getId(), subscriptionId);
|
||||
|
||||
// Verify entry is no longer starred
|
||||
Assertions.assertEquals(0, getCategoryEntries(CategoryREST.STARRED).getEntries().size());
|
||||
// Verify entry is no longer starred
|
||||
Assertions.assertEquals(
|
||||
0, getCategoryEntries(CategoryREST.STARRED).getEntries().size());
|
||||
|
||||
// Run cleanup for entries older than now
|
||||
Instant olderThan = Instant.now().plus(Duration.ofDays(1));
|
||||
databaseCleaningService.cleanEntriesOlderThan(olderThan);
|
||||
// Run cleanup for entries older than now
|
||||
Instant olderThan = Instant.now().plus(Duration.ofDays(1));
|
||||
databaseCleaningService.cleanEntriesOlderThan(olderThan);
|
||||
|
||||
// Verify both entries were deleted (neither is starred)
|
||||
Entries entriesAfter = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(0, entriesAfter.getEntries().size());
|
||||
}
|
||||
}
|
||||
// Verify both entries were deleted (neither is starred)
|
||||
Entries entriesAfter = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(0, entriesAfter.getEntries().size());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class AutoMarkAsRead {
|
||||
@Test
|
||||
void entriesAreMarkedAsReadAfterSpecifiedDays() {
|
||||
// Subscribe to feed
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
@Nested
|
||||
class AutoMarkAsRead {
|
||||
@Test
|
||||
void entriesAreMarkedAsReadAfterSpecifiedDays() {
|
||||
// Subscribe to feed
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
// verify we have 2 unread entries
|
||||
Entries entries = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(2, entries.getEntries().stream().filter(e -> !e.isRead()).count());
|
||||
// verify we have 2 unread entries
|
||||
Entries entries = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(
|
||||
2, entries.getEntries().stream().filter(e -> !e.isRead()).count());
|
||||
|
||||
// set auto-mark as read
|
||||
FeedModificationRequest req = new FeedModificationRequest();
|
||||
req.setId(subscriptionId);
|
||||
req.setAutoMarkAsReadAfterDays(1);
|
||||
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/feed/modify").then().statusCode(200);
|
||||
// set auto-mark as read
|
||||
FeedModificationRequest req = new FeedModificationRequest();
|
||||
req.setId(subscriptionId);
|
||||
req.setAutoMarkAsReadAfterDays(1);
|
||||
RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/modify")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
// run auto-mark as read
|
||||
databaseCleaningService.autoMarkAsRead();
|
||||
// run auto-mark as read
|
||||
databaseCleaningService.autoMarkAsRead();
|
||||
|
||||
// verify all entries are now read
|
||||
entries = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(0, entries.getEntries().stream().filter(e -> !e.isRead()).count());
|
||||
}
|
||||
}
|
||||
// verify all entries are now read
|
||||
entries = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(
|
||||
0, entries.getEntries().stream().filter(e -> !e.isRead()).count());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package com.commafeed.integration.rest;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.UserModel;
|
||||
import com.commafeed.frontend.model.request.AdminSaveUserRequest;
|
||||
import com.commafeed.frontend.model.request.IDRequest;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
@@ -9,96 +16,104 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.UserModel;
|
||||
import com.commafeed.frontend.model.request.AdminSaveUserRequest;
|
||||
import com.commafeed.frontend.model.request.IDRequest;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
|
||||
@QuarkusTest
|
||||
class AdminIT extends BaseIT {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Users {
|
||||
@Test
|
||||
void saveModifyAndDeleteNewUser() {
|
||||
List<UserModel> existingUsers = getAllUsers();
|
||||
@Nested
|
||||
class Users {
|
||||
@Test
|
||||
void saveModifyAndDeleteNewUser() {
|
||||
List<UserModel> existingUsers = getAllUsers();
|
||||
|
||||
long userId = createUser();
|
||||
Assertions.assertEquals(existingUsers.size() + 1, getAllUsers().size());
|
||||
long userId = createUser();
|
||||
Assertions.assertEquals(existingUsers.size() + 1, getAllUsers().size());
|
||||
|
||||
UserModel user = getUser(userId);
|
||||
Assertions.assertEquals("test", user.getName());
|
||||
UserModel user = getUser(userId);
|
||||
Assertions.assertEquals("test", user.getName());
|
||||
|
||||
modifyUser(user);
|
||||
Assertions.assertEquals(existingUsers.size() + 1, getAllUsers().size());
|
||||
modifyUser(user);
|
||||
Assertions.assertEquals(existingUsers.size() + 1, getAllUsers().size());
|
||||
|
||||
deleteUser();
|
||||
Assertions.assertEquals(existingUsers.size(), getAllUsers().size());
|
||||
}
|
||||
deleteUser();
|
||||
Assertions.assertEquals(existingUsers.size(), getAllUsers().size());
|
||||
}
|
||||
|
||||
private long createUser() {
|
||||
AdminSaveUserRequest user = new AdminSaveUserRequest();
|
||||
user.setName("test");
|
||||
user.setPassword("Test1234!");
|
||||
user.setEmail("test@test.com");
|
||||
user.setEnabled(true);
|
||||
String response = RestAssured.given()
|
||||
.body(user)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/admin/user/save")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.asString();
|
||||
return Long.parseLong(response);
|
||||
}
|
||||
private long createUser() {
|
||||
AdminSaveUserRequest user = new AdminSaveUserRequest();
|
||||
user.setName("test");
|
||||
user.setPassword("Test1234!");
|
||||
user.setEmail("test@test.com");
|
||||
user.setEnabled(true);
|
||||
String response =
|
||||
RestAssured.given()
|
||||
.body(user)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/admin/user/save")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.asString();
|
||||
return Long.parseLong(response);
|
||||
}
|
||||
|
||||
private UserModel getUser(long userId) {
|
||||
return RestAssured.given()
|
||||
.get("rest/admin/user/get/{id}", userId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(UserModel.class);
|
||||
}
|
||||
private UserModel getUser(long userId) {
|
||||
return RestAssured.given()
|
||||
.get("rest/admin/user/get/{id}", userId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(UserModel.class);
|
||||
}
|
||||
|
||||
private void modifyUser(UserModel user) {
|
||||
user.setEmail("new-email@provider.com");
|
||||
RestAssured.given().body(user).contentType(ContentType.JSON).post("rest/admin/user/save").then().statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
private void modifyUser(UserModel user) {
|
||||
user.setEmail("new-email@provider.com");
|
||||
RestAssured.given()
|
||||
.body(user)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/admin/user/save")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
|
||||
private void deleteUser() {
|
||||
List<UserModel> existingUsers = getAllUsers();
|
||||
UserModel user = existingUsers.stream()
|
||||
.filter(u -> u.getName().equals("test"))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NullPointerException("User not found"));
|
||||
private void deleteUser() {
|
||||
List<UserModel> existingUsers = getAllUsers();
|
||||
UserModel user =
|
||||
existingUsers.stream()
|
||||
.filter(u -> u.getName().equals("test"))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NullPointerException("User not found"));
|
||||
|
||||
IDRequest req = new IDRequest();
|
||||
req.setId(user.getId());
|
||||
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/admin/user/delete").then().statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
|
||||
private List<UserModel> getAllUsers() {
|
||||
return List.of(
|
||||
RestAssured.given().get("rest/admin/user/getAll").then().statusCode(HttpStatus.SC_OK).extract().as(UserModel[].class));
|
||||
}
|
||||
}
|
||||
IDRequest req = new IDRequest();
|
||||
req.setId(user.getId());
|
||||
RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/admin/user/delete")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
|
||||
private List<UserModel> getAllUsers() {
|
||||
return List.of(
|
||||
RestAssured.given()
|
||||
.get("rest/admin/user/getAll")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(UserModel[].class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
package com.commafeed.integration.rest;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.Category;
|
||||
import com.commafeed.frontend.model.Entries;
|
||||
@@ -27,214 +16,273 @@ import com.commafeed.integration.BaseIT;
|
||||
import com.rometools.rome.feed.synd.SyndFeed;
|
||||
import com.rometools.rome.io.FeedException;
|
||||
import com.rometools.rome.io.SyndFeedInput;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
@QuarkusTest
|
||||
class CategoryIT extends BaseIT {
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void modifyCategory() {
|
||||
String category1Id = createCategory("test-category-1");
|
||||
String category2Id = createCategory("test-category-2");
|
||||
String category3Id = createCategory("test-category-3");
|
||||
@Test
|
||||
void modifyCategory() {
|
||||
String category1Id = createCategory("test-category-1");
|
||||
String category2Id = createCategory("test-category-2");
|
||||
String category3Id = createCategory("test-category-3");
|
||||
|
||||
CategoryModificationRequest request = new CategoryModificationRequest();
|
||||
request.setId(Long.valueOf(category2Id));
|
||||
request.setName("modified-category-2");
|
||||
request.setParentId(category1Id);
|
||||
request.setPosition(2);
|
||||
RestAssured.given().body(request).contentType(ContentType.JSON).post("rest/category/modify").then().statusCode(200);
|
||||
CategoryModificationRequest request = new CategoryModificationRequest();
|
||||
request.setId(Long.valueOf(category2Id));
|
||||
request.setName("modified-category-2");
|
||||
request.setParentId(category1Id);
|
||||
request.setPosition(2);
|
||||
RestAssured.given()
|
||||
.body(request)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/category/modify")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
Category root = getRootCategory();
|
||||
Assertions.assertEquals(2, root.getChildren().size());
|
||||
Assertions.assertEquals("test-category-1", root.getChildren().getFirst().getName());
|
||||
Assertions.assertEquals(1, root.getChildren().getFirst().getChildren().size());
|
||||
Assertions.assertEquals("modified-category-2", root.getChildren().getFirst().getChildren().getFirst().getName());
|
||||
Category root = getRootCategory();
|
||||
Assertions.assertEquals(2, root.getChildren().size());
|
||||
Assertions.assertEquals("test-category-1", root.getChildren().getFirst().getName());
|
||||
Assertions.assertEquals(1, root.getChildren().getFirst().getChildren().size());
|
||||
Assertions.assertEquals(
|
||||
"modified-category-2",
|
||||
root.getChildren().getFirst().getChildren().getFirst().getName());
|
||||
|
||||
request = new CategoryModificationRequest();
|
||||
request.setId(Long.valueOf(category3Id));
|
||||
request.setPosition(0);
|
||||
RestAssured.given().body(request).contentType(ContentType.JSON).post("rest/category/modify").then().statusCode(200);
|
||||
request = new CategoryModificationRequest();
|
||||
request.setId(Long.valueOf(category3Id));
|
||||
request.setPosition(0);
|
||||
RestAssured.given()
|
||||
.body(request)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/category/modify")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
root = getRootCategory();
|
||||
Assertions.assertEquals(2, root.getChildren().size());
|
||||
Assertions.assertEquals("test-category-3", root.getChildren().get(0).getName());
|
||||
Assertions.assertEquals("test-category-1", root.getChildren().get(1).getName());
|
||||
}
|
||||
root = getRootCategory();
|
||||
Assertions.assertEquals(2, root.getChildren().size());
|
||||
Assertions.assertEquals("test-category-3", root.getChildren().get(0).getName());
|
||||
Assertions.assertEquals("test-category-1", root.getChildren().get(1).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void collapseCategory() {
|
||||
String categoryId = createCategory("test-category");
|
||||
@Test
|
||||
void collapseCategory() {
|
||||
String categoryId = createCategory("test-category");
|
||||
|
||||
Category root = getRootCategory();
|
||||
Assertions.assertEquals(1, root.getChildren().size());
|
||||
Assertions.assertTrue(root.getChildren().getFirst().isExpanded());
|
||||
Category root = getRootCategory();
|
||||
Assertions.assertEquals(1, root.getChildren().size());
|
||||
Assertions.assertTrue(root.getChildren().getFirst().isExpanded());
|
||||
|
||||
CollapseRequest request = new CollapseRequest();
|
||||
request.setId(Long.valueOf(categoryId));
|
||||
request.setCollapse(true);
|
||||
RestAssured.given().body(request).contentType(ContentType.JSON).post("rest/category/collapse").then().statusCode(200);
|
||||
CollapseRequest request = new CollapseRequest();
|
||||
request.setId(Long.valueOf(categoryId));
|
||||
request.setCollapse(true);
|
||||
RestAssured.given()
|
||||
.body(request)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/category/collapse")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
root = getRootCategory();
|
||||
Assertions.assertEquals(1, root.getChildren().size());
|
||||
Assertions.assertFalse(root.getChildren().getFirst().isExpanded());
|
||||
}
|
||||
root = getRootCategory();
|
||||
Assertions.assertEquals(1, root.getChildren().size());
|
||||
Assertions.assertFalse(root.getChildren().getFirst().isExpanded());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteCategory() {
|
||||
String categoryId = createCategory("test-category");
|
||||
Assertions.assertEquals(1, getRootCategory().getChildren().size());
|
||||
@Test
|
||||
void deleteCategory() {
|
||||
String categoryId = createCategory("test-category");
|
||||
Assertions.assertEquals(1, getRootCategory().getChildren().size());
|
||||
|
||||
IDRequest request = new IDRequest();
|
||||
request.setId(Long.valueOf(categoryId));
|
||||
RestAssured.given().body(request).contentType(ContentType.JSON).post("rest/category/delete").then().statusCode(200);
|
||||
Assertions.assertEquals(0, getRootCategory().getChildren().size());
|
||||
}
|
||||
IDRequest request = new IDRequest();
|
||||
request.setId(Long.valueOf(categoryId));
|
||||
RestAssured.given()
|
||||
.body(request)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/category/delete")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
Assertions.assertEquals(0, getRootCategory().getChildren().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unreadCount() {
|
||||
String categoryId = createCategory("test-category");
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl(), categoryId);
|
||||
Assertions.assertEquals(2, getCategoryEntries(categoryId).getEntries().size());
|
||||
@Test
|
||||
void unreadCount() {
|
||||
String categoryId = createCategory("test-category");
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl(), categoryId);
|
||||
Assertions.assertEquals(2, getCategoryEntries(categoryId).getEntries().size());
|
||||
|
||||
UnreadCount[] counts = RestAssured.given()
|
||||
.get("rest/category/unreadCount")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.as(UnreadCount[].class);
|
||||
UnreadCount[] counts =
|
||||
RestAssured.given()
|
||||
.get("rest/category/unreadCount")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.as(UnreadCount[].class);
|
||||
|
||||
Assertions.assertEquals(1, counts.length);
|
||||
Assertions.assertEquals(subscriptionId, counts[0].getFeedId());
|
||||
Assertions.assertEquals(2, counts[0].getUnreadCount());
|
||||
}
|
||||
Assertions.assertEquals(1, counts.length);
|
||||
Assertions.assertEquals(subscriptionId, counts[0].getFeedId());
|
||||
Assertions.assertEquals(2, counts[0].getUnreadCount());
|
||||
}
|
||||
|
||||
@Nested
|
||||
class MarkEntriesAsRead {
|
||||
@Test
|
||||
void all() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
Assertions.assertTrue(getCategoryEntries(CategoryREST.ALL).getEntries().stream().noneMatch(Entry::isRead));
|
||||
@Nested
|
||||
class MarkEntriesAsRead {
|
||||
@Test
|
||||
void all() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
Assertions.assertTrue(
|
||||
getCategoryEntries(CategoryREST.ALL).getEntries().stream()
|
||||
.noneMatch(Entry::isRead));
|
||||
|
||||
MarkRequest request = new MarkRequest();
|
||||
request.setId(CategoryREST.ALL);
|
||||
request.setRead(true);
|
||||
RestAssured.given().body(request).contentType(ContentType.JSON).post("rest/category/mark").then().statusCode(200);
|
||||
Assertions.assertTrue(getCategoryEntries(CategoryREST.ALL).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
MarkRequest request = new MarkRequest();
|
||||
request.setId(CategoryREST.ALL);
|
||||
request.setRead(true);
|
||||
RestAssured.given()
|
||||
.body(request)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/category/mark")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
Assertions.assertTrue(
|
||||
getCategoryEntries(CategoryREST.ALL).getEntries().stream()
|
||||
.allMatch(Entry::isRead));
|
||||
}
|
||||
|
||||
@Test
|
||||
void specificCategory() {
|
||||
String categoryId = createCategory("test-category");
|
||||
subscribeAndWaitForEntries(getFeedUrl(), categoryId);
|
||||
Assertions.assertTrue(getCategoryEntries(categoryId).getEntries().stream().noneMatch(Entry::isRead));
|
||||
@Test
|
||||
void specificCategory() {
|
||||
String categoryId = createCategory("test-category");
|
||||
subscribeAndWaitForEntries(getFeedUrl(), categoryId);
|
||||
Assertions.assertTrue(
|
||||
getCategoryEntries(categoryId).getEntries().stream().noneMatch(Entry::isRead));
|
||||
|
||||
MarkRequest request = new MarkRequest();
|
||||
request.setId(categoryId);
|
||||
request.setRead(true);
|
||||
RestAssured.given().body(request).contentType(ContentType.JSON).post("rest/category/mark").then().statusCode(200);
|
||||
Assertions.assertTrue(getCategoryEntries(categoryId).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
}
|
||||
MarkRequest request = new MarkRequest();
|
||||
request.setId(categoryId);
|
||||
request.setRead(true);
|
||||
RestAssured.given()
|
||||
.body(request)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/category/mark")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
Assertions.assertTrue(
|
||||
getCategoryEntries(categoryId).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class GetEntries {
|
||||
@Test
|
||||
void all() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entries entries = getCategoryEntries(CategoryREST.ALL);
|
||||
Assertions.assertEquals(2, entries.getEntries().size());
|
||||
}
|
||||
@Nested
|
||||
class GetEntries {
|
||||
@Test
|
||||
void all() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entries entries = getCategoryEntries(CategoryREST.ALL);
|
||||
Assertions.assertEquals(2, entries.getEntries().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pagination() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
@Test
|
||||
void pagination() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
Entries firstPage = getCategoryEntries(CategoryREST.ALL, 0, 1);
|
||||
Assertions.assertEquals(1, firstPage.getEntries().size());
|
||||
Assertions.assertTrue(firstPage.isHasMore());
|
||||
Entries firstPage = getCategoryEntries(CategoryREST.ALL, 0, 1);
|
||||
Assertions.assertEquals(1, firstPage.getEntries().size());
|
||||
Assertions.assertTrue(firstPage.isHasMore());
|
||||
|
||||
Entries lastPage = getCategoryEntries(CategoryREST.ALL, 1, 10);
|
||||
Assertions.assertEquals(1, lastPage.getEntries().size());
|
||||
Assertions.assertFalse(lastPage.isHasMore());
|
||||
}
|
||||
Entries lastPage = getCategoryEntries(CategoryREST.ALL, 1, 10);
|
||||
Assertions.assertEquals(1, lastPage.getEntries().size());
|
||||
Assertions.assertFalse(lastPage.isHasMore());
|
||||
}
|
||||
|
||||
@Test
|
||||
void allAsFeed() throws FeedException {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
String xml = RestAssured.given()
|
||||
.get("rest/category/entriesAsFeed?id=all")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.contentType(ContentType.XML)
|
||||
.extract()
|
||||
.asString();
|
||||
@Test
|
||||
void allAsFeed() throws FeedException {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
String xml =
|
||||
RestAssured.given()
|
||||
.get("rest/category/entriesAsFeed?id=all")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.contentType(ContentType.XML)
|
||||
.extract()
|
||||
.asString();
|
||||
|
||||
InputSource source = new InputSource(new StringReader(xml));
|
||||
SyndFeed feed = new SyndFeedInput().build(source);
|
||||
Assertions.assertEquals(2, feed.getEntries().size());
|
||||
}
|
||||
InputSource source = new InputSource(new StringReader(xml));
|
||||
SyndFeed feed = new SyndFeedInput().build(source);
|
||||
Assertions.assertEquals(2, feed.getEntries().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void starred() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Assertions.assertEquals(0, getCategoryEntries(CategoryREST.STARRED).getEntries().size());
|
||||
@Test
|
||||
void starred() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Assertions.assertEquals(
|
||||
0, getCategoryEntries(CategoryREST.STARRED).getEntries().size());
|
||||
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
|
||||
StarRequest starRequest = new StarRequest();
|
||||
starRequest.setId(entry.getId());
|
||||
starRequest.setFeedId(subscriptionId);
|
||||
starRequest.setStarred(true);
|
||||
RestAssured.given().body(starRequest).contentType(ContentType.JSON).post("rest/entry/star");
|
||||
StarRequest starRequest = new StarRequest();
|
||||
starRequest.setId(entry.getId());
|
||||
starRequest.setFeedId(subscriptionId);
|
||||
starRequest.setStarred(true);
|
||||
RestAssured.given()
|
||||
.body(starRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/entry/star");
|
||||
|
||||
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(1, starredEntries.getEntries().size());
|
||||
Assertions.assertEquals(entry.getId(), starredEntries.getEntries().getFirst().getId());
|
||||
}
|
||||
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
|
||||
Assertions.assertEquals(1, starredEntries.getEntries().size());
|
||||
Assertions.assertEquals(entry.getId(), starredEntries.getEntries().getFirst().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tagged() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Assertions.assertEquals(0, getTaggedEntries("my-tag").getEntries().size());
|
||||
@Test
|
||||
void tagged() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Assertions.assertEquals(0, getTaggedEntries("my-tag").getEntries().size());
|
||||
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
|
||||
TagRequest tagRequest = new TagRequest();
|
||||
tagRequest.setEntryId(Long.valueOf(entry.getId()));
|
||||
tagRequest.setTags(List.of("my-tag"));
|
||||
RestAssured.given().body(tagRequest).contentType(ContentType.JSON).post("rest/entry/tag");
|
||||
TagRequest tagRequest = new TagRequest();
|
||||
tagRequest.setEntryId(Long.valueOf(entry.getId()));
|
||||
tagRequest.setTags(List.of("my-tag"));
|
||||
RestAssured.given()
|
||||
.body(tagRequest)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/entry/tag");
|
||||
|
||||
Entries taggedEntries = getTaggedEntries("my-tag");
|
||||
Assertions.assertEquals(1, taggedEntries.getEntries().size());
|
||||
Assertions.assertEquals(entry.getId(), taggedEntries.getEntries().getFirst().getId());
|
||||
}
|
||||
Entries taggedEntries = getTaggedEntries("my-tag");
|
||||
Assertions.assertEquals(1, taggedEntries.getEntries().size());
|
||||
Assertions.assertEquals(entry.getId(), taggedEntries.getEntries().getFirst().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void keywords() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
Assertions.assertEquals(1, getCategoryEntries(CategoryREST.ALL, "Item 2 description").getEntries().size());
|
||||
}
|
||||
@Test
|
||||
void keywords() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
Assertions.assertEquals(
|
||||
1,
|
||||
getCategoryEntries(CategoryREST.ALL, "Item 2 description").getEntries().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void specificCategory() {
|
||||
String categoryId = createCategory("test-category");
|
||||
subscribeAndWaitForEntries(getFeedUrl(), categoryId);
|
||||
Entries entries = getCategoryEntries(categoryId);
|
||||
Assertions.assertEquals(2, entries.getEntries().size());
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void specificCategory() {
|
||||
String categoryId = createCategory("test-category");
|
||||
subscribeAndWaitForEntries(getFeedUrl(), categoryId);
|
||||
Entries entries = getCategoryEntries(categoryId);
|
||||
Assertions.assertEquals(2, entries.getEntries().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,5 @@
|
||||
package com.commafeed.integration.rest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Objects;
|
||||
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.Entries;
|
||||
import com.commafeed.frontend.model.Entry;
|
||||
@@ -36,278 +14,346 @@ import com.commafeed.integration.BaseIT;
|
||||
import com.rometools.rome.feed.synd.SyndFeed;
|
||||
import com.rometools.rome.io.FeedException;
|
||||
import com.rometools.rome.io.SyndFeedInput;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Objects;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
@QuarkusTest
|
||||
class FeedIT extends BaseIT {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Fetch {
|
||||
@Test
|
||||
void fetchFeed() {
|
||||
FeedInfoRequest req = new FeedInfoRequest();
|
||||
req.setUrl(getFeedUrl());
|
||||
@Nested
|
||||
class Fetch {
|
||||
@Test
|
||||
void fetchFeed() {
|
||||
FeedInfoRequest req = new FeedInfoRequest();
|
||||
req.setUrl(getFeedUrl());
|
||||
|
||||
FeedInfo feedInfo = RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/fetch")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(FeedInfo.class);
|
||||
Assertions.assertEquals("CommaFeed test feed", feedInfo.getTitle());
|
||||
Assertions.assertEquals(getFeedUrl(), feedInfo.getUrl());
|
||||
}
|
||||
}
|
||||
FeedInfo feedInfo =
|
||||
RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/fetch")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(FeedInfo.class);
|
||||
Assertions.assertEquals("CommaFeed test feed", feedInfo.getTitle());
|
||||
Assertions.assertEquals(getFeedUrl(), feedInfo.getUrl());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Subscribe {
|
||||
@Test
|
||||
void subscribeAndReadEntries() {
|
||||
long subscriptionId = subscribe(getFeedUrl());
|
||||
Awaitility.await().atMost(Duration.ofSeconds(15)).until(() -> getFeedEntries(subscriptionId), e -> e.getEntries().size() == 2);
|
||||
}
|
||||
@Nested
|
||||
class Subscribe {
|
||||
@Test
|
||||
void subscribeAndReadEntries() {
|
||||
long subscriptionId = subscribe(getFeedUrl());
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(15))
|
||||
.until(() -> getFeedEntries(subscriptionId), e -> e.getEntries().size() == 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void subscribeFromUrl() {
|
||||
RestAssured.given()
|
||||
.queryParam("url", getFeedUrl())
|
||||
.redirects()
|
||||
.follow(false)
|
||||
.get("rest/feed/subscribe")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_TEMPORARY_REDIRECT);
|
||||
}
|
||||
@Test
|
||||
void subscribeFromUrl() {
|
||||
RestAssured.given()
|
||||
.queryParam("url", getFeedUrl())
|
||||
.redirects()
|
||||
.follow(false)
|
||||
.get("rest/feed/subscribe")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_TEMPORARY_REDIRECT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unsubscribeFromUnknownFeed() {
|
||||
Assertions.assertEquals(HttpStatus.SC_NOT_FOUND, unsubsribe(1L));
|
||||
}
|
||||
@Test
|
||||
void unsubscribeFromUnknownFeed() {
|
||||
Assertions.assertEquals(HttpStatus.SC_NOT_FOUND, unsubsribe(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unsubscribeFromKnownFeed() {
|
||||
long subscriptionId = subscribe(getFeedUrl());
|
||||
Assertions.assertEquals(HttpStatus.SC_OK, unsubsribe(subscriptionId));
|
||||
}
|
||||
@Test
|
||||
void unsubscribeFromKnownFeed() {
|
||||
long subscriptionId = subscribe(getFeedUrl());
|
||||
Assertions.assertEquals(HttpStatus.SC_OK, unsubsribe(subscriptionId));
|
||||
}
|
||||
|
||||
private int unsubsribe(long subscriptionId) {
|
||||
IDRequest request = new IDRequest();
|
||||
request.setId(subscriptionId);
|
||||
private int unsubsribe(long subscriptionId) {
|
||||
IDRequest request = new IDRequest();
|
||||
request.setId(subscriptionId);
|
||||
|
||||
return RestAssured.given()
|
||||
.body(request)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/unsubscribe")
|
||||
.then()
|
||||
.extract()
|
||||
.statusCode();
|
||||
}
|
||||
}
|
||||
return RestAssured.given()
|
||||
.body(request)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/unsubscribe")
|
||||
.then()
|
||||
.extract()
|
||||
.statusCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Mark {
|
||||
@Test
|
||||
void markWithoutDates() {
|
||||
long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
markFeedEntries(subscriptionId, null, null);
|
||||
Assertions.assertTrue(getFeedEntries(subscriptionId).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
@Nested
|
||||
class Mark {
|
||||
@Test
|
||||
void markWithoutDates() {
|
||||
long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
markFeedEntries(subscriptionId, null, null);
|
||||
Assertions.assertTrue(
|
||||
getFeedEntries(subscriptionId).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
|
||||
@Test
|
||||
void markOlderThan() {
|
||||
long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
markFeedEntries(subscriptionId, LocalDate.of(2023, 12, 28).atStartOfDay().toInstant(ZoneOffset.UTC), null);
|
||||
Assertions.assertEquals(1, getFeedEntries(subscriptionId).getEntries().stream().filter(Entry::isRead).count());
|
||||
}
|
||||
@Test
|
||||
void markOlderThan() {
|
||||
long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
markFeedEntries(
|
||||
subscriptionId,
|
||||
LocalDate.of(2023, 12, 28).atStartOfDay().toInstant(ZoneOffset.UTC),
|
||||
null);
|
||||
Assertions.assertEquals(
|
||||
1,
|
||||
getFeedEntries(subscriptionId).getEntries().stream()
|
||||
.filter(Entry::isRead)
|
||||
.count());
|
||||
}
|
||||
|
||||
@Test
|
||||
void markInsertedBeforeBeforeSubscription() {
|
||||
// mariadb/mysql timestamp precision is 1 second
|
||||
Instant threshold = Instant.now().minus(Duration.ofSeconds(1));
|
||||
@Test
|
||||
void markInsertedBeforeBeforeSubscription() {
|
||||
// mariadb/mysql timestamp precision is 1 second
|
||||
Instant threshold = Instant.now().minus(Duration.ofSeconds(1));
|
||||
|
||||
long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
markFeedEntries(subscriptionId, null, threshold);
|
||||
Assertions.assertTrue(getFeedEntries(subscriptionId).getEntries().stream().noneMatch(Entry::isRead));
|
||||
}
|
||||
long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
markFeedEntries(subscriptionId, null, threshold);
|
||||
Assertions.assertTrue(
|
||||
getFeedEntries(subscriptionId).getEntries().stream().noneMatch(Entry::isRead));
|
||||
}
|
||||
|
||||
@Test
|
||||
void markInsertedBeforeAfterSubscription() {
|
||||
long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
@Test
|
||||
void markInsertedBeforeAfterSubscription() {
|
||||
long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
// mariadb/mysql timestamp precision is 1 second
|
||||
Instant threshold = Instant.now().plus(Duration.ofSeconds(1));
|
||||
// mariadb/mysql timestamp precision is 1 second
|
||||
Instant threshold = Instant.now().plus(Duration.ofSeconds(1));
|
||||
|
||||
markFeedEntries(subscriptionId, null, threshold);
|
||||
Assertions.assertTrue(getFeedEntries(subscriptionId).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
markFeedEntries(subscriptionId, null, threshold);
|
||||
Assertions.assertTrue(
|
||||
getFeedEntries(subscriptionId).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
|
||||
private void markFeedEntries(long subscriptionId, Instant olderThan, Instant insertedBefore) {
|
||||
MarkRequest request = new MarkRequest();
|
||||
request.setId(String.valueOf(subscriptionId));
|
||||
request.setOlderThan(olderThan == null ? null : olderThan.toEpochMilli());
|
||||
request.setInsertedBefore(insertedBefore == null ? null : insertedBefore.toEpochMilli());
|
||||
private void markFeedEntries(
|
||||
long subscriptionId, Instant olderThan, Instant insertedBefore) {
|
||||
MarkRequest request = new MarkRequest();
|
||||
request.setId(String.valueOf(subscriptionId));
|
||||
request.setOlderThan(olderThan == null ? null : olderThan.toEpochMilli());
|
||||
request.setInsertedBefore(
|
||||
insertedBefore == null ? null : insertedBefore.toEpochMilli());
|
||||
|
||||
RestAssured.given().body(request).contentType(ContentType.JSON).post("rest/feed/mark").then().statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
}
|
||||
RestAssured.given()
|
||||
.body(request)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/mark")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Refresh {
|
||||
@Nested
|
||||
class Refresh {
|
||||
|
||||
@Test
|
||||
void refreshAll() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Assertions.assertEquals(2, getCategoryEntries(CategoryREST.ALL).getEntries().size());
|
||||
@Test
|
||||
void refreshAll() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Assertions.assertEquals(2, getCategoryEntries(CategoryREST.ALL).getEntries().size());
|
||||
|
||||
// mariadb/mysql timestamp precision is 1 second
|
||||
Instant threshold = Instant.now().minus(Duration.ofSeconds(1));
|
||||
Assertions.assertEquals(HttpStatus.SC_OK, forceRefreshAllFeeds());
|
||||
// mariadb/mysql timestamp precision is 1 second
|
||||
Instant threshold = Instant.now().minus(Duration.ofSeconds(1));
|
||||
Assertions.assertEquals(HttpStatus.SC_OK, forceRefreshAllFeeds());
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(15))
|
||||
.until(() -> getSubscription(subscriptionId), f -> f.getLastRefresh().isAfter(threshold));
|
||||
Assertions.assertEquals(2, getCategoryEntries(CategoryREST.ALL).getEntries().size());
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(15))
|
||||
.until(
|
||||
() -> getSubscription(subscriptionId),
|
||||
f -> f.getLastRefresh().isAfter(threshold));
|
||||
Assertions.assertEquals(2, getCategoryEntries(CategoryREST.ALL).getEntries().size());
|
||||
|
||||
Assertions.assertEquals(HttpStatus.SC_TOO_MANY_REQUESTS, forceRefreshAllFeeds());
|
||||
}
|
||||
}
|
||||
Assertions.assertEquals(HttpStatus.SC_TOO_MANY_REQUESTS, forceRefreshAllFeeds());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class RSS {
|
||||
@Test
|
||||
void allAsFeed() throws FeedException {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
String xml = RestAssured.given()
|
||||
.get("rest/feed/entriesAsFeed?id={id}", subscriptionId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.contentType(ContentType.XML)
|
||||
.extract()
|
||||
.asString();
|
||||
@Nested
|
||||
class RSS {
|
||||
@Test
|
||||
void allAsFeed() throws FeedException {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
String xml =
|
||||
RestAssured.given()
|
||||
.get("rest/feed/entriesAsFeed?id={id}", subscriptionId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.contentType(ContentType.XML)
|
||||
.extract()
|
||||
.asString();
|
||||
|
||||
InputSource source = new InputSource(new StringReader(xml));
|
||||
SyndFeed feed = new SyndFeedInput().build(source);
|
||||
Assertions.assertEquals(2, feed.getEntries().size());
|
||||
}
|
||||
}
|
||||
InputSource source = new InputSource(new StringReader(xml));
|
||||
SyndFeed feed = new SyndFeedInput().build(source);
|
||||
Assertions.assertEquals(2, feed.getEntries().size());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Modify {
|
||||
@Test
|
||||
void modify() {
|
||||
Long subscriptionId = subscribe(getFeedUrl());
|
||||
@Nested
|
||||
class Modify {
|
||||
@Test
|
||||
void modify() {
|
||||
Long subscriptionId = subscribe(getFeedUrl());
|
||||
|
||||
Subscription subscription = getSubscription(subscriptionId);
|
||||
Subscription subscription = getSubscription(subscriptionId);
|
||||
|
||||
FeedModificationRequest req = new FeedModificationRequest();
|
||||
req.setId(subscriptionId);
|
||||
req.setName("new name");
|
||||
req.setCategoryId(subscription.getCategoryId());
|
||||
req.setPosition(1);
|
||||
req.setFilter("url.endsWith('commafeed')");
|
||||
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/feed/modify").then().statusCode(HttpStatus.SC_OK);
|
||||
FeedModificationRequest req = new FeedModificationRequest();
|
||||
req.setId(subscriptionId);
|
||||
req.setName("new name");
|
||||
req.setCategoryId(subscription.getCategoryId());
|
||||
req.setPosition(1);
|
||||
req.setFilter("url.endsWith('commafeed')");
|
||||
RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/modify")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
|
||||
subscription = getSubscription(subscriptionId);
|
||||
Assertions.assertEquals("new name", subscription.getName());
|
||||
Assertions.assertEquals("url.endsWith('commafeed')", subscription.getFilter());
|
||||
}
|
||||
}
|
||||
subscription = getSubscription(subscriptionId);
|
||||
Assertions.assertEquals("new name", subscription.getName());
|
||||
Assertions.assertEquals("url.endsWith('commafeed')", subscription.getFilter());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Favicon {
|
||||
@Test
|
||||
void favicon() throws IOException {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
@Nested
|
||||
class Favicon {
|
||||
@Test
|
||||
void favicon() throws IOException {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
byte[] icon = RestAssured.given()
|
||||
.get("rest/feed/favicon/{id}", subscriptionId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.header(HttpHeaders.CACHE_CONTROL, "max-age=2592000")
|
||||
.extract()
|
||||
.response()
|
||||
.asByteArray();
|
||||
byte[] defaultFavicon = IOUtils.toByteArray(Objects.requireNonNull(getClass().getResource("/images/default_favicon.gif")));
|
||||
Assertions.assertArrayEquals(defaultFavicon, icon);
|
||||
}
|
||||
}
|
||||
byte[] icon =
|
||||
RestAssured.given()
|
||||
.get("rest/feed/favicon/{id}", subscriptionId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.header(HttpHeaders.CACHE_CONTROL, "max-age=2592000")
|
||||
.extract()
|
||||
.response()
|
||||
.asByteArray();
|
||||
byte[] defaultFavicon =
|
||||
IOUtils.toByteArray(
|
||||
Objects.requireNonNull(
|
||||
getClass().getResource("/images/default_favicon.gif")));
|
||||
Assertions.assertArrayEquals(defaultFavicon, icon);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Opml {
|
||||
@Test
|
||||
void importExportOpml() {
|
||||
importOpml();
|
||||
String opml = RestAssured.given().get("rest/feed/export").then().statusCode(HttpStatus.SC_OK).extract().asString();
|
||||
Assertions.assertTrue(opml.contains("<title>admin subscriptions in CommaFeed</title>"));
|
||||
}
|
||||
@Nested
|
||||
class Opml {
|
||||
@Test
|
||||
void importExportOpml() {
|
||||
importOpml();
|
||||
String opml =
|
||||
RestAssured.given()
|
||||
.get("rest/feed/export")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.asString();
|
||||
Assertions.assertTrue(opml.contains("<title>admin subscriptions in CommaFeed</title>"));
|
||||
}
|
||||
|
||||
void importOpml() {
|
||||
InputStream stream = Objects.requireNonNull(getClass().getResourceAsStream("/opml/opml_v2.0.xml"));
|
||||
void importOpml() {
|
||||
InputStream stream =
|
||||
Objects.requireNonNull(getClass().getResourceAsStream("/opml/opml_v2.0.xml"));
|
||||
|
||||
RestAssured.given()
|
||||
.multiPart("file", "opml_v2.0.xml", stream, MediaType.MULTIPART_FORM_DATA)
|
||||
.post("rest/feed/import")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
}
|
||||
RestAssured.given()
|
||||
.multiPart("file", "opml_v2.0.xml", stream, MediaType.MULTIPART_FORM_DATA)
|
||||
.post("rest/feed/import")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Filter {
|
||||
@Test
|
||||
void filterEntriesOnNewFeedItems() throws IOException {
|
||||
// subscribe and wait for initial 2 entries
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entries initialEntries = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(2, initialEntries.getEntries().size());
|
||||
@Nested
|
||||
class Filter {
|
||||
@Test
|
||||
void filterEntriesOnNewFeedItems() throws IOException {
|
||||
// subscribe and wait for initial 2 entries
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entries initialEntries = getFeedEntries(subscriptionId);
|
||||
Assertions.assertEquals(2, initialEntries.getEntries().size());
|
||||
|
||||
// set up a filter that excludes entries with "item 4" in the title
|
||||
Subscription subscription = getSubscription(subscriptionId);
|
||||
FeedModificationRequest req = new FeedModificationRequest();
|
||||
req.setId(subscriptionId);
|
||||
req.setName(subscription.getName());
|
||||
req.setCategoryId(subscription.getCategoryId());
|
||||
req.setPosition(subscription.getPosition());
|
||||
req.setFilter("!titleLower.contains('item 4')");
|
||||
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/feed/modify").then().statusCode(HttpStatus.SC_OK);
|
||||
// set up a filter that excludes entries with "item 4" in the title
|
||||
Subscription subscription = getSubscription(subscriptionId);
|
||||
FeedModificationRequest req = new FeedModificationRequest();
|
||||
req.setId(subscriptionId);
|
||||
req.setName(subscription.getName());
|
||||
req.setCategoryId(subscription.getCategoryId());
|
||||
req.setPosition(subscription.getPosition());
|
||||
req.setFilter("!titleLower.contains('item 4')");
|
||||
RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/feed/modify")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
|
||||
// verify filter is set
|
||||
subscription = getSubscription(subscriptionId);
|
||||
Assertions.assertEquals("!titleLower.contains('item 4')", subscription.getFilter());
|
||||
// verify filter is set
|
||||
subscription = getSubscription(subscriptionId);
|
||||
Assertions.assertEquals("!titleLower.contains('item 4')", subscription.getFilter());
|
||||
|
||||
// feed now returns 2 more entries (Item 3 and Item 4)
|
||||
feedNowReturnsMoreEntries();
|
||||
forceRefreshAllFeeds();
|
||||
// feed now returns 2 more entries (Item 3 and Item 4)
|
||||
feedNowReturnsMoreEntries();
|
||||
forceRefreshAllFeeds();
|
||||
|
||||
// wait for new entries to be fetched
|
||||
Awaitility.await().atMost(Duration.ofSeconds(15)).until(() -> getCategoryEntries("all"), e -> e.getEntries().size() == 4);
|
||||
|
||||
// verify that Item 4 was marked as read because it matches the filter
|
||||
Entries unreadEntries = RestAssured.given()
|
||||
.get("rest/feed/entries?id={id}&readType=unread", subscriptionId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
Assertions.assertEquals(3, unreadEntries.getEntries().size());
|
||||
Assertions.assertTrue(unreadEntries.getEntries().stream().noneMatch(e -> e.getTitle().toLowerCase().contains("item 4")),
|
||||
"Item 4 should be filtered out (marked as read)");
|
||||
}
|
||||
}
|
||||
// wait for new entries to be fetched
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(15))
|
||||
.until(() -> getCategoryEntries("all"), e -> e.getEntries().size() == 4);
|
||||
|
||||
// verify that Item 4 was marked as read because it matches the filter
|
||||
Entries unreadEntries =
|
||||
RestAssured.given()
|
||||
.get("rest/feed/entries?id={id}&readType=unread", subscriptionId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(Entries.class);
|
||||
Assertions.assertEquals(3, unreadEntries.getEntries().size());
|
||||
Assertions.assertTrue(
|
||||
unreadEntries.getEntries().stream()
|
||||
.noneMatch(e -> e.getTitle().toLowerCase().contains("item 4")),
|
||||
"Item 4 should be filtered out (marked as read)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
package com.commafeed.integration.rest;
|
||||
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.backend.Digests;
|
||||
import com.commafeed.frontend.model.Entry;
|
||||
@@ -15,189 +9,247 @@ import com.commafeed.frontend.model.request.StarRequest;
|
||||
import com.commafeed.frontend.resource.fever.FeverResponse;
|
||||
import com.commafeed.frontend.resource.fever.FeverResponse.FeverItem;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import lombok.Setter;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
class FeverIT extends BaseIT {
|
||||
|
||||
private FeverClient client;
|
||||
private FeverClient client;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
|
||||
// create api key
|
||||
ProfileModificationRequest req = new ProfileModificationRequest();
|
||||
req.setCurrentPassword(TestConstants.ADMIN_PASSWORD);
|
||||
req.setNewApiKey(true);
|
||||
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/user/profile").then().statusCode(HttpStatus.SC_OK);
|
||||
// create api key
|
||||
ProfileModificationRequest req = new ProfileModificationRequest();
|
||||
req.setCurrentPassword(TestConstants.ADMIN_PASSWORD);
|
||||
req.setNewApiKey(true);
|
||||
RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
|
||||
// retrieve api key
|
||||
UserModel user = RestAssured.given().get("rest/user/profile").then().statusCode(HttpStatus.SC_OK).extract().as(UserModel.class);
|
||||
this.client = new FeverClient(user.getId(), user.getApiKey());
|
||||
}
|
||||
// retrieve api key
|
||||
UserModel user =
|
||||
RestAssured.given()
|
||||
.get("rest/user/profile")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(UserModel.class);
|
||||
this.client = new FeverClient(user.getId(), user.getApiKey());
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidApiKey() {
|
||||
client.apiKey = "invalid-key";
|
||||
@Test
|
||||
void invalidApiKey() {
|
||||
client.apiKey = "invalid-key";
|
||||
|
||||
FeverResponse response = client.execute("feeds");
|
||||
Assertions.assertFalse(response.isAuth());
|
||||
}
|
||||
FeverResponse response = client.execute("feeds");
|
||||
Assertions.assertFalse(response.isAuth());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validApiKey() {
|
||||
FeverResponse response = client.execute("feeds");
|
||||
Assertions.assertTrue(response.isAuth());
|
||||
}
|
||||
@Test
|
||||
void validApiKey() {
|
||||
FeverResponse response = client.execute("feeds");
|
||||
Assertions.assertTrue(response.isAuth());
|
||||
}
|
||||
|
||||
@Test
|
||||
void feeds() {
|
||||
subscribe(getFeedUrl());
|
||||
FeverResponse feverResponse = client.execute("feeds");
|
||||
Assertions.assertEquals(1, feverResponse.getFeeds().size());
|
||||
}
|
||||
@Test
|
||||
void feeds() {
|
||||
subscribe(getFeedUrl());
|
||||
FeverResponse feverResponse = client.execute("feeds");
|
||||
Assertions.assertEquals(1, feverResponse.getFeeds().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unreadEntries() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
FeverResponse feverResponse = client.execute("unread_item_ids");
|
||||
Assertions.assertEquals(2, feverResponse.getUnreadItemIds().size());
|
||||
}
|
||||
@Test
|
||||
void unreadEntries() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
FeverResponse feverResponse = client.execute("unread_item_ids");
|
||||
Assertions.assertEquals(2, feverResponse.getUnreadItemIds().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void entries() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
FeverResponse feverResponse = client.execute("items");
|
||||
Assertions.assertEquals(2, feverResponse.getItems().size());
|
||||
@Test
|
||||
void entries() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
FeverResponse feverResponse = client.execute("items");
|
||||
Assertions.assertEquals(2, feverResponse.getItems().size());
|
||||
|
||||
FeverItem item = feverResponse.getItems().getFirst();
|
||||
Assertions.assertEquals(subscriptionId, item.getFeedId());
|
||||
Assertions.assertEquals("Item 2", item.getTitle());
|
||||
Assertions.assertEquals("Item 2 description", item.getHtml());
|
||||
Assertions.assertEquals("https://hostname.local/commafeed/2", item.getUrl());
|
||||
Assertions.assertFalse(item.isSaved());
|
||||
Assertions.assertFalse(item.isRead());
|
||||
}
|
||||
FeverItem item = feverResponse.getItems().getFirst();
|
||||
Assertions.assertEquals(subscriptionId, item.getFeedId());
|
||||
Assertions.assertEquals("Item 2", item.getTitle());
|
||||
Assertions.assertEquals("Item 2 description", item.getHtml());
|
||||
Assertions.assertEquals("https://hostname.local/commafeed/2", item.getUrl());
|
||||
Assertions.assertFalse(item.isSaved());
|
||||
Assertions.assertFalse(item.isRead());
|
||||
}
|
||||
|
||||
@Test
|
||||
void entriesByIds() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
@Test
|
||||
void entriesByIds() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
|
||||
FeverResponse feverResponse = client.execute("items", new Param("with_ids", entry.getId()));
|
||||
Assertions.assertEquals(1, feverResponse.getItems().size());
|
||||
}
|
||||
FeverResponse feverResponse = client.execute("items", new Param("with_ids", entry.getId()));
|
||||
Assertions.assertEquals(1, feverResponse.getItems().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void savedEntries() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
@Test
|
||||
void savedEntries() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
|
||||
StarRequest starRequest = new StarRequest();
|
||||
starRequest.setId(entry.getId());
|
||||
starRequest.setFeedId(subscriptionId);
|
||||
starRequest.setStarred(true);
|
||||
RestAssured.given().body(starRequest).contentType(ContentType.JSON).post("rest/entry/star");
|
||||
StarRequest starRequest = new StarRequest();
|
||||
starRequest.setId(entry.getId());
|
||||
starRequest.setFeedId(subscriptionId);
|
||||
starRequest.setStarred(true);
|
||||
RestAssured.given().body(starRequest).contentType(ContentType.JSON).post("rest/entry/star");
|
||||
|
||||
FeverResponse feverResponse = client.execute("saved_item_ids");
|
||||
Assertions.assertEquals(1, feverResponse.getSavedItemIds().size());
|
||||
}
|
||||
FeverResponse feverResponse = client.execute("saved_item_ids");
|
||||
Assertions.assertEquals(1, feverResponse.getSavedItemIds().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void markEntry() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
@Test
|
||||
void markEntry() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
|
||||
client.execute("_", new Param("mark", "item"), new Param("id", entry.getId()), new Param("as", "read"));
|
||||
Assertions.assertEquals(1, getFeedEntries(subscriptionId).getEntries().stream().filter(Entry::isRead).count());
|
||||
client.execute(
|
||||
"_",
|
||||
new Param("mark", "item"),
|
||||
new Param("id", entry.getId()),
|
||||
new Param("as", "read"));
|
||||
Assertions.assertEquals(
|
||||
1,
|
||||
getFeedEntries(subscriptionId).getEntries().stream().filter(Entry::isRead).count());
|
||||
|
||||
client.execute("_", new Param("mark", "item"), new Param("id", entry.getId()), new Param("as", "unread"));
|
||||
Assertions.assertEquals(0, getFeedEntries(subscriptionId).getEntries().stream().filter(Entry::isRead).count());
|
||||
}
|
||||
client.execute(
|
||||
"_",
|
||||
new Param("mark", "item"),
|
||||
new Param("id", entry.getId()),
|
||||
new Param("as", "unread"));
|
||||
Assertions.assertEquals(
|
||||
0,
|
||||
getFeedEntries(subscriptionId).getEntries().stream().filter(Entry::isRead).count());
|
||||
}
|
||||
|
||||
@Test
|
||||
void markFeed() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
@Test
|
||||
void markFeed() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
client.execute("_", new Param("mark", "feed"), new Param("id", String.valueOf(subscriptionId)), new Param("as", "read"));
|
||||
client.execute(
|
||||
"_",
|
||||
new Param("mark", "feed"),
|
||||
new Param("id", String.valueOf(subscriptionId)),
|
||||
new Param("as", "read"));
|
||||
|
||||
Assertions.assertTrue(getFeedEntries(subscriptionId).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
Assertions.assertTrue(
|
||||
getFeedEntries(subscriptionId).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
|
||||
@Test
|
||||
void markCategory() {
|
||||
String categoryId = createCategory("test-category");
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl(), categoryId);
|
||||
@Test
|
||||
void markCategory() {
|
||||
String categoryId = createCategory("test-category");
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl(), categoryId);
|
||||
|
||||
client.execute("_", new Param("mark", "group"), new Param("id", String.valueOf(categoryId)), new Param("as", "read"));
|
||||
client.execute(
|
||||
"_",
|
||||
new Param("mark", "group"),
|
||||
new Param("id", String.valueOf(categoryId)),
|
||||
new Param("as", "read"));
|
||||
|
||||
Assertions.assertTrue(getFeedEntries(subscriptionId).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
Assertions.assertTrue(
|
||||
getFeedEntries(subscriptionId).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
|
||||
@Test
|
||||
void tagEntry() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
@Test
|
||||
void tagEntry() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
|
||||
|
||||
client.execute("_", new Param("mark", "item"), new Param("id", entry.getId()), new Param("as", "saved"));
|
||||
Assertions.assertEquals(1, getFeedEntries(subscriptionId).getEntries().stream().filter(Entry::isStarred).count());
|
||||
client.execute(
|
||||
"_",
|
||||
new Param("mark", "item"),
|
||||
new Param("id", entry.getId()),
|
||||
new Param("as", "saved"));
|
||||
Assertions.assertEquals(
|
||||
1,
|
||||
getFeedEntries(subscriptionId).getEntries().stream()
|
||||
.filter(Entry::isStarred)
|
||||
.count());
|
||||
|
||||
client.execute("_", new Param("mark", "item"), new Param("id", entry.getId()), new Param("as", "unsaved"));
|
||||
Assertions.assertEquals(0, getFeedEntries(subscriptionId).getEntries().stream().filter(Entry::isStarred).count());
|
||||
}
|
||||
client.execute(
|
||||
"_",
|
||||
new Param("mark", "item"),
|
||||
new Param("id", entry.getId()),
|
||||
new Param("as", "unsaved"));
|
||||
Assertions.assertEquals(
|
||||
0,
|
||||
getFeedEntries(subscriptionId).getEntries().stream()
|
||||
.filter(Entry::isStarred)
|
||||
.count());
|
||||
}
|
||||
|
||||
@Test
|
||||
void groups() {
|
||||
createCategory("category-1");
|
||||
FeverResponse feverResponse = client.execute("groups");
|
||||
Assertions.assertEquals(1, feverResponse.getGroups().size());
|
||||
Assertions.assertEquals("category-1", feverResponse.getGroups().getFirst().getTitle());
|
||||
}
|
||||
@Test
|
||||
void groups() {
|
||||
createCategory("category-1");
|
||||
FeverResponse feverResponse = client.execute("groups");
|
||||
Assertions.assertEquals(1, feverResponse.getGroups().size());
|
||||
Assertions.assertEquals("category-1", feverResponse.getGroups().getFirst().getTitle());
|
||||
}
|
||||
|
||||
@Test
|
||||
void links() {
|
||||
FeverResponse feverResponse = client.execute("links");
|
||||
Assertions.assertTrue(feverResponse.getLinks().isEmpty());
|
||||
}
|
||||
@Test
|
||||
void links() {
|
||||
FeverResponse feverResponse = client.execute("links");
|
||||
Assertions.assertTrue(feverResponse.getLinks().isEmpty());
|
||||
}
|
||||
|
||||
private static class FeverClient {
|
||||
private final Long userId;
|
||||
private static class FeverClient {
|
||||
private final Long userId;
|
||||
|
||||
@Setter
|
||||
private String apiKey;
|
||||
@Setter private String apiKey;
|
||||
|
||||
public FeverClient(Long userId, String apiKey) {
|
||||
this.userId = userId;
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
public FeverClient(Long userId, String apiKey) {
|
||||
this.userId = userId;
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
private FeverResponse execute(String action, Param... params) {
|
||||
RequestSpecification spec = RestAssured.given()
|
||||
.auth()
|
||||
.none()
|
||||
.formParam("api_key", Digests.md5Hex("admin:" + apiKey))
|
||||
.formParam(action, 1);
|
||||
private FeverResponse execute(String action, Param... params) {
|
||||
RequestSpecification spec =
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.none()
|
||||
.formParam("api_key", Digests.md5Hex("admin:" + apiKey))
|
||||
.formParam(action, 1);
|
||||
|
||||
for (Param param : params) {
|
||||
spec.formParam(param.name(), param.value());
|
||||
}
|
||||
for (Param param : params) {
|
||||
spec.formParam(param.name(), param.value());
|
||||
}
|
||||
|
||||
return spec.post("rest/fever/user/{userId}", userId).then().statusCode(HttpStatus.SC_OK).extract().as(FeverResponse.class);
|
||||
return spec.post("rest/fever/user/{userId}", userId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(FeverResponse.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private record Param(String name, String value) {}
|
||||
private record Param(String name, String value) {}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
package com.commafeed.integration.rest;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.frontend.model.ServerInfo;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
class ServerIT extends BaseIT {
|
||||
|
||||
@Test
|
||||
void getServerInfos() {
|
||||
ServerInfo serverInfos = RestAssured.given().get("/rest/server/get").then().statusCode(200).extract().as(ServerInfo.class);
|
||||
Assertions.assertTrue(serverInfos.isAllowRegistrations());
|
||||
Assertions.assertTrue(serverInfos.isSmtpEnabled());
|
||||
Assertions.assertTrue(serverInfos.isDemoAccountEnabled());
|
||||
Assertions.assertTrue(serverInfos.isWebsocketEnabled());
|
||||
Assertions.assertEquals(900000, serverInfos.getWebsocketPingInterval());
|
||||
Assertions.assertEquals(30000, serverInfos.getTreeReloadInterval());
|
||||
Assertions.assertEquals(60000, serverInfos.getForceRefreshCooldownDuration());
|
||||
Assertions.assertEquals(4, serverInfos.getMinimumPasswordLength());
|
||||
Assertions.assertTrue(serverInfos.isPushNotificationsEnabled());
|
||||
|
||||
}
|
||||
@Test
|
||||
void getServerInfos() {
|
||||
ServerInfo serverInfos =
|
||||
RestAssured.given()
|
||||
.get("/rest/server/get")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.as(ServerInfo.class);
|
||||
Assertions.assertTrue(serverInfos.isAllowRegistrations());
|
||||
Assertions.assertTrue(serverInfos.isSmtpEnabled());
|
||||
Assertions.assertTrue(serverInfos.isDemoAccountEnabled());
|
||||
Assertions.assertTrue(serverInfos.isWebsocketEnabled());
|
||||
Assertions.assertEquals(900000, serverInfos.getWebsocketPingInterval());
|
||||
Assertions.assertEquals(30000, serverInfos.getTreeReloadInterval());
|
||||
Assertions.assertEquals(60000, serverInfos.getForceRefreshCooldownDuration());
|
||||
Assertions.assertEquals(4, serverInfos.getMinimumPasswordLength());
|
||||
Assertions.assertTrue(serverInfos.isPushNotificationsEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
package com.commafeed.integration.rest;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.Settings;
|
||||
import com.commafeed.frontend.model.request.PasswordResetConfirmationRequest;
|
||||
import com.commafeed.frontend.model.request.PasswordResetRequest;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
import io.quarkus.mailer.MockMailbox;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.vertx.ext.mail.MailMessage;
|
||||
import jakarta.inject.Inject;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@@ -13,88 +21,97 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.Settings;
|
||||
import com.commafeed.frontend.model.request.PasswordResetConfirmationRequest;
|
||||
import com.commafeed.frontend.model.request.PasswordResetRequest;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
|
||||
import io.quarkus.mailer.MockMailbox;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.vertx.ext.mail.MailMessage;
|
||||
|
||||
@QuarkusTest
|
||||
class UserIT extends BaseIT {
|
||||
|
||||
@Inject
|
||||
MockMailbox mailbox;
|
||||
@Inject MockMailbox mailbox;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
|
||||
mailbox.clear();
|
||||
}
|
||||
mailbox.clear();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resetPassword() {
|
||||
PasswordResetRequest req = new PasswordResetRequest();
|
||||
req.setEmail("admin@commafeed.com");
|
||||
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/user/passwordReset").then().statusCode(200);
|
||||
@Test
|
||||
void resetPassword() {
|
||||
PasswordResetRequest req = new PasswordResetRequest();
|
||||
req.setEmail("admin@commafeed.com");
|
||||
RestAssured.given()
|
||||
.body(req)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/user/passwordReset")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
List<MailMessage> mails = mailbox.getMailMessagesSentTo("admin@commafeed.com");
|
||||
Assertions.assertEquals(1, mails.size());
|
||||
List<MailMessage> mails = mailbox.getMailMessagesSentTo("admin@commafeed.com");
|
||||
Assertions.assertEquals(1, mails.size());
|
||||
|
||||
MailMessage message = mails.getFirst();
|
||||
Assertions.assertEquals("CommaFeed - Password recovery", message.getSubject());
|
||||
Assertions.assertTrue(message.getHtml().startsWith("You asked for password recovery for account 'admin'"));
|
||||
Assertions.assertEquals("admin@commafeed.com", message.getTo().getFirst());
|
||||
MailMessage message = mails.getFirst();
|
||||
Assertions.assertEquals("CommaFeed - Password recovery", message.getSubject());
|
||||
Assertions.assertTrue(
|
||||
message.getHtml()
|
||||
.startsWith("You asked for password recovery for account 'admin'"));
|
||||
Assertions.assertEquals("admin@commafeed.com", message.getTo().getFirst());
|
||||
|
||||
Element a = Jsoup.parse(message.getHtml()).select("a").getFirst();
|
||||
String link = a.attr("href");
|
||||
Element a = Jsoup.parse(message.getHtml()).select("a").getFirst();
|
||||
String link = a.attr("href");
|
||||
|
||||
String email = null;
|
||||
String token = null;
|
||||
String queryString = link.substring(link.indexOf('?') + 1);
|
||||
for (String param : queryString.split("&")) {
|
||||
String[] keyValue = param.split("=");
|
||||
if ("email".equals(keyValue[0])) {
|
||||
email = URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8);
|
||||
} else if ("token".equals(keyValue[0])) {
|
||||
token = URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
String email = null;
|
||||
String token = null;
|
||||
String queryString = link.substring(link.indexOf('?') + 1);
|
||||
for (String param : queryString.split("&")) {
|
||||
String[] keyValue = param.split("=");
|
||||
if ("email".equals(keyValue[0])) {
|
||||
email = URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8);
|
||||
} else if ("token".equals(keyValue[0])) {
|
||||
token = URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
Assertions.assertNotNull(email);
|
||||
Assertions.assertNotNull(token);
|
||||
Assertions.assertTrue(link.contains("#/passwordReset?"));
|
||||
Assertions.assertNotNull(email);
|
||||
Assertions.assertNotNull(token);
|
||||
Assertions.assertTrue(link.contains("#/passwordReset?"));
|
||||
|
||||
String newPassword = "MyNewPassword123!";
|
||||
PasswordResetConfirmationRequest confirmReq = new PasswordResetConfirmationRequest();
|
||||
confirmReq.setEmail(email);
|
||||
confirmReq.setToken(token);
|
||||
confirmReq.setPassword(newPassword);
|
||||
RestAssured.given().body(confirmReq).contentType(ContentType.JSON).post("rest/user/passwordResetCallback").then().statusCode(200);
|
||||
String newPassword = "MyNewPassword123!";
|
||||
PasswordResetConfirmationRequest confirmReq = new PasswordResetConfirmationRequest();
|
||||
confirmReq.setEmail(email);
|
||||
confirmReq.setToken(token);
|
||||
confirmReq.setPassword(newPassword);
|
||||
RestAssured.given()
|
||||
.body(confirmReq)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/user/passwordResetCallback")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, newPassword);
|
||||
RestAssured.given().get("rest/user/settings").then().statusCode(200);
|
||||
}
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, newPassword);
|
||||
RestAssured.given().get("rest/user/settings").then().statusCode(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveSettings() {
|
||||
Settings settings = RestAssured.given().get("rest/user/settings").then().extract().as(Settings.class);
|
||||
settings.setLanguage("test");
|
||||
RestAssured.given().body(settings).contentType(ContentType.JSON).post("rest/user/settings").then().statusCode(200);
|
||||
@Test
|
||||
void saveSettings() {
|
||||
Settings settings =
|
||||
RestAssured.given().get("rest/user/settings").then().extract().as(Settings.class);
|
||||
settings.setLanguage("test");
|
||||
RestAssured.given()
|
||||
.body(settings)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/user/settings")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
Settings updatedSettings = RestAssured.given().get("rest/user/settings").then().extract().as(Settings.class);
|
||||
Assertions.assertEquals("test", updatedSettings.getLanguage());
|
||||
}
|
||||
Settings updatedSettings =
|
||||
RestAssured.given().get("rest/user/settings").then().extract().as(Settings.class);
|
||||
Assertions.assertEquals("test", updatedSettings.getLanguage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,64 @@
|
||||
package com.commafeed.integration.servlet;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.Settings;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.frontend.model.Settings;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
|
||||
@QuarkusTest
|
||||
class CustomCodeIT extends BaseIT {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
// get settings
|
||||
Settings settings = RestAssured.given().get("rest/user/settings").then().statusCode(200).extract().as(Settings.class);
|
||||
@Test
|
||||
void test() {
|
||||
// get settings
|
||||
Settings settings =
|
||||
RestAssured.given()
|
||||
.get("rest/user/settings")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.as(Settings.class);
|
||||
|
||||
// update settings
|
||||
settings.setCustomJs("custom-js");
|
||||
settings.setCustomCss("custom-css");
|
||||
RestAssured.given().body(settings).contentType(ContentType.JSON).post("rest/user/settings").then().statusCode(HttpStatus.SC_OK);
|
||||
// update settings
|
||||
settings.setCustomJs("custom-js");
|
||||
settings.setCustomCss("custom-css");
|
||||
RestAssured.given()
|
||||
.body(settings)
|
||||
.contentType(ContentType.JSON)
|
||||
.post("rest/user/settings")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK);
|
||||
|
||||
// check custom code servlets
|
||||
RestAssured.given().get("custom_js.js").then().statusCode(HttpStatus.SC_OK).body(CoreMatchers.is("custom-js"));
|
||||
RestAssured.given().get("custom_css.css").then().statusCode(HttpStatus.SC_OK).body(CoreMatchers.is("custom-css"));
|
||||
}
|
||||
// check custom code servlets
|
||||
RestAssured.given()
|
||||
.get("custom_js.js")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.body(CoreMatchers.is("custom-js"));
|
||||
RestAssured.given()
|
||||
.get("custom_css.css")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.body(CoreMatchers.is("custom-css"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,49 @@
|
||||
package com.commafeed.integration.servlet;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.Headers;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import java.net.HttpCookie;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.Headers;
|
||||
|
||||
@QuarkusTest
|
||||
class LogoutIT extends BaseIT {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
List<HttpCookie> cookies = login();
|
||||
Headers responseHeaders = RestAssured.given()
|
||||
.header(HttpHeaders.COOKIE, cookies.stream().map(HttpCookie::toString).collect(Collectors.joining(";")))
|
||||
.redirects()
|
||||
.follow(false)
|
||||
.get("logout")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_TEMPORARY_REDIRECT)
|
||||
.extract()
|
||||
.headers();
|
||||
@Test
|
||||
void test() {
|
||||
List<HttpCookie> cookies = login();
|
||||
Headers responseHeaders =
|
||||
RestAssured.given()
|
||||
.header(
|
||||
HttpHeaders.COOKIE,
|
||||
cookies.stream()
|
||||
.map(HttpCookie::toString)
|
||||
.collect(Collectors.joining(";")))
|
||||
.redirects()
|
||||
.follow(false)
|
||||
.get("logout")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_TEMPORARY_REDIRECT)
|
||||
.extract()
|
||||
.headers();
|
||||
|
||||
List<String> setCookieHeaders = responseHeaders.getValues(HttpHeaders.SET_COOKIE);
|
||||
Assertions.assertTrue(setCookieHeaders.stream().flatMap(c -> HttpCookie.parse(c).stream()).allMatch(c -> c.getMaxAge() == 0));
|
||||
}
|
||||
List<String> setCookieHeaders = responseHeaders.getValues(HttpHeaders.SET_COOKIE);
|
||||
Assertions.assertTrue(
|
||||
setCookieHeaders.stream()
|
||||
.flatMap(c -> HttpCookie.parse(c).stream())
|
||||
.allMatch(c -> c.getMaxAge() == 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,41 @@
|
||||
package com.commafeed.integration.servlet;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.TestConstants;
|
||||
import com.commafeed.integration.BaseIT;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
@QuarkusTest
|
||||
class NextUnreadIT extends BaseIT {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
RestAssured.authentication =
|
||||
RestAssured.preemptive()
|
||||
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
RestAssured.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
RestAssured.given()
|
||||
.redirects()
|
||||
.follow(false)
|
||||
.get("next")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_TEMPORARY_REDIRECT)
|
||||
.header(HttpHeaders.LOCATION, "https://hostname.local/commafeed/2");
|
||||
}
|
||||
@Test
|
||||
void test() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
RestAssured.given()
|
||||
.redirects()
|
||||
.follow(false)
|
||||
.get("next")
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_TEMPORARY_REDIRECT)
|
||||
.header(HttpHeaders.LOCATION, "https://hostname.local/commafeed/2");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
package com.commafeed.integration.servlet;
|
||||
|
||||
import com.commafeed.integration.BaseIT;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.integration.BaseIT;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
@QuarkusTest
|
||||
class RobotsTxtIT extends BaseIT {
|
||||
@Test
|
||||
void test() {
|
||||
RestAssured.given().get("robots.txt").then().statusCode(200).body(CoreMatchers.is("User-agent: *\nDisallow: /"));
|
||||
}
|
||||
@Test
|
||||
void test() {
|
||||
RestAssured.given()
|
||||
.get("robots.txt")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body(CoreMatchers.is("User-agent: *\nDisallow: /"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
package com.commafeed.tools;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
|
||||
class CommaFeedPropertiesGeneratorTest {
|
||||
|
||||
@Test
|
||||
void testGenerate() throws Exception {
|
||||
InputStream model = getClass().getResourceAsStream("/properties/quarkus-config-model.yaml");
|
||||
InputStream javadoc = getClass().getResourceAsStream("/properties/quarkus-config-javadoc.yaml");
|
||||
URL output = getClass().getResource("/properties/output.properties");
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
new CommaFeedPropertiesGenerator().generate(model, javadoc, baos);
|
||||
@Test
|
||||
void testGenerate() throws Exception {
|
||||
InputStream model = getClass().getResourceAsStream("/properties/quarkus-config-model.yaml");
|
||||
InputStream javadoc =
|
||||
getClass().getResourceAsStream("/properties/quarkus-config-javadoc.yaml");
|
||||
URL output = getClass().getResource("/properties/output.properties");
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
new CommaFeedPropertiesGenerator().generate(model, javadoc, baos);
|
||||
|
||||
Assertions.assertLinesMatch(Resources.readLines(output, StandardCharsets.UTF_8).stream(),
|
||||
baos.toString(StandardCharsets.UTF_8).lines());
|
||||
}
|
||||
|
||||
}
|
||||
Assertions.assertLinesMatch(
|
||||
Resources.readLines(output, StandardCharsets.UTF_8).stream(),
|
||||
baos.toString(StandardCharsets.UTF_8).lines());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user