From db49e41acf362c9c7c567707980cd5f2d22ed900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Guillot?= Date: Thu, 19 Jun 2025 17:51:45 -0700 Subject: [PATCH] refactor(processor): move FilterEntryMaxAgeDays filter to filter package --- internal/reader/filter/filter.go | 25 ++++++++ internal/reader/filter/filter_test.go | 65 +++++++++++++++++++++ internal/reader/processor/processor.go | 10 +--- internal/reader/processor/processor_test.go | 28 --------- 4 files changed, 92 insertions(+), 36 deletions(-) diff --git a/internal/reader/filter/filter.go b/internal/reader/filter/filter.go index 7d8de1b7..4fa8d7b3 100644 --- a/internal/reader/filter/filter.go +++ b/internal/reader/filter/filter.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "miniflux.app/v2/internal/config" "miniflux.app/v2/internal/model" ) @@ -21,7 +22,31 @@ const ( filterActionAllow filterActionType = "allow" ) +func isBlockedGlobally(entry *model.Entry) bool { + if config.Opts == nil { + return false + } + + if config.Opts.FilterEntryMaxAgeDays() > 0 { + maxAge := time.Duration(config.Opts.FilterEntryMaxAgeDays()) * 24 * time.Hour + if entry.Date.Before(time.Now().Add(-maxAge)) { + slog.Debug("Entry is blocked globally due to max age", + slog.String("entry_url", entry.URL), + slog.Time("entry_date", entry.Date), + slog.Duration("max_age", maxAge), + ) + return true + } + } + + return false +} + func IsBlockedEntry(feed *model.Feed, entry *model.Entry, user *model.User) bool { + if isBlockedGlobally(entry) { + return true + } + combinedRules := combineFilterRules(user.BlockFilterEntryRules, feed.BlockFilterEntryRules) if combinedRules != "" { if matchesEntryFilterRules(combinedRules, entry, feed, filterActionBlock) { diff --git a/internal/reader/filter/filter_test.go b/internal/reader/filter/filter_test.go index 4d2aa0aa..1540bab0 100644 --- a/internal/reader/filter/filter_test.go +++ b/internal/reader/filter/filter_test.go @@ -4,9 +4,11 @@ package filter // import "miniflux.app/v2/internal/reader/filter" import ( + "os" "testing" "time" + "miniflux.app/v2/internal/config" "miniflux.app/v2/internal/model" ) @@ -171,3 +173,66 @@ func TestMaxAgeFilter(t *testing.T) { t.Error("Expected new entry to not be blocked with max-age:1d") } } + +func TestIsBlockedGlobally(t *testing.T) { + var err error + config.Opts, err = config.NewParser().ParseEnvironmentVariables() + if err != nil { + t.Fatalf(`Parsing failure: %v`, err) + } + + if isBlockedGlobally(&model.Entry{Title: "Test Entry", Date: time.Date(2020, 5, 1, 05, 05, 05, 05, time.UTC)}) { + t.Error("Expected no entries to be blocked globally when max-age is not set") + } + + os.Setenv("FILTER_ENTRY_MAX_AGE_DAYS", "30") + defer os.Clearenv() + + config.Opts, err = config.NewParser().ParseEnvironmentVariables() + if err != nil { + t.Fatalf(`Parsing failure: %v`, err) + } + + if !isBlockedGlobally(&model.Entry{Title: "Test Entry", Date: time.Date(2020, 5, 1, 05, 05, 05, 05, time.UTC)}) { + t.Error("Expected entries to be blocked globally when max-age is set") + } + + if isBlockedGlobally(&model.Entry{Title: "Test Entry", Date: time.Now().Add(-2 * time.Hour)}) { + t.Error("Expected entries not to be blocked globally when they are within the max-age limit") + } +} + +func TestIsBlockedEntryWithGlobalMaxAge(t *testing.T) { + os.Setenv("FILTER_ENTRY_MAX_AGE_DAYS", "30") + defer os.Clearenv() + + var err error + config.Opts, err = config.NewParser().ParseEnvironmentVariables() + if err != nil { + t.Fatalf(`Parsing failure: %v`, err) + } + + entry := &model.Entry{Title: "Test Entry", Date: time.Now().Add(-31 * 24 * time.Hour)} // 31 days old + feed := &model.Feed{ID: 1} + user := &model.User{} + + if !IsBlockedEntry(feed, entry, user) { + t.Error("Expected entry to be blocked due to global max-age rule") + } +} + +func TestIsBlockedEntryWithDefaultGlobalMaxAge(t *testing.T) { + var err error + config.Opts, err = config.NewParser().ParseEnvironmentVariables() + if err != nil { + t.Fatalf(`Parsing failure: %v`, err) + } + + entry := &model.Entry{Title: "Test Entry", Date: time.Now().Add(-31 * 24 * time.Hour)} // 31 days old + feed := &model.Feed{ID: 1} + user := &model.User{} + + if IsBlockedEntry(feed, entry, user) { + t.Error("Expected entry not to be blocked due to default global max-age rule") + } +} diff --git a/internal/reader/processor/processor.go b/internal/reader/processor/processor.go index 7155cf44..966ea972 100644 --- a/internal/reader/processor/processor.go +++ b/internal/reader/processor/processor.go @@ -47,7 +47,8 @@ func ProcessFeedEntries(store *storage.Storage, feed *model.Feed, userID int64, slog.Int64("feed_id", feed.ID), slog.String("feed_url", feed.FeedURL), ) - if filter.IsBlockedEntry(feed, entry, user) || !filter.IsAllowedEntry(feed, entry, user) || !isRecentEntry(entry) { + + if filter.IsBlockedEntry(feed, entry, user) || !filter.IsAllowedEntry(feed, entry, user) { continue } @@ -183,10 +184,3 @@ func ProcessEntryWebPage(feed *model.Feed, entry *model.Entry, user *model.User) return nil } - -func isRecentEntry(entry *model.Entry) bool { - if config.Opts.FilterEntryMaxAgeDays() == 0 || entry.Date.After(time.Now().AddDate(0, 0, -config.Opts.FilterEntryMaxAgeDays())) { - return true - } - return false -} diff --git a/internal/reader/processor/processor_test.go b/internal/reader/processor/processor_test.go index 1628dbb9..99a182c2 100644 --- a/internal/reader/processor/processor_test.go +++ b/internal/reader/processor/processor_test.go @@ -5,36 +5,8 @@ package processor // import "miniflux.app/v2/internal/reader/processor" import ( "testing" - "time" - - "miniflux.app/v2/internal/config" - "miniflux.app/v2/internal/model" ) -func TestIsRecentEntry(t *testing.T) { - parser := config.NewParser() - var err error - config.Opts, err = parser.ParseEnvironmentVariables() - if err != nil { - t.Fatalf(`Parsing failure: %v`, err) - } - var scenarios = []struct { - entry *model.Entry - expected bool - }{ - {&model.Entry{Title: "Example1", Date: time.Date(2005, 5, 1, 05, 05, 05, 05, time.UTC)}, true}, - {&model.Entry{Title: "Example2", Date: time.Date(2010, 5, 1, 05, 05, 05, 05, time.UTC)}, true}, - {&model.Entry{Title: "Example3", Date: time.Date(2020, 5, 1, 05, 05, 05, 05, time.UTC)}, true}, - {&model.Entry{Title: "Example4", Date: time.Date(2024, 3, 15, 05, 05, 05, 05, time.UTC)}, true}, - } - for _, tc := range scenarios { - result := isRecentEntry(tc.entry) - if tc.expected != result { - t.Errorf(`Unexpected result, got %v for entry %q`, result, tc.entry.Title) - } - } -} - func TestMinifyEntryContent(t *testing.T) { input := `

Some text with a link

` expected := `

Some text with a link

`