From 04239ec807f00ba14617c4d723e264f1c18dd6cc Mon Sep 17 00:00:00 2001 From: Chris Sexton Date: Mon, 15 Jul 2019 13:39:40 -0400 Subject: [PATCH] bid: add some tests and tables --- plugins/newsbid/webshit/webshit.go | 106 ++++++++++++++++++++++-- plugins/newsbid/webshit/webshit_test.go | 43 +++++++--- 2 files changed, 131 insertions(+), 18 deletions(-) diff --git a/plugins/newsbid/webshit/webshit.go b/plugins/newsbid/webshit/webshit.go index a06b6eb..67ea1c9 100644 --- a/plugins/newsbid/webshit/webshit.go +++ b/plugins/newsbid/webshit/webshit.go @@ -7,13 +7,31 @@ import ( "github.com/PuerkitoBio/goquery" "github.com/jmoiron/sqlx" "github.com/mmcdole/gofeed" + "github.com/rs/zerolog/log" + "net/http" "net/url" + "strings" ) type Webshit struct { db *sqlx.DB } +type Weekly []string + +type Story struct { + Title string + URL string +} + +type Bid struct { + ID int + User string + Title string + URL string + Bid int +} + func New(db *sqlx.DB) *Webshit { w := &Webshit{db} w.setup() @@ -22,10 +40,26 @@ func New(db *sqlx.DB) *Webshit { // setup will create any necessary SQL tables and populate them with minimal data func (w *Webshit) setup() { + if _, err := w.db.Exec(`create table if not exists webshit_bids ( + id integer primary key, + user string, + title string, + url string, + bid integer + )`); err != nil { + log.Fatal().Err(err) + } + if _, err := w.db.Exec(`create table if not exists webshit_balances ( + user string primary key, + balance int, + score int + )`); err != nil { + log.Fatal().Err(err) + } } // GetHeadlines will return the current possible news headlines for bidding -func (w *Webshit) GetHeadlines() ([]hacknews.Post, error) { +func (w *Webshit) GetHeadlines() ([]Story, error) { news := hacknews.Initializer{Story: "topstories", NbPosts: 10} ids, err := news.GetCodesStory() if err != nil { @@ -35,11 +69,16 @@ func (w *Webshit) GetHeadlines() ([]hacknews.Post, error) { if err != nil { return nil, err } - return posts, nil + var stories []Story + for _, p := range posts { + stories = append(stories, Story{ + Title: p.Title, + URL: p.Url, + }) + } + return stories, nil } -type Weekly []string - // GetWeekly will return the headlines in the last webshit weekly report func (w *Webshit) GetWeekly() (Weekly, error) { fp := gofeed.NewParser() @@ -67,10 +106,63 @@ func (w *Webshit) GetWeekly() (Weekly, error) { // GetBalances returns the current balance for all known users // Any unknown user has a default balance on their first bid -func (w *Webshit) GetBalances() { +func (w *Webshit) GetBalance(user string) int { + q := `select balance from webshit_balances where user=?` + var balance int + err := w.db.Get(&balance, q, user) + if err != nil { + return 100 + } + return balance } // Bid allows a user to place a bid on a particular story -func (w *Webshit) Bid(user string, amount int, URL url.URL) error { - return nil +func (w *Webshit) Bid(user string, amount int, URL string) error { + bal := w.GetBalance(user) + if bal < amount { + return fmt.Errorf("cannot bid more than balance, %d", bal) + } + story, err := w.getStoryByURL(URL) + if err != nil { + return err + } + + // Need a transaction here to deduct from the users balance (or create it) + _, err = w.db.Exec(`insert into webshit_bids (user,title,url,bid) values (?,?,?,?)`, + user, story.Title, story.URL, amount) + + return err +} + +// getStoryByURL scrapes the URL for a title +func (w *Webshit) getStoryByURL(URL string) (Story, error) { + u, err := url.Parse(URL) + if err != nil { + return Story{}, err + } + if u.Host != "news.ycombinator.com" { + return Story{}, fmt.Errorf("expected HN link") + } + res, err := http.Get(URL) + if err != nil { + return Story{}, err + } + defer res.Body.Close() + if res.StatusCode != 200 { + return Story{}, fmt.Errorf("bad response code: %d", res.StatusCode) + } + + // Load the HTML document + doc, err := goquery.NewDocumentFromReader(res.Body) + if err != nil { + return Story{}, err + } + + // Find the review items + title := doc.Find("title").Text() + title = strings.ReplaceAll(title, " | Hacker News", "") + return Story{ + Title: title, + URL: URL, + }, nil } diff --git a/plugins/newsbid/webshit/webshit_test.go b/plugins/newsbid/webshit/webshit_test.go index 4d0c278..bae1ae9 100644 --- a/plugins/newsbid/webshit/webshit_test.go +++ b/plugins/newsbid/webshit/webshit_test.go @@ -2,6 +2,7 @@ package webshit import ( "github.com/jmoiron/sqlx" + "github.com/stretchr/testify/assert" "testing" _ "github.com/mattn/go-sqlite3" @@ -10,21 +11,41 @@ import ( func make(t *testing.T) *Webshit { db := sqlx.MustOpen("sqlite3", "file::memory:?mode=memory&cache=shared") w := New(db) - if w.db != db { - t.Fail() - } + assert.Equal(t, w.db, db) return w } func TestWebshit_GetWeekly(t *testing.T) { w := make(t) weekly, err := w.GetWeekly() - if err != nil { - t.Errorf("Could not get weekly: %s", err) - t.Fail() - } - if len(weekly) < 5 { - t.Errorf("Weekly content:\n%+v", weekly) - t.Fail() - } + assert.Nil(t, err) + assert.NotEmpty(t, weekly) +} + +func TestWebshit_GetHeadlines(t *testing.T) { + w := make(t) + headlines, err := w.GetHeadlines() + assert.Nil(t, err) + assert.NotEmpty(t, headlines) +} + +func TestWebshit_getStoryByURL(t *testing.T) { + w := make(t) + expected := "Developer Tropes: “Google Does It”" + s, err := w.getStoryByURL("https://news.ycombinator.com/item?id=20432887") + assert.Nil(t, err) + assert.Equal(t, s.Title, expected) +} + +func TestWebshit_getStoryByURL_BadURL(t *testing.T) { + w := make(t) + _, err := w.getStoryByURL("https://google.com") + assert.Error(t, err) +} + +func TestWebshit_GetBalance(t *testing.T) { + w := make(t) + expected := 100 + actual := w.GetBalance("foo") + assert.Equal(t, expected, actual) }