feedwriter_test.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. //
  2. // Copyright (C) 2017-2021 Marcus Rohrmoser, http://purl.mro.name/ShaarliGo
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. //
  17. package main
  18. import (
  19. "os"
  20. "path"
  21. "regexp"
  22. "sort"
  23. "time"
  24. "github.com/stretchr/testify/assert"
  25. "testing"
  26. )
  27. func TestSliceIndices(t *testing.T) {
  28. t.Parallel()
  29. s := []string{"a", "b", "c"}
  30. p := s[0:2]
  31. assert.Equal(t, 2, len(p), "Oha")
  32. assert.Equal(t, "a", p[0], "Oha")
  33. assert.Equal(t, "b", p[1], "Oha")
  34. p = s[2:]
  35. assert.Equal(t, 1, len(p), "Oha")
  36. assert.Equal(t, "c", p[0], "Oha")
  37. }
  38. func TestUriBasics(t *testing.T) {
  39. t.Parallel()
  40. u1 := mustParseURL("b")
  41. assert.Equal(t, "a/b", path.Join("a", u1.Path), "Oha")
  42. assert.Equal(t, "/b", mustParseURL("a").ResolveReference(u1).String(), "Oha")
  43. assert.Equal(t, "/b", mustParseURL(".").ResolveReference(u1).String(), "Oha")
  44. assert.Equal(t, "https://mro.name/b", mustParseURL("https://mro.name").ResolveReference(u1).String(), "Oha")
  45. assert.Equal(t, "https://mro.name/b", mustParseURL("https://mro.name/").ResolveReference(u1).String(), "Oha")
  46. assert.Equal(t, "https://mro.name/b", mustParseURL("https://mro.name/sub").ResolveReference(u1).String(), "Oha")
  47. assert.Equal(t, "https://mro.name/sub/b", mustParseURL("https://mro.name/sub/").ResolveReference(u1).String(), "Oha")
  48. urn := mustParseURL("urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6")
  49. assert.Equal(t, "urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6", urn.String(), "Oha")
  50. assert.Equal(t, "urn", urn.Scheme, "Oha")
  51. assert.Equal(t, "uuid:60a76c80-d399-11d9-b93C-0003939e0af6", urn.Opaque, "Oha")
  52. assert.Equal(t, "", urn.Host, "Oha")
  53. assert.Equal(t, "", urn.Path, "Oha")
  54. assert.Equal(t, "", urn.RawQuery, "Oha")
  55. assert.Equal(t, "", urn.Fragment, "Oha")
  56. assert.Equal(t, "../../..", regexp.MustCompile("[^/]+").ReplaceAllString("a/b/c", ".."), "Oha")
  57. }
  58. func TestComputeLastPage(t *testing.T) {
  59. t.Parallel()
  60. assert.Equal(t, 1, computePageCount(0, 100), "Oha")
  61. assert.Equal(t, 1, computePageCount(1, 100), "Oha")
  62. assert.Equal(t, 1, computePageCount(100, 100), "Oha")
  63. assert.Equal(t, 2, computePageCount(101, 100), "Oha")
  64. }
  65. func TestEntryFeedFilters(t *testing.T) {
  66. t.Parallel()
  67. itm := &Entry{
  68. Id: "id_0",
  69. Published: iso8601(mustParseRFC3339("2010-12-31T00:11:22Z")),
  70. Categories: []Category{{Term: "🐳"}},
  71. }
  72. keys := uriSliceSorted(itm.FeedFilters(nil))
  73. assert.Equal(t, []string{
  74. uriPubDays + "2010-12-31" + "/",
  75. uriPubPosts,
  76. uriPubPosts + "id_0" + "/",
  77. uriPubTags,
  78. uriPubTags + "🐳" + "/",
  79. }, keys, "Oha")
  80. }
  81. func TestPathJoin(t *testing.T) {
  82. t.Parallel()
  83. assert.Equal(t, "a/b", path.Join("a", "b", ""), "Oha")
  84. }
  85. func TestAppendPageNumber(t *testing.T) {
  86. t.Parallel()
  87. s := "abc/"
  88. assert.Equal(t, "/", s[len(s)-1:], "Oha")
  89. assert.Equal(t, uriPub+"/"+uriPosts+"-"+"0"+"/", appendPageNumber(uriPubPosts, 0, 1+1), "Oha")
  90. assert.Equal(t, uriPubPosts, appendPageNumber(uriPubPosts, 1, 1+1), "Oha")
  91. }
  92. func TestWriteFeedsEmpty0(t *testing.T) {
  93. assert.Equal(t, "feed/@xml:base must be set to an absolute URL with a trailing slash but not ''", Server{}.PublishFeedsForModifiedEntries(Feed{}, nil).Error(), "aha")
  94. }
  95. func TestWriteFeedsAddOneAndOneAndRemoveFirst(t *testing.T) {
  96. t.Parallel()
  97. feed := &Feed{XmlBase: Iri("http://example.com/")}
  98. {
  99. entry := &Entry{
  100. Id: "id_0",
  101. Published: iso8601(mustParseRFC3339("2010-12-31T00:11:22Z")),
  102. Categories: []Category{{Term: "🐳"}},
  103. }
  104. feed.Append(entry)
  105. complete := feed.CompleteFeedsForModifiedEntries([]*Entry{entry})
  106. assert.Equal(t, 5, len(complete), "ja")
  107. assert.Equal(t, Id(uriPubDays+"2010-12-31"+"/"), complete[0].Id, "ja")
  108. assert.Equal(t, 1, len(complete[0].Entries), "ja")
  109. assert.Equal(t, Id(uriPubPosts), complete[1].Id, "ja")
  110. assert.Equal(t, 1, len(complete[1].Entries), "ja")
  111. assert.Equal(t, Id(uriPubPosts+"id_0/"), complete[2].Id, "ja")
  112. assert.Equal(t, 1, len(complete[2].Entries), "ja")
  113. assert.Equal(t, Id(uriPubTags), complete[3].Id, "ja")
  114. assert.Equal(t, 0, len(complete[3].Entries), "ja")
  115. assert.Equal(t, Id(uriPubTags+"🐳/"), complete[4].Id, "ja")
  116. assert.Equal(t, 1, len(complete[4].Entries), "ja")
  117. }
  118. {
  119. entry := &Entry{
  120. Id: "id_1",
  121. Published: iso8601(mustParseRFC3339("2010-12-30T00:11:22Z")),
  122. Categories: []Category{{Term: "foo"}},
  123. }
  124. feed.Append(entry)
  125. complete := feed.CompleteFeedsForModifiedEntries([]*Entry{entry})
  126. assert.Equal(t, 5, len(complete), "ja")
  127. assert.Equal(t, Id(uriPubDays+"2010-12-30"+"/"), complete[0].Id, "ja")
  128. assert.Equal(t, 1, len(complete[0].Entries), "ja")
  129. assert.Equal(t, Id(uriPubPosts), complete[1].Id, "ja")
  130. assert.Equal(t, 2, len(complete[1].Entries), "ja")
  131. assert.Equal(t, Id(uriPubPosts+"id_1"+"/"), complete[2].Id, "ja")
  132. assert.Equal(t, 1, len(complete[2].Entries), "ja")
  133. assert.Equal(t, Id(uriPubTags), complete[3].Id, "ja")
  134. assert.Equal(t, 0, len(complete[3].Entries), "ja")
  135. assert.Equal(t, Id(uriPubTags+"foo"+"/"), complete[4].Id, "ja")
  136. assert.Equal(t, 1, len(complete[4].Entries), "ja")
  137. }
  138. {
  139. e0 := *feed.Entries[0]
  140. feed.deleteEntryById("id_0")
  141. complete := feed.CompleteFeedsForModifiedEntries([]*Entry{&e0})
  142. assert.Equal(t, 5, len(complete), "ja")
  143. assert.Equal(t, Id(uriPubDays+"2010-12-31"+"/"), complete[0].Id, "ja")
  144. assert.Equal(t, 0, len(complete[0].Entries), "ja")
  145. assert.Equal(t, Id(uriPubPosts), complete[1].Id, "ja")
  146. assert.Equal(t, 1, len(complete[1].Entries), "ja")
  147. assert.Equal(t, Id(uriPubPosts+"id_0"+"/"), complete[2].Id, "ja")
  148. assert.Equal(t, 0, len(complete[2].Entries), "ja")
  149. assert.Equal(t, Id(uriPubTags), complete[3].Id, "ja")
  150. assert.Equal(t, 0, len(complete[3].Entries), "ja")
  151. assert.Equal(t, Id(uriPubTags+"🐳"+"/"), complete[4].Id, "ja")
  152. assert.Equal(t, 0, len(complete[4].Entries), "ja")
  153. }
  154. }
  155. func TestWriteFeedsPaged(t *testing.T) {
  156. t.Parallel()
  157. feed := &Feed{
  158. XmlBase: Iri("http://example.com/"),
  159. XmlLang: "deu",
  160. Id: Id(mustParseURL("http://example.com").String()),
  161. Title: HumanText{Body: "Hello, Atom!"},
  162. Entries: []*Entry{
  163. {
  164. Id: "e2",
  165. Title: HumanText{Body: "Hello, Entry 2!"},
  166. Published: iso8601(mustParseRFC3339("1990-12-31T02:02:02+01:00")),
  167. },
  168. {
  169. Id: "e1",
  170. Title: HumanText{Body: "Hello, Entry 1!"},
  171. Published: iso8601(mustParseRFC3339("1990-12-31T01:01:01+01:00")),
  172. },
  173. {
  174. Id: "e0",
  175. Title: HumanText{Body: "Hello, Entry 0!"},
  176. Published: iso8601(mustParseRFC3339("1990-12-30T00:00:00+01:00")),
  177. },
  178. },
  179. }
  180. sort.Sort(ByPublishedDesc(feed.Entries))
  181. complete := feed.CompleteFeedsForModifiedEntries(feed.Entries)
  182. assert.Equal(t, 7, len(complete), "uhu")
  183. pages, err := feed.PagedFeeds(complete, 1)
  184. assert.Nil(t, err, "uhu")
  185. assert.Equal(t, 10, len(pages), "uhu")
  186. i := 0
  187. assert.Equal(t, Id(uriPubDays+"1990-12-30/"), pages[i].Id, "ja")
  188. assert.Equal(t, uriPubDays+"1990-12-30/", LinkRelSelf(pages[i].Links).Href, "ja")
  189. assert.Equal(t, 1, len(pages[i].Entries), "ja")
  190. i++
  191. assert.Equal(t, Id(uriPubDays+"1990-12-31/"), pages[i].Id, "ja")
  192. assert.Equal(t, uriPubDays+"1990-12-31"+"-"+"0"+"/", LinkRelSelf(pages[i].Links).Href, "ja")
  193. assert.Equal(t, 1, len(pages[i].Entries), "ja")
  194. i++
  195. assert.Equal(t, Id(uriPubDays+"1990-12-31/"), pages[i].Id, "ja")
  196. assert.Equal(t, uriPubDays+"1990-12-31/", LinkRelSelf(pages[i].Links).Href, "ja")
  197. assert.Equal(t, 1, len(pages[i].Entries), "ja")
  198. i++
  199. assert.Equal(t, Id(uriPubPosts), pages[i].Id, "ja")
  200. assert.Equal(t, uriPub+"/"+uriPosts+"-"+"0"+"/", LinkRelSelf(pages[i].Links).Href, "ja")
  201. assert.Equal(t, 1, len(pages[i].Entries), "ja")
  202. i++
  203. assert.Equal(t, Id(uriPubPosts), pages[i].Id, "ja")
  204. assert.Equal(t, uriPub+"/"+uriPosts+"-"+"1"+"/", LinkRelSelf(pages[i].Links).Href, "ja")
  205. assert.Equal(t, 1, len(pages[i].Entries), "ja")
  206. i++
  207. assert.Equal(t, Id(uriPubPosts), pages[i].Id, "ja")
  208. assert.Equal(t, uriPubPosts, LinkRelSelf(pages[i].Links).Href, "ja")
  209. assert.Equal(t, 1, len(pages[i].Entries), "ja")
  210. i++
  211. assert.Equal(t, Id(uriPubPosts+"e0/"), pages[i].Id, "ja")
  212. assert.Equal(t, uriPubPosts+"e0/", LinkRelSelf(pages[i].Links).Href, "ja")
  213. assert.Equal(t, 1, len(pages[i].Entries), "ja")
  214. i++
  215. assert.Equal(t, Id(uriPubPosts+"e1/"), pages[i].Id, "ja")
  216. assert.Equal(t, uriPubPosts+"e1/", LinkRelSelf(pages[i].Links).Href, "ja")
  217. assert.Equal(t, 1, len(pages[i].Entries), "ja")
  218. i++
  219. assert.Equal(t, Id(uriPubPosts+"e2/"), pages[i].Id, "ja")
  220. assert.Equal(t, uriPubPosts+"e2/", LinkRelSelf(pages[i].Links).Href, "ja")
  221. assert.Equal(t, 1, len(pages[i].Entries), "ja")
  222. i++
  223. assert.Equal(t, Id(uriPubTags), pages[i].Id, "ja")
  224. assert.Equal(t, uriPubTags, LinkRelSelf(pages[i].Links).Href, "ja")
  225. assert.Equal(t, 0, len(pages[i].Entries), "ja")
  226. i++
  227. assert.Equal(t, len(pages), i, "ja")
  228. }
  229. func TestPagedFeeds(t *testing.T) {
  230. t.Parallel()
  231. feed, err := FeedFromFileName("testdata/feedwriter.TestPagedFeeds.pub.atom")
  232. assert.Nil(t, err, "ja")
  233. assert.Equal(t, 5, len(feed.Entries), "ja")
  234. feed.XmlBase = "http://foo.eu/s/"
  235. feeds := feed.CompleteFeedsForModifiedEntries([]*Entry{feed.Entries[0]})
  236. assert.Equal(t, 4, len(feeds), "ja")
  237. assert.Equal(t, Id(uriPubDays+"2018-01-22/"), feeds[0].Id, "ja")
  238. assert.Equal(t, Id(uriPubPosts), feeds[1].Id, "ja")
  239. assert.Equal(t, Id(uriPubPosts+"XsuMcA/"), feeds[2].Id, "ja")
  240. assert.Equal(t, Id(uriPubTags), feeds[3].Id, "ja")
  241. // test low level
  242. assert.Equal(t, uriPubPosts, LinkRelSelf(feeds[1].Pages(100)[0].Links).Href, "ja")
  243. pages := make([]Feed, 0, 2*len(feeds))
  244. for _, comp := range feeds {
  245. pages = append(pages, comp.Pages(100)...)
  246. }
  247. assert.Equal(t, 4, len(pages), "ja")
  248. assert.Equal(t, Id(uriPubPosts), pages[1].Id, "ja")
  249. assert.Equal(t, uriPubPosts, LinkRelSelf(pages[1].Links).Href, "ja")
  250. // high level
  251. pages, err = feed.PagedFeeds(feeds, 100)
  252. assert.Nil(t, err, "ja")
  253. assert.Equal(t, 4, len(pages), "ja")
  254. assert.Equal(t, Id(uriPubDays+"2018-01-22/"), pages[0].Id, "ja")
  255. assert.Equal(t, Id(uriPubPosts), pages[1].Id, "ja")
  256. assert.Equal(t, Id(uriPubPosts+"XsuMcA/"), pages[2].Id, "ja")
  257. assert.Equal(t, Id(uriPubTags), pages[3].Id, "ja")
  258. assert.Equal(t, uriPubDays+"2018-01-22/", LinkRelSelf(pages[0].Links).Href, "ja")
  259. assert.Equal(t, uriPubPosts, LinkRelSelf(pages[1].Links).Href, "ja")
  260. assert.Equal(t, uriPubPosts+"XsuMcA/", LinkRelSelf(pages[2].Links).Href, "ja")
  261. assert.Equal(t, uriPubTags, LinkRelSelf(pages[3].Links).Href, "ja")
  262. pages = feeds[1].Pages(100)
  263. assert.Equal(t, Id(uriPubPosts), pages[0].Id, "ja")
  264. assert.Equal(t, uriPubPosts, LinkRelSelf(pages[0].Links).Href, "ja")
  265. }
  266. func BenchmarkFileStat(b *testing.B) {
  267. t0 := time.Time{}
  268. for i := 0; i < b.N; i++ {
  269. if fi, err := os.Stat("."); (fi != nil && fi.ModTime().Before(t0)) || os.IsNotExist(err) {
  270. // older or missing: (re-)create
  271. }
  272. }
  273. }
  274. /*
  275. func TestWriteFeedsEmpty1(t *testing.T) {
  276. feed := &Feed{
  277. XmlBase: mustParseURL("http://example.com/").String(),
  278. Entries: []*Entry{&Entry{Id: "abcd"}}, // a single, but almost empty, entry
  279. }
  280. sfw := saveFeedWriter{feeds: make(map[string]Feed), entries: make(map[string]Entry), bufs: make(map[string]buff)}
  281. err := feed.writeFeeds(2, sfw)
  282. assert.Nil(t, err, "soso")
  283. assert.Equal(t, []string{uriPubDays+"0001-01-01/", uriPubPosts, uriPubPosts+"abcd/"}, keys4map(sfw.bufs), "soso")
  284. assert.Equal(t, 1472, len(sfw.bufs[uriPubDays+"0001-01-01/"].b), "aha")
  285. assert.Equal(t, `<?xml version="1.0" encoding="UTF-8"?>
  286. <?xml-stylesheet type='text/xsl' href='../../themes/current/posts.xslt'?>
  287. <!--
  288. https://developer.mozilla.org/en/docs/XSL_Transformations_in_Mozilla_FAQ#Why_isn.27t_my_stylesheet_applied.3F
  289. Note that Firefox will override your XSLT stylesheet if your XML is
  290. detected as a RSS or Atom feed. A known workaround is to add a
  291. sufficiently long XML comment to the beginning of your XML file in
  292. order to 'push' the <.feed> or <.rss> tag out of the first 512 bytes,
  293. which is analyzed by Firefox to determine if it's a feed or not. See
  294. the discussion on bug
  295. https://bugzilla.mozilla.org/show_bug.cgi?id=338621#c72 for more
  296. information.
  297. For best results serve both atom feed and xslt as 'text/xml' or
  298. 'application/xml' without charset specified.
  299. -->
  300. <feed xmlns="http://www.w3.org/2005/Atom" xml:base="http://example.com/">
  301. <title></title>
  302. <id></id>
  303. <updated>0001-01-01T00:00:00Z</updated>
  304. <link href=uriPubPosts rel="self" title="1"></link>
  305. <entry xmlns="http://www.w3.org/2005/Atom" xml:base="http://example.com/">
  306. <title></title>
  307. <id>http://example.com/"+uriPubPosts+"abcd/</id>
  308. <updated>0001-01-01T00:00:00Z</updated>
  309. <published>0001-01-01T00:00:00Z</published>
  310. <link href=uriPubPosts+"abcd/" rel="self"></link>
  311. <link href="shaarligo.cgi?post="+uriPubPosts+"abcd/" rel="edit"></link>
  312. <link href="../" rel="up"></link>
  313. </entry>
  314. </feed>
  315. `, string(sfw.bufs[uriPubPosts].b), "soso")
  316. }
  317. func TestWriteFeedsUnpaged(t *testing.T) {
  318. feed := &Feed{
  319. XmlBase: mustParseURL("http://example.com/").String(),
  320. Id: mustParseURL("http://example.com/").String(),
  321. Title: HumanText{Body: "Hello, Atom!"},
  322. Entries: []*Entry{&Entry{
  323. Id: "e0",
  324. Title: HumanText{Body: "Hello, Entry!"},
  325. Updated: iso8601{mustParseRFC3339("1990-12-31T01:02:03+01:00")},
  326. Categories: []Category{Category{Term: "aha"}},
  327. }},
  328. }
  329. sfw := saveFeedWriter{feeds: make(map[string]Feed), entries: make(map[string]Entry), bufs: make(map[string]buff)}
  330. err := feed.writeFeeds(2, sfw)
  331. assert.Nil(t, err, "soso")
  332. assert.Equal(t, []string{
  333. uriPubDays+"1990-12-31/",
  334. uriPubPosts,
  335. uriPubPosts+"e0/",
  336. uriPubTags+"aha/",
  337. }, keys4map(sfw.bufs), "soso")
  338. assert.Equal(t, 1661, len(sfw.bufs[uriPubDays+"1990-12-31/"].b), "aha")
  339. assert.Equal(t, `<?xml version="1.0" encoding="UTF-8"?>
  340. <?xml-stylesheet type='text/xsl' href='../../themes/current/posts.xslt'?>
  341. <!--
  342. https://developer.mozilla.org/en/docs/XSL_Transformations_in_Mozilla_FAQ#Why_isn.27t_my_stylesheet_applied.3F
  343. Note that Firefox will override your XSLT stylesheet if your XML is
  344. detected as a RSS or Atom feed. A known workaround is to add a
  345. sufficiently long XML comment to the beginning of your XML file in
  346. order to 'push' the <.feed> or <.rss> tag out of the first 512 bytes,
  347. which is analyzed by Firefox to determine if it's a feed or not. See
  348. the discussion on bug
  349. https://bugzilla.mozilla.org/show_bug.cgi?id=338621#c72 for more
  350. information.
  351. For best results serve both atom feed and xslt as 'text/xml' or
  352. 'application/xml' without charset specified.
  353. -->
  354. <feed xmlns="http://www.w3.org/2005/Atom" xml:base="http://example.com/">
  355. <title>Hello, Atom!</title>
  356. <id>http://example.com/</id>
  357. <updated>1990-12-31T01:02:03+01:00</updated>
  358. <link href=uriPubPosts rel="self" title="1"></link>
  359. <category term="aha" label="1"></category>
  360. <entry xmlns="http://www.w3.org/2005/Atom" xml:base="http://example.com/">
  361. <title>Hello, Entry!</title>
  362. <id>http://example.com/"+uriPubPosts+"e0/</id>
  363. <updated>1990-12-31T01:02:03+01:00</updated>
  364. <published>0001-01-01T00:00:00Z</published>
  365. <link href=uriPubPosts+"e0/" rel="self"></link>
  366. <link href="shaarligo.cgi?post="+uriPubPosts+"e0/" rel="edit"></link>
  367. <link href="../" rel="up" title="Hello, Atom!"></link>
  368. <category term="aha" scheme="http://example.com/"+uriPubTags></category>
  369. </entry>
  370. </feed>
  371. `, string(sfw.bufs[uriPubPosts].b), "soso")
  372. assert.Equal(t, `<?xml version="1.0" encoding="UTF-8"?>
  373. <?xml-stylesheet type='text/xsl' href='../../../themes/current/posts.xslt'?>
  374. <!--
  375. https://developer.mozilla.org/en/docs/XSL_Transformations_in_Mozilla_FAQ#Why_isn.27t_my_stylesheet_applied.3F
  376. Note that Firefox will override your XSLT stylesheet if your XML is
  377. detected as a RSS or Atom feed. A known workaround is to add a
  378. sufficiently long XML comment to the beginning of your XML file in
  379. order to 'push' the <.feed> or <.rss> tag out of the first 512 bytes,
  380. which is analyzed by Firefox to determine if it's a feed or not. See
  381. the discussion on bug
  382. https://bugzilla.mozilla.org/show_bug.cgi?id=338621#c72 for more
  383. information.
  384. For best results serve both atom feed and xslt as 'text/xml' or
  385. 'application/xml' without charset specified.
  386. -->
  387. <entry xmlns="http://www.w3.org/2005/Atom" xml:base="http://example.com/">
  388. <title>Hello, Entry!</title>
  389. <id>http://example.com/"+uriPubPosts+"e0/</id>
  390. <updated>1990-12-31T01:02:03+01:00</updated>
  391. <published>0001-01-01T00:00:00Z</published>
  392. <link href=uriPubPosts+"e0/" rel="self"></link>
  393. <link href="shaarligo.cgi?post="+uriPubPosts+"e0/" rel="edit"></link>
  394. <link href="../" rel="up" title="Hello, Atom!"></link>
  395. <category term="aha" scheme="http://example.com/"+uriPubTags></category>
  396. </entry>
  397. `, string(sfw.bufs[uriPubPosts+"e0/"].b), "soso")
  398. }
  399. func TestWriteFeedsPaged(t *testing.T) {
  400. feed := &Feed{
  401. XmlBase: mustParseURL("http://example.com/").String(),
  402. XmlLang: "deu",
  403. Id: mustParseURL("http://example.com").String(),
  404. Title: HumanText{Body: "Hello, Atom!"},
  405. Entries: []*Entry{
  406. &Entry{
  407. Id: "e2",
  408. Title: HumanText{Body: "Hello, Entry 2!"},
  409. Updated: iso8601{mustParseRFC3339("1990-12-31T02:02:02+01:00")},
  410. },
  411. &Entry{
  412. Id: "e1",
  413. Title: HumanText{Body: "Hello, Entry 1!"},
  414. Updated: iso8601{mustParseRFC3339("1990-12-31T01:01:01+01:00")},
  415. },
  416. &Entry{
  417. Id: "e0",
  418. Title: HumanText{Body: "Hello, Entry 0!"},
  419. Updated: iso8601{mustParseRFC3339("1990-12-30T00:00:00+01:00")},
  420. },
  421. },
  422. }
  423. sfw := saveFeedWriter{feeds: make(map[string]Feed), entries: make(map[string]Entry), bufs: make(map[string]buff)}
  424. err := feed.writeFeeds(2, sfw)
  425. assert.Nil(t, err, "soso")
  426. assert.Equal(t, []string{
  427. uriPubDays+"1990-12-30/",
  428. uriPubDays+"1990-12-31/",
  429. "pub/posts-1/",
  430. uriPubPosts,
  431. uriPubPosts+"e0/",
  432. uriPubPosts+"e1/",
  433. uriPubPosts+"e2/",
  434. }, keys4map(sfw.bufs), "soso")
  435. assert.Equal(t, `<?xml version="1.0" encoding="UTF-8"?>
  436. <?xml-stylesheet type='text/xsl' href='../../themes/current/posts.xslt'?>
  437. <!--
  438. https://developer.mozilla.org/en/docs/XSL_Transformations_in_Mozilla_FAQ#Why_isn.27t_my_stylesheet_applied.3F
  439. Note that Firefox will override your XSLT stylesheet if your XML is
  440. detected as a RSS or Atom feed. A known workaround is to add a
  441. sufficiently long XML comment to the beginning of your XML file in
  442. order to 'push' the <.feed> or <.rss> tag out of the first 512 bytes,
  443. which is analyzed by Firefox to determine if it's a feed or not. See
  444. the discussion on bug
  445. https://bugzilla.mozilla.org/show_bug.cgi?id=338621#c72 for more
  446. information.
  447. For best results serve both atom feed and xslt as 'text/xml' or
  448. 'application/xml' without charset specified.
  449. -->
  450. <feed xmlns="http://www.w3.org/2005/Atom" xml:base="http://example.com/" xml:lang="deu">
  451. <title>Hello, Atom!</title>
  452. <id>http://example.com</id>
  453. <updated>1990-12-30T00:00:00+01:00</updated>
  454. <link href=uriPubPosts rel="self" title="1"></link>
  455. <link href=uriPubPosts rel="first" title="1"></link>
  456. <link href="pub/posts-1/" rel="next" title="2"></link>
  457. <link href="pub/posts-1/" rel="last" title="2"></link>
  458. <entry xmlns="http://www.w3.org/2005/Atom" xml:base="http://example.com/">
  459. <title>Hello, Entry 0!</title>
  460. <id>http://example.com/"+uriPubPosts+"e0/</id>
  461. <updated>1990-12-30T00:00:00+01:00</updated>
  462. <published>0001-01-01T00:00:00Z</published>
  463. <link href=uriPubPosts+"e0/" rel="self"></link>
  464. <link href="shaarligo.cgi?post="+uriPubPosts+"e0/" rel="edit"></link>
  465. <link href="../" rel="up" title="Hello, Atom!"></link>
  466. </entry>
  467. <entry xmlns="http://www.w3.org/2005/Atom" xml:base="http://example.com/">
  468. <title>Hello, Entry 1!</title>
  469. <id>http://example.com/"+uriPubPosts+"e1/</id>
  470. <updated>1990-12-31T01:01:01+01:00</updated>
  471. <published>0001-01-01T00:00:00Z</published>
  472. <link href=uriPubPosts+"e1/" rel="self"></link>
  473. <link href="shaarligo.cgi?post="+uriPubPosts+"e1/" rel="edit"></link>
  474. <link href="../" rel="up" title="Hello, Atom!"></link>
  475. </entry>
  476. </feed>
  477. `,
  478. string(sfw.bufs[uriPubPosts].b), "page 1")
  479. assert.Equal(t, `<?xml version="1.0" encoding="UTF-8"?>
  480. <?xml-stylesheet type='text/xsl' href='../../themes/current/posts.xslt'?>
  481. <!--
  482. https://developer.mozilla.org/en/docs/XSL_Transformations_in_Mozilla_FAQ#Why_isn.27t_my_stylesheet_applied.3F
  483. Note that Firefox will override your XSLT stylesheet if your XML is
  484. detected as a RSS or Atom feed. A known workaround is to add a
  485. sufficiently long XML comment to the beginning of your XML file in
  486. order to 'push' the <.feed> or <.rss> tag out of the first 512 bytes,
  487. which is analyzed by Firefox to determine if it's a feed or not. See
  488. the discussion on bug
  489. https://bugzilla.mozilla.org/show_bug.cgi?id=338621#c72 for more
  490. information.
  491. For best results serve both atom feed and xslt as 'text/xml' or
  492. 'application/xml' without charset specified.
  493. -->
  494. <feed xmlns="http://www.w3.org/2005/Atom" xml:base="http://example.com/" xml:lang="deu">
  495. <title>Hello, Atom!</title>
  496. <id>http://example.com</id>
  497. <updated>1990-12-30T00:00:00+01:00</updated>
  498. <link href="pub/posts-1/" rel="self" title="2"></link>
  499. <link href=uriPubPosts rel="first" title="1"></link>
  500. <link href=uriPubPosts rel="previous" title="1"></link>
  501. <link href="pub/posts-1/" rel="last" title="2"></link>
  502. <entry xmlns="http://www.w3.org/2005/Atom" xml:base="http://example.com/">
  503. <title>Hello, Entry 2!</title>
  504. <id>http://example.com/"+uriPubPosts+"e2/</id>
  505. <updated>1990-12-31T02:02:02+01:00</updated>
  506. <published>0001-01-01T00:00:00Z</published>
  507. <link href=uriPubPosts+"e2/" rel="self"></link>
  508. <link href="shaarligo.cgi?post="+uriPubPosts+"e2/" rel="edit"></link>
  509. <link href="../" rel="up" title="Hello, Atom!"></link>
  510. </entry>
  511. </feed>
  512. `,
  513. string(sfw.bufs["pub/posts-1/"].b), "page 2")
  514. assert.Equal(t, `<?xml version="1.0" encoding="UTF-8"?>
  515. <?xml-stylesheet type='text/xsl' href='../../../themes/current/posts.xslt'?>
  516. <!--
  517. https://developer.mozilla.org/en/docs/XSL_Transformations_in_Mozilla_FAQ#Why_isn.27t_my_stylesheet_applied.3F
  518. Note that Firefox will override your XSLT stylesheet if your XML is
  519. detected as a RSS or Atom feed. A known workaround is to add a
  520. sufficiently long XML comment to the beginning of your XML file in
  521. order to 'push' the <.feed> or <.rss> tag out of the first 512 bytes,
  522. which is analyzed by Firefox to determine if it's a feed or not. See
  523. the discussion on bug
  524. https://bugzilla.mozilla.org/show_bug.cgi?id=338621#c72 for more
  525. information.
  526. For best results serve both atom feed and xslt as 'text/xml' or
  527. 'application/xml' without charset specified.
  528. -->
  529. <entry xmlns="http://www.w3.org/2005/Atom" xml:base="http://example.com/">
  530. <title>Hello, Entry 0!</title>
  531. <id>http://example.com/"+uriPubPosts+"e0/</id>
  532. <updated>1990-12-30T00:00:00+01:00</updated>
  533. <published>0001-01-01T00:00:00Z</published>
  534. <link href=uriPubPosts+"e0/" rel="self"></link>
  535. <link href="shaarligo.cgi?post="+uriPubPosts+"e0/" rel="edit"></link>
  536. <link href="../" rel="up" title="Hello, Atom!"></link>
  537. </entry>
  538. `,
  539. string(sfw.bufs[uriPubPosts+"e0/"].b), "page 2")
  540. }
  541. func BenchmarkWriteFeedsPaged(b *testing.B) {
  542. for i := 0; i < b.N; i++ {
  543. feed := &Feed{
  544. XmlBase: mustParseURL("http://example.com/").String(),
  545. XmlLang: "deu",
  546. Id: mustParseURL("http://example.com").String(),
  547. Title: HumanText{Body: "Hello, Atom!"},
  548. Entries: []*Entry{
  549. &Entry{
  550. Id: "e2",
  551. Title: HumanText{Body: "Hello, Entry 2!"},
  552. Updated: iso8601{mustParseRFC3339("1990-12-31T02:02:02+01:00")},
  553. },
  554. &Entry{
  555. Id: "e1",
  556. Title: HumanText{Body: "Hello, Entry 1!"},
  557. Updated: iso8601{mustParseRFC3339("1990-12-31T01:01:01+01:00")},
  558. },
  559. &Entry{
  560. Id: "e0",
  561. Title: HumanText{Body: "Hello, Entry 0!"},
  562. Updated: iso8601{mustParseRFC3339("1990-12-30T00:00:00+01:00")},
  563. },
  564. },
  565. }
  566. sfw := saveFeedWriter{feeds: make(map[string]Feed), entries: make(map[string]Entry), bufs: make(map[string]buff)}
  567. feed.writeFeeds(2, sfw)
  568. }
  569. }
  570. */