yao/rss/rss_test.go

229 lines
8 KiB
Go

package rss
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const testRSS = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>Example Blog</title>
<link>https://example.com</link>
<description>An example blog feed</description>
<language>en-us</language>
<lastBuildDate>Mon, 01 Jan 2024 00:00:00 GMT</lastBuildDate>
<item>
<title>First Post</title>
<link>https://example.com/first</link>
<description>A short summary</description>
<content:encoded><![CDATA[<p>Full content of the first post</p>]]></content:encoded>
<dc:creator>Alice</dc:creator>
<pubDate>Sun, 31 Dec 2023 12:00:00 GMT</pubDate>
<guid isPermaLink="true">https://example.com/first</guid>
<category>Tech</category>
<category>Go</category>
<enclosure url="https://example.com/audio.mp3" type="audio/mpeg" length="12345678"/>
</item>
<item>
<title>Second Post</title>
<link>https://example.com/second</link>
<description>Another summary</description>
<author>bob@example.com</author>
<pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate>
<guid>https://example.com/second</guid>
</item>
</channel>
</rss>`
func TestParseRSS_Basic(t *testing.T) {
feed, err := parseRSS([]byte(testRSS))
require.NoError(t, err)
assert.Equal(t, "rss2.0", feed.Format)
assert.Equal(t, "Example Blog", feed.Title)
assert.Equal(t, "https://example.com", feed.Link)
assert.Equal(t, "An example blog feed", feed.Description)
assert.Equal(t, "en-us", feed.Language)
assert.Equal(t, "Mon, 01 Jan 2024 00:00:00 GMT", feed.Updated)
assert.Nil(t, feed.Podcast, "non-podcast feed should have nil Podcast")
require.Len(t, feed.Items, 2)
// First item
item0 := feed.Items[0]
assert.Equal(t, "First Post", item0.Title)
assert.Equal(t, "https://example.com/first", item0.Link)
assert.Equal(t, "A short summary", item0.Description)
assert.Equal(t, "<p>Full content of the first post</p>", item0.Content)
assert.Equal(t, "Alice", item0.Author) // dc:creator preferred
assert.Equal(t, "Sun, 31 Dec 2023 12:00:00 GMT", item0.Published)
assert.Equal(t, "https://example.com/first", item0.GUID)
assert.Equal(t, []string{"Tech", "Go"}, item0.Categories)
require.Len(t, item0.Enclosures, 1)
assert.Equal(t, "https://example.com/audio.mp3", item0.Enclosures[0].URL)
assert.Equal(t, "audio/mpeg", item0.Enclosures[0].Type)
assert.Equal(t, "12345678", item0.Enclosures[0].Length)
assert.Nil(t, item0.Episode)
// Second item
item1 := feed.Items[1]
assert.Equal(t, "Second Post", item1.Title)
assert.Equal(t, "bob@example.com", item1.Author) // fallback to <author>
assert.Empty(t, item1.Categories)
assert.Empty(t, item1.Enclosures)
}
const testPodcast = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>My Awesome Podcast</title>
<link>https://podcast.example.com</link>
<description>A podcast about technology</description>
<language>en</language>
<itunes:author>Jane Doe</itunes:author>
<itunes:summary>Weekly tech discussions</itunes:summary>
<itunes:image href="https://podcast.example.com/cover.jpg"/>
<itunes:owner>
<itunes:name>Jane Doe</itunes:name>
<itunes:email>jane@example.com</itunes:email>
</itunes:owner>
<itunes:category text="Technology">
<itunes:category text="Podcasting"/>
</itunes:category>
<itunes:category text="Education"/>
<itunes:explicit>no</itunes:explicit>
<itunes:type>episodic</itunes:type>
<item>
<title>Episode 1: Getting Started</title>
<link>https://podcast.example.com/ep1</link>
<description>Our first episode</description>
<enclosure url="https://podcast.example.com/ep1.mp3" type="audio/mpeg" length="50000000"/>
<pubDate>Wed, 15 Nov 2023 08:00:00 GMT</pubDate>
<guid>https://podcast.example.com/ep1</guid>
<itunes:duration>01:23:45</itunes:duration>
<itunes:season>1</itunes:season>
<itunes:episode>1</itunes:episode>
<itunes:episodeType>full</itunes:episodeType>
<itunes:explicit>no</itunes:explicit>
<itunes:image href="https://podcast.example.com/ep1-cover.jpg"/>
<itunes:summary>In this episode we discuss getting started with podcasting</itunes:summary>
</item>
<item>
<title>Trailer</title>
<link>https://podcast.example.com/trailer</link>
<description>Preview of the show</description>
<enclosure url="https://podcast.example.com/trailer.mp3" type="audio/mpeg" length="5000000"/>
<itunes:duration>120</itunes:duration>
<itunes:episodeType>trailer</itunes:episodeType>
<itunes:explicit>yes</itunes:explicit>
</item>
</channel>
</rss>`
func TestParseRSS_Podcast(t *testing.T) {
feed, err := parseRSS([]byte(testPodcast))
require.NoError(t, err)
assert.Equal(t, "rss2.0", feed.Format)
assert.Equal(t, "My Awesome Podcast", feed.Title)
// Podcast metadata
require.NotNil(t, feed.Podcast)
p := feed.Podcast
assert.Equal(t, "Jane Doe", p.Author)
assert.Equal(t, "Weekly tech discussions", p.Summary)
assert.Equal(t, "https://podcast.example.com/cover.jpg", p.Image)
assert.Equal(t, false, p.Explicit)
assert.Equal(t, "episodic", p.Type)
require.NotNil(t, p.Owner)
assert.Equal(t, "Jane Doe", p.Owner.Name)
assert.Equal(t, "jane@example.com", p.Owner.Email)
// Categories: "Technology", "Technology > Podcasting", "Education"
require.Len(t, p.Category, 3)
assert.Equal(t, "Technology", p.Category[0])
assert.Equal(t, "Technology > Podcasting", p.Category[1])
assert.Equal(t, "Education", p.Category[2])
// Episodes
require.Len(t, feed.Items, 2)
ep0 := feed.Items[0]
require.NotNil(t, ep0.Episode)
assert.Equal(t, "01:23:45", ep0.Episode.Duration)
assert.Equal(t, 1, ep0.Episode.Season)
assert.Equal(t, 1, ep0.Episode.Number)
assert.Equal(t, "full", ep0.Episode.Type)
assert.Equal(t, false, ep0.Episode.Explicit)
assert.Equal(t, "https://podcast.example.com/ep1-cover.jpg", ep0.Episode.Image)
assert.Equal(t, "In this episode we discuss getting started with podcasting", ep0.Episode.Summary)
ep1 := feed.Items[1]
require.NotNil(t, ep1.Episode)
assert.Equal(t, "120", ep1.Episode.Duration)
assert.Equal(t, "trailer", ep1.Episode.Type)
assert.Equal(t, true, ep1.Episode.Explicit)
assert.Equal(t, 0, ep1.Episode.Season) // not set
assert.Equal(t, 0, ep1.Episode.Number) // not set
}
func TestParseRSS_MinimalFeed(t *testing.T) {
xml := `<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>Minimal</title>
<link>https://example.com</link>
<description>Bare minimum</description>
</channel>
</rss>`
feed, err := parseRSS([]byte(xml))
require.NoError(t, err)
assert.Equal(t, "Minimal", feed.Title)
assert.Empty(t, feed.Items)
assert.Nil(t, feed.Podcast)
}
func TestParseRSS_InvalidXML(t *testing.T) {
_, err := parseRSS([]byte(`<rss><channel><title>broken`))
assert.Error(t, err)
}
func TestIsExplicit(t *testing.T) {
assert.True(t, isExplicit("yes"))
assert.True(t, isExplicit("Yes"))
assert.True(t, isExplicit("true"))
assert.True(t, isExplicit("explicit"))
assert.False(t, isExplicit("no"))
assert.False(t, isExplicit("false"))
assert.False(t, isExplicit("clean"))
assert.False(t, isExplicit(""))
}
func TestFirstNonEmpty(t *testing.T) {
assert.Equal(t, "a", firstNonEmpty("a", "b"))
assert.Equal(t, "b", firstNonEmpty("", "b"))
assert.Equal(t, "c", firstNonEmpty("", "", "c"))
assert.Equal(t, "", firstNonEmpty("", ""))
assert.Equal(t, "x", firstNonEmpty(" ", " x "))
}
func TestFlattenCategories(t *testing.T) {
cats := []itunesCategory{
{Text: "Technology", Sub: []itunesCategory{{Text: "Podcasting"}}},
{Text: "Education"},
}
result := flattenCategories(cats)
assert.Equal(t, []string{"Technology", "Technology > Podcasting", "Education"}, result)
}
func TestFlattenCategories_Empty(t *testing.T) {
result := flattenCategories(nil)
assert.Nil(t, result)
}