forked from Archives/Athou_commafeed
keep starred entries (#1581)
This commit is contained in:
@@ -312,6 +312,12 @@ public interface CommaFeedConfiguration {
|
|||||||
@WithDefault("100")
|
@WithDefault("100")
|
||||||
int batchSize();
|
int batchSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to keep starred entries when cleaning up old entries.
|
||||||
|
*/
|
||||||
|
@WithDefault("true")
|
||||||
|
boolean keepStarredEntries();
|
||||||
|
|
||||||
default Instant statusesInstantThreshold() {
|
default Instant statusesInstantThreshold() {
|
||||||
return statusesMaxAge().toMillis() > 0 ? Instant.now().minus(statusesMaxAge()) : null;
|
return statusesMaxAge().toMillis() > 0 ? Instant.now().minus(statusesMaxAge()) : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.commafeed.backend.model.FeedEntry;
|
|||||||
import com.commafeed.backend.model.QFeedEntry;
|
import com.commafeed.backend.model.QFeedEntry;
|
||||||
import com.querydsl.core.Tuple;
|
import com.querydsl.core.Tuple;
|
||||||
import com.querydsl.core.types.dsl.NumberExpression;
|
import com.querydsl.core.types.dsl.NumberExpression;
|
||||||
|
import com.querydsl.jpa.impl.JPAQuery;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
||||||
@@ -25,15 +26,21 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
|||||||
return query().select(ENTRY).from(ENTRY).where(ENTRY.guidHash.eq(guidHash), ENTRY.feed.eq(feed)).limit(1).fetchOne();
|
return query().select(ENTRY).from(ENTRY).where(ENTRY.guidHash.eq(guidHash), ENTRY.feed.eq(feed)).limit(1).fetchOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FeedCapacity> findFeedsExceedingCapacity(long maxCapacity, long max) {
|
public List<FeedCapacity> findFeedsExceedingCapacity(long maxCapacity, long max, boolean keepStarredEntries) {
|
||||||
NumberExpression<Long> count = ENTRY.id.count();
|
NumberExpression<Long> count = ENTRY.id.count();
|
||||||
List<Tuple> tuples = query().select(ENTRY.feed.id, count)
|
JPAQuery<Tuple> query = query().select(ENTRY.feed.id, count).from(ENTRY);
|
||||||
.from(ENTRY)
|
|
||||||
.groupBy(ENTRY.feed)
|
if (keepStarredEntries) {
|
||||||
|
query.where(Predicates.isNotStarred(ENTRY));
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.groupBy(ENTRY.feed)
|
||||||
.having(count.gt(maxCapacity))
|
.having(count.gt(maxCapacity))
|
||||||
.limit(max)
|
.limit(max)
|
||||||
.fetch();
|
.fetch()
|
||||||
return tuples.stream().map(t -> new FeedCapacity(t.get(ENTRY.feed.id), t.get(count))).toList();
|
.stream()
|
||||||
|
.map(t -> new FeedCapacity(t.get(ENTRY.feed.id), t.get(count)))
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int delete(Long feedId, long max) {
|
public int delete(Long feedId, long max) {
|
||||||
@@ -44,21 +51,30 @@ public class FeedEntryDAO extends GenericDAO<FeedEntry> {
|
|||||||
/**
|
/**
|
||||||
* Delete entries older than a certain date
|
* Delete entries older than a certain date
|
||||||
*/
|
*/
|
||||||
public int deleteEntriesOlderThan(Instant olderThan, long max) {
|
public int deleteEntriesOlderThan(Instant olderThan, long max, boolean keepStarredEntries) {
|
||||||
List<FeedEntry> list = query().selectFrom(ENTRY)
|
JPAQuery<FeedEntry> query = query().selectFrom(ENTRY)
|
||||||
.where(ENTRY.published.lt(olderThan))
|
.where(ENTRY.published.lt(olderThan))
|
||||||
.orderBy(ENTRY.published.asc())
|
.orderBy(ENTRY.published.asc())
|
||||||
.limit(max)
|
.limit(max);
|
||||||
.fetch();
|
|
||||||
return delete(list);
|
if (keepStarredEntries) {
|
||||||
|
query.where(Predicates.isNotStarred(ENTRY));
|
||||||
|
}
|
||||||
|
|
||||||
|
return delete(query.fetch());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the oldest entries of a feed
|
* Delete the oldest entries of a feed
|
||||||
*/
|
*/
|
||||||
public int deleteOldEntries(Long feedId, long max) {
|
public int deleteOldEntries(Long feedId, long max, boolean keepStarredEntries) {
|
||||||
List<FeedEntry> list = query().selectFrom(ENTRY).where(ENTRY.feed.id.eq(feedId)).orderBy(ENTRY.published.asc()).limit(max).fetch();
|
JPAQuery<FeedEntry> query = query().selectFrom(ENTRY).where(ENTRY.feed.id.eq(feedId)).orderBy(ENTRY.published.asc()).limit(max);
|
||||||
return delete(list);
|
|
||||||
|
if (keepStarredEntries) {
|
||||||
|
query.where(Predicates.isNotStarred(ENTRY));
|
||||||
|
}
|
||||||
|
|
||||||
|
return delete(query.fetch());
|
||||||
}
|
}
|
||||||
|
|
||||||
public record FeedCapacity(Long id, Long capacity) {
|
public record FeedCapacity(Long id, Long capacity) {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.commafeed.backend.dao;
|
||||||
|
|
||||||
|
import com.commafeed.backend.model.QFeedEntry;
|
||||||
|
import com.commafeed.backend.model.QFeedEntryStatus;
|
||||||
|
import com.querydsl.core.types.dsl.BooleanExpression;
|
||||||
|
import com.querydsl.jpa.JPAExpressions;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class Predicates {
|
||||||
|
|
||||||
|
private static final QFeedEntryStatus STATUS = QFeedEntryStatus.feedEntryStatus;
|
||||||
|
|
||||||
|
public static BooleanExpression isNotStarred(QFeedEntry entry) {
|
||||||
|
return JPAExpressions.selectOne().from(STATUS).where(STATUS.entry.eq(entry).and(STATUS.starred.isTrue())).notExists();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,13 +27,13 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class DatabaseCleaningService {
|
public class DatabaseCleaningService {
|
||||||
|
|
||||||
private final int batchSize;
|
|
||||||
|
|
||||||
private final UnitOfWork unitOfWork;
|
private final UnitOfWork unitOfWork;
|
||||||
private final FeedDAO feedDAO;
|
private final FeedDAO feedDAO;
|
||||||
private final FeedEntryDAO feedEntryDAO;
|
private final FeedEntryDAO feedEntryDAO;
|
||||||
private final FeedEntryContentDAO feedEntryContentDAO;
|
private final FeedEntryContentDAO feedEntryContentDAO;
|
||||||
private final FeedEntryStatusDAO feedEntryStatusDAO;
|
private final FeedEntryStatusDAO feedEntryStatusDAO;
|
||||||
|
private final int batchSize;
|
||||||
|
private final boolean keepStarredEntries;
|
||||||
private final Meter entriesDeletedMeter;
|
private final Meter entriesDeletedMeter;
|
||||||
|
|
||||||
public DatabaseCleaningService(CommaFeedConfiguration config, UnitOfWork unitOfWork, FeedDAO feedDAO, FeedEntryDAO feedEntryDAO,
|
public DatabaseCleaningService(CommaFeedConfiguration config, UnitOfWork unitOfWork, FeedDAO feedDAO, FeedEntryDAO feedEntryDAO,
|
||||||
@@ -44,6 +44,7 @@ public class DatabaseCleaningService {
|
|||||||
this.feedEntryContentDAO = feedEntryContentDAO;
|
this.feedEntryContentDAO = feedEntryContentDAO;
|
||||||
this.feedEntryStatusDAO = feedEntryStatusDAO;
|
this.feedEntryStatusDAO = feedEntryStatusDAO;
|
||||||
this.batchSize = config.database().cleanup().batchSize();
|
this.batchSize = config.database().cleanup().batchSize();
|
||||||
|
this.keepStarredEntries = config.database().cleanup().keepStarredEntries();
|
||||||
this.entriesDeletedMeter = metrics.meter(MetricRegistry.name(getClass(), "entriesDeleted"));
|
this.entriesDeletedMeter = metrics.meter(MetricRegistry.name(getClass(), "entriesDeleted"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,21 +87,23 @@ public class DatabaseCleaningService {
|
|||||||
log.info("cleaning entries exceeding feed capacity");
|
log.info("cleaning entries exceeding feed capacity");
|
||||||
long total = 0;
|
long total = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
List<FeedCapacity> feeds = unitOfWork.call(() -> feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, batchSize));
|
List<FeedCapacity> feeds = unitOfWork
|
||||||
|
.call(() -> feedEntryDAO.findFeedsExceedingCapacity(maxFeedCapacity, batchSize, keepStarredEntries));
|
||||||
if (feeds.isEmpty()) {
|
if (feeds.isEmpty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final FeedCapacity feed : feeds) {
|
for (final FeedCapacity feed : feeds) {
|
||||||
long remaining = feed.capacity() - maxFeedCapacity;
|
long remaining = feed.capacity() - maxFeedCapacity;
|
||||||
|
int deleted;
|
||||||
do {
|
do {
|
||||||
final long rem = remaining;
|
final long rem = remaining;
|
||||||
int deleted = unitOfWork.call(() -> feedEntryDAO.deleteOldEntries(feed.id(), Math.min(batchSize, rem)));
|
deleted = unitOfWork.call(() -> feedEntryDAO.deleteOldEntries(feed.id(), Math.min(batchSize, rem), keepStarredEntries));
|
||||||
entriesDeletedMeter.mark(deleted);
|
entriesDeletedMeter.mark(deleted);
|
||||||
total += deleted;
|
total += deleted;
|
||||||
remaining -= deleted;
|
remaining -= deleted;
|
||||||
log.debug("removed {} entries for feeds exceeding capacity", total);
|
log.debug("removed {} entries for feeds exceeding capacity", total);
|
||||||
} while (remaining > 0);
|
} while (deleted > 0 && remaining > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.info("cleanup done: {} entries for feeds exceeding capacity deleted", total);
|
log.info("cleanup done: {} entries for feeds exceeding capacity deleted", total);
|
||||||
@@ -111,7 +114,7 @@ public class DatabaseCleaningService {
|
|||||||
long total = 0;
|
long total = 0;
|
||||||
long deleted;
|
long deleted;
|
||||||
do {
|
do {
|
||||||
deleted = unitOfWork.call(() -> feedEntryDAO.deleteEntriesOlderThan(olderThan, batchSize));
|
deleted = unitOfWork.call(() -> feedEntryDAO.deleteEntriesOlderThan(olderThan, batchSize, keepStarredEntries));
|
||||||
entriesDeletedMeter.mark(deleted);
|
entriesDeletedMeter.mark(deleted);
|
||||||
total += deleted;
|
total += deleted;
|
||||||
log.debug("removed {} old entries", total);
|
log.debug("removed {} old entries", total);
|
||||||
|
|||||||
@@ -115,13 +115,13 @@ class DatabaseCleaningServiceTest {
|
|||||||
Mockito.when(feed2.id()).thenReturn(2L);
|
Mockito.when(feed2.id()).thenReturn(2L);
|
||||||
Mockito.when(feed2.capacity()).thenReturn(120L);
|
Mockito.when(feed2.capacity()).thenReturn(120L);
|
||||||
|
|
||||||
Mockito.when(feedEntryDAO.findFeedsExceedingCapacity(50, BATCH_SIZE))
|
Mockito.when(feedEntryDAO.findFeedsExceedingCapacity(50, BATCH_SIZE, false))
|
||||||
.thenReturn(Arrays.asList(feed1, feed2))
|
.thenReturn(Arrays.asList(feed1, feed2))
|
||||||
.thenReturn(Collections.emptyList());
|
.thenReturn(Collections.emptyList());
|
||||||
|
|
||||||
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 100)).thenReturn(80);
|
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 100, false)).thenReturn(80);
|
||||||
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 50)).thenReturn(50);
|
Mockito.when(feedEntryDAO.deleteOldEntries(1L, 50, false)).thenReturn(50);
|
||||||
Mockito.when(feedEntryDAO.deleteOldEntries(2L, 70)).thenReturn(70);
|
Mockito.when(feedEntryDAO.deleteOldEntries(2L, 70, false)).thenReturn(70);
|
||||||
|
|
||||||
service.cleanEntriesForFeedsExceedingCapacity(50);
|
service.cleanEntriesForFeedsExceedingCapacity(50);
|
||||||
|
|
||||||
@@ -132,11 +132,11 @@ class DatabaseCleaningServiceTest {
|
|||||||
void cleanEntriesOlderThanDeletesOldEntries() {
|
void cleanEntriesOlderThanDeletesOldEntries() {
|
||||||
Instant cutoff = LocalDate.now().minusDays(30).atStartOfDay().toInstant(ZoneOffset.UTC);
|
Instant cutoff = LocalDate.now().minusDays(30).atStartOfDay().toInstant(ZoneOffset.UTC);
|
||||||
|
|
||||||
Mockito.when(feedEntryDAO.deleteEntriesOlderThan(cutoff, BATCH_SIZE)).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);
|
Mockito.verify(feedEntryDAO, Mockito.times(3)).deleteEntriesOlderThan(cutoff, BATCH_SIZE, false);
|
||||||
Mockito.verify(entriesDeletedMeter, Mockito.times(3)).mark(Mockito.anyLong());
|
Mockito.verify(entriesDeletedMeter, Mockito.times(3)).mark(Mockito.anyLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,185 @@
|
|||||||
|
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;
|
||||||
|
import com.commafeed.frontend.model.Entry;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@QuarkusTest
|
||||||
|
class DatabaseCleaningIT extends BaseIT {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
DatabaseCleaningService databaseCleaningService;
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class KeepStarredEntries {
|
||||||
|
|
||||||
|
@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());
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
// 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 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());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
@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());
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
// 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 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());
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
// Unstar the entry
|
||||||
|
unstarEntry(entry.getId(), subscriptionId);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Verify both entries were deleted (neither is starred)
|
||||||
|
Entries entriesAfter = getFeedEntries(subscriptionId);
|
||||||
|
Assertions.assertEquals(0, entriesAfter.getEntries().size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user