all repos

rss-tools @ 1e9eff1

get rss feed from sources that(i need and) dont provide one

rss-tools/app/atom_test.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
telegram: turn links into links; return youtube links in youtube official feed format, 1 month ago
1
package app
2
3
import (
4
	"bytes"
5
	"encoding/xml"
6
	"net/http"
7
	"net/http/httptest"
8
	"strings"
9
	"testing"
10
	"time"
11
12
	"olexsmir.xyz/x/is"
13
)
14
15
func TestFeedBuilderAddEntryDefaults(t *testing.T) {
16
	feed := NewFeed("test", "feed-id")
17
	feed.Add(FeedEntry{Title: "entry", Content: "body"})
18
19
	is.Equal(t, 1, len(feed.f.Entries))
20
	entry := feed.f.Entries[0]
21
	is.NotEqual(t, "", entry.ID)
22
	is.NotEqual(t, "", entry.Updated)
23
}
24
25
func TestFeedBuilderBytesAndWriteTo(t *testing.T) {
26
	updated := time.Date(2026, 4, 20, 12, 30, 0, 0, time.UTC)
27
	feed := NewFeed("test", "feed-id").
28
		WithSubtitle("subtitle").
29
		Add(FeedEntry{Title: "entry", Content: "content", Updated: updated})
30
31
	raw, err := feed.Bytes()
32
	is.Err(t, err, nil)
33
	if !bytes.Contains(raw, []byte("<subtitle>subtitle</subtitle>")) {
34
		t.Fatalf("expected subtitle in serialized feed")
35
	}
36
37
	var parsed AtomFeed
38
	is.Err(t, xml.Unmarshal(raw, &parsed), nil)
39
	is.Equal(t, "test", parsed.Title)
40
}
41
42
func TestFeedBuilderRender(t *testing.T) {
43
	r := httptest.NewRecorder()
44
	err := NewFeed("test", "feed-id").
45
		Add(FeedEntry{
46
			Title:   "entry",
47
			ID:      "entry-id",
48
			Content: "content",
49
			Updated: time.Date(2026, 4, 20, 8, 0, 0, 0, time.UTC),
50
		}).
51
		Render(r)
52
	is.Err(t, err, nil)
53
54
	is.Equal(t, http.StatusOK, r.Code)
55
	if got := r.Header().Get("Content-Type"); !strings.Contains(got, "application/atom+xml") {
56
		t.Fatalf("unexpected content type: %q", got)
57
	}
58
}
59
60
func TestFeedEntryTextContent(t *testing.T) {
61
	feed := NewFeed("test", "feed-id").
62
		Add(FeedEntry{
63
			Title:       "text entry",
64
			Content:     "plain text content",
65
			ContentType: "text",
66
			Updated:     time.Date(2026, 4, 20, 12, 0, 0, 0, time.UTC),
67
		})
68
69
	raw, err := feed.Bytes()
70
	is.Err(t, err, nil)
71
	if !strings.Contains(string(raw), `<content type="text">plain text content</content>`) {
72
		t.Fatalf("expected text content with type attribute in serialized feed")
73
	}
74
75
	var parsed AtomFeed
76
	is.Err(t, xml.Unmarshal(raw, &parsed), nil)
77
	is.Equal(t, 1, len(parsed.Entries))
78
79
	entry := parsed.Entries[0]
80
	is.Equal(t, "text", entry.Content.Type)
81
	is.Equal(t, "plain text content", entry.Content.Value)
82
}
83
84
func TestFeedEntryHtmlContent(t *testing.T) {
85
	htmlContent := "<p>Hello <strong>World</strong></p>"
86
	feed := NewFeed("test", "feed-id").
87
		Add(FeedEntry{
88
			Title:       "html entry",
89
			Content:     htmlContent,
90
			ContentType: "html",
91
			Updated:     time.Date(2026, 4, 20, 12, 0, 0, 0, time.UTC),
92
		})
93
94
	raw, err := feed.Bytes()
95
	is.Err(t, err, nil)
96
	if !strings.Contains(string(raw), `<content type="html">`) {
97
		t.Fatalf("expected HTML content with type='html' attribute in serialized feed")
98
	}
99
100
	var parsed AtomFeed
101
	is.Err(t, xml.Unmarshal(raw, &parsed), nil)
102
	is.Equal(t, 1, len(parsed.Entries))
103
104
	entry := parsed.Entries[0]
105
	is.Equal(t, "html", entry.Content.Type)
106
	is.Equal(t, htmlContent, entry.Content.Value)
107
}
108
109
func TestFeedEntryLinks(t *testing.T) {
110
	feed := NewFeed("test", "feed-id").
111
		Add(FeedEntry{
112
			Title:   "entry",
113
			Content: "hello",
114
			Links: []FeedLink{
115
				{Rel: "alternate", Type: "text/html", Href: "https://example.com/item"},
116
			},
117
			Updated: time.Date(2026, 4, 20, 12, 0, 0, 0, time.UTC),
118
		})
119
120
	raw, err := feed.Bytes()
121
	is.Err(t, err, nil)
122
	if !strings.Contains(string(raw), `<link rel="alternate" type="text/html" href="https://example.com/item"></link>`) {
123
		t.Fatalf("expected link element in serialized feed")
124
	}
125
126
	var parsed AtomFeed
127
	is.Err(t, xml.Unmarshal(raw, &parsed), nil)
128
	is.Equal(t, 1, len(parsed.Entries))
129
	is.Equal(t, 1, len(parsed.Entries[0].Links))
130
	is.Equal(t, "https://example.com/item", parsed.Entries[0].Links[0].Href)
131
}
132
133
func TestFeedMultipleEntriesWithMixedContentTypes(t *testing.T) {
134
	updated := time.Date(2026, 4, 20, 12, 0, 0, 0, time.UTC)
135
	feed := NewFeed("test", "feed-id").
136
		Add(FeedEntry{
137
			Title:       "text entry",
138
			Content:     "plain text",
139
			ContentType: "text",
140
			Updated:     updated,
141
		}).
142
		Add(FeedEntry{
143
			Title:       "html entry",
144
			Content:     "<p>html content</p>",
145
			ContentType: "html",
146
			Updated:     updated,
147
		}).
148
		Add(FeedEntry{
149
			Title:   "default entry",
150
			Content: "default content",
151
			Updated: updated,
152
		})
153
154
	raw, err := feed.Bytes()
155
	is.Err(t, err, nil)
156
157
	var parsed AtomFeed
158
	is.Err(t, xml.Unmarshal(raw, &parsed), nil)
159
	is.Equal(t, 3, len(parsed.Entries))
160
161
	tests := []struct {
162
		name         string
163
		expectedType string
164
		expectedText string
165
	}{
166
		{"text entry", "text", "plain text"},
167
		{"html entry", "html", "<p>html content</p>"},
168
		{"default entry", "text", "default content"},
169
	}
170
	for i, tc := range tests {
171
		is.Equal(t, tc.name, parsed.Entries[i].Title)
172
		is.Equal(t, tc.expectedText, parsed.Entries[i].Content.Value)
173
		is.Equal(t, tc.expectedType, parsed.Entries[i].Content.Type)
174
	}
175
}