diff --git a/internal/cardsinfo/card.go b/internal/cardsinfo/card.go index b6b2f80..a8c6543 100644 --- a/internal/cardsinfo/card.go +++ b/internal/cardsinfo/card.go @@ -5,26 +5,22 @@ import ( "strings" ) -type CardPrice interface { - Format() string +type scgCardPrice struct { + price string + edition string + link string } -type ScgCardPrice struct { - Price string - Edition string - Link string +func (s *scgCardPrice) format() string { + return fmt.Sprintf("%v: %v\n%v\n", s.edition, s.price, s.link) } -func (s *ScgCardPrice) Format() string { - return fmt.Sprintf("%v: %v\n%v\n", s.Edition, s.Price, s.Link) -} - -type Card struct { +type card struct { Name string `json:"name"` Layout string `json:"layout"` } -func (c *Card) getName() string { +func (c *card) getName() string { if c.Layout == "transform" { return strings.Replace(c.Name, "//", "|", 1) } diff --git a/internal/cardsinfo/format.go b/internal/cardsinfo/format.go index 5def049..3147322 100644 --- a/internal/cardsinfo/format.go +++ b/internal/cardsinfo/format.go @@ -4,11 +4,19 @@ import ( "fmt" ) -func (f *Fetcher) FormatCardPrices(name string, prices []CardPrice) string { +func (f *Fetcher) GetFormattedCardPrices(name string) (string, error) { + prices, err := f.getPrices(name) + if err != nil { + return "", err + } + return f.formatCardPrices(name, prices), nil +} + +func (f *Fetcher) formatCardPrices(name string, prices []scgCardPrice) string { message := fmt.Sprintf("Оригинальное название: %v\n", name) message += fmt.Sprintf("Результатов: %v\n", len(prices)) for i, v := range prices { - message += fmt.Sprintf("%v. %v", i+1, v.Format()) + message += fmt.Sprintf("%v. %v", i+1, v.format()) } return message } diff --git a/internal/cardsinfo/format_test.go b/internal/cardsinfo/format_test.go index 26d0e1c..68b94cb 100644 --- a/internal/cardsinfo/format_test.go +++ b/internal/cardsinfo/format_test.go @@ -2,16 +2,39 @@ package cardsinfo import ( "github.com/stretchr/testify/assert" + "gopkg.in/h2non/gock.v1" + "net/http" + "os" "testing" ) +func TestFetcher_GetFormattedCardPrices_Error(t *testing.T) { + defer gock.Off() + + gock.New(scgSearchUrlTemplate + "card").Reply(http.StatusInternalServerError) + f := &Fetcher{} + _, err := f.GetFormattedCardPrices("card") + assert.NotNil(t, err) +} + +func TestFetcher_GetFormattedCardPrices_Empty(t *testing.T) { + defer gock.Off() + + resp, _ := os.Open("test_data/EmptyTest.html") + gock.New(scgSearchUrlTemplate + "card").Reply(http.StatusOK).Body(resp) + f := &Fetcher{} + msg, err := f.GetFormattedCardPrices("card") + assert.Nil(t, err) + assert.Equal(t, "Оригинальное название: card\nРезультатов: 0\n", msg) +} + func TestFormatCardPrices(t *testing.T) { f := &Fetcher{} - formatted := f.FormatCardPrices("card", []CardPrice{ - &ScgCardPrice{ - Price: "1.5$", - Edition: "ED", - Link: "scg.com", + formatted := f.formatCardPrices("card", []scgCardPrice{ + { + price: "1.5$", + edition: "ED", + link: "scg.com", }, }) assert.Equal(t, "Оригинальное название: card\nРезультатов: 1\n1. ED: 1.5$\nscg.com\n", formatted) diff --git a/internal/cardsinfo/names.go b/internal/cardsinfo/names.go index d2a7ec8..4d3a629 100644 --- a/internal/cardsinfo/names.go +++ b/internal/cardsinfo/names.go @@ -9,26 +9,26 @@ import ( "strings" ) -const ScryfallUrl = "https://api.scryfall.com" +const scryfallUrl = "https://api.scryfall.com" func (f *Fetcher) GetNameByCardId(set string, number string) string { /* Note: number is string because some cards contain letters in their numbers. */ - path := ScryfallUrl + "/cards/" + strings.ToLower(set) + "/" + number - return GetCardByUrl(path) + path := scryfallUrl + "/cards/" + strings.ToLower(set) + "/" + number + return getCardByUrl(path) } func (f *Fetcher) GetOriginalName(name string) string { - path := ScryfallUrl + "/cards/named?fuzzy=" + ApplyFilters(name) - result := GetCardByUrl(path) + path := scryfallUrl + "/cards/named?fuzzy=" + applyFilters(name) + result := getCardByUrl(path) if result == "" && f.Dict != nil { result, _ = dicttranslate.FindFromReader(name, f.Dict, 5) } return result } -func ApplyFilters(name string) string { +func applyFilters(name string) string { /* Despite of the rules of Russian language, letter ё is replaced with e on cards Sometimes it leads to wrong search results @@ -37,13 +37,13 @@ func ApplyFilters(name string) string { return url.QueryEscape(name) } -func GetCardByUrl(path string) string { +func getCardByUrl(path string) string { response, _ := http.Get(path) defer func() { _ = response.Body.Close() }() data, _ := ioutil.ReadAll(response.Body) - var v Card + var v card err := json.Unmarshal(data, &v) if err != nil { return "" diff --git a/internal/cardsinfo/names_test.go b/internal/cardsinfo/names_test.go index f94db60..5208430 100644 --- a/internal/cardsinfo/names_test.go +++ b/internal/cardsinfo/names_test.go @@ -12,7 +12,7 @@ import ( func TestGetNameByCardId(t *testing.T) { defer gock.Off() - gock.New(ScryfallUrl + "/set/1").Reply(http.StatusOK).JSON(Card{ + gock.New(scryfallUrl + "/set/1").Reply(http.StatusOK).JSON(card{ Name: "card", }) f := &Fetcher{} @@ -23,7 +23,7 @@ func TestGetNameByCardId(t *testing.T) { func TestGetOriginalName_Scryfall(t *testing.T) { defer gock.Off() - gock.New(ScryfallUrl + "/cards/named?fuzzy=card").Reply(http.StatusOK).JSON(Card{ + gock.New(scryfallUrl + "/cards/named?fuzzy=card").Reply(http.StatusOK).JSON(card{ Name: "Result Card", }) f := &Fetcher{} @@ -34,7 +34,7 @@ func TestGetOriginalName_Scryfall(t *testing.T) { func TestGetOriginalName_Dict(t *testing.T) { defer gock.Off() - gock.New(ScryfallUrl + "/cards/named?fuzzy=card").Reply(http.StatusOK).JSON(Card{}) + gock.New(scryfallUrl + "/cards/named?fuzzy=card").Reply(http.StatusOK).JSON(card{}) serialized, _ := json.Marshal(map[string]string{ "card": "Card", }) @@ -49,7 +49,7 @@ func TestGetOriginalName_Dict(t *testing.T) { func TestGetOriginalName_BadJson(t *testing.T) { defer gock.Off() - gock.New(ScryfallUrl + "/cards/named?fuzzy=card").Reply(http.StatusOK).BodyString("}") + gock.New(scryfallUrl + "/cards/named?fuzzy=card").Reply(http.StatusOK).BodyString("}") f := &Fetcher{} name := f.GetOriginalName("card") assert.Equal(t, "", name) @@ -58,7 +58,7 @@ func TestGetOriginalName_BadJson(t *testing.T) { func TestGetOriginalName_DoubleSide(t *testing.T) { defer gock.Off() - gock.New(ScryfallUrl + "/cards/named?fuzzy=card").Reply(http.StatusOK).JSON(Card{ + gock.New(scryfallUrl + "/cards/named?fuzzy=card").Reply(http.StatusOK).JSON(card{ Name: "Legion's Landing // Adanto, the First Fort", Layout: "transform", }) diff --git a/internal/cardsinfo/prices.go b/internal/cardsinfo/prices.go index 24300d6..3aefa1d 100644 --- a/internal/cardsinfo/prices.go +++ b/internal/cardsinfo/prices.go @@ -10,8 +10,8 @@ import ( const scgDomain = "https://starcitygames.com" const scgSearchUrlTemplate = "https://starcitygames.hawksearch.com/sites/starcitygames/?search_query=" -func (f *Fetcher) GetPrices(name string) ([]CardPrice, error) { - prices, err := GetPricesScg(name) +func (f *Fetcher) getPrices(name string) ([]scgCardPrice, error) { + prices, err := getPricesScg(name) if err != nil { return nil, err } @@ -21,7 +21,7 @@ func (f *Fetcher) GetPrices(name string) ([]CardPrice, error) { return prices, nil } -func GetPricesScg(name string) ([]CardPrice, error) { +func getPricesScg(name string) ([]scgCardPrice, error) { escapedName := url.QueryEscape(name) searchUrl := scgSearchUrlTemplate + escapedName node, err := htmlquery.LoadURL(searchUrl) @@ -29,23 +29,23 @@ func GetPricesScg(name string) ([]CardPrice, error) { return nil, errors.Wrap(err, "cannot load url") } blocks := htmlquery.Find(node, "//div[@class=\"hawk-results-item\"]") - var results []CardPrice + var results []scgCardPrice for _, block := range blocks { - price := &ScgCardPrice{} + price := scgCardPrice{} linkNode := htmlquery.FindOne(block, "//h2/a") for _, attr := range linkNode.Attr { if attr.Key == "href" { - price.Link = scgDomain + attr.Val + price.link = scgDomain + attr.Val break } } editionNode := htmlquery.FindOne(block, "//p[@class=\"hawk-results-item__category\"]/a") if editionNode.FirstChild != nil { - price.Edition = editionNode.FirstChild.Data + price.edition = editionNode.FirstChild.Data } priceNode := htmlquery.FindOne(block, "//span[@class='hawk-old-price']|//div[contains(concat(' ',normalize-space(@class),' '),' hawk-results-item__options-table-cell--price ')]") if priceNode.FirstChild != nil { - price.Price = priceNode.FirstChild.Data + price.price = priceNode.FirstChild.Data } results = append(results, price) } diff --git a/internal/cardsinfo/prices_test.go b/internal/cardsinfo/prices_test.go index bb6ccfc..3aa2782 100644 --- a/internal/cardsinfo/prices_test.go +++ b/internal/cardsinfo/prices_test.go @@ -15,33 +15,33 @@ func TestGetPrices_Ok(t *testing.T) { file, _ := os.Open("test_data/AcademyRuinsTest.html") gock.New(scgSearchUrlTemplate + "card").Reply(http.StatusOK).Body(file) f := &Fetcher{} - prices, err := f.GetPrices("card") + prices, err := f.getPrices("card") assert.Nil(t, err) - assert.Equal(t, []CardPrice{ - &ScgCardPrice{ - Price: "$6.99", - Edition: "Double Masters", - Link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm-309-enn/?sku=SGL-MTG-2XM-309-ENN1", + assert.Equal(t, []scgCardPrice{ + { + price: "$6.99", + edition: "Double Masters", + link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm-309-enn/?sku=SGL-MTG-2XM-309-ENN1", }, - &ScgCardPrice{ - Price: "$9.99", - Edition: "Double Masters (Foil)", - Link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm-309-enf/?sku=SGL-MTG-2XM-309-ENF1", + { + price: "$9.99", + edition: "Double Masters (Foil)", + link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm-309-enf/?sku=SGL-MTG-2XM-309-ENF1", }, - &ScgCardPrice{ - Price: "$11.99", - Edition: "Double Masters - Variants", - Link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm2-369-enn/?sku=SGL-MTG-2XM2-369-ENN1", + { + price: "$11.99", + edition: "Double Masters - Variants", + link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm2-369-enn/?sku=SGL-MTG-2XM2-369-ENN1", }, - &ScgCardPrice{ - Price: "$14.99", - Edition: "Double Masters - Variants (Foil)", - Link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm2-369-enf/?sku=SGL-MTG-2XM2-369-ENF1", + { + price: "$14.99", + edition: "Double Masters - Variants (Foil)", + link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm2-369-enf/?sku=SGL-MTG-2XM2-369-ENF1", }, - &ScgCardPrice{ - Price: "$7.99", - Edition: "Modern Masters: 2013 Edition", - Link: "https://starcitygames.com/academy-ruins-sgl-mtg-mma-219-enn/?sku=SGL-MTG-MMA-219-ENN1", + { + price: "$7.99", + edition: "Modern Masters: 2013 Edition", + link: "https://starcitygames.com/academy-ruins-sgl-mtg-mma-219-enn/?sku=SGL-MTG-MMA-219-ENN1", }, }, prices) } @@ -51,7 +51,7 @@ func TestGetPrices_Unavailable(t *testing.T) { gock.New(scgSearchUrlTemplate + "card").Reply(http.StatusBadGateway) f := &Fetcher{} - _, err := f.GetPrices("card") + _, err := f.getPrices("card") assert.NotNil(t, err) } @@ -61,7 +61,7 @@ func TestGetPrices_Empty(t *testing.T) { file, _ := os.Open("test_data/EmptyTest.html") gock.New(scgSearchUrlTemplate + "card").Reply(http.StatusOK).Body(file) f := &Fetcher{} - prices, err := f.GetPrices("card") + prices, err := f.getPrices("card") assert.Nil(t, err) assert.Nil(t, prices) } diff --git a/internal/cardsinfo/test_data/AcademyRuinsTest.html b/internal/cardsinfo/test_data/AcademyRuinsTest.html index 16102be..83be959 100644 --- a/internal/cardsinfo/test_data/AcademyRuinsTest.html +++ b/internal/cardsinfo/test_data/AcademyRuinsTest.html @@ -119,7 +119,7 @@