// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package rss // import "miniflux.app/v2/internal/reader/rss"
import (
"bytes"
"testing"
"time"
)
func TestParseRss2Sample(t *testing.T) {
data := `
Liftoff News
http://liftoff.msfc.nasa.gov/
Liftoff to Space Exploration.
http://liftoff.msfc.nasa.gov/HomePageXtra/MeatBall.gif
NASA
http://liftoff.msfc.nasa.gov/
en-us
Tue, 10 Jun 2003 04:00:00 GMT
Tue, 10 Jun 2003 09:41:01 GMT
http://blogs.law.harvard.edu/tech/rss
Weblog Editor 2.0
editor@example.com
webmaster@example.com
-
Star City
http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp
How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's <a href="http://howe.iki.rssi.ru/GCTC/gctc_e.htm">Star City</a>.
Tue, 03 Jun 2003 09:39:21 GMT
http://liftoff.msfc.nasa.gov/2003/06/03.html#item573
-
Sky watchers in Europe, Asia, and parts of Alaska and Canada will experience a <a href="http://science.nasa.gov/headlines/y2003/30may_solareclipse.htm">partial eclipse of the Sun</a> on Saturday, May 31st.
Fri, 30 May 2003 11:06:42 GMT
http://liftoff.msfc.nasa.gov/2003/05/30.html#item572
-
The Engine That Does More
http://liftoff.msfc.nasa.gov/news/2003/news-VASIMR.asp
Before man travels to Mars, NASA hopes to design new engines that will let us fly through the Solar System more quickly. The proposed VASIMR engine would do that.
Tue, 27 May 2003 08:37:32 GMT
http://liftoff.msfc.nasa.gov/2003/05/27.html#item571
-
Astronauts' Dirty Laundry
http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp
Compared to earlier spacecraft, the International Space Station has many luxuries, but laundry facilities are not one of them. Instead, astronauts have other options.
Tue, 20 May 2003 08:56:02 GMT
http://liftoff.msfc.nasa.gov/2003/05/20.html#item570
`
feed, err := Parse("http://liftoff.msfc.nasa.gov/rss.xml", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Title != "Liftoff News" {
t.Errorf("Incorrect title, got: %s", feed.Title)
}
if feed.FeedURL != "http://liftoff.msfc.nasa.gov/rss.xml" {
t.Errorf("Incorrect feed URL, got: %s", feed.FeedURL)
}
if feed.SiteURL != "http://liftoff.msfc.nasa.gov/" {
t.Errorf("Incorrect site URL, got: %s", feed.SiteURL)
}
if feed.IconURL != "http://liftoff.msfc.nasa.gov/HomePageXtra/MeatBall.gif" {
t.Errorf("Incorrect image URL, got: %s", feed.IconURL)
}
if len(feed.Entries) != 4 {
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
}
expectedDate := time.Date(2003, time.June, 3, 9, 39, 21, 0, time.UTC)
if !feed.Entries[0].Date.Equal(expectedDate) {
t.Errorf("Incorrect entry date, got: %v, want: %v", feed.Entries[0].Date, expectedDate)
}
if feed.Entries[0].Hash != "5b2b4ac2fe1786ddf0fd2da2f1b07f64e691264f41f2db3ea360f31bb6d9152b" {
t.Errorf("Incorrect entry hash, got: %s", feed.Entries[0].Hash)
}
if feed.Entries[0].URL != "http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp" {
t.Errorf("Incorrect entry URL, got: %s", feed.Entries[0].URL)
}
if feed.Entries[0].Title != "Star City" {
t.Errorf("Incorrect entry title, got: %s", feed.Entries[0].Title)
}
if feed.Entries[0].Content != `How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's Star City.` {
t.Errorf("Incorrect entry content, got: %s", feed.Entries[0].Content)
}
if feed.Entries[1].URL != "http://liftoff.msfc.nasa.gov/2003/05/30.html#item572" {
t.Errorf("Incorrect entry URL, got: %s", feed.Entries[1].URL)
}
}
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 := `
https://example.org/
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Title != "https://example.org/" {
t.Errorf("Incorrect feed title, got: %s", feed.Title)
}
}
func TestParseEntryWithoutTitleAndDescription(t *testing.T) {
data := `
https://example.org/
-
https://example.org/item
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Title != "https://example.org/item" {
t.Errorf("Incorrect entry title, got: %s", feed.Entries[0].Title)
}
}
func TestParseEntryWithoutTitleButWithDescription(t *testing.T) {
data := `
https://example.org/
-
https://example.org/item
This is the description
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Title != "This is the description" {
t.Errorf("Incorrect entry title, got: %s", feed.Entries[0].Title)
}
}
func TestParseEntryWithMediaTitle(t *testing.T) {
data := `
https://example.org/
-
Entry Title
https://example.org/item
Media Title
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Title != "Entry Title" {
t.Errorf("Incorrect entry title, got: %q", feed.Entries[0].Title)
}
}
func TestParseEntryWithDCTitleOnly(t *testing.T) {
data := `
https://example.org/
-
Entry Title
https://example.org/item
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Title != "Entry Title" {
t.Errorf("Incorrect entry title, got: %q", feed.Entries[0].Title)
}
}
func TestParseEntryWithoutLink(t *testing.T) {
data := `
https://example.org/
-
1234
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].URL != "https://example.org/" {
t.Errorf("Incorrect entry link, got: %s", feed.Entries[0].URL)
}
if feed.Entries[0].Hash != "03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4" {
t.Errorf("Incorrect entry hash, got: %s", feed.Entries[0].Hash)
}
}
func TestParseEntryWithOnlyGuidPermalink(t *testing.T) {
data := `
https://example.org/
-
https://example.org/some-article.html
-
https://example.org/another-article.html
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].URL != "https://example.org/some-article.html" {
t.Errorf("Incorrect entry link, got: %s", feed.Entries[0].URL)
}
if feed.Entries[1].URL != "https://example.org/another-article.html" {
t.Errorf("Incorrect entry link, got: %s", feed.Entries[1].URL)
}
}
func TestParseEntryWithAtomLink(t *testing.T) {
data := `
https://example.org/
-
Test
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].URL != "https://example.org/item" {
t.Errorf("Incorrect entry link, got: %s", feed.Entries[0].URL)
}
}
func TestParseEntryWithMultipleAtomLinks(t *testing.T) {
data := `
https://example.org/
-
Test
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].URL != "https://example.org/b" {
t.Errorf("Incorrect entry link, got: %s", feed.Entries[0].URL)
}
}
func TestParseFeedURLWithAtomLink(t *testing.T) {
data := `
Example
https://example.org/
`
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)
}
if feed.SiteURL != "https://example.org/" {
t.Errorf("Incorrect site URL, got: %s", feed.SiteURL)
}
}
func TestParseFeedWithWebmaster(t *testing.T) {
data := `
Example
https://example.org/
webmaster@example.com
-
Test
https://example.org/item
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "webmaster@example.com"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseFeedWithManagingEditor(t *testing.T) {
data := `
Example
https://example.org/
webmaster@example.com
editor@example.com
-
Test
https://example.org/item
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "editor@example.com"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseEntryWithAuthorAndInnerHTML(t *testing.T) {
data := `
Example
https://example.org/
-
Test
https://example.org/item
by Foo Bar
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "by Foo Bar"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseEntryWithAuthorAndCDATA(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)
}
expected := "by Foo Bar"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseEntryWithAtomAuthorEmail(t *testing.T) {
data := `
Example
https://example.org/
-
Test
https://example.org/item
author@example.org
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "author@example.org"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseEntryWithAtomAuthorName(t *testing.T) {
data := `
Example
https://example.org/
-
Test
https://example.org/item
Foo Bar
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "Foo Bar"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got: %q instead of %q", result, expected)
}
}
func TestParseEntryWithDublinCoreAuthor(t *testing.T) {
data := `
Example
https://example.org/
-
Test
https://example.org/item
Me (me@example.com)
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "Me (me@example.com)"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseEntryWithItunesAuthor(t *testing.T) {
data := `
Example
https://example.org/
-
Test
https://example.org/item
Someone
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "Someone"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseFeedWithItunesAuthor(t *testing.T) {
data := `
Example
https://example.org/
Someone
-
Test
https://example.org/item
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "Someone"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseFeedWithItunesOwner(t *testing.T) {
data := `
Example
https://example.org/
John Doe
john.doe@example.com
-
Test
https://example.org/item
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "John Doe"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseFeedWithItunesOwnerEmail(t *testing.T) {
data := `
Example
https://example.org/
john.doe@example.com
-
Test
https://example.org/item
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "john.doe@example.com"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseEntryWithGooglePlayAuthor(t *testing.T) {
data := `
Example
https://example.org/
-
Test
https://example.org/item
Someone
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "Someone"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseFeedWithGooglePlayAuthor(t *testing.T) {
data := `
Example
https://example.org/
Someone
-
Test
https://example.org/item
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := "Someone"
result := feed.Entries[0].Author
if result != expected {
t.Errorf("Incorrect entry author, got %q instead of %q", result, expected)
}
}
func TestParseEntryWithDublinCoreDate(t *testing.T) {
data := `
Example
http://example.org/
-
Item 1
http://example.org/item1
Description.
UUID
2002-09-29T23:40:06-05:00
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
location, _ := time.LoadLocation("EST")
expectedDate := time.Date(2002, time.September, 29, 23, 40, 06, 0, location)
if !feed.Entries[0].Date.Equal(expectedDate) {
t.Errorf("Incorrect entry date, got: %v, want: %v", feed.Entries[0].Date, expectedDate)
}
}
func TestParseEntryWithContentEncoded(t *testing.T) {
data := `
Example
http://example.org/
-
Item 1
http://example.org/item1
Description.
UUID
Example.
]]>
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Content != `Example.
` {
t.Errorf("Incorrect entry content, got: %s", feed.Entries[0].Content)
}
}
// https://www.rssboard.org/rss-encoding-examples
func TestParseEntryDescriptionWithEncodedHTMLTags(t *testing.T) {
data := `
Example
http://example.org/
-
Item 1
http://example.org/item1
this is <b>bold</b>
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Content != `this is bold` {
t.Errorf("Incorrect entry content, got: %q", feed.Entries[0].Content)
}
}
// https://www.rssboard.org/rss-encoding-examples
func TestParseEntryWithDescriptionWithHTMLCDATA(t *testing.T) {
data := `
Example
http://example.org/
-
Item 1
http://example.org/item1
bold]]>
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Content != `this is bold` {
t.Errorf("Incorrect entry content, got: %q", feed.Entries[0].Content)
}
}
// https://www.rssboard.org/rss-encoding-examples
func TestParseEntryDescriptionWithEncodingAngleBracketsInText(t *testing.T) {
data := `
Example
http://example.org/
-
Item 1
http://example.org/item1
5 < 8, ticker symbol <BIGCO>
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Content != `5 < 8, ticker symbol <BIGCO>` {
t.Errorf("Incorrect entry content, got: %q", feed.Entries[0].Content)
}
}
// https://www.rssboard.org/rss-encoding-examples
func TestParseEntryDescriptionWithEncodingAngleBracketsWithinCDATASection(t *testing.T) {
data := `
Example
http://example.org/
-
Item 1
http://example.org/item1
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Content != `5 < 8, ticker symbol <BIGCO>` {
t.Errorf("Incorrect entry content, got: %q", feed.Entries[0].Content)
}
}
func TestParseEntryWithFeedBurnerLink(t *testing.T) {
data := `
Example
http://example.org/
-
Item 1
http://example.org/item1
http://example.org/original
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].URL != "http://example.org/original" {
t.Errorf("Incorrect entry content, got: %s", feed.Entries[0].URL)
}
}
func TestParseEntryTitleWithWhitespaces(t *testing.T) {
data := `
Example
http://example.org
-
Some Title
http://www.example.org/entries/1
Fri, 15 Jul 2005 00:00:00 -0500
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Title != "Some Title" {
t.Errorf("Incorrect entry title, got: %s", feed.Entries[0].Title)
}
}
func TestParseEntryWithEnclosures(t *testing.T) {
data := `
My Podcast Feed
http://example.org
some.email@example.org
-
Podcasting with RSS
http://www.example.org/entries/1
An overview of RSS podcasting
Fri, 15 Jul 2005 00:00:00 -0500
http://www.example.org/entries/1
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Fatalf("Incorrect number of entries, got: %d", len(feed.Entries))
}
if len(feed.Entries[0].Enclosures) != 1 {
t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures))
}
if feed.Entries[0].Enclosures[0].URL != "http://www.example.org/myaudiofile.mp3" {
t.Errorf("Incorrect enclosure URL, got: %s", feed.Entries[0].Enclosures[0].URL)
}
if feed.Entries[0].Enclosures[0].MimeType != "audio/mpeg" {
t.Errorf("Incorrect enclosure type, got: %s", feed.Entries[0].Enclosures[0].MimeType)
}
if feed.Entries[0].Enclosures[0].Size != 12345 {
t.Errorf("Incorrect enclosure length, got: %d", feed.Entries[0].Enclosures[0].Size)
}
}
func TestParseEntryWithIncorrectEnclosureLength(t *testing.T) {
data := `
My Podcast Feed
http://example.org
some.email@example.org
-
Podcasting with RSS
http://www.example.org/entries/1
An overview of RSS podcasting
Fri, 15 Jul 2005 00:00:00 -0500
http://www.example.org/entries/1
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Fatalf("Incorrect number of entries, got: %d", len(feed.Entries))
}
if len(feed.Entries[0].Enclosures) != 2 {
t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures))
}
if feed.Entries[0].Enclosures[0].URL != "http://www.example.org/myaudiofile.mp3" {
t.Errorf("Incorrect enclosure URL, got: %s", feed.Entries[0].Enclosures[0].URL)
}
if feed.Entries[0].Enclosures[0].MimeType != "audio/mpeg" {
t.Errorf("Incorrect enclosure type, got: %s", feed.Entries[0].Enclosures[0].MimeType)
}
if feed.Entries[0].Enclosures[0].Size != 0 {
t.Errorf("Incorrect enclosure length, got: %d", feed.Entries[0].Enclosures[0].Size)
}
if feed.Entries[0].Enclosures[1].Size != 0 {
t.Errorf("Incorrect enclosure length, got: %d", feed.Entries[0].Enclosures[0].Size)
}
}
func TestParseEntryWithDuplicatedEnclosureURL(t *testing.T) {
data := `
My Podcast Feed
http://example.org
-
Podcasting with RSS
http://www.example.org/entries/1
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Fatalf("Incorrect number of entries, got: %d", len(feed.Entries))
}
if len(feed.Entries[0].Enclosures) != 1 {
t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures))
}
if feed.Entries[0].Enclosures[0].URL != "http://www.example.org/myaudiofile.mp3" {
t.Errorf("Incorrect enclosure URL, got: %s", feed.Entries[0].Enclosures[0].URL)
}
}
func TestParseEntryWithEmptyEnclosureURL(t *testing.T) {
data := `
My Podcast Feed
http://example.org
some.email@example.org
-
Podcasting with RSS
http://www.example.org/entries/1
An overview of RSS podcasting
Fri, 15 Jul 2005 00:00:00 -0500
http://www.example.org/entries/1
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Fatalf("Incorrect number of entries, got: %d", len(feed.Entries))
}
if len(feed.Entries[0].Enclosures) != 0 {
t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures))
}
}
func TestParseEntryWithRelativeEnclosureURL(t *testing.T) {
data := `
My Podcast Feed
http://example.org
some.email@example.org
-
Podcasting with RSS
http://www.example.org/entries/1
An overview of RSS podcasting
Fri, 15 Jul 2005 00:00:00 -0500
http://www.example.org/entries/1
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Fatalf("Incorrect number of entries, got: %d", len(feed.Entries))
}
if len(feed.Entries[0].Enclosures) != 1 {
t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures))
}
if feed.Entries[0].Enclosures[0].URL != "http://example.org/files/file.mp3" {
t.Errorf("Incorrect enclosure URL, got: %q", feed.Entries[0].Enclosures[0].URL)
}
}
func TestParseEntryWithFeedBurnerEnclosures(t *testing.T) {
data := `
My Example Feed
http://example.org
some.email@example.org
-
Example Item
http://www.example.org/entries/1
http://example.org/67ca416c-f22a-4228-a681-68fc9998ec10/File.mp3
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Fatalf("Incorrect number of entries, got: %d", len(feed.Entries))
}
if len(feed.Entries[0].Enclosures) != 1 {
t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures))
}
if feed.Entries[0].Enclosures[0].URL != "http://example.org/67ca416c-f22a-4228-a681-68fc9998ec10/File.mp3" {
t.Errorf("Incorrect enclosure URL, got: %s", feed.Entries[0].Enclosures[0].URL)
}
if feed.Entries[0].Enclosures[0].MimeType != "audio/mpeg" {
t.Errorf("Incorrect enclosure type, got: %s", feed.Entries[0].Enclosures[0].MimeType)
}
if feed.Entries[0].Enclosures[0].Size != 76192460 {
t.Errorf("Incorrect enclosure length, got: %d", feed.Entries[0].Enclosures[0].Size)
}
}
func TestParseEntryWithFeedBurnerEnclosuresAndRelativeURL(t *testing.T) {
data := `
My Example Feed
http://example.org
-
Example Item
http://www.example.org/entries/1
/67ca416c-f22a-4228-a681-68fc9998ec10/File.mp3
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
}
if len(feed.Entries[0].Enclosures) != 1 {
t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures))
}
if feed.Entries[0].Enclosures[0].URL != "http://example.org/67ca416c-f22a-4228-a681-68fc9998ec10/File.mp3" {
t.Errorf("Incorrect enclosure URL, got: %s", feed.Entries[0].Enclosures[0].URL)
}
}
func TestParseEntryWithRelativeURL(t *testing.T) {
data := `
https://example.org/
-
item.html
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Title != "https://example.org/item.html" {
t.Errorf("Incorrect entry title, got: %s", feed.Entries[0].Title)
}
}
func TestParseEntryWithCommentsURL(t *testing.T) {
data := `
https://example.org/
-
Item 1
https://example.org/item1
https://example.org/comments
42
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].CommentsURL != "https://example.org/comments" {
t.Errorf("Incorrect entry comments URL, got: %q", feed.Entries[0].CommentsURL)
}
}
func TestParseEntryWithInvalidCommentsURL(t *testing.T) {
data := `
https://example.org/
-
Item 1
https://example.org/item1
Some text
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].CommentsURL != "" {
t.Errorf("Incorrect entry comments URL, got: %q", feed.Entries[0].CommentsURL)
}
}
func TestParseInvalidXml(t *testing.T) {
data := `garbage`
_, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err == nil {
t.Error("Parse should returns an error")
}
}
func TestParseFeedTitleWithHTMLEntity(t *testing.T) {
data := `
https://example.org/
Example Feed
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Title != "Example \u00a0 Feed" {
t.Errorf(`Incorrect title, got: %q`, feed.Title)
}
}
func TestParseFeedTitleWithUnicodeEntityAndCdata(t *testing.T) {
data := `
https://example.org/
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Title != `Jenny’s Newsletter` {
t.Errorf(`Incorrect title, got: %q`, feed.Title)
}
}
func TestParseItemTitleWithHTMLEntity(t *testing.T) {
data := `
https://example.org/
Example
-
</example>
http://www.example.org/entries/1
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Title != "" {
t.Errorf(`Incorrect title, got: %q`, feed.Entries[0].Title)
}
}
func TestParseItemTitleWithNumericCharacterReference(t *testing.T) {
data := `
https://example.org/
Example
-
Σ ß
http://www.example.org/article.html
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Title != "Σ ß" {
t.Errorf(`Incorrect title, got: %q`, feed.Entries[0].Title)
}
}
func TestParseItemTitleWithDoubleEncodedEntities(t *testing.T) {
data := `
https://example.org/
Example
-
'Text'
http://www.example.org/article.html
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.Entries[0].Title != "'Text'" {
t.Errorf(`Incorrect title, got: %q`, feed.Entries[0].Title)
}
}
func TestParseFeedLinkWithInvalidCharacterEntity(t *testing.T) {
data := `
https://example.org/a&b
Example Feed
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.SiteURL != "https://example.org/a&b" {
t.Errorf(`Incorrect url, got: %q`, feed.SiteURL)
}
}
func TestParseEntryWithMediaGroup(t *testing.T) {
data := `
My Example Feed
https://example.org
-
Example Item
http://www.example.org/entries/1
nonadult
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
}
if len(feed.Entries[0].Enclosures) != 6 {
t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures))
}
expectedResults := []struct {
url string
mimeType string
size int64
}{
{"https://example.org/image.jpg", "image/*", 0},
{"https://example.org/file3.torrent", "application/x-bittorrent", 670053113},
{"https://example.org/file1.torrent", "application/x-bittorrent", 0},
{"https://example.org/file2.torrent", "application/x-bittorrent", 0},
{"https://example.org/file4.torrent", "application/x-bittorrent", 0},
{"https://example.org/file5.torrent", "application/x-bittorrent", 42},
}
for index, enclosure := range feed.Entries[0].Enclosures {
if expectedResults[index].url != enclosure.URL {
t.Errorf(`Unexpected enclosure URL, got %q instead of %q`, enclosure.URL, expectedResults[index].url)
}
if expectedResults[index].mimeType != enclosure.MimeType {
t.Errorf(`Unexpected enclosure type, got %q instead of %q`, enclosure.MimeType, expectedResults[index].mimeType)
}
if expectedResults[index].size != enclosure.Size {
t.Errorf(`Unexpected enclosure size, got %d instead of %d`, enclosure.Size, expectedResults[index].size)
}
}
}
func TestParseEntryWithMediaContent(t *testing.T) {
data := `
My Example Feed
https://example.org
-
Example Item
http://www.example.org/entries/1
Some Title for Media 1
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Fatalf("Incorrect number of entries, got: %d", len(feed.Entries))
}
if len(feed.Entries[0].Enclosures) != 4 {
t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures))
}
expectedResults := []struct {
url string
mimeType string
size int64
}{
{"https://example.org/thumbnail.jpg", "image/*", 0},
{"https://example.org/thumbnail.jpg", "image/*", 0},
{"https://example.org/media1.jpg", "image/*", 0},
{"https://example.org/media2.jpg", "image/*", 0},
}
for index, enclosure := range feed.Entries[0].Enclosures {
if expectedResults[index].url != enclosure.URL {
t.Errorf(`Unexpected enclosure URL, got %q instead of %q`, enclosure.URL, expectedResults[index].url)
}
if expectedResults[index].mimeType != enclosure.MimeType {
t.Errorf(`Unexpected enclosure type, got %q instead of %q`, enclosure.MimeType, expectedResults[index].mimeType)
}
if expectedResults[index].size != enclosure.Size {
t.Errorf(`Unexpected enclosure size, got %d instead of %d`, enclosure.Size, expectedResults[index].size)
}
}
}
func TestParseEntryWithMediaPeerLink(t *testing.T) {
data := `
My Example Feed
https://website.example.org
-
Example Item
http://www.example.org/entries/1
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Fatalf("Incorrect number of entries, got: %d", len(feed.Entries))
}
if len(feed.Entries[0].Enclosures) != 2 {
t.Fatalf("Incorrect number of enclosures, got: %d", len(feed.Entries[0].Enclosures))
}
expectedResults := []struct {
url string
mimeType string
size int64
}{
{"https://www.example.org/file.torrent", "application/x-bittorrent", 0},
{"https://website.example.org/file2.torrent", "application/x-bittorrent", 0},
}
for index, enclosure := range feed.Entries[0].Enclosures {
if expectedResults[index].url != enclosure.URL {
t.Errorf(`Unexpected enclosure URL, got %q instead of %q`, enclosure.URL, expectedResults[index].url)
}
if expectedResults[index].mimeType != enclosure.MimeType {
t.Errorf(`Unexpected enclosure type, got %q instead of %q`, enclosure.MimeType, expectedResults[index].mimeType)
}
if expectedResults[index].size != enclosure.Size {
t.Errorf(`Unexpected enclosure size, got %d instead of %d`, enclosure.Size, expectedResults[index].size)
}
}
}
func TestParseItunesDuration(t *testing.T) {
data := `
Podcast Example
http://www.example.com/index.html
-
Podcast Episode
http://example.com/episode.m4a
Tue, 08 Mar 2016 12:00:00 GMT
1:23:45
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := 83
result := feed.Entries[0].ReadingTime
if expected != result {
t.Errorf(`Unexpected podcast duration, got %d instead of %d`, result, expected)
}
}
func TestParseIncorrectItunesDuration(t *testing.T) {
data := `
Podcast Example
http://www.example.com/index.html
-
Podcast Episode
http://example.com/episode.m4a
Tue, 08 Mar 2016 12:00:00 GMT
invalid
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
expected := 0
result := feed.Entries[0].ReadingTime
if expected != result {
t.Errorf(`Unexpected podcast duration, got %d instead of %d`, result, expected)
}
}
func TestEntryDescriptionFromItunesSummary(t *testing.T) {
data := `
Podcast Example
http://www.example.com/index.html
-
Podcast Episode
http://example.com/episode.m4a
Tue, 08 Mar 2016 12:00:00 GMT
Episode Subtitle
Episode Summary
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
}
expected := "Episode Summary"
result := feed.Entries[0].Content
if expected != result {
t.Errorf(`Unexpected podcast content, got %q instead of %q`, result, expected)
}
}
func TestEntryDescriptionFromItunesSubtitle(t *testing.T) {
data := `
Podcast Example
http://www.example.com/index.html
-
Podcast Episode
http://example.com/episode.m4a
Tue, 08 Mar 2016 12:00:00 GMT
Episode Subtitle
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
}
expected := "Episode Subtitle"
result := feed.Entries[0].Content
if expected != result {
t.Errorf(`Unexpected podcast content, got %q instead of %q`, result, expected)
}
}
func TestEntryDescriptionFromGooglePlayDescription(t *testing.T) {
data := `
Podcast Example
http://www.example.com/index.html
-
Podcast Episode
http://example.com/episode.m4a
Tue, 08 Mar 2016 12:00:00 GMT
Episode Subtitle
Episode Description
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
}
expected := "Episode Description"
result := feed.Entries[0].Content
if expected != result {
t.Errorf(`Unexpected podcast content, got %q instead of %q`, result, expected)
}
}
func TestParseEntryWithRSSDescriptionAndMediaDescription(t *testing.T) {
data := `
Podcast Example
http://www.example.com/index.html
-
Entry Title
http://www.example.com/entries/1
Entry Description
Media Description
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries) != 1 {
t.Errorf("Incorrect number of entries, got: %d", len(feed.Entries))
}
expected := "Entry Description"
result := feed.Entries[0].Content
if expected != result {
t.Errorf(`Unexpected description, got %q instead of %q`, result, expected)
}
}
func TestParseFeedWithCategories(t *testing.T) {
data := `
Example
https://example.org/
Category 1
-
Test
https://example.org/item
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries[0].Tags) != 2 {
t.Errorf("Incorrect number of tags, got: %d", len(feed.Entries[0].Tags))
}
expected := []string{"Category 1", "Category 2"}
result := feed.Entries[0].Tags
for i, tag := range result {
if tag != expected[i] {
t.Errorf("Incorrect tag, got: %q", tag)
}
}
}
func TestParseEntryWithCategories(t *testing.T) {
data := `
Example
https://example.org/
Category 3
-
Test
https://example.org/item
Category 1
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries[0].Tags) != 2 {
t.Fatalf("Incorrect number of tags, got: %d", len(feed.Entries[0].Tags))
}
expected := []string{"Category 1", "Category 2"}
result := feed.Entries[0].Tags
for i, tag := range result {
if tag != expected[i] {
t.Errorf("Incorrect tag, got: %q", tag)
}
}
}
func TestParseFeedWithItunesCategories(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 len(feed.Entries[0].Tags) != 4 {
t.Errorf("Incorrect number of tags, got: %d", len(feed.Entries[0].Tags))
}
expected := []string{"Society & Culture", "Documentary", "Health", "Mental Health"}
result := feed.Entries[0].Tags
for i, tag := range result {
if tag != expected[i] {
t.Errorf("Incorrect tag, got: %q", tag)
}
}
}
func TestParseFeedWithGooglePlayCategory(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 len(feed.Entries[0].Tags) != 1 {
t.Errorf("Incorrect number of tags, got: %d", len(feed.Entries[0].Tags))
}
expected := []string{"Art"}
result := feed.Entries[0].Tags
for i, tag := range result {
if tag != expected[i] {
t.Errorf("Incorrect tag, got: %q", tag)
}
}
}
func TestParseEntryWithMediaCategories(t *testing.T) {
data := `
Example
https://example.org/
-
Test
https://example.org/item
visual_art
music/artist/album/song
ycantpark mobile
Arts/Movies/Titles/A/Ace_Ventura_Series/Ace_Ventura_ -_Pet_Detective
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if len(feed.Entries[0].Tags) != 2 {
t.Errorf("Incorrect number of tags, got: %d", len(feed.Entries[0].Tags))
}
expected := []string{"Visual Art", "Ace Ventura - Pet Detective"}
result := feed.Entries[0].Tags
for i, tag := range result {
if tag != expected[i] {
t.Errorf("Incorrect tag, got: %q", tag)
}
}
}
func TestParseFeedWithTTLField(t *testing.T) {
data := `
Example
https://example.org/
60
-
Test
https://example.org/item
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.TTL != 60 {
t.Errorf("Incorrect TTL, got: %d", feed.TTL)
}
}
func TestParseFeedWithIncorrectTTLValue(t *testing.T) {
data := `
Example
https://example.org/
invalid
-
Test
https://example.org/item
`
feed, err := Parse("https://example.org/", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err)
}
if feed.TTL != 0 {
t.Errorf("Incorrect TTL, got: %d", feed.TTL)
}
}