diff --git a/internal/reader/atom/atom_10_test.go b/internal/reader/atom/atom_10_test.go index be6e9148..7b69f29d 100644 --- a/internal/reader/atom/atom_10_test.go +++ b/internal/reader/atom/atom_10_test.go @@ -217,12 +217,31 @@ func TestParseFeedURL(t *testing.T) { } } -func TestParseFeedWithRelativeURL(t *testing.T) { +func TestParseFeedWithRelativeFeedURL(t *testing.T) { + data := ` + + Example Feed + + + 2003-12-13T18:30:02Z + ` + + feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)), "10") + if err != nil { + t.Fatal(err) + } + + if feed.FeedURL != "https://example.org/feed" { + t.Errorf("Incorrect feed URL, got: %s", feed.FeedURL) + } +} + +func TestParseFeedWithRelativeSiteURL(t *testing.T) { data := ` Example Feed - + Test @@ -241,15 +260,47 @@ func TestParseFeedWithRelativeURL(t *testing.T) { } if feed.FeedURL != "https://example.org/blog/atom.xml" { - t.Errorf("Incorrect feed URL, got: %s", feed.FeedURL) + t.Errorf("Incorrect feed URL, got: %q", feed.FeedURL) } if feed.SiteURL != "https://example.org/blog" { - t.Errorf("Incorrect site URL, got: %s", feed.SiteURL) + t.Errorf("Incorrect site URL, got: %q", feed.SiteURL) } if feed.Entries[0].URL != "https://example.org/blog/article.html" { - t.Errorf("Incorrect entry URL, got: %s", feed.Entries[0].URL) + t.Errorf("Incorrect entry URL, got: %q", feed.Entries[0].URL) + } +} + +func TestParseFeedSiteURLWithTrailingSpace(t *testing.T) { + data := ` + + + ` + + feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)), "10") + if err != nil { + t.Fatal(err) + } + + if feed.SiteURL != "http://example.org" { + t.Errorf("Incorrect site URL, got: %q", feed.SiteURL) + } +} + +func TestParseFeedWithFeedURLWithTrailingSpace(t *testing.T) { + data := ` + + + ` + + feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)), "10") + if err != nil { + t.Fatal(err) + } + + if feed.FeedURL != "https://example.org/blog/atom.xml" { + t.Errorf("Incorrect site URL, got: %q", feed.FeedURL) } } diff --git a/internal/reader/json/adapter.go b/internal/reader/json/adapter.go index e944f75a..cf54100f 100644 --- a/internal/reader/json/adapter.go +++ b/internal/reader/json/adapter.go @@ -24,15 +24,15 @@ func NewJSONAdapter(jsonFeed *JSONFeed) *JSONAdapter { return &JSONAdapter{jsonFeed} } -func (j *JSONAdapter) BuildFeed(feedURL string) *model.Feed { +func (j *JSONAdapter) BuildFeed(baseURL string) *model.Feed { feed := &model.Feed{ Title: strings.TrimSpace(j.jsonFeed.Title), - FeedURL: j.jsonFeed.FeedURL, - SiteURL: j.jsonFeed.HomePageURL, + FeedURL: strings.TrimSpace(j.jsonFeed.FeedURL), + SiteURL: strings.TrimSpace(j.jsonFeed.HomePageURL), } if feed.FeedURL == "" { - feed.FeedURL = feedURL + feed.FeedURL = strings.TrimSpace(baseURL) } // Fallback to the feed URL if the site URL is empty. @@ -40,11 +40,11 @@ func (j *JSONAdapter) BuildFeed(feedURL string) *model.Feed { feed.SiteURL = feed.FeedURL } - if feedURL, err := urllib.AbsoluteURL(feedURL, j.jsonFeed.FeedURL); err == nil { + if feedURL, err := urllib.AbsoluteURL(baseURL, feed.FeedURL); err == nil { feed.FeedURL = feedURL } - if siteURL, err := urllib.AbsoluteURL(feedURL, j.jsonFeed.HomePageURL); err == nil { + if siteURL, err := urllib.AbsoluteURL(baseURL, feed.SiteURL); err == nil { feed.SiteURL = siteURL } diff --git a/internal/reader/json/parser_test.go b/internal/reader/json/parser_test.go index 8e4102e0..0bbd39ec 100644 --- a/internal/reader/json/parser_test.go +++ b/internal/reader/json/parser_test.go @@ -177,6 +177,82 @@ func TestParsePodcast(t *testing.T) { } } +func TestParseFeedWithFeedURLWithTrailingSpace(t *testing.T) { + data := `{ + "version": "https://jsonfeed.org/version/1", + "title": "My Example Feed", + "home_page_url": "https://example.org/", + "feed_url": "https://example.org/feed.json ", + "items": [] + }` + + feed, err := Parse("https://example.org/feed.json", bytes.NewBufferString(data)) + if err != nil { + t.Fatal(err) + } + + if feed.FeedURL != "https://example.org/feed.json" { + t.Errorf("Incorrect feed URL, got: %s", feed.FeedURL) + } +} + +func TestParseFeedWithRelativeFeedURL(t *testing.T) { + data := `{ + "version": "https://jsonfeed.org/version/1", + "title": "My Example Feed", + "home_page_url": "https://example.org/", + "feed_url": "/feed.json", + "items": [] + }` + + feed, err := Parse("https://example.org/feed.json", bytes.NewBufferString(data)) + if err != nil { + t.Fatal(err) + } + + if feed.FeedURL != "https://example.org/feed.json" { + t.Errorf("Incorrect feed URL, got: %s", feed.FeedURL) + } +} + +func TestParseFeedSiteURLWithTrailingSpace(t *testing.T) { + data := `{ + "version": "https://jsonfeed.org/version/1", + "title": "My Example Feed", + "home_page_url": "https://example.org/ ", + "feed_url": "https://example.org/feed.json", + "items": [] + }` + + feed, err := Parse("https://example.org/feed.json", bytes.NewBufferString(data)) + if err != nil { + t.Fatal(err) + } + + if feed.SiteURL != "https://example.org/" { + t.Errorf("Incorrect site URL, got: %s", feed.SiteURL) + } +} + +func TestParseFeedWithRelativeSiteURL(t *testing.T) { + data := `{ + "version": "https://jsonfeed.org/version/1", + "title": "My Example Feed", + "home_page_url": "/home ", + "feed_url": "https://example.org/feed.json", + "items": [] + }` + + feed, err := Parse("https://example.org/feed.json", bytes.NewBufferString(data)) + if err != nil { + t.Fatal(err) + } + + if feed.SiteURL != "https://example.org/home" { + t.Errorf("Incorrect site URL, got: %s", feed.SiteURL) + } +} + func TestParseFeedWithoutTitle(t *testing.T) { data := `{ "version": "https://jsonfeed.org/version/1", diff --git a/internal/reader/rdf/adapter.go b/internal/reader/rdf/adapter.go index bc8c76ed..f90ebaca 100644 --- a/internal/reader/rdf/adapter.go +++ b/internal/reader/rdf/adapter.go @@ -24,18 +24,18 @@ func NewRDFAdapter(rdf *RDF) *RDFAdapter { return &RDFAdapter{rdf} } -func (r *RDFAdapter) BuildFeed(feedURL string) *model.Feed { +func (r *RDFAdapter) BuildFeed(baseURL string) *model.Feed { feed := &model.Feed{ Title: stripTags(r.rdf.Channel.Title), - FeedURL: feedURL, - SiteURL: r.rdf.Channel.Link, + FeedURL: strings.TrimSpace(baseURL), + SiteURL: strings.TrimSpace(r.rdf.Channel.Link), } if feed.Title == "" { - feed.Title = feedURL + feed.Title = baseURL } - if siteURL, err := urllib.AbsoluteURL(feedURL, r.rdf.Channel.Link); err == nil { + if siteURL, err := urllib.AbsoluteURL(feed.FeedURL, feed.SiteURL); err == nil { feed.SiteURL = siteURL } diff --git a/internal/reader/rdf/parser_test.go b/internal/reader/rdf/parser_test.go index 5009a412..021772b4 100644 --- a/internal/reader/rdf/parser_test.go +++ b/internal/reader/rdf/parser_test.go @@ -289,7 +289,37 @@ func TestParseRDFFeedWithRelativeLink(t *testing.T) { xmlns="http://purl.org/rss/1.0/"> Example Feed - /test/index.html + /test/index.html + + + Example + http://example.org/item + Test + + ` + + feed, err := Parse("http://example.org/feed", bytes.NewReader([]byte(data))) + if err != nil { + t.Fatal(err) + } + + if feed.SiteURL != "http://example.org/test/index.html" { + t.Errorf(`Incorrect SiteURL, got: %q`, feed.SiteURL) + } + + if feed.FeedURL != "http://example.org/feed" { + t.Errorf(`Incorrect FeedURL, got: %q`, feed.FeedURL) + } +} + +func TestParseRDFFeedSiteURLWithTrailingSpace(t *testing.T) { + data := ` + + + Example Feed + http://example.org/test/index.html Example diff --git a/internal/reader/rss/adapter.go b/internal/reader/rss/adapter.go index 531cc53f..48def825 100644 --- a/internal/reader/rss/adapter.go +++ b/internal/reader/rss/adapter.go @@ -26,14 +26,15 @@ func NewRSSAdapter(rss *RSS) *RSSAdapter { return &RSSAdapter{rss} } -func (r *RSSAdapter) BuildFeed(feedURL string) *model.Feed { +func (r *RSSAdapter) BuildFeed(baseURL string) *model.Feed { feed := &model.Feed{ Title: html.UnescapeString(strings.TrimSpace(r.rss.Channel.Title)), - FeedURL: feedURL, - SiteURL: r.rss.Channel.Link, + FeedURL: strings.TrimSpace(baseURL), + SiteURL: strings.TrimSpace(r.rss.Channel.Link), } - if siteURL, err := urllib.AbsoluteURL(feedURL, r.rss.Channel.Link); err == nil { + // Ensure the Site URL is absolute. + if siteURL, err := urllib.AbsoluteURL(baseURL, feed.SiteURL); err == nil { feed.SiteURL = siteURL } @@ -41,7 +42,7 @@ func (r *RSSAdapter) BuildFeed(feedURL string) *model.Feed { for _, atomLink := range r.rss.Channel.AtomLinks.Links { atomLinkHref := strings.TrimSpace(atomLink.Href) if atomLinkHref != "" && atomLink.Rel == "self" { - if absoluteFeedURL, err := urllib.AbsoluteURL(feedURL, atomLinkHref); err == nil { + if absoluteFeedURL, err := urllib.AbsoluteURL(feed.FeedURL, atomLinkHref); err == nil { feed.FeedURL = absoluteFeedURL break } diff --git a/internal/reader/rss/parser_test.go b/internal/reader/rss/parser_test.go index d9dacac6..e3f8450f 100644 --- a/internal/reader/rss/parser_test.go +++ b/internal/reader/rss/parser_test.go @@ -109,6 +109,100 @@ func TestParseRss2Sample(t *testing.T) { } } +func TestParseFeedWithFeedURLWithTrailingSpace(t *testing.T) { + data := ` + + + Example + https://example.org/ + + + Test + https://example.org/item + + + ` + + feed, err := Parse("https://example.org/ ", bytes.NewReader([]byte(data))) + if err != nil { + t.Fatal(err) + } + + if feed.FeedURL != "https://example.org/rss" { + t.Errorf("Incorrect feed URL, got: %s", feed.FeedURL) + } +} + +func TestParseFeedWithRelativeFeedURL(t *testing.T) { + data := ` + + + Example + https://example.org/ + + + Test + https://example.org/item + + + ` + + feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data))) + if err != nil { + t.Fatal(err) + } + + if feed.FeedURL != "https://example.org/rss" { + t.Errorf("Incorrect feed URL, got: %s", feed.FeedURL) + } +} + +func TestParseFeedSiteURLWithTrailingSpace(t *testing.T) { + data := ` + + + Example + https://example.org/ + + Test + https://example.org/item + + + ` + + feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data))) + if err != nil { + t.Fatal(err) + } + + if feed.SiteURL != "https://example.org/" { + t.Errorf("Incorrect site URL, got: %s", feed.SiteURL) + } +} + +func TestParseFeedWithRelativeSiteURL(t *testing.T) { + data := ` + + + Example + /example + + Test + https://example.org/item + + + ` + + feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data))) + if err != nil { + t.Fatal(err) + } + + if feed.SiteURL != "https://example.org/example" { + t.Errorf("Incorrect site URL, got: %s", feed.SiteURL) + } +} + func TestParseFeedWithoutTitle(t *testing.T) { data := `