From cdb46ab947524f6c31ae016b6ade8e4eadac95e8 Mon Sep 17 00:00:00 2001 From: vycdev2 Date: Sat, 1 Aug 2026 14:46:30 +0000 Subject: [PATCH] fix: preserve RSS history across feed rollover --- Jobs/RssFeedJob.cs | 7 ++++++- Morpheus.Tests/RssFeedJobTests.cs | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Jobs/RssFeedJob.cs b/Jobs/RssFeedJob.cs index fe27bc0..b10a9ec 100644 --- a/Jobs/RssFeedJob.cs +++ b/Jobs/RssFeedJob.cs @@ -44,7 +44,9 @@ public async Task Execute(IJobExecutionContext context) // If nothing from this feed has ever been seen, this is an initial run: mark // everything seen and only post the latest entry to avoid backfilling history. - bool initialSeed = !entries.Any(e => seen.Contains(e.EntryId)); + // Check all history for the feed because older seen entries may have rolled out of + // the feed's current response. + bool initialSeed = !await HasFeedHistoryAsync(db, feedUrl); if (initialSeed) { RssFeedService.FeedEntry latest = entries.OrderByDescending(e => e.Published).First(); @@ -97,6 +99,9 @@ internal static async Task DispatchAsync( return allSucceeded; } + internal static Task HasFeedHistoryAsync(DB db, string feedUrl) => + db.RssSeenEntries.AnyAsync(entry => entry.FeedUrl == feedUrl); + private async Task SendAsync(RssSubscription sub, string content) { if (sub.Webhook == null) diff --git a/Morpheus.Tests/RssFeedJobTests.cs b/Morpheus.Tests/RssFeedJobTests.cs index c3ceed1..afb528b 100644 --- a/Morpheus.Tests/RssFeedJobTests.cs +++ b/Morpheus.Tests/RssFeedJobTests.cs @@ -1,3 +1,6 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Morpheus.Database; using Morpheus.Database.Models; using Morpheus.Jobs; using Morpheus.Services; @@ -6,6 +9,28 @@ namespace Morpheus.Tests; public class RssFeedJobTests { + [Fact] + public async Task HasFeedHistoryAsync_WhenOlderEntryRolledOutOfFeed_ReturnsTrue() + { + await using SqliteConnection connection = new("Data Source=:memory:"); + await connection.OpenAsync(); + DbContextOptions options = new DbContextOptionsBuilder() + .UseSqlite(connection) + .Options; + await using DB db = new(options); + await db.Database.EnsureCreatedAsync(); + db.RssSeenEntries.Add(new RssSeenEntry + { + FeedUrl = "https://example.com/feed", + EntryId = "entry-that-is-no-longer-in-the-feed" + }); + await db.SaveChangesAsync(); + + bool hasHistory = await RssFeedJob.HasFeedHistoryAsync(db, "https://example.com/feed"); + + Assert.True(hasHistory); + } + [Fact] public async Task DispatchAsync_WhenOneDeliveryFails_ReportsFailureAndAttemptsEverySubscriber() {