forked from Archives/Athou_commafeed
add more integration tests
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
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;
|
||||
import com.commafeed.backend.dao.FeedDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryContentDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryDAO;
|
||||
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;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DatabaseCleaningServiceTest {
|
||||
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
@Mock
|
||||
private CommaFeedConfiguration config;
|
||||
|
||||
@Mock
|
||||
private CommaFeedConfiguration.Database databaseConfig;
|
||||
|
||||
@Mock
|
||||
private CommaFeedConfiguration.Database.Cleanup cleaningConfig;
|
||||
|
||||
@Mock
|
||||
private UnitOfWork unitOfWork;
|
||||
|
||||
@Mock
|
||||
private FeedDAO feedDAO;
|
||||
|
||||
@Mock
|
||||
private FeedEntryDAO feedEntryDAO;
|
||||
|
||||
@Mock
|
||||
private FeedEntryContentDAO feedEntryContentDAO;
|
||||
|
||||
@Mock
|
||||
private FeedEntryStatusDAO feedEntryStatusDAO;
|
||||
|
||||
@Mock
|
||||
private MetricRegistry metrics;
|
||||
|
||||
@Mock
|
||||
private Meter entriesDeletedMeter;
|
||||
|
||||
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);
|
||||
|
||||
Mockito.when(unitOfWork.call(Mockito.any())).thenAnswer(invocation -> ((Callable<?>) invocation.getArgument(0)).call());
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
service.cleanFeedsWithoutSubscriptions();
|
||||
|
||||
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);
|
||||
|
||||
service.cleanContentsWithoutEntries();
|
||||
|
||||
Mockito.verify(feedEntryContentDAO, Mockito.times(3)).deleteWithoutEntries(Mockito.anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cleanEntriesForFeedsExceedingCapacityDeletesOldEntries() {
|
||||
FeedCapacity feed1 = Mockito.mock(FeedCapacity.class);
|
||||
Mockito.when(feed1.getId()).thenReturn(1L);
|
||||
Mockito.when(feed1.getCapacity()).thenReturn(180L);
|
||||
|
||||
FeedCapacity feed2 = Mockito.mock(FeedCapacity.class);
|
||||
Mockito.when(feed2.getId()).thenReturn(2L);
|
||||
Mockito.when(feed2.getCapacity()).thenReturn(120L);
|
||||
|
||||
Mockito.when(feedEntryDAO.findFeedsExceedingCapacity(50, BATCH_SIZE))
|
||||
.thenReturn(Arrays.asList(feed1, feed2))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 100)).thenReturn(80);
|
||||
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 50)).thenReturn(50);
|
||||
Mockito.when(feedEntryDAO.deleteOldEntries(2L, 70)).thenReturn(70);
|
||||
|
||||
service.cleanEntriesForFeedsExceedingCapacity(50);
|
||||
|
||||
Mockito.verify(entriesDeletedMeter, Mockito.times(3)).mark(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cleanEntriesOlderThanDeletesOldEntries() {
|
||||
Instant cutoff = LocalDate.now().minusDays(30).atStartOfDay().toInstant(ZoneOffset.UTC);
|
||||
|
||||
Mockito.when(feedEntryDAO.deleteEntriesOlderThan(cutoff, BATCH_SIZE)).thenReturn(100, 50, 0);
|
||||
|
||||
service.cleanEntriesOlderThan(cutoff);
|
||||
|
||||
Mockito.verify(feedEntryDAO, Mockito.times(3)).deleteEntriesOlderThan(cutoff, BATCH_SIZE);
|
||||
Mockito.verify(entriesDeletedMeter, Mockito.times(3)).mark(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cleanStatusesOlderThanDeletesOldStatuses() {
|
||||
Instant cutoff = LocalDate.now().minusDays(60).atStartOfDay().toInstant(ZoneOffset.UTC);
|
||||
|
||||
Mockito.when(feedEntryStatusDAO.deleteOldStatuses(cutoff, BATCH_SIZE)).thenReturn(200L, 100L, 0L);
|
||||
|
||||
service.cleanStatusesOlderThan(cutoff);
|
||||
|
||||
Mockito.verify(feedEntryStatusDAO, Mockito.times(3)).deleteOldStatuses(cutoff, BATCH_SIZE);
|
||||
}
|
||||
}
|
||||
@@ -7,20 +7,24 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.commafeed.backend.Digests;
|
||||
import com.commafeed.frontend.model.Entry;
|
||||
import com.commafeed.frontend.model.UserModel;
|
||||
import com.commafeed.frontend.model.request.ProfileModificationRequest;
|
||||
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;
|
||||
|
||||
@QuarkusTest
|
||||
class FeverIT extends BaseIT {
|
||||
|
||||
private Long userId;
|
||||
private String apiKey;
|
||||
private FeverClient client;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
@@ -34,8 +38,7 @@ class FeverIT extends BaseIT {
|
||||
|
||||
// retrieve api key
|
||||
UserModel user = RestAssured.given().get("rest/user/profile").then().statusCode(HttpStatus.SC_OK).extract().as(UserModel.class);
|
||||
this.apiKey = user.getApiKey();
|
||||
this.userId = user.getId();
|
||||
this.client = new FeverClient(user.getId(), user.getApiKey());
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -45,65 +48,155 @@ class FeverIT extends BaseIT {
|
||||
|
||||
@Test
|
||||
void invalidApiKey() {
|
||||
FeverResponse response = fetch("feeds", "invalid-key");
|
||||
client.apiKey = "invalid-key";
|
||||
|
||||
FeverResponse response = client.execute("feeds");
|
||||
Assertions.assertFalse(response.isAuth());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validApiKey() {
|
||||
FeverResponse response = fetch("feeds", apiKey);
|
||||
FeverResponse response = client.execute("feeds");
|
||||
Assertions.assertTrue(response.isAuth());
|
||||
}
|
||||
|
||||
@Test
|
||||
void feeds() {
|
||||
subscribe(getFeedUrl());
|
||||
FeverResponse feverResponse = fetch("feeds");
|
||||
FeverResponse feverResponse = client.execute("feeds");
|
||||
Assertions.assertEquals(1, feverResponse.getFeeds().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unreadEntries() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
FeverResponse feverResponse = fetch("unread_item_ids");
|
||||
FeverResponse feverResponse = client.execute("unread_item_ids");
|
||||
Assertions.assertEquals(2, feverResponse.getUnreadItemIds().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void entries() {
|
||||
subscribeAndWaitForEntries(getFeedUrl());
|
||||
FeverResponse feverResponse = fetch("items");
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
FeverResponse feverResponse = client.execute("items");
|
||||
Assertions.assertEquals(2, feverResponse.getItems().size());
|
||||
|
||||
FeverItem item = feverResponse.getItems().get(0);
|
||||
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().get(0);
|
||||
|
||||
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().get(0);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
void markEntry() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().get(0);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
void markFeed() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@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"));
|
||||
|
||||
Assertions.assertTrue(getFeedEntries(subscriptionId).getEntries().stream().allMatch(Entry::isRead));
|
||||
}
|
||||
|
||||
@Test
|
||||
void tagEntry() {
|
||||
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
|
||||
Entry entry = getFeedEntries(subscriptionId).getEntries().get(0);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
void groups() {
|
||||
createCategory("category-1");
|
||||
FeverResponse feverResponse = fetch("groups");
|
||||
FeverResponse feverResponse = client.execute("groups");
|
||||
Assertions.assertEquals(1, feverResponse.getGroups().size());
|
||||
Assertions.assertEquals("category-1", feverResponse.getGroups().get(0).getTitle());
|
||||
}
|
||||
|
||||
@Test
|
||||
void links() {
|
||||
FeverResponse feverResponse = fetch("links");
|
||||
FeverResponse feverResponse = client.execute("links");
|
||||
Assertions.assertTrue(feverResponse.getLinks().isEmpty());
|
||||
}
|
||||
|
||||
private FeverResponse fetch(String what) {
|
||||
return fetch(what, apiKey);
|
||||
private static class FeverClient {
|
||||
private final Long userId;
|
||||
|
||||
@Setter
|
||||
private String 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);
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private FeverResponse fetch(String what, String apiKey) {
|
||||
return RestAssured.given()
|
||||
.auth()
|
||||
.none()
|
||||
.formParam("api_key", Digests.md5Hex("admin:" + apiKey))
|
||||
.formParam(what, 1)
|
||||
.post("rest/fever/user/{userId}", userId)
|
||||
.then()
|
||||
.statusCode(HttpStatus.SC_OK)
|
||||
.extract()
|
||||
.as(FeverResponse.class);
|
||||
private record Param(String name, String value) {
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user