clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

feat/tinyagent
tinyrelay / tinyclient / podcast_rss_native_test.go
podcast_rss_native_test.go 3.4K
1package tinyclient23import (4 "encoding/xml"5 "net/http"6 "net/http/httptest"7 "strings"8 "testing"910 "github.com/FelineStateMachine/tinyrelay/internal/event"11 "github.com/FelineStateMachine/tinyrelay/internal/podcasts"12 "github.com/FelineStateMachine/tinyrelay/internal/policy"13)1415func TestPodcastRSSUsesShowMetadataAndNativeShowNotes(t *testing.T) {16 key := strings.Repeat("a", 64)17 episode := event.Event{ID: strings.Repeat("b", 64), PubKey: key, Kind: 54, CreatedAt: 1700000000, Content: "## Links\n\nA **good** conversation.", Tags: [][]string{{"title", "Episode & one"}, {"description", "An <introduction>"}, {"audio", "https://example.test/one.mp3", "audio/mpeg"}, {"audio", "https://example.test/one.ogg", "audio/ogg"}}}18 show := podcasts.Show{ID: "metadata", Pubkey: key, Title: "Trail & Radio", Description: "A show about walking", Image: "https://example.test/cover.png", Websites: []string{"https://example.test/podcast"}}19 b := &podcastRSSBackend{fakeBackend: fakeBackend{policy: policy.Defaults(key)}, result: map[string]any{"show": show, "items": []any{map[string]any{"id": episode.ID, "kind": 54, "pubkey": key, "event": episode, "podcast": show}}}}20 a := &App{backend: b}21 w := httptest.NewRecorder()22 a.podcastRSS(w, httptest.NewRequest(http.MethodGet, "/social/podcasts.rss?author="+key, nil))23 var feed struct {24 Channel struct {25 Title, Description, Link string26 Items []struct {27 GUID string `xml:"guid"`28 Enclosures []struct {29 URL string `xml:"url,attr"`30 } `xml:"enclosure"`31 Content string `xml:"http://purl.org/rss/1.0/modules/content/ encoded"`32 } `xml:"item"`33 } `xml:"channel"`34 }35 // Lowercase RSS element names are required.36 var document podcastRSS37 if err := xml.Unmarshal(w.Body.Bytes(), &document); err != nil {38 t.Fatal(err)39 }40 if document.Channel.Title != show.Title || document.Channel.Description != show.Description || !strings.Contains(w.Body.String(), "<link>"+show.Websites[0]+"</link>") {41 t.Fatalf("show metadata: %#v", document.Channel)42 }43 if err := xml.Unmarshal(w.Body.Bytes(), &feed); err != nil {44 t.Fatal(err)45 }46 if len(feed.Channel.Items) != 1 || len(feed.Channel.Items[0].Enclosures) != 1 || feed.Channel.Items[0].Enclosures[0].URL != "https://example.test/one.mp3" || feed.Channel.Items[0].GUID != episode.ID {47 t.Fatalf("RSS episode: %s", w.Body.String())48 }49 if !strings.Contains(feed.Channel.Items[0].Content, "<strong>good</strong>") || !strings.Contains(w.Body.String(), `itunes:image href="https://example.test/cover.png"`) {50 t.Fatalf("artwork/show notes: %s", w.Body.String())51 }52}5354func TestPodcastRSSRejectsAudioNotesAndOmitsEmptyArtwork(t *testing.T) {55 for _, kind := range []int{1, 30023} {56 _, ok := podcastFeedItem("https://relay.test", map[string]any{"id": "ordinary", "kind": kind, "tags": [][]string{{"audio", "https://example.test/one.mp3"}, {"imeta", "url https://example.test/one.mp3", "m audio/mpeg"}}})57 if ok {58 t.Fatalf("kind %d became a podcast", kind)59 }60 }61 b := &podcastRSSBackend{fakeBackend: fakeBackend{policy: policy.Defaults(strings.Repeat("a", 64))}, result: map[string]any{"show": podcasts.Show{Pubkey: strings.Repeat("a", 64)}}}62 w := httptest.NewRecorder()63 (&App{backend: b}).podcastRSS(w, httptest.NewRequest(http.MethodGet, "/social/podcasts.rss", nil))64 if strings.Contains(w.Body.String(), "itunes:image") || strings.Contains(w.Body.String(), "<description></description>") {65 t.Fatalf("empty optional metadata: %s", w.Body.String())66 }67}