git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/feeds/feed_test.go (about)

     1  package feeds
     2  
     3  import (
     4  	"bytes"
     5  	"testing"
     6  	"time"
     7  )
     8  
     9  var atomOutput = `<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
    10    <title>jmoiron.net blog</title>
    11    <id>http://jmoiron.net/blog</id>
    12    <updated>2013-01-16T21:52:35-05:00</updated>
    13    <rights>This work is copyright © Benjamin Button</rights>
    14    <subtitle>discussion about tech, footie, photos</subtitle>
    15    <link href="http://jmoiron.net/blog"></link>
    16    <author>
    17      <name>Jason Moiron</name>
    18      <email>jmoiron@jmoiron.net</email>
    19    </author>
    20    <entry>
    21      <title>Limiting Concurrency in Go</title>
    22      <updated>2013-01-16T21:52:35-05:00</updated>
    23      <id>tag:jmoiron.net,2013-01-16:/blog/limiting-concurrency-in-go/</id>
    24      <content type="html">&lt;p&gt;Go&#39;s goroutines make it easy to make &lt;a href=&#34;http://collectiveidea.com/blog/archives/2012/12/03/playing-with-go-embarrassingly-parallel-scripts/&#34;&gt;embarrassingly parallel programs&lt;/a&gt;, but in many &amp;quot;real world&amp;quot; cases resources can be limited and attempting to do everything at once can exhaust your access to them.&lt;/p&gt;</content>
    25      <link href="http://jmoiron.net/blog/limiting-concurrency-in-go/" rel="alternate"></link>
    26      <summary type="html">A discussion on controlled parallelism in golang</summary>
    27      <author>
    28        <name>Jason Moiron</name>
    29        <email>jmoiron@jmoiron.net</email>
    30      </author>
    31    </entry>
    32    <entry>
    33      <title>Logic-less Template Redux</title>
    34      <updated>2013-01-16T21:52:35-05:00</updated>
    35      <id>tag:jmoiron.net,2013-01-16:/blog/logicless-template-redux/</id>
    36      <link href="http://jmoiron.net/blog/logicless-template-redux/" rel="alternate"></link>
    37      <summary type="html">More thoughts on logicless templates</summary>
    38    </entry>
    39    <entry>
    40      <title>Idiomatic Code Reuse in Go</title>
    41      <updated>2013-01-16T21:52:35-05:00</updated>
    42      <id>tag:jmoiron.net,2013-01-16:/blog/idiomatic-code-reuse-in-go/</id>
    43      <link href="http://jmoiron.net/blog/idiomatic-code-reuse-in-go/" rel="alternate"></link>
    44      <link href="http://example.com/cover.jpg" rel="enclosure" type="image/jpg" length="123456"></link>
    45      <summary type="html">How to use interfaces &lt;em&gt;effectively&lt;/em&gt;</summary>
    46    </entry>
    47    <entry>
    48      <title>Never Gonna Give You Up Mp3</title>
    49      <updated>2013-01-16T21:52:35-05:00</updated>
    50      <id>tag:example.com,2013-01-16:/RickRoll.mp3</id>
    51      <link href="http://example.com/RickRoll.mp3" rel="alternate"></link>
    52      <link href="http://example.com/RickRoll.mp3" rel="enclosure" type="audio/mpeg" length="123456"></link>
    53      <summary type="html">Never gonna give you up - Never gonna let you down.</summary>
    54    </entry>
    55    <entry>
    56      <title>String formatting in Go</title>
    57      <updated>2013-01-16T21:52:35-05:00</updated>
    58      <id>tag:example.com,2013-01-16:/strings</id>
    59      <link href="http://example.com/strings" rel="alternate"></link>
    60      <summary type="html">How to use things like %s, %v, %d, etc.</summary>
    61    </entry>
    62  </feed>`
    63  
    64  var rssOutput = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    65    <channel>
    66      <title>jmoiron.net blog</title>
    67      <link>http://jmoiron.net/blog</link>
    68      <description>discussion about tech, footie, photos</description>
    69      <copyright>This work is copyright © Benjamin Button</copyright>
    70      <managingEditor>jmoiron@jmoiron.net (Jason Moiron)</managingEditor>
    71      <pubDate>Wed, 16 Jan 2013 21:52:35 -0500</pubDate>
    72      <item>
    73        <title>Limiting Concurrency in Go</title>
    74        <link>http://jmoiron.net/blog/limiting-concurrency-in-go/</link>
    75        <description>A discussion on controlled parallelism in golang</description>
    76        <content:encoded><![CDATA[<p>Go's goroutines make it easy to make <a href="http://collectiveidea.com/blog/archives/2012/12/03/playing-with-go-embarrassingly-parallel-scripts/">embarrassingly parallel programs</a>, but in many &quot;real world&quot; cases resources can be limited and attempting to do everything at once can exhaust your access to them.</p>]]></content:encoded>
    77        <author>Jason Moiron</author>
    78        <pubDate>Wed, 16 Jan 2013 21:52:35 -0500</pubDate>
    79      </item>
    80      <item>
    81        <title>Logic-less Template Redux</title>
    82        <link>http://jmoiron.net/blog/logicless-template-redux/</link>
    83        <description>More thoughts on logicless templates</description>
    84        <pubDate>Wed, 16 Jan 2013 21:52:35 -0500</pubDate>
    85      </item>
    86      <item>
    87        <title>Idiomatic Code Reuse in Go</title>
    88        <link>http://jmoiron.net/blog/idiomatic-code-reuse-in-go/</link>
    89        <description>How to use interfaces &lt;em&gt;effectively&lt;/em&gt;</description>
    90        <enclosure url="http://example.com/cover.jpg" length="123456" type="image/jpg"></enclosure>
    91        <pubDate>Wed, 16 Jan 2013 21:52:35 -0500</pubDate>
    92      </item>
    93      <item>
    94        <title>Never Gonna Give You Up Mp3</title>
    95        <link>http://example.com/RickRoll.mp3</link>
    96        <description>Never gonna give you up - Never gonna let you down.</description>
    97        <enclosure url="http://example.com/RickRoll.mp3" length="123456" type="audio/mpeg"></enclosure>
    98        <pubDate>Wed, 16 Jan 2013 21:52:35 -0500</pubDate>
    99      </item>
   100      <item>
   101        <title>String formatting in Go</title>
   102        <link>http://example.com/strings</link>
   103        <description>How to use things like %s, %v, %d, etc.</description>
   104        <pubDate>Wed, 16 Jan 2013 21:52:35 -0500</pubDate>
   105      </item>
   106    </channel>
   107  </rss>`
   108  
   109  var jsonOutput = `{
   110    "version": "https://jsonfeed.org/version/1.1",
   111    "title": "jmoiron.net blog",
   112    "home_page_url": "http://jmoiron.net/blog",
   113    "description": "discussion about tech, footie, photos",
   114    "author": {
   115      "name": "Jason Moiron"
   116    },
   117    "authors": [
   118      {
   119        "name": "Jason Moiron"
   120      }
   121    ],
   122    "items": [
   123      {
   124        "id": "",
   125        "url": "http://jmoiron.net/blog/limiting-concurrency-in-go/",
   126        "title": "Limiting Concurrency in Go",
   127        "content_html": "\u003cp\u003eGo's goroutines make it easy to make \u003ca href=\"http://collectiveidea.com/blog/archives/2012/12/03/playing-with-go-embarrassingly-parallel-scripts/\"\u003eembarrassingly parallel programs\u003c/a\u003e, but in many \u0026quot;real world\u0026quot; cases resources can be limited and attempting to do everything at once can exhaust your access to them.\u003c/p\u003e",
   128        "summary": "A discussion on controlled parallelism in golang",
   129        "date_published": "2013-01-16T21:52:35-05:00",
   130        "author": {
   131          "name": "Jason Moiron"
   132        },
   133        "authors": [
   134          {
   135            "name": "Jason Moiron"
   136          }
   137        ]
   138      },
   139      {
   140        "id": "",
   141        "url": "http://jmoiron.net/blog/logicless-template-redux/",
   142        "title": "Logic-less Template Redux",
   143        "summary": "More thoughts on logicless templates",
   144        "date_published": "2013-01-16T21:52:35-05:00"
   145      },
   146      {
   147        "id": "",
   148        "url": "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/",
   149        "title": "Idiomatic Code Reuse in Go",
   150        "summary": "How to use interfaces \u003cem\u003eeffectively\u003c/em\u003e",
   151        "image": "http://example.com/cover.jpg",
   152        "date_published": "2013-01-16T21:52:35-05:00"
   153      },
   154      {
   155        "id": "",
   156        "url": "http://example.com/RickRoll.mp3",
   157        "title": "Never Gonna Give You Up Mp3",
   158        "summary": "Never gonna give you up - Never gonna let you down.",
   159        "date_published": "2013-01-16T21:52:35-05:00",
   160        "attachments": [
   161          {
   162            "url": "http://example.com/RickRoll.mp3",
   163            "mime_type": "audio/mpeg",
   164            "size_in_bytes": 123456
   165          }
   166        ]
   167      },
   168      {
   169        "id": "",
   170        "url": "http://example.com/strings",
   171        "title": "String formatting in Go",
   172        "summary": "How to use things like %s, %v, %d, etc.",
   173        "date_published": "2013-01-16T21:52:35-05:00"
   174      }
   175    ]
   176  }`
   177  
   178  func TestFeed(t *testing.T) {
   179  	now, err := time.Parse(time.RFC3339, "2013-01-16T21:52:35-05:00")
   180  	if err != nil {
   181  		t.Error(err)
   182  	}
   183  	tz := time.FixedZone("EST", -5*60*60)
   184  	now = now.In(tz)
   185  
   186  	feed := &Feed{
   187  		Title:       "jmoiron.net blog",
   188  		Link:        &Link{Href: "http://jmoiron.net/blog"},
   189  		Description: "discussion about tech, footie, photos",
   190  		Author:      &Author{Name: "Jason Moiron", Email: "jmoiron@jmoiron.net"},
   191  		Created:     now,
   192  		Copyright:   "This work is copyright © Benjamin Button",
   193  	}
   194  
   195  	feed.Items = []*Item{
   196  		{
   197  			Title:       "Limiting Concurrency in Go",
   198  			Link:        &Link{Href: "http://jmoiron.net/blog/limiting-concurrency-in-go/"},
   199  			Description: "A discussion on controlled parallelism in golang",
   200  			Author:      &Author{Name: "Jason Moiron", Email: "jmoiron@jmoiron.net"},
   201  			Created:     now,
   202  			Content:     `<p>Go's goroutines make it easy to make <a href="http://collectiveidea.com/blog/archives/2012/12/03/playing-with-go-embarrassingly-parallel-scripts/">embarrassingly parallel programs</a>, but in many &quot;real world&quot; cases resources can be limited and attempting to do everything at once can exhaust your access to them.</p>`,
   203  		},
   204  		{
   205  			Title:       "Logic-less Template Redux",
   206  			Link:        &Link{Href: "http://jmoiron.net/blog/logicless-template-redux/"},
   207  			Description: "More thoughts on logicless templates",
   208  			Created:     now,
   209  		},
   210  		{
   211  			Title:       "Idiomatic Code Reuse in Go",
   212  			Link:        &Link{Href: "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/"},
   213  			Description: "How to use interfaces <em>effectively</em>",
   214  			Enclosure:   &Enclosure{Url: "http://example.com/cover.jpg", Length: "123456", Type: "image/jpg"},
   215  			Created:     now,
   216  		},
   217  		{
   218  			Title:       "Never Gonna Give You Up Mp3",
   219  			Link:        &Link{Href: "http://example.com/RickRoll.mp3"},
   220  			Enclosure:   &Enclosure{Url: "http://example.com/RickRoll.mp3", Length: "123456", Type: "audio/mpeg"},
   221  			Description: "Never gonna give you up - Never gonna let you down.",
   222  			Created:     now,
   223  		},
   224  		{
   225  			Title:       "String formatting in Go",
   226  			Link:        &Link{Href: "http://example.com/strings"},
   227  			Description: "How to use things like %s, %v, %d, etc.",
   228  			Created:     now,
   229  		}}
   230  
   231  	atom, err := feed.ToAtom()
   232  	if err != nil {
   233  		t.Errorf("unexpected error encoding Atom: %v", err)
   234  	}
   235  	if atom != atomOutput {
   236  		t.Errorf("Atom not what was expected.  Got:\n%s\n\nExpected:\n%s\n", atom, atomOutput)
   237  	}
   238  	var buf bytes.Buffer
   239  	if err := feed.WriteAtom(&buf); err != nil {
   240  		t.Errorf("unexpected error writing Atom: %v", err)
   241  	}
   242  	if got := buf.String(); got != atomOutput {
   243  		t.Errorf("Atom not what was expected.  Got:\n%s\n\nExpected:\n%s\n", got, atomOutput)
   244  	}
   245  
   246  	rss, err := feed.ToRss()
   247  	if err != nil {
   248  		t.Errorf("unexpected error encoding RSS: %v", err)
   249  	}
   250  	if rss != rssOutput {
   251  		t.Errorf("Rss not what was expected.  Got:\n%s\n\nExpected:\n%s\n", rss, rssOutput)
   252  	}
   253  	buf.Reset()
   254  	if err := feed.WriteRss(&buf); err != nil {
   255  		t.Errorf("unexpected error writing RSS: %v", err)
   256  	}
   257  	if got := buf.String(); got != rssOutput {
   258  		t.Errorf("Rss not what was expected.  Got:\n%s\n\nExpected:\n%s\n", got, rssOutput)
   259  	}
   260  
   261  	json, err := feed.ToJSON()
   262  	if err != nil {
   263  		t.Errorf("unexpected error encoding JSON: %v", err)
   264  	}
   265  	if json != jsonOutput {
   266  		t.Errorf("JSON not what was expected.  Got:\n%s\n\nExpected:\n%s\n", json, jsonOutput)
   267  	}
   268  	buf.Reset()
   269  	if err := feed.WriteJSON(&buf); err != nil {
   270  		t.Errorf("unexpected error writing JSON: %v", err)
   271  	}
   272  	if got := buf.String(); got != jsonOutput+"\n" { //json.Encode appends a newline after the JSON output: https://github.com/golang/go/commit/6f25f1d4c901417af1da65e41992d71c30f64f8f#diff-50848cbd686f250623a2ef6ddb07e157
   273  		t.Errorf("JSON not what was expected.  Got:\n||%s||\n\nExpected:\n||%s||\n", got, jsonOutput)
   274  	}
   275  }
   276  
   277  var atomOutputSorted = `<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
   278    <title>jmoiron.net blog</title>
   279    <id>http://jmoiron.net/blog</id>
   280    <updated>2013-01-16T21:52:35-05:00</updated>
   281    <rights>This work is copyright © Benjamin Button</rights>
   282    <subtitle>discussion about tech, footie, photos</subtitle>
   283    <link href="http://jmoiron.net/blog"></link>
   284    <author>
   285      <name>Jason Moiron</name>
   286      <email>jmoiron@jmoiron.net</email>
   287    </author>
   288    <entry>
   289      <title>Limiting Concurrency in Go</title>
   290      <updated>2013-01-18T21:52:35-05:00</updated>
   291      <id>tag:jmoiron.net,2013-01-18:/blog/limiting-concurrency-in-go/</id>
   292      <link href="http://jmoiron.net/blog/limiting-concurrency-in-go/" rel="alternate"></link>
   293      <summary type="html"></summary>
   294    </entry>
   295    <entry>
   296      <title>Logic-less Template Redux</title>
   297      <updated>2013-01-17T21:52:35-05:00</updated>
   298      <id>tag:jmoiron.net,2013-01-17:/blog/logicless-template-redux/</id>
   299      <link href="http://jmoiron.net/blog/logicless-template-redux/" rel="alternate"></link>
   300      <summary type="html"></summary>
   301    </entry>
   302    <entry>
   303      <title>Idiomatic Code Reuse in Go</title>
   304      <updated>2013-01-17T09:52:35-05:00</updated>
   305      <id>tag:jmoiron.net,2013-01-17:/blog/idiomatic-code-reuse-in-go/</id>
   306      <link href="http://jmoiron.net/blog/idiomatic-code-reuse-in-go/" rel="alternate"></link>
   307      <summary type="html"></summary>
   308    </entry>
   309    <entry>
   310      <title>Never Gonna Give You Up Mp3</title>
   311      <updated>2013-01-17T07:52:35-05:00</updated>
   312      <id>tag:example.com,2013-01-17:/RickRoll.mp3</id>
   313      <link href="http://example.com/RickRoll.mp3" rel="alternate"></link>
   314      <summary type="html"></summary>
   315    </entry>
   316    <entry>
   317      <title>String formatting in Go</title>
   318      <updated>2013-01-16T21:52:35-05:00</updated>
   319      <id>tag:example.com,2013-01-16:/strings</id>
   320      <link href="http://example.com/strings" rel="alternate"></link>
   321      <summary type="html"></summary>
   322    </entry>
   323  </feed>`
   324  
   325  var rssOutputSorted = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
   326    <channel>
   327      <title>jmoiron.net blog</title>
   328      <link>http://jmoiron.net/blog</link>
   329      <description>discussion about tech, footie, photos</description>
   330      <copyright>This work is copyright © Benjamin Button</copyright>
   331      <managingEditor>jmoiron@jmoiron.net (Jason Moiron)</managingEditor>
   332      <pubDate>Wed, 16 Jan 2013 21:52:35 -0500</pubDate>
   333      <item>
   334        <title>Limiting Concurrency in Go</title>
   335        <link>http://jmoiron.net/blog/limiting-concurrency-in-go/</link>
   336        <description></description>
   337        <pubDate>Fri, 18 Jan 2013 21:52:35 -0500</pubDate>
   338      </item>
   339      <item>
   340        <title>Logic-less Template Redux</title>
   341        <link>http://jmoiron.net/blog/logicless-template-redux/</link>
   342        <description></description>
   343        <pubDate>Thu, 17 Jan 2013 21:52:35 -0500</pubDate>
   344      </item>
   345      <item>
   346        <title>Idiomatic Code Reuse in Go</title>
   347        <link>http://jmoiron.net/blog/idiomatic-code-reuse-in-go/</link>
   348        <description></description>
   349        <pubDate>Thu, 17 Jan 2013 09:52:35 -0500</pubDate>
   350      </item>
   351      <item>
   352        <title>Never Gonna Give You Up Mp3</title>
   353        <link>http://example.com/RickRoll.mp3</link>
   354        <description></description>
   355        <pubDate>Thu, 17 Jan 2013 07:52:35 -0500</pubDate>
   356      </item>
   357      <item>
   358        <title>String formatting in Go</title>
   359        <link>http://example.com/strings</link>
   360        <description></description>
   361        <pubDate>Wed, 16 Jan 2013 21:52:35 -0500</pubDate>
   362      </item>
   363    </channel>
   364  </rss>`
   365  
   366  var jsonOutputSorted = `{
   367    "version": "https://jsonfeed.org/version/1.1",
   368    "title": "jmoiron.net blog",
   369    "home_page_url": "http://jmoiron.net/blog",
   370    "description": "discussion about tech, footie, photos",
   371    "author": {
   372      "name": "Jason Moiron"
   373    },
   374    "authors": [
   375      {
   376        "name": "Jason Moiron"
   377      }
   378    ],
   379    "items": [
   380      {
   381        "id": "",
   382        "url": "http://jmoiron.net/blog/limiting-concurrency-in-go/",
   383        "title": "Limiting Concurrency in Go",
   384        "date_published": "2013-01-18T21:52:35-05:00"
   385      },
   386      {
   387        "id": "",
   388        "url": "http://jmoiron.net/blog/logicless-template-redux/",
   389        "title": "Logic-less Template Redux",
   390        "date_published": "2013-01-17T21:52:35-05:00"
   391      },
   392      {
   393        "id": "",
   394        "url": "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/",
   395        "title": "Idiomatic Code Reuse in Go",
   396        "date_published": "2013-01-17T09:52:35-05:00"
   397      },
   398      {
   399        "id": "",
   400        "url": "http://example.com/RickRoll.mp3",
   401        "title": "Never Gonna Give You Up Mp3",
   402        "date_published": "2013-01-17T07:52:35-05:00"
   403      },
   404      {
   405        "id": "",
   406        "url": "http://example.com/strings",
   407        "title": "String formatting in Go",
   408        "date_published": "2013-01-16T21:52:35-05:00"
   409      }
   410    ]
   411  }`
   412  
   413  func TestFeedSorted(t *testing.T) {
   414  	now, err := time.Parse(time.RFC3339, "2013-01-16T21:52:35-05:00")
   415  	if err != nil {
   416  		t.Error(err)
   417  	}
   418  	tz := time.FixedZone("EST", -5*60*60)
   419  	now = now.In(tz)
   420  
   421  	feed := &Feed{
   422  		Title:       "jmoiron.net blog",
   423  		Link:        &Link{Href: "http://jmoiron.net/blog"},
   424  		Description: "discussion about tech, footie, photos",
   425  		Author:      &Author{Name: "Jason Moiron", Email: "jmoiron@jmoiron.net"},
   426  		Created:     now,
   427  		Copyright:   "This work is copyright © Benjamin Button",
   428  	}
   429  
   430  	feed.Items = []*Item{
   431  		{
   432  			Title:   "Limiting Concurrency in Go",
   433  			Link:    &Link{Href: "http://jmoiron.net/blog/limiting-concurrency-in-go/"},
   434  			Created: now.Add(time.Duration(time.Hour * 48)),
   435  		},
   436  		{
   437  			Title:   "Logic-less Template Redux",
   438  			Link:    &Link{Href: "http://jmoiron.net/blog/logicless-template-redux/"},
   439  			Created: now.Add(time.Duration(time.Hour * 24)),
   440  		},
   441  		{
   442  			Title:   "Idiomatic Code Reuse in Go",
   443  			Link:    &Link{Href: "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/"},
   444  			Created: now.Add(time.Duration(time.Hour * 12)),
   445  		},
   446  		{
   447  			Title:   "Never Gonna Give You Up Mp3",
   448  			Link:    &Link{Href: "http://example.com/RickRoll.mp3"},
   449  			Created: now.Add(time.Duration(time.Hour * 10)),
   450  		},
   451  		{
   452  			Title:   "String formatting in Go",
   453  			Link:    &Link{Href: "http://example.com/strings"},
   454  			Created: now,
   455  		}}
   456  
   457  	feed.Sort(func(a, b *Item) bool {
   458  		return a.Created.After(b.Created)
   459  	})
   460  	atom, err := feed.ToAtom()
   461  	if err != nil {
   462  		t.Errorf("unexpected error encoding Atom: %v", err)
   463  	}
   464  	if atom != atomOutputSorted {
   465  		t.Errorf("Atom not what was expected.  Got:\n%s\n\nExpected:\n%s\n", atom, atomOutputSorted)
   466  	}
   467  	var buf bytes.Buffer
   468  	if err := feed.WriteAtom(&buf); err != nil {
   469  		t.Errorf("unexpected error writing Atom: %v", err)
   470  	}
   471  	if got := buf.String(); got != atomOutputSorted {
   472  		t.Errorf("Atom not what was expected.  Got:\n%s\n\nExpected:\n%s\n", got, atomOutputSorted)
   473  	}
   474  
   475  	rss, err := feed.ToRss()
   476  	if err != nil {
   477  		t.Errorf("unexpected error encoding RSS: %v", err)
   478  	}
   479  
   480  	if rss != rssOutputSorted {
   481  		t.Errorf("Rss not what was expected.  Got:\n%s\n\nExpected:\n%s\n", rss, rssOutputSorted)
   482  	}
   483  	buf.Reset()
   484  	if err := feed.WriteRss(&buf); err != nil {
   485  		t.Errorf("unexpected error writing RSS: %v", err)
   486  	}
   487  	if got := buf.String(); got != rssOutputSorted {
   488  		t.Errorf("Rss not what was expected.  Got:\n%s\n\nExpected:\n%s\n", got, rssOutputSorted)
   489  	}
   490  
   491  	json, err := feed.ToJSON()
   492  	if err != nil {
   493  		t.Errorf("unexpected error encoding JSON: %v", err)
   494  	}
   495  	if json != jsonOutputSorted {
   496  		t.Errorf("JSON not what was expected.  Got:\n%s\n\nExpected:\n%s\n", json, jsonOutputSorted)
   497  	}
   498  	buf.Reset()
   499  	if err := feed.WriteJSON(&buf); err != nil {
   500  		t.Errorf("unexpected error writing JSON: %v", err)
   501  	}
   502  	if got := buf.String(); got != jsonOutputSorted+"\n" { //json.Encode appends a newline after the JSON output: https://github.com/golang/go/commit/6f25f1d4c901417af1da65e41992d71c30f64f8f#diff-50848cbd686f250623a2ef6ddb07e157
   503  		t.Errorf("JSON not what was expected.  Got:\n||%s||\n\nExpected:\n||%s||\n", got, jsonOutputSorted)
   504  	}
   505  }
   506  
   507  var jsonOutputHub = `{
   508    "version": "https://jsonfeed.org/version/1.1",
   509    "title": "feed title",
   510    "hubs": [
   511      {
   512        "type": "WebSub",
   513        "url": "https://websub-hub.example"
   514      }
   515    ]
   516  }`
   517  
   518  func TestJSONHub(t *testing.T) {
   519  	feed := &JSONFeed{
   520  		Version: "https://jsonfeed.org/version/1.1",
   521  		Title:   "feed title",
   522  		Hubs: []*JSONHub{
   523  			{
   524  				Type: "WebSub",
   525  				Url:  "https://websub-hub.example",
   526  			},
   527  		},
   528  	}
   529  	json, err := feed.ToJSON()
   530  	if err != nil {
   531  		t.Errorf("unexpected error encoding JSON: %v", err)
   532  	}
   533  	if json != jsonOutputHub {
   534  		t.Errorf("JSON not what was expected.  Got:\n%s\n\nExpected:\n%s\n", json, jsonOutputHub)
   535  	}
   536  }